diff options
author | James E. Blair <jeblair@hp.com> | 2012-02-14 15:54:59 -0800 |
---|---|---|
committer | James E. Blair <jeblair@hp.com> | 2012-02-14 15:57:37 -0800 |
commit | eef1f0d93ae19f04601b75cd7a2514e81b4005b9 (patch) | |
tree | 2b1b8b4a45f884414bd89c4bec7e31056a20a351 /keystone | |
parent | 9452cf04bc8b0a4dc66dc640615d5ace1ca715f2 (diff) | |
parent | 90068b0143af788869116d08533d5ebc99874a17 (diff) | |
download | keystone-eef1f0d93ae19f04601b75cd7a2514e81b4005b9.tar.gz keystone-eef1f0d93ae19f04601b75cd7a2514e81b4005b9.tar.xz keystone-eef1f0d93ae19f04601b75cd7a2514e81b4005b9.zip |
Merge redux branch (keystone light)
Change-Id: I2cb5b198a06848f42f919ea49e338443131e263e
Diffstat (limited to 'keystone')
490 files changed, 4964 insertions, 48276 deletions
diff --git a/keystone/__init__.py b/keystone/__init__.py index e3771d3e..e69de29b 100644 --- a/keystone/__init__.py +++ b/keystone/__init__.py @@ -1,19 +0,0 @@ -# Copyright (C) 2011 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 gettext - -# This installs the _(...) function as a built-in so all other modules -# don't need to. -gettext.install('keystone') diff --git a/keystone/backends/__init__.py b/keystone/backends/__init__.py deleted file mode 100755 index 84d73d04..00000000 --- a/keystone/backends/__init__.py +++ /dev/null @@ -1,56 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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 logging - -from keystone.cfg import NoSuchOptError -from keystone import config -from keystone import utils - -LOG = logging.getLogger(__name__) - -CONF = config.CONF -DEFAULT_BACKENDS = "keystone.backends.sqlalchemy" - -#Configs applicable to all backends. -SHOULD_HASH_PASSWORD = True - - -class GroupConf(CONF.__class__): - """ Allows direct access to the values in the backend groups.""" - def __init__(self, group, *args, **kwargs): - self.group = group - super(GroupConf, self).__init__(*args, **kwargs) - - def __getattr__(self, att): - try: - # pylint: disable=W0212 - return CONF._get(att, self.group) - except NoSuchOptError: - return None - - -def configure_backends(): - """Load backends given in the 'backends' option.""" - global SHOULD_HASH_PASSWORD # pylint: disable=W0603 - SHOULD_HASH_PASSWORD = CONF.hash_password - - backend_names = CONF.backends or DEFAULT_BACKENDS - for module_name in backend_names.split(","): - backend_module = utils.import_module(module_name) - backend_conf = GroupConf(module_name) - backend_module.configure_backend(backend_conf) diff --git a/keystone/backends/api.py b/keystone/backends/api.py deleted file mode 100755 index c44805b3..00000000 --- a/keystone/backends/api.py +++ /dev/null @@ -1,439 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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. - -# pylint: disable=W0603, R0921 - - -#Base APIs -class BaseUserAPI(object): - def __init__(self, *args, **kw): - pass - - def get_all(self): - """ Get all users """ - raise NotImplementedError - - def create(self, values): - """ Create a user - - The backend will assign an ID if is not passed in - - :param values: dict of user attributes (models.User works) - :returns: models.User - the created user object - - """ - raise NotImplementedError - - def get(self, id): - """ Get a user - - :param id: string - the user ID to get - :returns: models.User - the user object - - """ - raise NotImplementedError - - def get_by_name(self, name): - """ Get a user by username - - :param name: string - the user name - :returns: models.User - - """ - raise NotImplementedError - - def get_by_email(self, email): - """ Get a user by email - - :param name: string - the user email - :returns: models.User - - """ - raise NotImplementedError - - def get_page(self, marker, limit): - raise NotImplementedError - - def get_page_markers(self, marker, limit): - raise NotImplementedError - - def user_roles_by_tenant(self, user_id, tenant_id): - raise NotImplementedError - - def update(self, id, values): - """ Update a user - - :param values: dict of user attributes (models.User works) - :returns: models.User - the updated user object - - """ - raise NotImplementedError - - def delete(self, id): - """ Delete a user - - :param id: string - the user id - - """ - raise NotImplementedError - - def get_by_tenant(self, user_id, tenant_id): - """ Gets a user for a tenant - - Same as get user, but also validates the user is related to that tenant - either through the default tenant (user.tenant_id) or by role - - :param user_id: string - id of user - :param tenant_id: string - id of tenant - :returns: models.User - the user object valid on the tenant, othwerwise - None - - """ - raise NotImplementedError - - def get_by_access(self, access): - raise NotImplementedError - - def users_get_by_tenant(self, user_id, tenant_id): - raise NotImplementedError - - def user_role_add(self, values): - """ Adds a user to a role (optionally for a tenant) - 'grant' - - This creates a new UserRoleAssociation based on the passed in values - - :param values: dict of values containing user_id, role_id, and - optionally a tenant_id - - """ - raise NotImplementedError - - def users_get_page(self, marker, limit): - raise NotImplementedError - - def users_get_page_markers(self, marker, limit): - raise NotImplementedError - - def users_get_by_tenant_get_page(self, tenant_id, role_id, marker, limit): - raise NotImplementedError - - def users_get_by_tenant_get_page_markers(self, tenant_id, - role_id, marker, limit): - raise NotImplementedError - - def check_password(self, user_id, password): - """ Check a user password - - The backend should handle any encryption/decryption - - :param user_id: string - user id - :param password: string - the password to check - :returns: True/False - - """ - raise NotImplementedError - - -class BaseTokenAPI(object): - def __init__(self, *args, **kw): - pass - - def create(self, values): - raise NotImplementedError - - def get(self, id): - raise NotImplementedError - - def delete(self, id): - raise NotImplementedError - - def get_for_user(self, user_id): - raise NotImplementedError - - def get_for_user_by_tenant(self, user_id, tenant_id): - raise NotImplementedError - - def get_all(self): - raise NotImplementedError - - -class BaseTenantAPI(object): - def __init__(self, *args, **kw): - pass - - def create(self, values): - raise NotImplementedError - - def get(self, id): - raise NotImplementedError - - def get_by_name(self, name): - raise NotImplementedError - - def get_all(self): - raise NotImplementedError - - def list_for_user_get_page(self, user, marker, limit): - raise NotImplementedError - - def list_for_user_get_page_markers(self, user, marker, limit): - raise NotImplementedError - - def get_page(self, marker, limit): - raise NotImplementedError - - def get_page_markers(self, marker, limit): - raise NotImplementedError - - def update(self, id, values): - raise NotImplementedError - - def delete(self, id): - raise NotImplementedError - - def get_all_endpoints(self, tenant_id): - raise NotImplementedError - - def get_role_assignments(self, tenant_id): - raise NotImplementedError - - -class BaseRoleAPI(object): - def __init__(self, *args, **kw): - pass - - # - # Role Methods - # - def create(self, values): - raise NotImplementedError - - def delete(self, id): - raise NotImplementedError - - def get(self, id): - raise NotImplementedError - - def get_by_name(self, name): - raise NotImplementedError - - def get_by_service(self, service_id): - raise NotImplementedError - - def get_by_service_get_page(self, service_id, marker, limit): - """ Get one page of roles by service""" - raise NotImplementedError - - def get_by_service_get_page_markers(self, service_id, marker, limit): - """ Calculate pagination markers for roles by service """ - raise NotImplementedError - - def get_all(self): - raise NotImplementedError - - def get_page(self, marker, limit): - raise NotImplementedError - - def get_page_markers(self, marker, limit): - raise NotImplementedError - - # - # Role-Grant Methods - # - def rolegrant_get(self, id): - """ Get a UserRoleAssociation (role grant) by id """ - raise NotImplementedError - - def rolegrant_delete(self, id): - """ Delete a UserRoleAssociation (role grant) by id """ - raise NotImplementedError - - def rolegrant_list_by_role(self, id): - """ Get a list of all (global and tenant) grants for this role """ - raise NotImplementedError - - def rolegrant_get_by_ids(self, user_id, role_id, tenant_id): - raise NotImplementedError - - def list_global_roles_for_user(self, user_id): - """ Get a list of all global roles granted to this user. - - :param user_id: string - id of user - - """ - raise NotImplementedError - - def list_tenant_roles_for_user(self, user_id, tenant_id): - """ Get a list of all tenant roles granted to this user. - - :param user_id: string - id of user - :param tenant_id: string - id of tenant - - """ - raise NotImplementedError - - def rolegrant_get_page(self, marker, limit, user_id, tenant_id): - raise NotImplementedError - - def rolegrant_get_page_markers(self, user_id, tenant_id, marker, limit): - raise NotImplementedError - - -class BaseEndpointTemplateAPI(object): - def __init__(self, *args, **kw): - pass - - def create(self, values): - raise NotImplementedError - - def update(self, id, values): - raise NotImplementedError - - def delete(self, id): - raise NotImplementedError - - def get(self, id): - raise NotImplementedError - - def get_all(self): - raise NotImplementedError - - def get_by_service(self, service_id): - raise NotImplementedError - - def get_page(self, marker, limit): - raise NotImplementedError - - def get_page_markers(self, marker, limit): - raise NotImplementedError - - def get_by_service_get_page(self, service_id, marker, limit): - raise NotImplementedError - - def get_by_service_get_page_markers(self, service_id, marker, limit): - raise NotImplementedError - - def endpoint_get_by_tenant_get_page(self, tenant_id, marker, limit): - raise NotImplementedError - - def endpoint_get_by_tenant_get_page_markers(self, tenant_id, marker, - limit): - raise NotImplementedError - - def endpoint_get_by_endpoint_template(self, endpoint_template_id): - raise NotImplementedError - - def endpoint_add(self, values): - raise NotImplementedError - - def endpoint_get(self, id): - raise NotImplementedError - - def endpoint_get_by_tenant(self, tenant_id): - raise NotImplementedError - - def endpoint_delete(self, id): - raise NotImplementedError - - -class BaseServiceAPI(object): - def __init__(self, *args, **kw): - pass - - def create(self, values): - raise NotImplementedError - - def get(self, id): - raise NotImplementedError - - def get_by_name(self, name): - raise NotImplementedError - - def get_by_name_and_type(self, name, type): - raise NotImplementedError - - def get_all(self): - raise NotImplementedError - - def get_page(self, marker, limit): - raise NotImplementedError - - def get_page_markers(self, marker, limit): - raise NotImplementedError - - def delete(self, id): - raise NotImplementedError - - -class BaseCredentialsAPI(object): - def __init__(self, *args, **kw): - pass - - def create(self, values): - raise NotImplementedError - - def update(self, id, credential): - raise NotImplementedError - - def delete(self, id): - raise NotImplementedError - - def get(self, id): - raise NotImplementedError - - def get_all(self): - raise NotImplementedError - - def get_by_access(self, access): - raise NotImplementedError - - -#API -#TODO(Yogi) Refactor all API to separate classes specific to models. -ENDPOINT_TEMPLATE = BaseEndpointTemplateAPI() -ROLE = BaseRoleAPI() -TENANT = BaseTenantAPI() -TOKEN = BaseTokenAPI() -USER = BaseUserAPI() -SERVICE = BaseServiceAPI() -CREDENTIALS = BaseCredentialsAPI() - - -# Function to dynamically set module references. -def set_value(variable_name, value): - if variable_name == 'endpoint_template': - global ENDPOINT_TEMPLATE - ENDPOINT_TEMPLATE = value - elif variable_name == 'role': - global ROLE - ROLE = value - elif variable_name == 'tenant': - global TENANT - TENANT = value - elif variable_name == 'token': - global TOKEN - TOKEN = value - elif variable_name == 'user': - global USER - USER = value - elif variable_name == 'service': - global SERVICE - SERVICE = value - elif variable_name == 'credentials': - global CREDENTIALS - CREDENTIALS = value diff --git a/keystone/backends/backendutils.py b/keystone/backends/backendutils.py deleted file mode 100644 index 3e16f214..00000000 --- a/keystone/backends/backendutils.py +++ /dev/null @@ -1,60 +0,0 @@ -import logging -logger = logging.getLogger(__name__) # pylint: disable=C0103 - -from keystone.backends import models -import keystone.backends as backends -# pylint: disable=E0611 -try: - from passlib.hash import sha512_crypt as sc -except ImportError as exc: - logger.exception(exc) - raise exc - - -def __get_hashed_password(password): - if password: - return __make_password(password) - else: - return None - - -def set_hashed_password(values): - """ - Sets hashed password for password. - """ - if backends.SHOULD_HASH_PASSWORD: - if isinstance(values, dict) and 'password' in values.keys(): - values['password'] = __get_hashed_password(values['password']) - elif isinstance(values, models.User): - values.password = __get_hashed_password(values.password) - else: - logger.warn("Could not hash password on unsupported type: %s" % - type(values)) - - -def check_password(raw_password, enc_password): - """ - Compares raw password and encoded password. - """ - if not raw_password: - return False - if backends.SHOULD_HASH_PASSWORD: - return sc.verify(raw_password, enc_password) - else: - return enc_password == raw_password - - -def __make_password(raw_password): - """ - Produce a new encoded password. - """ - if raw_password is None: - return None - hsh = __get_hexdigest(raw_password) - return '%s' % (hsh) - - -#Refer http://packages.python.org/passlib/lib/passlib.hash.sha512_crypt.html -#Using the default properties as of now.Salt gets generated automatically. -def __get_hexdigest(raw_password): - return sc.encrypt(raw_password) diff --git a/keystone/backends/ldap/__init__.py b/keystone/backends/ldap/__init__.py deleted file mode 100644 index b3e0b31b..00000000 --- a/keystone/backends/ldap/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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 ldap - -import keystone.backends.api as top_api -import keystone.backends.models as top_models -from keystone import utils - -from . import api -from . import models - - -def configure_backend(conf): - api_obj = api.API(conf) - for name in api_obj.apis: - top_api.set_value(name, getattr(api_obj, name)) - for model_name in models.__all__: - top_models.set_value(model_name, getattr(models, model_name)) diff --git a/keystone/backends/ldap/api/__init__.py b/keystone/backends/ldap/api/__init__.py deleted file mode 100644 index c739b4da..00000000 --- a/keystone/backends/ldap/api/__init__.py +++ /dev/null @@ -1,111 +0,0 @@ -import ldap -import logging - -from .. import fakeldap -from .tenant import TenantAPI -from .user import UserAPI -from .role import RoleAPI - -LOG = logging.getLogger('keystone.backends.ldap.api') - - -def py2ldap(val): - if isinstance(val, str): - return val - elif isinstance(val, bool): - return 'TRUE' if val else 'FALSE' - else: - return str(val) - -LDAP_VALUES = { - 'TRUE': True, - 'FALSE': False, -} - - -def ldap2py(val): - try: - return LDAP_VALUES[val] - except KeyError: - pass - try: - return int(val) - except ValueError: - pass - return val - - -def safe_iter(attrs): - if attrs is None: - return - elif isinstance(attrs, list): - for e in attrs: - yield e - else: - yield attrs - - -class LDAPWrapper(object): - def __init__(self, url): - LOG.debug("LDAP init: url=%s", url) - self.conn = ldap.initialize(url) - - def simple_bind_s(self, user, password): - LOG.debug("LDAP bind: dn=%s", user) - return self.conn.simple_bind_s(user, password) - - def add_s(self, dn, attrs): - ldap_attrs = [(typ, map(py2ldap, safe_iter(values))) - for typ, values in attrs] - if LOG.isEnabledFor(logging.DEBUG): - sane_attrs = [(typ, values if typ != 'userPassword' else ['****']) - for typ, values in ldap_attrs] - LOG.debug("LDAP add: dn=%s, attrs=%s", dn, sane_attrs) - return self.conn.add_s(dn, ldap_attrs) - - def search_s(self, dn, scope, query): - if LOG.isEnabledFor(logging.DEBUG): - LOG.debug("LDAP search: dn=%s, scope=%s, query=%s", dn, - fakeldap.scope_names[scope], query) - res = self.conn.search_s(dn, scope, query) - return [(dn, dict([(typ, map(ldap2py, values)) - for typ, values in attrs.iteritems()])) - for dn, attrs in res] - - def modify_s(self, dn, modlist): - ldap_modlist = [(op, typ, None if values is None else - map(py2ldap, safe_iter(values))) - for op, typ, values in modlist] - if LOG.isEnabledFor(logging.DEBUG): - sane_modlist = [(op, typ, values if typ != 'userPassword' - else ['****']) for op, typ, values in ldap_modlist] - LOG.debug("LDAP modify: dn=%s, modlist=%s", dn, sane_modlist) - return self.conn.modify_s(dn, ldap_modlist) - - def delete_s(self, dn): - LOG.debug("LDAP delete: dn=%s", dn) - return self.conn.delete_s(dn) - - -class API(object): - apis = ['tenant', 'user', 'role'] - - def __init__(self, conf): - self.LDAP_URL = conf.ldap_url - self.LDAP_USER = conf.ldap_user - self.LDAP_PASSWORD = conf.ldap_password - self.tenant = TenantAPI(self, conf) - self.user = UserAPI(self, conf) - self.role = RoleAPI(self, conf) - - def get_connection(self, user=None, password=None): - if self.LDAP_URL.startswith('fake://'): - conn = fakeldap.initialize(self.LDAP_URL) - else: - conn = LDAPWrapper(self.LDAP_URL) - if user is None: - user = self.LDAP_USER - if password is None: - password = self.LDAP_PASSWORD - conn.simple_bind_s(user, password) - return conn diff --git a/keystone/backends/ldap/api/base.py b/keystone/backends/ldap/api/base.py deleted file mode 100644 index 1275c08d..00000000 --- a/keystone/backends/ldap/api/base.py +++ /dev/null @@ -1,183 +0,0 @@ -import ast -import ldap -from itertools import izip, count - - -def _get_redirect(cls, method): - # pylint: disable=W0613 - def inner(self, *args): - return getattr(cls(), method)(*args) - return inner - - -def add_redirects(loc, cls, methods): - for method in methods: - loc[method] = _get_redirect(cls, method) - - -class BaseLdapAPI(object): - DEFAULT_TREE_DN = None - DEFAULT_STRUCTURAL_CLASSES = None - DEFAULT_ID_ATTR = 'cn' - DUMB_MEMBER_DN = 'cn=dumb,dc=nonexistent' - options_name = None - object_class = 'top' - model = None - attribute_mapping = {} - attribute_ignore = [] - - def __init__(self, api, conf): - self.api = api - if self.options_name is not None: - dn = '%s_tree_dn' % self.options_name - self.tree_dn = conf[dn] or self.DEFAULT_TREE_DN - structs = '%s_structural_classes' % self.options_name - lst = conf[structs] or self.DEFAULT_STRUCTURAL_CLASSES - self.structural_classes = ast.literal_eval(str(lst)) - idatt = '%s_id_attr' % self.options_name - self.id_attr = conf[idatt] or self.DEFAULT_ID_ATTR - self.use_dumb_member = conf.use_dumb_member or True - - def _id_to_dn(self, id): - return '%s=%s,%s' % (self.id_attr, ldap.dn.escape_dn_chars(str(id)), - self.tree_dn) - - @staticmethod - def _dn_to_id(dn): - return ldap.dn.str2dn(dn)[0][0][1] - - # pylint: disable=E1102 - def _ldap_res_to_model(self, res): - obj = self.model(id=self._dn_to_id(res[0])) - obj['name'] = obj['id'] - for k in obj: - if k in self.attribute_ignore: - continue - try: - v = res[1][self.attribute_mapping.get(k, k)] - except KeyError: - pass - else: - try: - obj[k] = v[0] - except IndexError: - obj[k] = None - return obj - - # pylint: disable=E1102 - def create(self, values): - conn = self.api.get_connection() - object_classes = self.structural_classes + [self.object_class] - attrs = [('objectClass', object_classes)] - for k, v in values.iteritems(): - if k == 'id' or k in self.attribute_ignore: - continue - if v is not None: - attr_type = self.attribute_mapping.get(k, k) - attrs.append((attr_type, [v])) - if 'groupOfNames' in object_classes and self.use_dumb_member: - attrs.append(('member', [self.DUMB_MEMBER_DN])) - conn.add_s(self._id_to_dn(values['id']), attrs) - return self.model(**values) - - def _ldap_get(self, id, filter=None): - conn = self.api.get_connection() - query = '(objectClass=%s)' % (self.object_class,) - if filter is not None: - query = '(&%s%s)' % (filter, query) - try: - res = conn.search_s(self._id_to_dn(id), ldap.SCOPE_BASE, query) - except ldap.NO_SUCH_OBJECT: - return None - try: - return res[0] - except IndexError: - return None - - def _ldap_get_all(self, filter=None): - conn = self.api.get_connection() - query = '(objectClass=%s)' % (self.object_class,) - if filter is not None: - query = '(&%s%s)' % (filter, query) - try: - return conn.search_s(self.tree_dn, ldap.SCOPE_ONELEVEL, query) - except ldap.NO_SUCH_OBJECT: - return [] - - def get(self, id, filter=None): - res = self._ldap_get(id, filter) - if res is None: - return None - else: - return self._ldap_res_to_model(res) - - # pylint: disable=W0141 - def get_all(self, filter=None): - return map(self._ldap_res_to_model, self._ldap_get_all(filter)) - - def get_page(self, marker, limit): - return self._get_page(marker, limit, self.get_all()) - - def get_page_markers(self, marker, limit): - return self._get_page_markers(marker, limit, self.get_all()) - - # pylint: disable=W0141 - @staticmethod - def _get_page(marker, limit, lst, key=lambda e: e.id): - lst.sort(key=key) - if not marker: - return lst[:limit] - else: - return filter(lambda e: key(e) > marker, lst)[:limit] - - @staticmethod - def _get_page_markers(marker, limit, lst, key=lambda e: e.id): - if len(lst) < limit: - return (None, None) - lst.sort(key=key) - if marker is None: - if len(lst) <= limit + 1: - nxt = None - else: - nxt = key(lst[limit]) - return (None, nxt) - - for i, item in izip(count(), lst): - k = key(item) - if k >= marker: - break - # pylint: disable=W0631 - if i <= limit: - prv = None - else: - prv = key(lst[i - limit]) - if i + limit >= len(lst) - 1: - nxt = None - else: - nxt = key(lst[i + limit]) - return (prv, nxt) - - def update(self, id, values, old_obj=None): - if old_obj is None: - old_obj = self.get(id) - modlist = [] - for k, v in values.iteritems(): - if k == 'id' or k in self.attribute_ignore: - continue - if v is None: - if old_obj[k] is not None: - modlist.append((ldap.MOD_DELETE, - self.attribute_mapping.get(k, k), None)) - else: - if old_obj[k] != v: - if old_obj[k] is None: - op = ldap.MOD_ADD - else: - op = ldap.MOD_REPLACE - modlist.append((op, self.attribute_mapping.get(k, k), [v])) - conn = self.api.get_connection() - conn.modify_s(self._id_to_dn(id), modlist) - - def delete(self, id): - conn = self.api.get_connection() - conn.delete_s(self._id_to_dn(id)) diff --git a/keystone/backends/ldap/api/role.py b/keystone/backends/ldap/api/role.py deleted file mode 100644 index 5ec81949..00000000 --- a/keystone/backends/ldap/api/role.py +++ /dev/null @@ -1,288 +0,0 @@ -import ldap - -from keystone.backends.api import BaseTenantAPI -from keystone.common import exception - -from keystone import models -from .base import BaseLdapAPI - - -# pylint: disable=W0212, W0223 -class RoleAPI(BaseLdapAPI, BaseTenantAPI): - DEFAULT_TREE_DN = 'ou=Groups,dc=example,dc=com' - DEFAULT_STRUCTURAL_CLASSES = ['groupOfNames'] - options_name = 'role' - object_class = 'keystoneRole' - model = models.Role - attribute_mapping = {'description': 'desc', 'serviceId': 'service_id'} - - @staticmethod - def _create_ref(role_id, tenant_id, user_id): - role_id = '' if role_id is None else str(role_id) - tenant_id = '' if tenant_id is None else str(tenant_id) - user_id = '' if user_id is None else str(user_id) - return '%d-%d-%s%s%s' % (len(role_id), len(tenant_id), - role_id, tenant_id, user_id) - - @staticmethod - def _explode_ref(rolegrant): - a = rolegrant.split('-', 2) - len_role = int(a[0]) - len_tenant = int(a[1]) - role_id = a[2][:len_role] - role_id = None if len(role_id) == 0 else str(role_id) - tenant_id = a[2][len_role:len_tenant + len_role] - tenant_id = None if len(tenant_id) == 0 else str(tenant_id) - user_id = a[2][len_tenant + len_role:] - user_id = None if len(user_id) == 0 else str(user_id) - return role_id, tenant_id, user_id - - def _subrole_id_to_dn(self, role_id, tenant_id): - if tenant_id is None: - return self._id_to_dn(role_id) - else: - return "cn=%s,%s" % (ldap.dn.escape_dn_chars(role_id), - self.api.tenant._id_to_dn(tenant_id)) - - def get(self, id, filter=None): - model = super(RoleAPI, self).get(id, filter) - if model: - model['name'] = model['id'] - return model - - def create(self, values): - values['id'] = values['name'] - delattr(values, 'name') - - return super(RoleAPI, self).create(values) - - # pylint: disable=W0221 - def get_by_name(self, name, filter=None): - return self.get(name, filter) - - def add_user(self, role_id, user_id, tenant_id=None): - user = self.api.user.get(user_id) - if user is None: - raise exception.NotFound("User %s not found" % (user_id,)) - role_dn = self._subrole_id_to_dn(role_id, tenant_id) - conn = self.api.get_connection() - user_dn = self.api.user._id_to_dn(user_id) - try: - conn.modify_s(role_dn, [(ldap.MOD_ADD, 'member', user_dn)]) - except ldap.TYPE_OR_VALUE_EXISTS: - raise exception.Duplicate( - "User %s already has role %s in tenant %s" % (user_id, - role_id, tenant_id)) - except ldap.NO_SUCH_OBJECT: - if tenant_id is None or self.get(role_id) is None: - raise exception.NotFound("Role %s not found" % (role_id,)) - attrs = [ - ('objectClass', ['keystoneTenantRole', 'groupOfNames']), - ('member', [user_dn]), - ('keystoneRole', self._id_to_dn(role_id)), - ] - if self.use_dumb_member: - attrs[1][1].append(self.DUMB_MEMBER_DN) - conn.add_s(role_dn, attrs) - return models.UserRoleAssociation( - id=self._create_ref(role_id, tenant_id, user_id), - role_id=role_id, user_id=user_id, tenant_id=tenant_id) - - def get_by_service(self, service_id): - roles = self.get_all('(service_id=%s)' % \ - (ldap.filter.escape_filter_chars(service_id),)) - try: - res = [] - for role in roles: - res.append(role) - return res - except IndexError: - return None - - def get_role_assignments(self, tenant_id): - conn = self.api.get_connection() - query = '(objectClass=keystoneTenantRole)' - tenant_dn = self.api.tenant._id_to_dn(tenant_id) - try: - roles = conn.search_s(tenant_dn, ldap.SCOPE_ONELEVEL, query) - except ldap.NO_SUCH_OBJECT: - return [] - res = [] - for role_dn, attrs in roles: - try: - user_dns = attrs['member'] - except KeyError: - continue - for user_dn in user_dns: - if self.use_dumb_member and user_dn == self.DUMB_MEMBER_DN: - continue - user_id = self.api.user._dn_to_id(user_dn) - role_id = self._dn_to_id(role_dn) - res.append(models.UserRoleAssociation( - id=self._create_ref(role_id, tenant_id, user_id), - user_id=user_id, - role_id=role_id, - tenant_id=tenant_id)) - return res - - def list_global_roles_for_user(self, user_id): - user_dn = self.api.user._id_to_dn(user_id) - roles = self.get_all('(member=%s)' % (user_dn,)) - return [models.UserRoleAssociation( - id=self._create_ref(role.id, None, user_id), - role_id=role.id, - user_id=user_id) for role in roles] - - def list_tenant_roles_for_user(self, user_id, tenant_id=None): - conn = self.api.get_connection() - user_dn = self.api.user._id_to_dn(user_id) - query = '(&(objectClass=keystoneTenantRole)(member=%s))' % (user_dn,) - if tenant_id is not None: - tenant_dn = self.api.tenant._id_to_dn(tenant_id) - try: - roles = conn.search_s(tenant_dn, ldap.SCOPE_ONELEVEL, query) - except ldap.NO_SUCH_OBJECT: - return [] - res = [] - for role_dn, _ in roles: - role_id = self._dn_to_id(role_dn) - res.append(models.UserRoleAssociation( - id=self._create_ref(role_id, tenant_id, user_id), - user_id=user_id, - role_id=role_id, - tenant_id=tenant_id)) - return res - else: - try: - roles = conn.search_s(self.api.tenant.tree_dn, - ldap.SCOPE_SUBTREE, query) - except ldap.NO_SUCH_OBJECT: - return [] - res = [] - for role_dn, _ in roles: - role_id = self._dn_to_id(role_dn) - tenant_id = ldap.dn.str2dn(role_dn)[1][0][1] - res.append(models.UserRoleAssociation( - id=self._create_ref(role_id, tenant_id, user_id), - user_id=user_id, - role_id=role_id, - tenant_id=tenant_id)) - return res - - def rolegrant_get(self, id): - role_id, tenant_id, user_id = self._explode_ref(id) - user_dn = self.api.user._id_to_dn(user_id) - role_dn = self._subrole_id_to_dn(role_id, tenant_id) - query = '(&(objectClass=keystoneTenantRole)(member=%s))' % (user_dn,) - conn = self.api.get_connection() - try: - res = conn.search_s(role_dn, ldap.SCOPE_BASE, query) - except ldap.NO_SUCH_OBJECT: - return None - if len(res) == 0: - return None - return models.UserRoleAssociation(id=id, role_id=role_id, - tenant_id=tenant_id, user_id=user_id) - - def rolegrant_delete(self, id): - role_id, tenant_id, user_id = self._explode_ref(id) - user_dn = self.api.user._id_to_dn(user_id) - role_dn = self._subrole_id_to_dn(role_id, tenant_id) - conn = self.api.get_connection() - try: - conn.modify_s(role_dn, [(ldap.MOD_DELETE, 'member', [user_dn])]) - except ldap.NO_SUCH_ATTRIBUTE: - raise exception.NotFound("No such user in role") - - def rolegrant_get_page(self, marker, limit, user_id, tenant_id): - all_roles = [] - if tenant_id is None: - all_roles += self.list_global_roles_for_user(user_id) - else: - for tenant in self.api.tenant.get_all(): - all_roles += self.list_tenant_roles_for_user(user_id, - tenant.id) - return self._get_page(marker, limit, all_roles) - - def rolegrant_get_page_markers(self, user_id, tenant_id, marker, limit): - all_roles = [] - if tenant_id is None: - all_roles = self.list_global_roles_for_user(user_id) - else: - for tenant in self.api.tenant.get_all(): - all_roles += self.list_tenant_roles_for_user(user_id, - tenant.id) - return self._get_page_markers(marker, limit, all_roles) - - def get_by_service_get_page(self, service_id, marker, limit): - all_roles = self.get_by_service(service_id) - return self._get_page(marker, limit, all_roles) - - def get_by_service_get_page_markers(self, service_id, marker, limit): - all_roles = self.get_by_service(service_id) - return self._get_page_markers(marker, limit, all_roles) - - def rolegrant_list_by_role(self, id): - role_dn = self._id_to_dn(id) - try: - roles = self.get_all('(keystoneRole=%s)' % (role_dn,)) - except ldap.NO_SUCH_OBJECT: - return [] - res = [] - for role_dn, attrs in roles: - try: - user_dns = attrs['member'] - tenant_dns = attrs['tenant'] - except KeyError: - continue - for user_dn in user_dns: - if self.use_dumb_member and user_dn == self.DUMB_MEMBER_DN: - continue - user_id = self.api.user._dn_to_id(user_dn) - tenant_id = None - if tenant_dns is not None: - for tenant_dn in tenant_dns: - tenant_id = self.api.tenant._dn_to_id(tenant_dn) - role_id = self._dn_to_id(role_dn) - res.append(models.UserRoleAssociation( - id=self._create_ref(role_id, tenant_id, user_id), - user_id=user_id, - role_id=role_id, - tenant_id=tenant_id)) - return res - - def rolegrant_get_by_ids(self, user_id, role_id, tenant_id): - conn = self.api.get_connection() - user_dn = self.api.user._id_to_dn(user_id) - query = '(&(objectClass=keystoneTenantRole)(member=%s))' % (user_dn,) - if tenant_id is not None: - tenant_dn = self.api.tenant._id_to_dn(tenant_id) - try: - roles = conn.search_s(tenant_dn, ldap.SCOPE_ONELEVEL, query) - except ldap.NO_SUCH_OBJECT: - return None - if len(roles) == 0: - return None - for role_dn, _ in roles: - ldap_role_id = self._dn_to_id(role_dn) - if role_id == ldap_role_id: - res = models.UserRoleAssociation( - id=self._create_ref(role_id, tenant_id, user_id), - user_id=user_id, - role_id=role_id, - tenant_id=tenant_id) - return res - else: - try: - roles = self.get_all('(member=%s)' % (user_dn,)) - except ldap.NO_SUCH_OBJECT: - return None - if len(roles) == 0: - return None - for role in roles: - if role.id == role_id: - return models.UserRoleAssociation( - id=self._create_ref(role.id, None, user_id), - role_id=role.id, - user_id=user_id) - return None diff --git a/keystone/backends/ldap/api/tenant.py b/keystone/backends/ldap/api/tenant.py deleted file mode 100644 index fbc795ec..00000000 --- a/keystone/backends/ldap/api/tenant.py +++ /dev/null @@ -1,109 +0,0 @@ -import ldap -import uuid - -from keystone.backends.api import BaseTenantAPI -from keystone.backends.sqlalchemy.api.tenant import TenantAPI as SQLTenantAPI - -from keystone import models -from .base import BaseLdapAPI, add_redirects - - -class TenantAPI(BaseLdapAPI, BaseTenantAPI): # pylint: disable=W0223 - DEFAULT_TREE_DN = 'ou=Groups,dc=example,dc=com' - DEFAULT_STRUCTURAL_CLASSES = ['groupOfNames'] - options_name = 'tenant' - object_class = 'keystoneTenant' - model = models.Tenant - attribute_mapping = {'description': 'desc', 'enabled': 'keystoneEnabled', - 'name': 'keystoneName'} - - def get_by_name(self, name, filter=None): # pylint: disable=W0221,W0613 - tenants = self.get_all('(keystoneName=%s)' % \ - (ldap.filter.escape_filter_chars(name),)) - try: - return tenants[0] - except IndexError: - return None - - def create(self, values): - data = values.copy() - if 'id' not in data or data['id'] is None: - data['id'] = str(uuid.uuid4()) - return super(TenantAPI, self).create(data) - - def get_user_tenants(self, user_id, include_roles=True): - """Returns list of tenants a user has access to - - Always includes default tenants. - Adds role assignments if 'include_roles' is True. - """ - user_dn = self.api.user._id_to_dn(user_id) # pylint: disable=W0212 - query = '(member=%s)' % (user_dn,) - memberships = self.get_all(query) - if include_roles: - roles = self.api.role.list_tenant_roles_for_user(user_id) - for role in roles: - exists = False - for tenant in memberships: - if tenant['id'] == role.tenant_id: - exists = True - break - if not exists: - memberships.append(self.get(role.tenant_id)) - return memberships - - def list_for_user_get_page(self, user, marker, limit): - return self._get_page(marker, limit, self.get_user_tenants(user.id)) - - def list_for_user_get_page_markers(self, user, marker, limit): - return self._get_page_markers(marker, limit, - self.get_user_tenants(user.id)) - - def is_empty(self, id): - tenant = self._ldap_get(id) - members = tenant[1].get('member', []) - if self.use_dumb_member: - empty = members == [self.DUMB_MEMBER_DN] - else: - empty = len(members) == 0 - return empty and len(self.api.role.get_role_assignments(id)) == 0 - - def get_role_assignments(self, tenant_id): - return self.api.role.get_role_assignments(tenant_id) - - def add_user(self, tenant_id, user_id): - conn = self.api.get_connection() - conn.modify_s(self._id_to_dn(tenant_id), - [(ldap.MOD_ADD, 'member', - self.api.user._id_to_dn(user_id))]) # pylint: disable=W0212 - - def remove_user(self, tenant_id, user_id): - conn = self.api.get_connection() - conn.modify_s(self._id_to_dn(tenant_id), - [(ldap.MOD_DELETE, 'member', - self.api.user._id_to_dn(user_id))]) # pylint: disable=W0212 - - def get_users(self, tenant_id, role_id=None): - tenant = self._ldap_get(tenant_id) - res = [] - if not role_id: - # Get users who have default tenant mapping - for user_dn in tenant[1].get('member', []): - if self.use_dumb_member and user_dn == self.DUMB_MEMBER_DN: - continue - #pylint: disable=W0212 - res.append(self.api.user.get(self.api.user._dn_to_id(user_dn))) - rolegrants = self.api.role.get_role_assignments(tenant_id) - # Get users who are explicitly mapped via a tenant - for rolegrant in rolegrants: - if role_id is None or rolegrant.role_id == role_id: - res.append(self.api.user.get(rolegrant.user_id)) - return res - - add_redirects(locals(), SQLTenantAPI, ['get_all_endpoints']) - - def delete(self, id): - if not self.is_empty(id): - raise fault.ForbiddenFault("You may not delete a tenant that " - "contains users") - super(TenantAPI, self).delete(id) diff --git a/keystone/backends/ldap/api/user.py b/keystone/backends/ldap/api/user.py deleted file mode 100644 index 77bb6e22..00000000 --- a/keystone/backends/ldap/api/user.py +++ /dev/null @@ -1,124 +0,0 @@ -import ldap -import ldap.filter -import uuid - -import keystone.backends.backendutils as utils -from keystone.backends.api import BaseUserAPI -from keystone.backends.sqlalchemy.api.user import UserAPI as SQLUserAPI - -from keystone import models -from .base import BaseLdapAPI, add_redirects - - -class UserAPI(BaseLdapAPI, BaseUserAPI): - DEFAULT_TREE_DN = 'ou=Users,dc=example,dc=com' - DEFAULT_STRUCTURAL_CLASSES = ['keystoneUidObject'] - DEFAULT_ID_ATTR = 'uid' - options_name = 'user' - object_class = 'keystoneUser' - model = models.User - attribute_mapping = { - 'password': 'userPassword', - 'email': 'mail', - 'enabled': 'keystoneEnabled', - 'name': 'keystoneName', - } - attribute_ignore = ['tenant_id'] - - def _ldap_res_to_model(self, res): - obj = super(UserAPI, self)._ldap_res_to_model(res) - tenants = self.api.tenant.get_user_tenants(obj.id, False) - if len(tenants) > 0: - obj.tenant_id = tenants[0].id - return obj - - def get_by_name(self, name, filter=None): - users = self.get_all('(keystoneName=%s)' % \ - (ldap.filter.escape_filter_chars(name),)) - try: - return users[0] - except IndexError: - return None - - def create(self, values): - values['id'] = str(uuid.uuid4()) - utils.set_hashed_password(values) - values = super(UserAPI, self).create(values) - if values['tenant_id'] is not None: - self.api.tenant.add_user(values['tenant_id'], values['id']) - return values - - def update(self, id, values): - old_obj = self.get(id) - try: - new_tenant = values['tenant_id'] - except KeyError: - pass - else: - if old_obj.tenant_id != new_tenant: - if old_obj.tenant_id: - self.api.tenant.remove_user(old_obj.tenant_id, id) - if new_tenant: - self.api.tenant.add_user(new_tenant, id) - utils.set_hashed_password(values) - super(UserAPI, self).update(id, values, old_obj) - - def delete(self, id): - user = self.get(id) - if user.tenant_id: - self.api.tenant.remove_user(user.tenant_id, id) - super(UserAPI, self).delete(id) - for ref in self.api.role.list_global_roles_for_user(id): - self.api.role.rolegrant_delete(ref.id) - for ref in self.api.role.list_tenant_roles_for_user(id): - self.api.role.rolegrant_delete(ref.id) - - def get_by_email(self, email): - users = self.get_all('(mail=%s)' % \ - (ldap.filter.escape_filter_chars(email),)) - try: - return users[0] - except IndexError: - return None - - def user_roles_by_tenant(self, user_id, tenant_id): - return self.api.role.list_tenant_roles_for_user(user_id, tenant_id) - - def get_by_tenant(self, user_id, tenant_id): - user_dn = self._id_to_dn(user_id) - user = self.get(user_id) - tenant = self.api.tenant._ldap_get(tenant_id, - '(member=%s)' % (user_dn,)) - if tenant is not None: - return user - else: - if self.api.role.list_tenant_roles_for_user(user_id, tenant_id): - return user - return None - - def user_role_add(self, values): - return self.api.role.add_user(values.role_id, values.user_id, - values.tenant_id) - - def users_get_page(self, marker, limit): - return self.get_page(marker, limit) - - def users_get_page_markers(self, marker, limit): - return self.get_page_markers(marker, limit) - - def users_get_by_tenant_get_page(self, tenant_id, role_id, marker, limit): - return self._get_page(marker, limit, - self.api.tenant.get_users(tenant_id, role_id)) - - def users_get_by_tenant_get_page_markers(self, tenant_id, - role_id, marker, limit): - return self._get_page_markers(marker, limit, - self.api.tenant.get_users(tenant_id, role_id)) - - def check_password(self, user_id, password): - user = self.get(user_id) - return utils.check_password(password, user.password) - - add_redirects(locals(), SQLUserAPI, ['get_by_group', 'tenant_group', - 'tenant_group_delete', 'user_groups_get_all', - 'users_tenant_group_get_page', 'users_tenant_group_get_page_markers']) diff --git a/keystone/backends/ldap/fakeldap.py b/keystone/backends/ldap/fakeldap.py deleted file mode 100644 index ee80d15a..00000000 --- a/keystone/backends/ldap/fakeldap.py +++ /dev/null @@ -1,315 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# 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. -"""Fake LDAP server for test harness. - -This class does very little error checking, and knows nothing about ldap -class definitions. It implements the minimum emulation of the python ldap -library to work with nova. - -""" - -import logging -import re -import shelve - -import ldap - - -scope_names = { - ldap.SCOPE_BASE: 'SCOPE_BASE', - ldap.SCOPE_ONELEVEL: 'SCOPE_ONELEVEL', - ldap.SCOPE_SUBTREE: 'SCOPE_SUBTREE', -} - - -LOG = logging.getLogger('keystone.backends.ldap.fakeldap') - - -def initialize(uri): - """Opens a fake connection with an LDAP server.""" - return FakeLDAP(uri) - - -def _match_query(query, attrs): - """Match an ldap query to an attribute dictionary. - - The characters &, |, and ! are supported in the query. No syntax checking - is performed, so malformed querys will not work correctly. - """ - # cut off the parentheses - inner = query[1:-1] - if inner.startswith('&'): - # cut off the & - l, r = _paren_groups(inner[1:]) - return _match_query(l, attrs) and _match_query(r, attrs) - if inner.startswith('|'): - # cut off the | - l, r = _paren_groups(inner[1:]) - return _match_query(l, attrs) or _match_query(r, attrs) - if inner.startswith('!'): - # cut off the ! and the nested parentheses - return not _match_query(query[2:-1], attrs) - - (k, _sep, v) = inner.partition('=') - return _match(k, v, attrs) - - -def _paren_groups(source): - """Split a string into parenthesized groups.""" - count = 0 - start = 0 - result = [] - for pos in xrange(len(source)): - if source[pos] == '(': - if count == 0: - start = pos - count += 1 - if source[pos] == ')': - count -= 1 - if count == 0: - result.append(source[start:pos + 1]) - return result - - -def _match(key, value, attrs): - """Match a given key and value against an attribute list.""" - if key not in attrs: - return False - # This is a wild card search. Implemented as all or nothing for now. - if value == "*": - return True - if key == 'serviceId': - # for serviceId, the backend is returning a list of numbers - # make sure we convert them to strings first before comparing - # them - str_sids = map(lambda x: str(x), attrs[key]) - return str(value) in str_sids - if key != "objectclass": - return value in attrs[key] - # it is an objectclass check, so check subclasses - values = _subs(value) - for v in values: - if v in attrs[key]: - return True - return False - - -def _subs(value): - """Returns a list of subclass strings. - - The strings represent the ldap objectclass plus any subclasses that - inherit from it. Fakeldap doesn't know about the ldap object structure, - so subclasses need to be defined manually in the dictionary below. - - """ - subs = {'groupOfNames': [ - 'keystoneTenant', - 'keystoneRole', - 'keystoneTenantRole']} - if value in subs: - return [value] + subs[value] - return [value] - - -server_fail = False - - -class FakeShelve(dict): - @classmethod - def get_instance(cls): - try: - return cls.__instance - except AttributeError: - cls.__instance = cls() - return cls.__instance - - def sync(self): - pass - - -class FakeLDAP(object): - """Fake LDAP connection.""" - - def __init__(self, url): - LOG.debug("FakeLDAP initialize url=%s" % (url,)) - if url == 'fake://memory': - self.db = FakeShelve.get_instance() - else: - self.db = shelve.open(url[7:]) - - def simple_bind_s(self, dn, password): - """This method is ignored, but provided for compatibility.""" - if server_fail: - raise ldap.SERVER_DOWN - LOG.debug("FakeLDAP bind dn=%s" % (dn,)) - if dn == 'cn=Admin' and password == 'password': - return - try: - attrs = self.db["%s%s" % (self.__prefix, dn)] - except KeyError: - LOG.error("FakeLDAP bind fail: dn=%s not found" % (dn,)) - raise ldap.NO_SUCH_OBJECT - db_passwd = None - try: - db_passwd = attrs['userPassword'][0] - except (KeyError, IndexError): - LOG.error("FakeLDAP bind fail: password for dn=%s not found" % dn) - raise ldap.INAPPROPRIATE_AUTH - if db_passwd != password: - LOG.error("FakeLDAP bind fail: password for dn=%s does not match" % - dn) - raise ldap.INVALID_CREDENTIALS - - def unbind_s(self): - """This method is ignored, but provided for compatibility.""" - if server_fail: - raise ldap.SERVER_DOWN - - def add_s(self, dn, attrs): - """Add an object with the specified attributes at dn.""" - if server_fail: - raise ldap.SERVER_DOWN - - key = "%s%s" % (self.__prefix, dn) - LOG.debug("FakeLDAP add item: dn=%s, attrs=%s" % (dn, attrs)) - if key in self.db: - LOG.error( - "FakeLDAP add item failed: dn '%s' is already in store." % dn) - raise ldap.ALREADY_EXISTS(dn) - self.db[key] = dict([(k, v if isinstance(v, list) else [v]) - for k, v in attrs]) - self.db.sync() - - def delete_s(self, dn): - """Remove the ldap object at specified dn.""" - if server_fail: - raise ldap.SERVER_DOWN - - key = "%s%s" % (self.__prefix, dn) - LOG.debug("FakeLDAP delete item: dn=%s" % (dn,)) - try: - del self.db[key] - except KeyError: - LOG.error("FakeLDAP delete item failed: dn '%s' not found." % dn) - raise ldap.NO_SUCH_OBJECT - self.db.sync() - - def modify_s(self, dn, attrs): - """Modify the object at dn using the attribute list. - - :param dn: an LDAP DN - :param attrs: a list of tuples in the following form: - ([MOD_ADD | MOD_DELETE | MOD_REPACE], attribute, value) - """ - if server_fail: - raise ldap.SERVER_DOWN - - key = "%s%s" % (self.__prefix, dn) - LOG.debug("FakeLDAP modify item: dn=%s attrs=%s" % (dn, attrs)) - try: - entry = self.db[key] - except KeyError: - LOG.error("FakeLDAP modify item failed: dn '%s' not found." % dn) - raise ldap.NO_SUCH_OBJECT - - for cmd, k, v in attrs: - values = entry.setdefault(k, []) - if cmd == ldap.MOD_ADD: - if isinstance(v, list): - values += v - else: - values.append(v) - elif cmd == ldap.MOD_REPLACE: - values[:] = v if isinstance(v, list) else [v] - elif cmd == ldap.MOD_DELETE: - if v is None: - if len(values) == 0: - LOG.error("FakeLDAP modify item failed: " - "item has no attribute '%s' to delete" % k) - raise ldap.NO_SUCH_ATTRIBUTE - values[:] = [] - else: - if not isinstance(v, list): - v = [v] - for val in v: - try: - values.remove(val) - except ValueError: - LOG.error("FakeLDAP modify item failed: " - "item has no attribute '%s' with value '%s'" - " to delete" % (k, val)) - raise ldap.NO_SUCH_ATTRIBUTE - else: - LOG.error("FakeLDAP modify item failed: unknown command %s" - % (cmd,)) - raise NotImplementedError(\ - "modify_s action %s not implemented" % (cmd,)) - self.db[key] = entry - self.db.sync() - - def search_s(self, dn, scope, query=None, fields=None): - """Search for all matching objects under dn using the query. - - Args: - dn -- dn to search under - scope -- only SCOPE_BASE and SCOPE_SUBTREE are supported - query -- query to filter objects by - fields -- fields to return. Returns all fields if not specified - - """ - if server_fail: - raise ldap.SERVER_DOWN - - LOG.debug("FakeLDAP search at dn=%s scope=%s query='%s'" % - (dn, scope_names.get(scope, scope), query)) - if scope == ldap.SCOPE_BASE: - try: - item_dict = self.db["%s%s" % (self.__prefix, dn)] - except KeyError: - LOG.debug("FakeLDAP search fail: dn not found for SCOPE_BASE") - raise ldap.NO_SUCH_OBJECT - results = [(dn, item_dict)] - elif scope == ldap.SCOPE_SUBTREE: - results = [(k[len(self.__prefix):], v) - for k, v in self.db.iteritems() - if re.match("%s.*,%s" % (self.__prefix, dn), k)] - elif scope == ldap.SCOPE_ONELEVEL: - results = [(k[len(self.__prefix):], v) - for k, v in self.db.iteritems() - if re.match("%s\w+=[^,]+,%s" % (self.__prefix, dn), k)] - else: - LOG.error("FakeLDAP search fail: unknown scope %s" % (scope,)) - raise NotImplementedError("Search scope %s not implemented." % - (scope,)) - - objects = [] - for dn, attrs in results: - # filter the objects by query - if not query or _match_query(query, attrs): - # filter the attributes by fields - attrs = dict([(k, v) for k, v in attrs.iteritems() - if not fields or k in fields]) - objects.append((dn, attrs)) - # pylint: enable=E1103 - LOG.debug("FakeLDAP search result: %s" % (objects,)) - return objects - - @property - def __prefix(self): # pylint: disable=R0201 - """Get the prefix to use for all keys.""" - return 'ldap:' diff --git a/keystone/backends/ldap/keystone.ldif b/keystone/backends/ldap/keystone.ldif deleted file mode 100644 index 831b161c..00000000 --- a/keystone/backends/ldap/keystone.ldif +++ /dev/null @@ -1,74 +0,0 @@ -dn: cn=keystone,cn=schema,cn=config -objectClass: olcSchemaConfig -cn: keystone -olcAttributeTypes: ( - 1.3.6.1.3.1.666.667.3.1 - NAME 'keystoneEnabled' - EQUALITY booleanMatch - SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 - SINGLE-VALUE - ) -olcAttributeTypes: ( - 1.3.6.1.3.1.666.667.3.2 - NAME 'keystoneTenant' - SUP distinguishedName - SINGLE-VALUE - ) -olcAttributeTypes: ( - 1.3.6.1.3.1.666.667.3.3 - NAME 'keystoneRole' - SUP distinguishedName - SINGLE-VALUE - ) -olcAttributeTypes: ( - 1.3.6.1.3.1.666.667.3.4 - NAME 'serviceId' - EQUALITY caseExactIA5Match - SUBSTR caseExactIA5SubstringsMatch - SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 - SINGLE-VALUE - ) -olcAttributeTypes: ( - 1.3.6.1.3.1.666.667.3.5 - NAME 'keystoneName' - SUP name - SINGLE-VALUE - ) -olcObjectClasses: ( - 1.3.6.1.3.1.666.667.4.1 - NAME 'keystoneUidObject' - SUP top - STRUCTURAL - MUST ( uid ) - ) -olcObjectClasses: ( - 1.3.6.1.3.1.666.667.4.2 - NAME 'keystoneUser' - SUP top - AUXILIARY - MUST ( keystoneName $ keystoneEnabled ) - MAY ( mail $ userPassword ) - ) -olcObjectClasses: ( - 1.3.6.1.3.1.666.667.4.3 - NAME 'keystoneRole' - SUP top - AUXILIARY - MAY ( member $ description $ serviceId ) - ) -olcObjectClasses: ( - 1.3.6.1.3.1.666.667.4.4 - NAME 'keystoneTenant' - SUP top - AUXILIARY - MUST ( keystoneName $ keystoneEnabled ) - MAY ( member $ description ) - ) -olcObjectClasses: ( - 1.3.6.1.3.1.666.667.4.5 - NAME 'keystoneTenantRole' - SUP top - AUXILIARY - MUST ( keystoneRole ) - MAY ( member ) - ) diff --git a/keystone/backends/ldap/keystone.schema b/keystone/backends/ldap/keystone.schema deleted file mode 100644 index 68f0d562..00000000 --- a/keystone/backends/ldap/keystone.schema +++ /dev/null @@ -1,83 +0,0 @@ -objectidentifier keystoneSchema 1.3.6.1.3.1.666.667 -objectidentifier keystoneAttrs keystoneSchema:3 -objectidentifier keystoneOCs keystoneSchema:4 - -attributetype ( - keystoneAttrs:1 - NAME 'keystoneEnabled' - EQUALITY booleanMatch - SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 - SINGLE-VALUE - ) - -attributetype ( - keystoneAttrs:2 - NAME 'keystoneTenant' - SUP distinguishedName - SINGLE-VALUE - ) - -attributetype ( - keystoneAttrs:3 - NAME 'keystoneRole' - SUP distinguishedName - SINGLE-VALUE - ) - -attributetype ( - keystoneAttrs:4 - NAME 'serviceId' - EQUALITY caseExactIA5Match - SUBSTR caseExactIA5SubstringsMatch - SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 - SINGLE-VALUE - ) -attributetype: ( - keystoneAttrs:5 - NAME 'keystoneName' - SUP name - SINGLE-VALUE - ) -objectClass ( - keystoneOCs:1 - NAME 'keystoneUidObject' - SUP top - STRUCTURAL - MUST ( uid ) - ) - -objectClass ( - keystoneOCs:2 - NAME 'keystoneUser' - SUP top - AUXILIARY - MUST ( keystoneName $ keystoneEnabled ) - MAY ( mail $ userPassword ) - ) - -objectClass ( - keystoneOCs:3 - NAME 'keystoneRole' - SUP top - AUXILIARY - MUST ( cn ) - MAY ( member $ description $ serviceId ) - ) - -objectClass ( - keystoneOCs:4 - NAME 'keystoneTenant' - SUP top - AUXILIARY - MUST ( keystoneName $ keystoneEnabled ) - MAY ( member $ description ) - ) - -objectClass ( - keystoneOCs:5 - NAME 'keystoneTenantRole' - SUP top - AUXILIARY - MUST ( keystoneRole ) - MAY ( member ) - ) diff --git a/keystone/backends/ldap/models.py b/keystone/backends/ldap/models.py deleted file mode 100644 index d0c9ffca..00000000 --- a/keystone/backends/ldap/models.py +++ /dev/null @@ -1,52 +0,0 @@ -from collections import Mapping - -__all__ = ['UserRoleAssociation', 'Role', 'Tenant', 'User'] - - -def create_model(name, attrs): - class Cmapper(Mapping): - __slots__ = attrs - - def __init__(self, arg=None, **kwargs): - if arg is None: - arg = kwargs - if isinstance(arg, dict): - missed_attrs = set(attrs) - for k, v in kwargs.iteritems(): - setattr(self, k, v) - missed_attrs.remove(k) - for name in missed_attrs: - setattr(self, name, None) - elif isinstance(arg, C): - for name in attrs: - setattr(self, name, getattr(arg, name)) - else: - raise ValueError - - def __getitem__(self, name): - return getattr(self, name) - - def __setitem__(self, name, value): - return setattr(self, name, value) - - def __iter__(self): - return iter(attrs) - - def __len__(self): - return len(attrs) - Cmapper.__name__ = name - return Cmapper - - -UserRoleAssociation = create_model( - 'UserRoleAssociation', ['id', 'user_id', 'role_id', 'tenant_id']) -Role = create_model( - 'Role', ['id', 'desc', 'service_id']) -Tenant = create_model( - 'Tenant', ['id', 'name', 'desc', 'enabled']) -User = create_model( - 'User', ['id', 'name', 'password', 'email', 'enabled', 'tenant_id']) -#Endpoints = create_model( -# 'Endpoints', ['id', 'tenant_id', 'endpoint_template_id']) -#Credentials = create_model( -# 'Credentials', ['id', 'user_id', 'type', 'key', 'secret']) diff --git a/keystone/backends/memcache/__init__.py b/keystone/backends/memcache/__init__.py deleted file mode 100755 index 084c36d6..00000000 --- a/keystone/backends/memcache/__init__.py +++ /dev/null @@ -1,81 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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 ast -import logging - -from keystone.common import config -from keystone.backends.memcache import models -import keystone.utils as utils -import keystone.backends.api as top_api -import keystone.backends.models as top_models -import memcache - -MODEL_PREFIX = 'keystone.backends.memcache.models.' -API_PREFIX = 'keystone.backends.memcache.api.' -MEMCACHE_SERVER = None -CACHE_TIME = 86400 - - -def configure_backend(options): - hosts = options['memcache_hosts'] - global MEMCACHE_SERVER - if not MEMCACHE_SERVER: - MEMCACHE_SERVER = Memcache_Server(hosts) - register_models(options) - global CACHE_TIME - CACHE_TIME = config.get_option( - options, 'cache_time', type='int', default=86400) - - -class Memcache_Server(): - def __init__(self, hosts): - self.hosts = hosts - self.server = memcache.Client([self.hosts]) - - def set(self, key, value, expiry=CACHE_TIME): - """ - This method is used to set a new value - in the memcache server. - """ - self.server.set(key.encode('utf-8'), value, expiry) - - def get(self, key): - """ - This method is used to retrieve a value - from the memcache server - """ - return self.server.get(key.encode('utf-8')) - - def delete(self, key): - """ - This method is used to delete a value from the - memcached server. Lazy delete - """ - self.server.delete(key.encode('utf-8')) - - -def register_models(options): - """Register Models and create properties""" - supported_memcache_models = ast.literal_eval( - options["backend_entities"]) - for supported_memcache_model in supported_memcache_models: - model = utils.import_module(MODEL_PREFIX + supported_memcache_model) - top_models.set_value(supported_memcache_model, model) - if model.__api__ is not None: - model_api = utils.import_module(API_PREFIX + model.__api__) - top_api.set_value(model.__api__, model_api.get()) diff --git a/keystone/backends/memcache/api/__init__.py b/keystone/backends/memcache/api/__init__.py deleted file mode 100644 index 86d2910e..00000000 --- a/keystone/backends/memcache/api/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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 . import token diff --git a/keystone/backends/memcache/api/token.py b/keystone/backends/memcache/api/token.py deleted file mode 100755 index 4bbb5fa3..00000000 --- a/keystone/backends/memcache/api/token.py +++ /dev/null @@ -1,74 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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.backends.memcache import MEMCACHE_SERVER -from keystone.backends.api import BaseTokenAPI - - -# pylint: disable=W0223 -class TokenAPI(BaseTokenAPI): - def __init__(self, *args, **kw): - super(TokenAPI, self).__init__(*args, **kw) - - def create(self, token): - if not hasattr(token, 'tenant_id'): - token.tenant_id = None - if token.tenant_id is not None: - tenant_user_key = "%s::%s" % (token.tenant_id, token.user_id) - else: - tenant_user_key = "U%s" % token.user_id - - MEMCACHE_SERVER.set(token.id, token) - MEMCACHE_SERVER.set(tenant_user_key, token) - - def get(self, id): - token = MEMCACHE_SERVER.get(id) - if token is not None and not hasattr(token, 'tenant_id'): - token.tenant_id = None - return token - - # pylint: disable=E1103 - def delete(self, id): - token = self.get(id) - if token is not None: - MEMCACHE_SERVER.delete(id) - if token is not None and not hasattr(token, 'tenant_id'): - token.tenant_id = None - if token.tenant_id is not None: - MEMCACHE_SERVER.delete("%s::%s" % (token.tenant_id, - token.user_id)) - else: - MEMCACHE_SERVER.delete(token.id) - MEMCACHE_SERVER.delete("U%s" % token.user_id) - - def get_for_user(self, user_id): - token = MEMCACHE_SERVER.get("U%s" % user_id) - if token is not None and not hasattr(token, 'tenant_id'): - token.tenant_id = None - return token - - def get_for_user_by_tenant(self, user_id, tenant_id): - if tenant_id is not None: - token = MEMCACHE_SERVER.get("%s::%s" % (tenant_id, user_id)) - else: - token = MEMCACHE_SERVER.get("U%s" % user_id) - if token is not None and not hasattr(token, 'tenant_id'): - token.tenant_id = None - return token - - -def get(): - return TokenAPI() diff --git a/keystone/backends/memcache/models.py b/keystone/backends/memcache/models.py deleted file mode 100755 index 58dc3f03..00000000 --- a/keystone/backends/memcache/models.py +++ /dev/null @@ -1,19 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-2011 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. - - -class Token(): - __api__ = 'token' diff --git a/keystone/backends/models.py b/keystone/backends/models.py deleted file mode 100755 index 10ac6267..00000000 --- a/keystone/backends/models.py +++ /dev/null @@ -1,60 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-2011 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. - -# pylint: disable=C0103,W0603 -#Current Models -UserRoleAssociation = None -Endpoints = None -Role = None -Tenant = None -User = None -Credentials = None -Token = None -EndpointTemplates = None -Service = None - - -# Function to dynamically set model references. -def set_value(variable_name, value): - if variable_name == 'UserRoleAssociation': - global UserRoleAssociation - UserRoleAssociation = value - elif variable_name == 'Endpoints': - global Endpoints - Endpoints = value - elif variable_name == 'Role': - global Role - Role = value - elif variable_name == 'Tenant': - global Tenant - Tenant = value - elif variable_name == 'User': - global User - User = value - elif variable_name == 'Credentials': - global Credentials - Credentials = value - elif variable_name == 'Token': - global Token - Token = value - elif variable_name == 'EndpointTemplates': - global EndpointTemplates - EndpointTemplates = value - elif variable_name == 'Service': - global Service - Service = value - else: - raise IndexError("Unrecognized model type: %s" % variable_name) diff --git a/keystone/backends/sqlalchemy/__init__.py b/keystone/backends/sqlalchemy/__init__.py deleted file mode 100755 index cea111c1..00000000 --- a/keystone/backends/sqlalchemy/__init__.py +++ /dev/null @@ -1,169 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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. - -# pylint: disable=W0602,W0603 - -from sqlalchemy.orm import joinedload, aliased, sessionmaker - -import ast -import logging -import os -import sys - -from sqlalchemy import create_engine -from sqlalchemy.pool import StaticPool - -try: - # pylint: disable=E0611 - from migrate.versioning import exceptions as versioning_exceptions -except ImportError: - from migrate import exceptions as versioning_exceptions - -from keystone import utils -from keystone.backends.sqlalchemy import models -from keystone.backends.sqlalchemy import migration -import keystone.backends.api as top_api -import keystone.backends.models as top_models - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - -_DRIVER = None - - -class Driver(): - def __init__(self, conf): - self.session = None - self._engine = None - self.connection_str = conf.sql_connection - model_list = ast.literal_eval(conf.backend_entities) - self._init_engine(model_list) - self._init_models(model_list) - self._init_session_maker() - - def _init_engine(self, model_list): - logger.info("Initializing sqlalchemy backend: %s" % \ - self.connection_str) - if self.connection_str == "sqlite://": - # initialize in-memory sqlite (i.e. for testing) - self._engine = create_engine( - self.connection_str, - connect_args={'check_same_thread': False}, - poolclass=StaticPool) - - # TODO(dolph): we should be using version control, but - # we don't have a way to pass our in-memory instance to - # the versioning api - self._init_tables(model_list) - else: - # initialize a "real" database - self._engine = create_engine( - self.connection_str, - pool_recycle=3600) - self._init_version_control() - self._init_tables(model_list) - - def _init_version_control(self): - """Verify the state of the database""" - repo_path = migration.get_migrate_repo_path() - - try: - repo_version = migration.get_repo_version(repo_path) - db_version = migration.get_db_version(self._engine, repo_path) - - if repo_version != db_version: - msg = ('Database (%s) is not up to date (current=%s, ' - 'latest=%s); run `keystone-manage sync_database` or ' - 'override your migrate version manually (see docs)' % - (self.connection_str, db_version, repo_version)) - logging.warning(msg) - raise Exception(msg) - except versioning_exceptions.DatabaseNotControlledError: - msg = ('Database (%s) is not version controlled; ' - 'run `keystone-manage sync_database` or ' - 'override your migrate version manually (see docs)' % - (self.connection_str)) - logging.warning(msg) - - @staticmethod - def _init_models(model_list): - for model in model_list: - model_class = getattr(models, model) - top_models.set_value(model, model_class) - - if model_class.__api__ is not None: - api_path = '.'.join([__package__, 'api', model_class.__api__]) - api_module = sys.modules.get(api_path) - if api_module is None: - api_module = utils.import_module(api_path) - top_api.set_value(model_class.__api__, api_module.get()) - - def _init_tables(self, model_list): - tables = [] - - for model in model_list: - model_class = getattr(models, model) - tables.append(model_class.__table__) - - tables_to_create = [] - for table in reversed(models.Base.metadata.sorted_tables): - if table in tables: - tables_to_create.append(table) - - logger.debug('Creating tables: %s' % \ - ','.join([table.name for table in tables_to_create])) - models.Base.metadata.create_all(self._engine, tables=tables_to_create, - checkfirst=True) - - def _init_session_maker(self): - self.session = sessionmaker( - bind=self._engine, - autocommit=True, - expire_on_commit=False) - - def get_session(self): - """Creates a pre-configured database session""" - return self.session() - - def reset(self): - """Unregister models and reset DB engine. - - Useful clearing out data before testing - - TODO(dolph):: - - ... but what does this *do*? Issue DROP TABLE statements? - TRUNCATE TABLE? Or is the scope of impact limited to python? - """ - if self._engine is not None: - models.Base.metadata.drop_all(self._engine) - self._engine = None - - -def configure_backend(conf): - global _DRIVER - _DRIVER = Driver(conf) - - -def get_session(): - global _DRIVER - return _DRIVER.get_session() - - -def unregister_models(): - global _DRIVER - if _DRIVER: - return _DRIVER.reset() diff --git a/keystone/backends/sqlalchemy/api/credentials.py b/keystone/backends/sqlalchemy/api/credentials.py deleted file mode 100755 index 22105694..00000000 --- a/keystone/backends/sqlalchemy/api/credentials.py +++ /dev/null @@ -1,137 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# 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.backends.sqlalchemy import get_session, models -from keystone.backends import api -from keystone.models import Credentials -from keystone.logic.types import fault - - -# pylint: disable=E1103,W0221 -class CredentialsAPI(api.BaseCredentialsAPI): - def __init__(self, *args, **kw): - super(CredentialsAPI, self).__init__(*args, **kw) - - @staticmethod - def transpose(ref): - """ Transposes field names from domain to sql model""" - if hasattr(api.TENANT, 'uid_to_id'): - if 'tenant_id' in ref: - ref['tenant_id'] = api.TENANT.uid_to_id(ref['tenant_id']) - elif hasattr(ref, 'tenant_id'): - ref.tenant_id = api.TENANT.uid_to_id(ref.tenant_id) - - if hasattr(api.USER, 'uid_to_id'): - if 'user_id' in ref: - ref['user_id'] = api.USER.uid_to_id(ref['user_id']) - elif hasattr(ref, 'tenant_id'): - ref.user_id = api.USER.uid_to_id(ref.user_id) - - @staticmethod - def to_model(ref): - """ Returns Keystone model object based on SQLAlchemy model""" - if ref: - if hasattr(api.TENANT, 'uid_to_id'): - if 'tenant_id' in ref: - ref['tenant_id'] = api.TENANT.id_to_uid(ref['tenant_id']) - elif hasattr(ref, 'tenant_id'): - ref.tenant_id = api.TENANT.id_to_uid(ref.tenant_id) - - if hasattr(api.USER, 'uid_to_id'): - if 'user_id' in ref: - ref['user_id'] = api.USER.id_to_uid(ref['user_id']) - elif hasattr(ref, 'user_id'): - ref.user_id = api.USER.id_to_uid(ref.user_id) - - return Credentials(id=ref.id, user_id=ref.user_id, - tenant_id=ref.tenant_id, type=ref.type, key=ref.key, - secret=ref.secret) - - @staticmethod - def to_model_list(refs): - return [CredentialsAPI.to_model(ref) for ref in refs] - - def create(self, values): - data = values.copy() - CredentialsAPI.transpose(data) - - if 'tenant_id' in values: - if data['tenant_id'] is None and values['tenant_id'] is not None: - raise fault.ItemNotFoundFault('Invalid tenant id: %s' % \ - values['tenant_id']) - - credentials_ref = models.Credentials() - credentials_ref.update(data) - credentials_ref.save() - - return CredentialsAPI.to_model(credentials_ref) - - @staticmethod - def update(id, values, session=None): - if not session: - session = get_session() - - CredentialsAPI.transpose(values) - - with session.begin(): - ref = session.query(models.Credentials).filter_by(id=id).first() - ref.update(values) - ref.save(session=session) - - def get(self, id, session=None): - result = self._get(id, session) - - return CredentialsAPI.to_model(result) - - @staticmethod - def _get(id, session=None): - if id is None: - return None - - session = session or get_session() - - return session.query(models.Credentials).filter_by(id=id).first() - - @staticmethod - def get_all(session=None): - if not session: - session = get_session() - - results = session.query(models.Credentials).all() - - return CredentialsAPI.to_model_list(results) - - def get_by_access(self, access, session=None): - if not session: - session = get_session() - - result = session.query(models.Credentials).\ - filter_by(type="EC2", key=access).first() - - return CredentialsAPI.to_model(result) - - def delete(self, id, session=None): - if not session: - session = get_session() - - with session.begin(): - group_ref = self._get(id, session) - session.delete(group_ref) - - -def get(): - return CredentialsAPI() diff --git a/keystone/backends/sqlalchemy/api/endpoint_template.py b/keystone/backends/sqlalchemy/api/endpoint_template.py deleted file mode 100755 index 84fce454..00000000 --- a/keystone/backends/sqlalchemy/api/endpoint_template.py +++ /dev/null @@ -1,381 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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.backends.sqlalchemy import get_session, models, aliased -from keystone.backends import api - - -# pylint: disable=E1103,W0221 -class EndpointTemplateAPI(api.BaseEndpointTemplateAPI): - def __init__(self, *args, **kw): - super(EndpointTemplateAPI, self).__init__(*args, **kw) - - @staticmethod - def transpose(values): - """ Transposes field names from domain to sql model""" - pass - - @staticmethod - def to_model(ref): - """ Returns Keystone model object based on SQLAlchemy model""" - pass - - @staticmethod - def to_model_list(refs): - return [EndpointTemplateAPI.to_model(ref) for ref in refs] - - def create(self, values): - endpoint_template = models.EndpointTemplates() - endpoint_template.update(values) - endpoint_template.save() - return endpoint_template - - def update(self, id, values, session=None): - if not session: - session = get_session() - with session.begin(): - ref = self.get(id, session) - ref.update(values) - ref.save(session=session) - return ref - - def delete(self, id, session=None): - if not session: - session = get_session() - with session.begin(): - endpoint_template = self.get(id, session) - session.delete(endpoint_template) - - def get(self, id, session=None): - if id is None: - return None - - session = session or get_session() - - return session.query(models.EndpointTemplates).\ - filter_by(id=id).first() - - def get_all(self, session=None): - if not session: - session = get_session() - - return session.query(models.EndpointTemplates).all() - - def get_by_service(self, service_id, session=None): - if not session: - session = get_session() - return session.query(models.EndpointTemplates).\ - filter_by(service_id=service_id).all() - - def get_by_service_get_page(self, service_id, marker, limit, session=None): - if not session: - session = get_session() - - if marker: - return session.query(models.EndpointTemplates).\ - filter("id>:marker").params(\ - marker='%s' % marker).filter_by(\ - service_id=service_id).order_by(\ - models.EndpointTemplates.id.desc()).limit(int(limit)).all() - else: - return session.query(models.EndpointTemplates).filter_by(\ - service_id=service_id).order_by(\ - models.EndpointTemplates.id.desc()).\ - limit(int(limit)).all() - - # pylint: disable=R0912 - def get_by_service_get_page_markers(self, service_id, marker, \ - limit, session=None): - if not session: - session = get_session() - first = session.query(models.EndpointTemplates).filter_by(\ - service_id=service_id).order_by(\ - models.EndpointTemplates.id).first() - last = session.query(models.EndpointTemplates).filter_by(\ - service_id=service_id).order_by(\ - models.EndpointTemplates.id.desc()).first() - if first is None: - return (None, None) - if marker is None: - marker = first.id - next_page = session.query(models.EndpointTemplates).\ - filter("id > :marker").\ - filter_by(service_id=service_id).\ - params(marker='%s' % marker).\ - order_by(models.EndpointTemplates.id).\ - limit(int(limit)).\ - all() - prev_page = session.query(models.EndpointTemplates).\ - filter("id < :marker").\ - filter_by(service_id=service_id).\ - params(marker='%s' % marker).\ - order_by(models.EndpointTemplates.id.desc()).\ - limit(int(limit)).\ - all() - if len(next_page) == 0: - next_page = last - else: - for t in next_page: - next_page = t - if len(prev_page) == 0: - prev_page = first - else: - for t in prev_page: - prev_page = t - if prev_page.id == marker: - prev_page = None - else: - prev_page = prev_page.id - if next_page.id == last.id: - next_page = None - else: - next_page = next_page.id - return (prev_page, next_page) - - def get_page(self, marker, limit, session=None): - if not session: - session = get_session() - - if marker: - return session.query(models.EndpointTemplates).\ - filter("id>:marker").params(\ - marker='%s' % marker).order_by(\ - models.EndpointTemplates.id.desc()).limit(int(limit)).all() - else: - return session.query(models.EndpointTemplates).order_by(\ - models.EndpointTemplates.id.desc()).\ - limit(int(limit)).all() - - # pylint: disable=R0912 - def get_page_markers(self, marker, limit, session=None): - if not session: - session = get_session() - first = session.query(models.EndpointTemplates).order_by(\ - models.EndpointTemplates.id).first() - last = session.query(models.EndpointTemplates).order_by(\ - models.EndpointTemplates.id.desc()).first() - if first is None: - return (None, None) - if marker is None: - marker = first.id - next_page = session.query(models.EndpointTemplates).\ - filter("id > :marker").\ - params(marker='%s' % marker).\ - order_by(models.EndpointTemplates.id).\ - limit(int(limit)).\ - all() - prev_page = session.query(models.EndpointTemplates).\ - filter("id < :marker").\ - params(marker='%s' % marker).\ - order_by(models.EndpointTemplates.id.desc()).\ - limit(int(limit)).\ - all() - if len(next_page) == 0: - next_page = last - else: - for t in next_page: - next_page = t - if len(prev_page) == 0: - prev_page = first - else: - for t in prev_page: - prev_page = t - if prev_page.id == marker: - prev_page = None - else: - prev_page = prev_page.id - if next_page.id == last.id: - next_page = None - else: - next_page = next_page.id - return (prev_page, next_page) - - def endpoint_get_by_tenant_get_page(self, tenant_id, marker, limit, - session=None): - if not session: - session = get_session() - - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - if marker: - results = session.query(models.Endpoints).\ - filter(models.Endpoints.tenant_id == tenant_id).\ - filter("id >= :marker").params( - marker='%s' % marker).order_by( - models.Endpoints.id).limit(int(limit)).all() - else: - results = session.query(models.Endpoints).\ - filter(models.Endpoints.tenant_id == tenant_id).\ - order_by(models.Endpoints.id).limit(int(limit)).all() - - if hasattr(api.TENANT, 'id_to_uid'): - for result in results: - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return results - - # pylint: disable=R0912 - def endpoint_get_by_tenant_get_page_markers(self, tenant_id, marker, limit, - session=None): - if not session: - session = get_session() - - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - tba = aliased(models.Endpoints) - first = session.query(tba).\ - filter(tba.tenant_id == tenant_id).\ - order_by(tba.id).first() - last = session.query(tba).\ - filter(tba.tenant_id == tenant_id).\ - order_by(tba.id.desc()).first() - if first is None: - return (None, None) - if marker is None: - marker = first.id - next_page = session.query(tba).\ - filter(tba.tenant_id == tenant_id).\ - filter("id>=:marker").params( - marker='%s' % marker).order_by( - tba.id).limit(int(limit)).all() - - prev_page = session.query(tba).\ - filter(tba.tenant_id == tenant_id).\ - filter("id < :marker").params( - marker='%s' % marker).order_by( - tba.id).limit(int(limit) + 1).all() - next_len = len(next_page) - prev_len = len(prev_page) - - if next_len == 0: - next_page = last - else: - for t in next_page: - next_page = t - if prev_len == 0: - prev_page = first - else: - for t in prev_page: - prev_page = t - if first.id == marker: - prev_page = None - else: - prev_page = prev_page.id - if marker == last.id: - next_page = None - else: - next_page = next_page.id - return (prev_page, next_page) - - def endpoint_add(self, values): - if hasattr(api.TENANT, 'uid_to_id'): - values.tenant_id = api.TENANT.uid_to_id(values.tenant_id) - - endpoints = models.Endpoints() - endpoints.update(values) - endpoints.save() - - if hasattr(api.TENANT, 'id_to_uid'): - endpoints.tenant_id = api.TENANT.id_to_uid(endpoints.tenant_id) - - return endpoints - - def endpoint_get(self, id, session=None): - if not session: - session = get_session() - - result = session.query(models.Endpoints).\ - filter_by(id=id).first() - - if hasattr(api.TENANT, 'id_to_uid'): - if result: - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return result - - @staticmethod - def endpoint_get_by_ids(endpoint_template_id, tenant_id, - session=None): - if not session: - session = get_session() - - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - result = session.query(models.Endpoints).\ - filter_by(endpoint_template_id=endpoint_template_id).\ - filter_by(tenant_id=tenant_id).first() - - if hasattr(api.TENANT, 'id_to_uid'): - if result: - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return result - - @staticmethod - def endpoint_get_all(session=None): - if not session: - session = get_session() - - results = session.query(models.Endpoints).all() - - for result in results: - if hasattr(api.TENANT, 'id_to_uid'): - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return results - - def endpoint_get_by_tenant(self, tenant_id, session=None): - if not session: - session = get_session() - - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - result = session.query(models.Endpoints).\ - filter_by(tenant_id=tenant_id).first() - - if hasattr(api.TENANT, 'id_to_uid'): - if result: - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return result - - def endpoint_get_by_endpoint_template( - self, endpoint_template_id, session=None): - if not session: - session = get_session() - - result = session.query(models.Endpoints).\ - filter_by(endpoint_template_id=endpoint_template_id).all() - - return result - - def endpoint_delete(self, id, session=None): - if not session: - session = get_session() - - with session.begin(): - endpoints = self.endpoint_get(id, session) - if endpoints: - session.delete(endpoints) - - -def get(): - return EndpointTemplateAPI() diff --git a/keystone/backends/sqlalchemy/api/role.py b/keystone/backends/sqlalchemy/api/role.py deleted file mode 100755 index 5419924f..00000000 --- a/keystone/backends/sqlalchemy/api/role.py +++ /dev/null @@ -1,463 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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.backends.sqlalchemy import get_session, models -from keystone.backends import api -from keystone.models import Role, UserRoleAssociation - - -# pylint: disable=E1103,W0221 -class RoleAPI(api.BaseRoleAPI): - def __init__(self, *args, **kw): - super(RoleAPI, self).__init__(*args, **kw) - - @staticmethod - def transpose(values): - """ Handles transposing field names from Keystone model to - sqlalchemy mode - - Differences: - desc <-> description - """ - if 'description' in values: - values['desc'] = values.pop('description') - - @staticmethod - def to_model(ref): - """ Returns Keystone model object based on SQLAlchemy model""" - if ref: - return Role(id=str(ref.id), name=ref.name, description=ref.desc, - service_id=ref.service_id) - - @staticmethod - def to_model_list(refs): - return [RoleAPI.to_model(ref) for ref in refs] - - @staticmethod - def to_ura_model(ref): - """ Returns Keystone model object based on SQLAlchemy model""" - if ref: - return UserRoleAssociation(id=ref.id, - role_id=ref.role_id, - user_id=ref.user_id, - tenant_id=ref.tenant_id) - - @staticmethod - def to_ura_model_list(refs): - return [RoleAPI.to_ura_model(ref) for ref in refs] - - # pylint: disable=W0221 - def create(self, values): - data = values.copy() - RoleAPI.transpose(data) - role = models.Role() - role.update(data) - role.save() - return RoleAPI.to_model(role) - - def delete(self, id, session=None): - if not session: - session = get_session() - with session.begin(): - role = session.query(models.Role).filter_by(id=id).first() - session.delete(role) - - @staticmethod - def update(id, values, session=None): - if not session: - session = get_session() - - RoleAPI.transpose(values) - - with session.begin(): - ref = session.query(models.Role).filter_by(id=id).first() - ref.update(values) - ref.save(session=session) - - def get(self, id, session=None): - if id is None: - return None - - session = session or get_session() - return RoleAPI.to_model( - session.query(models.Role).filter_by(id=id).first()) - - def get_by_name(self, name, session=None): - if not session: - session = get_session() - return RoleAPI.to_model( - session.query(models.Role).filter_by(name=name).first()) - - def get_by_service(self, service_id, session=None): - if not session: - session = get_session() - results = session.query(models.Role).\ - filter_by(service_id=service_id).all() - return RoleAPI.to_model_list(results) - - def get_all(self, session=None): - if not session: - session = get_session() - return RoleAPI.to_model_list(session.query(models.Role).all()) - - def get_page(self, marker, limit, session=None): - if not session: - session = get_session() - - if marker: - results = session.query(models.Role).filter("id>:marker").params( - marker='%s' % marker).order_by( - models.Role.id.desc()).limit(int(limit)).all() - else: - results = session.query(models.Role).order_by( - models.Role.id.desc()).limit(int(limit)).all() - return RoleAPI.to_model_list(results) - - # pylint: disable=R0912 - def get_page_markers(self, marker, limit, session=None): - if not session: - session = get_session() - first = session.query(models.Role).order_by( - models.Role.id).first() - last = session.query(models.Role).order_by( - models.Role.id.desc()).first() - if first is None: - return (None, None) - if marker is None: - marker = first.id - next_page = session.query(models.Role).filter("id > :marker").params( - marker='%s' % marker).order_by( - models.Role.id).limit(int(limit)).all() - prev_page = session.query(models.Role).filter("id < :marker").params( - marker='%s' % marker).order_by( - models.Role.id.desc()).limit(int(limit)).all() - if not next_page: - next_page = last - else: - next_page = next_page[-1] - if not prev_page: - prev_page = first - else: - prev_page = prev_page[-1] - if prev_page.id == marker: - prev_page = None - else: - prev_page = prev_page.id - if next_page.id == last.id: - next_page = None - else: - next_page = next_page.id - return (prev_page, next_page) - - def get_by_service_get_page(self, service_id, marker, limit, session=None): - if not session: - session = get_session() - - if marker: - results = session.query(models.Role).filter("id>:marker").params( - marker='%s' % marker).filter_by( - service_id=service_id).order_by( - models.Role.id.desc()).limit(int(limit)).all() - else: - results = session.query(models.Role).filter_by( - service_id=service_id).order_by( - models.Role.id.desc()).limit(int(limit)).all() - return RoleAPI.to_model_list(results) - - # pylint: disable=R0912 - def get_by_service_get_page_markers(self, - service_id, marker, limit, session=None): - if not session: - session = get_session() - first = session.query(models.Role).filter_by( - service_id=service_id).order_by( - models.Role.id).first() - last = session.query(models.Role).filter_by( - service_id=service_id).order_by( - models.Role.id.desc()).first() - if first is None: - return (None, None) - if marker is None: - marker = first.id - next_page = session.query(models.Role).filter("id > :marker").params( - marker='%s' % marker).filter_by( - service_id=service_id).order_by( - models.Role.id).limit(int(limit)).all() - prev_page = session.query(models.Role).filter("id < :marker").params( - marker='%s' % marker).filter_by( - service_id=service_id).order_by( - models.Role.id.desc()).limit(int(limit)).all() - if not next_page: - next_page = last - else: - next_page = next_page[-1] - if not prev_page: - prev_page = first - else: - prev_page = prev_page[-1] - if prev_page.id == marker: - prev_page = None - else: - prev_page = prev_page.id - if next_page.id == last.id: - next_page = None - else: - next_page = next_page.id - return (prev_page, next_page) - - # - # Role Grants start here - # - def rolegrant_get(self, id, session=None): - if not session: - session = get_session() - - result = session.query(models.UserRoleAssociation).filter_by(id=id).\ - first() - - if result: - if hasattr(api.USER, 'uid_to_id'): - result.user_id = api.USER.id_to_uid(result.user_id) - if hasattr(api.TENANT, 'uid_to_id'): - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return RoleAPI.to_ura_model(result) - - def list_role_grants(self, role_id=None, user_id=None, tenant_id=None, - session=None): - """Lists all role grants; optionally specify a role ID, user ID, or - tenant ID. - - If tenant ID is provided as False (tenant_id=False), global grants will - be returned.""" - - session = session or get_session() - - is_global = tenant_id == False - - if hasattr(api.USER, 'uid_to_id'): - user_id = api.USER.uid_to_id(user_id) - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - query = session.query(models.UserRoleAssociation) - - if role_id is not None: - query = query.filter_by(role_id=role_id) - - if user_id is not None: - query = query.filter_by(user_id=user_id) - - if is_global: - query = query.filter(models.UserRoleAssociation.tenant_id == None) - elif tenant_id is not None: - query = query.filter_by(tenant_id=tenant_id) - - results = query.all() - - for result in results: - if hasattr(api.USER, 'uid_to_id'): - result.user_id = api.USER.id_to_uid(result.user_id) - if hasattr(api.TENANT, 'uid_to_id'): - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return RoleAPI.to_ura_model_list(results) - - def rolegrant_delete(self, id, session=None): - if not session: - session = get_session() - - with session.begin(): - rolegrant = session.query(models.UserRoleAssociation).\ - filter_by(id=id).first() - session.delete(rolegrant) - - # pylint: disable=R0912 - def rolegrant_get_page_markers(self, user_id, tenant_id, marker, - limit, session=None): - if not session: - session = get_session() - - if hasattr(api.USER, 'uid_to_id'): - user_id = api.USER.uid_to_id(user_id) - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - query = session.query(models.UserRoleAssociation).filter_by( - user_id=user_id) - if tenant_id: - query = query.filter_by(tenant_id=tenant_id) - else: - query = query.filter("tenant_id is null") - first = query.order_by(models.UserRoleAssociation.id).first() - last = query.order_by(models.UserRoleAssociation.id.desc()).first() - if first is None: - return (None, None) - if marker is None: - marker = first.id - next_page = query.\ - filter("id > :marker").\ - params(marker='%s' % marker).\ - order_by(models.UserRoleAssociation.id).\ - limit(int(limit)).\ - all() - prev_page = query.\ - filter("id < :marker").\ - params(marker='%s' % marker).\ - order_by(models.UserRoleAssociation.id.desc()).\ - limit(int(limit)).\ - all() - - if not next_page: - next_page = last - else: - next_page = next_page[-1] - if not prev_page: - prev_page = first - else: - prev_page = prev_page[-1] - if prev_page.id == marker: - prev_page = None - else: - prev_page = prev_page.id - if next_page.id == last.id: - next_page = None - else: - next_page = next_page.id - return (prev_page, next_page) - - def rolegrant_get_page(self, marker, limit, user_id, tenant_id, - session=None): - if not session: - session = get_session() - - if hasattr(api.USER, 'uid_to_id'): - user_id = api.USER.uid_to_id(user_id) - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - query = session.query(models.UserRoleAssociation).\ - filter_by(user_id=user_id) - if tenant_id: - query = query.filter_by(tenant_id=tenant_id) - else: - query = query.filter("tenant_id is null") - if marker: - results = query.filter("id>:marker").params( - marker='%s' % marker).order_by( - models.UserRoleAssociation.id.desc()).limit( - int(limit)).all() - else: - results = query.order_by( - models.UserRoleAssociation.id.desc()).limit( - int(limit)).all() - - for result in results: - if hasattr(api.USER, 'uid_to_id'): - result.user_id = api.USER.id_to_uid(result.user_id) - if hasattr(api.TENANT, 'uid_to_id'): - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return RoleAPI.to_ura_model_list(results) - - def list_global_roles_for_user(self, user_id, session=None): - if not session: - session = get_session() - - if hasattr(api.USER, 'uid_to_id'): - user_id = api.USER.uid_to_id(user_id) - - results = session.query(models.UserRoleAssociation).\ - filter_by(user_id=user_id).filter("tenant_id is null").all() - - for result in results: - result['role_id'] = str(result['role_id']) - if hasattr(api.USER, 'uid_to_id'): - result.user_id = api.USER.id_to_uid(result.user_id) - if hasattr(api.TENANT, 'uid_to_id'): - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return RoleAPI.to_ura_model_list(results) - - def list_tenant_roles_for_user(self, user_id, tenant_id, session=None): - if not session: - session = get_session() - - if hasattr(api.USER, 'uid_to_id'): - user_id = api.USER.uid_to_id(user_id) - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - results = session.query(models.UserRoleAssociation).\ - filter_by(user_id=user_id).filter_by(tenant_id=tenant_id).all() - - for result in results: - result['role_id'] = str(result['role_id']) - if hasattr(api.USER, 'uid_to_id'): - result.user_id = api.USER.id_to_uid(result.user_id) - if hasattr(api.TENANT, 'uid_to_id'): - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return RoleAPI.to_ura_model_list(results) - - def rolegrant_list_by_role(self, role_id, session=None): - """ Get a list of all (global and tenant) grants for this role """ - if not session: - session = get_session() - - results = session.query(models.UserRoleAssociation).\ - filter_by(role_id=role_id).all() - - for result in results: - result['role_id'] = str(result['role_id']) - if hasattr(api.USER, 'uid_to_id'): - result.user_id = api.USER.id_to_uid(result.user_id) - if hasattr(api.TENANT, 'uid_to_id'): - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return RoleAPI.to_ura_model_list(results) - - def rolegrant_get_by_ids(self, user_id, role_id, tenant_id, session=None): - if not session: - session = get_session() - - if hasattr(api.USER, 'uid_to_id'): - user_id = api.USER.uid_to_id(user_id) - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - if tenant_id is None: - result = session.query(models.UserRoleAssociation).\ - filter_by(user_id=user_id).filter("tenant_id is null").\ - filter_by(role_id=role_id).first() - else: - result = session.query(models.UserRoleAssociation).\ - filter_by(user_id=user_id).filter_by(tenant_id=tenant_id).\ - filter_by(role_id=role_id).first() - - if result: - result['role_id'] = str(result['role_id']) - if hasattr(api.USER, 'uid_to_id'): - result.user_id = api.USER.id_to_uid(result.user_id) - if hasattr(api.TENANT, 'uid_to_id'): - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return RoleAPI.to_ura_model(result) - - -def get(): - return RoleAPI() diff --git a/keystone/backends/sqlalchemy/api/service.py b/keystone/backends/sqlalchemy/api/service.py deleted file mode 100644 index 3e546106..00000000 --- a/keystone/backends/sqlalchemy/api/service.py +++ /dev/null @@ -1,194 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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.backends.sqlalchemy import get_session, models -from keystone.backends import api -from keystone.models import Service - - -# pylint: disable=E1103,W0221 -class ServiceAPI(api.BaseServiceAPI): - def __init__(self, *args, **kw): - super(ServiceAPI, self).__init__(*args, **kw) - - # pylint: disable=W0221 - @staticmethod - def transpose(values): - """ Handles transposing field names from Keystone model to - sqlalchemy model - - Differences: - desc <-> description - id <-> uid (coming soon) - """ - if 'description' in values: - values['desc'] = values.pop('description') - - if hasattr(api.USER, 'uid_to_id'): - if 'owner_id' in values: - values['owner_id'] = api.USER.uid_to_id(values['owner_id']) - elif hasattr(values, 'owner_id'): - values.owner_id = api.USER.uid_to_id(values.owner_id) - - @staticmethod - def from_model(ref): - """ Returns SQLAlchemy model object based on Keystone model""" - if ref: - if hasattr(api.USER, 'uid_to_id'): - if 'owner_id' in ref: - ref['owner_id'] = api.USER.uid_to_id(ref['owner_id']) - elif hasattr(ref, 'owner_id'): - ref.owner_id = api.USER.uid_to_id(ref.owner_id) - - result = models.Service() - try: - result.id = int(ref.id) - except (AttributeError, TypeError): - # Unspecified or invalid ID -- ignore it. - pass - result.update(ref) - if hasattr(ref, 'description'): - result.desc = ref.description - return result - - @staticmethod - def to_model(ref): - """ Returns Keystone model object based on SQLAlchemy model""" - if ref: - if hasattr(api.USER, 'id_to_uid'): - if 'owner_id' in ref: - ref['owner_id'] = api.USER.id_to_uid(ref['owner_id']) - elif hasattr(ref, 'owner_id'): - ref.owner_id = api.USER.id_to_uid(ref.owner_id) - - return Service(id=str(ref.id), name=ref.name, description=ref.desc, - type=ref.type, owner_id=ref.owner_id) - - @staticmethod - def to_model_list(refs): - return [ServiceAPI.to_model(ref) for ref in refs] - - # pylint: disable=W0221 - def create(self, values): - service_ref = ServiceAPI.from_model(values) - service_ref.save() - return ServiceAPI.to_model(service_ref) - - @staticmethod - def update(id, values, session=None): - if not session: - session = get_session() - - ServiceAPI.transpose(values) - - with session.begin(): - service_ref = session.query(models.Service).filter_by(id=id).\ - first() - service_ref.update(values) - service_ref.save(session=session) - - def get(self, id, session=None): - if id is None: - return None - - session = session or get_session() - return ServiceAPI.to_model(session.query(models.Service). - filter_by(id=id).first()) - - def get_by_name(self, name, session=None): - if not session: - session = get_session() - return ServiceAPI.to_model(session.query(models.Service). - filter_by(name=name).first()) - - def get_by_name_and_type(self, name, type, session=None): - if not session: - session = get_session() - result = session.query(models.Service).\ - filter_by(name=name).\ - filter_by(type=type).\ - first() - return ServiceAPI.to_model(result) - - def get_all(self, session=None): - if not session: - session = get_session() - return ServiceAPI.to_model_list(session.query(models.Service).all()) - - def get_page(self, marker, limit, session=None): - if not session: - session = get_session() - if marker: - return session.query(models.Service).filter("id>:marker").params( - marker='%s' % marker).order_by( - models.Service.id.desc()).limit(int(limit)).all() - else: - return session.query(models.Service).order_by( - models.Service.id.desc()).limit( - int(limit)).all() - - @staticmethod - def get_page_markers(marker, limit, session=None): - if not session: - session = get_session() - first = session.query(models.Service).order_by( - models.Service.id).first() - last = session.query(models.Service).order_by( - models.Service.id.desc()).first() - if first is None: - return (None, None) - if marker is None: - marker = first.id - next_page = session.query(models.Service).\ - filter("id > :marker").params( - marker='%s' % marker).order_by( - models.Service.id).limit(int(limit)).all() - prev_page = session.query(models.Service).\ - filter("id < :marker").params( - marker='%s' % marker).order_by( - models.Service.id.desc()).limit(int(limit)).all() - if len(next_page) == 0: - next_page = last - else: - for t in next_page: - next_page = t - if len(prev_page) == 0: - prev_page = first - else: - for t in prev_page: - prev_page = t - if prev_page.id == marker: - prev_page = None - else: - prev_page = prev_page.id - if next_page.id == last.id: - next_page = None - else: - next_page = next_page.id - return (prev_page, next_page) - - def delete(self, id, session=None): - if not session: - session = get_session() - with session.begin(): - service_ref = session.query(models.Service).\ - filter_by(id=id).first() - session.delete(service_ref) - - -def get(): - return ServiceAPI() diff --git a/keystone/backends/sqlalchemy/api/tenant.py b/keystone/backends/sqlalchemy/api/tenant.py deleted file mode 100755 index db516b15..00000000 --- a/keystone/backends/sqlalchemy/api/tenant.py +++ /dev/null @@ -1,377 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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 uuid - -from keystone.backends.sqlalchemy import get_session, models, aliased -from keystone.backends import api -from keystone.models import Tenant - - -# pylint: disable=E1103,W0221 -class TenantAPI(api.BaseTenantAPI): - def __init__(self, *args, **kw): - super(TenantAPI, self).__init__(*args, **kw) - - # pylint: disable=W0221 - @staticmethod - def transpose(values): - """ Handles transposing field names from Keystone model to - sqlalchemy mode - - Differences: - desc <-> description - id <-> uid (coming soon) - """ - if 'id' in values: - values['uid'] = values['id'] - del values['id'] - if 'description' in values: - values['desc'] = values['description'] - del values['description'] - if 'enabled' in values: - if values['enabled'] in [1, 'true', 'True', True]: - values['enabled'] = True - else: - values['enabled'] = False - - @staticmethod - def to_model(ref): - """ Returns Keystone model object based on SQLAlchemy model""" - if ref: - return Tenant(id=ref.uid, name=ref.name, description=ref.desc, - enabled=bool(ref.enabled)) - - @staticmethod - def to_model_list(refs): - return [TenantAPI.to_model(ref) for ref in refs] - - def create(self, values): - data = values.copy() - TenantAPI.transpose(data) - tenant_ref = models.Tenant() - tenant_ref.update(data) - if tenant_ref.uid is None: - tenant_ref.uid = uuid.uuid4().hex - tenant_ref.save() - return TenantAPI.to_model(tenant_ref) - - def get(self, id, session=None): - """Returns a tenant by ID. - - .warning:: - - Internally, the provided ID is matched against the ``tenants.UID``, - not the PK (``tenants.id``) column. - - For PK lookups from within the sqlalchemy backend, - use ``_get_by_id()`` instead. - """ - if id is None: - return None - - session = session or get_session() - - result = session.query(models.Tenant).filter_by(uid=id).first() - - return TenantAPI.to_model(result) - - @staticmethod - def _get_by_id(id, session=None): - """Returns a tenant by ID (PK). - - .warning:: - - The provided ID is matched against the PK (``tenants.ID``). - - This is **only** for use within the sqlalchemy backend. - """ - if id is None: - return None - - session = session or get_session() - - return session.query(models.Tenant).filter_by(id=id).first() - - @staticmethod - def id_to_uid(id, session=None): - if id is None: - return None - - session = session or get_session() - tenant = session.query(models.Tenant).filter_by(id=id).first() - return tenant.uid if tenant else None - - @staticmethod - def uid_to_id(uid, session=None): - if uid is None: - return None - - session = session or get_session() - tenant = session.query(models.Tenant).filter_by(uid=uid).first() - return tenant.id if tenant else None - - def get_by_name(self, name, session=None): - session = session or get_session() - - result = session.query(models.Tenant).filter_by(name=name).first() - - return TenantAPI.to_model(result) - - def get_all(self, session=None): - if not session: - session = get_session() - - results = session.query(models.Tenant).all() - - return TenantAPI.to_model_list(results) - - def list_for_user_get_page(self, user_id, marker, limit, session=None): - if not session: - session = get_session() - - user = api.USER.get(user_id) - if hasattr(api.USER, 'uid_to_id'): - backend_user_id = api.USER.uid_to_id(user_id) - else: - backend_user_id = user_id - - ura = aliased(models.UserRoleAssociation) - tenant = aliased(models.Tenant) - q1 = session.query(tenant).join((ura, ura.tenant_id == tenant.id)).\ - filter(ura.user_id == backend_user_id) - if 'tenant_id' in user: - if hasattr(api.TENANT, 'uid_to_id'): - backend_tenant_id = api.TENANT.uid_to_id(user.tenant_id) - else: - backend_tenant_id = user.tenant_id - q2 = session.query(tenant).filter(tenant.id == backend_tenant_id) - q3 = q1.union(q2) - else: - q3 = q1 - if marker: - results = q3.filter("tenant.id>:marker").params( - marker='%s' % marker).order_by( - tenant.id.desc()).limit(int(limit)).all() - else: - results = q3.order_by(tenant.id.desc()).limit(int(limit)).all() - - return TenantAPI.to_model_list(results) - - # pylint: disable=R0912 - def list_for_user_get_page_markers(self, user_id, marker, limit, - session=None): - if not session: - session = get_session() - - user = api.USER.get(user_id) - if hasattr(api.USER, 'uid_to_id'): - backend_user_id = api.USER.uid_to_id(user_id) - else: - backend_user_id = user_id - - ura = aliased(models.UserRoleAssociation) - tenant = aliased(models.Tenant) - q1 = session.query(tenant).join((ura, ura.tenant_id == tenant.id)).\ - filter(ura.user_id == backend_user_id) - if 'tenant_id' in user: - if hasattr(api.TENANT, 'uid_to_id'): - backend_tenant_id = api.TENANT.uid_to_id(user.tenant_id) - else: - backend_tenant_id = user.tenant_id - q2 = session.query(tenant).filter(tenant.id == backend_tenant_id) - q3 = q1.union(q2) - else: - q3 = q1 - - first = q3.order_by(tenant.id).first() - last = q3.order_by(tenant.id.desc()).first() - if first is None: - return (None, None) - if marker is None: - marker = first.id - next_page = q3.filter(tenant.id > marker).order_by( - tenant.id).limit(int(limit)).all() - prev_page = q3.filter(tenant.id > marker).order_by( - tenant.id.desc()).limit(int(limit)).all() - if len(next_page) == 0: - next_page = last - else: - for t in next_page: - next_page = t - if len(prev_page) == 0: - prev_page = first - else: - for t in prev_page: - prev_page = t - if prev_page.id == marker: - prev_page = None - else: - prev_page = prev_page.id - if next_page.id == last.id: - next_page = None - else: - next_page = next_page.id - return (prev_page, next_page) - - def get_page(self, marker, limit, session=None): - if not session: - session = get_session() - - if marker: - tenants = session.query(models.Tenant).\ - filter("id>:marker").params( - marker='%s' % marker).order_by( - models.Tenant.id.desc()).limit(int(limit)).all() - else: - tenants = session.query(models.Tenant).order_by( - models.Tenant.id.desc()).limit(int(limit)).all() - - return self.to_model_list(tenants) - - # pylint: disable=R0912 - def get_page_markers(self, marker, limit, session=None): - if not session: - session = get_session() - first = session.query(models.Tenant).order_by( - models.Tenant.id).first() - last = session.query(models.Tenant).order_by( - models.Tenant.id.desc()).first() - if first is None: - return (None, None) - if marker is None: - marker = first.id - next_page = session.query(models.Tenant).\ - filter("id > :marker").\ - params(marker='%s' % marker).\ - order_by(models.Tenant.id).\ - limit(int(limit)).\ - all() - prev_page = session.query(models.Tenant).\ - filter("id < :marker").\ - params(marker='%s' % marker).\ - order_by(models.Tenant.id.desc()).\ - limit(int(limit)).\ - all() - if len(next_page) == 0: - next_page = last - else: - for t in next_page: - next_page = t - if len(prev_page) == 0: - prev_page = first - else: - for t in prev_page: - prev_page = t - if prev_page.id == marker: - prev_page = None - else: - prev_page = prev_page.id - if next_page.id == last.id: - next_page = None - else: - next_page = next_page.id - return (prev_page, next_page) - - def is_empty(self, id, session=None): - if not session: - session = get_session() - - if hasattr(api.TENANT, 'uid_to_id'): - id = self.uid_to_id(id) - - a_user = session.query(models.UserRoleAssociation).filter_by( - tenant_id=id).first() - if a_user is not None: - return False - a_user = session.query(models.User).filter_by(tenant_id=id).first() - if a_user is not None: - return False - return True - - def update(self, id, values, session=None): - if not session: - session = get_session() - - if hasattr(api.TENANT, 'uid_to_id'): - pkid = self.uid_to_id(id) - else: - pkid = id - - data = values.copy() - TenantAPI.transpose(data) - - with session.begin(): - tenant_ref = self._get_by_id(pkid, session) - tenant_ref.update(data) - tenant_ref.save(session=session) - return self.get(id, session) - - def delete(self, id, session=None): - if not session: - session = get_session() - - if not self.is_empty(id): - raise fault.ForbiddenFault("You may not delete a tenant that " - "contains users") - - if hasattr(api.TENANT, 'uid_to_id'): - id = self.uid_to_id(id) - - with session.begin(): - tenant_ref = self._get_by_id(id, session) - session.delete(tenant_ref) - - def get_all_endpoints(self, tenant_id, session=None): - if not session: - session = get_session() - - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = self.uid_to_id(tenant_id) - - endpoint_templates = aliased(models.EndpointTemplates) - q = session.query(endpoint_templates).\ - filter(endpoint_templates.is_global == True) - if tenant_id: - ep = aliased(models.Endpoints) - q1 = session.query(endpoint_templates).join((ep, - ep.endpoint_template_id == endpoint_templates.id)).\ - filter(ep.tenant_id == tenant_id) - q = q.union(q1) - return q.all() - - def get_role_assignments(self, tenant_id, session=None): - if not session: - session = get_session() - - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = TenantAPI.uid_to_id(tenant_id) - - results = session.query(models.UserRoleAssociation).\ - filter_by(tenant_id=tenant_id) - - for result in results: - if hasattr(api.USER, 'uid_to_id'): - result.user_id = api.USER.id_to_uid(result.user_id) - if hasattr(api.TENANT, 'uid_to_id'): - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return results - - -def get(): - return TenantAPI() diff --git a/keystone/backends/sqlalchemy/api/token.py b/keystone/backends/sqlalchemy/api/token.py deleted file mode 100755 index 869822bb..00000000 --- a/keystone/backends/sqlalchemy/api/token.py +++ /dev/null @@ -1,149 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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.backends.sqlalchemy import get_session, models -from keystone.backends import api -from keystone.models import Token - - -# pylint: disable=E1103,W0221 -class TokenAPI(api.BaseTokenAPI): - def __init__(self, *args, **kw): - super(TokenAPI, self).__init__(*args, **kw) - - @staticmethod - def transpose(ref): - """ Transposes field names from domain to sql model""" - if hasattr(api.TENANT, 'uid_to_id'): - if 'tenant_id' in ref: - ref['tenant_id'] = api.TENANT.uid_to_id(ref['tenant_id']) - elif hasattr(ref, 'tenant_id'): - ref.tenant_id = api.TENANT.uid_to_id(ref.tenant_id) - - if hasattr(api.USER, 'uid_to_id'): - if 'user_id' in ref: - ref['user_id'] = api.USER.uid_to_id(ref['user_id']) - elif hasattr(ref, 'tenant_id'): - ref.user_id = api.USER.uid_to_id(ref.user_id) - - @staticmethod - def to_model(ref): - """ Returns Keystone model object based on SQLAlchemy model""" - if ref: - if hasattr(api.TENANT, 'uid_to_id'): - if 'tenant_id' in ref: - ref['tenant_id'] = api.TENANT.id_to_uid(ref['tenant_id']) - elif hasattr(ref, 'tenant_id'): - ref.tenant_id = api.TENANT.id_to_uid(ref.tenant_id) - - if hasattr(api.USER, 'uid_to_id'): - if 'user_id' in ref: - ref['user_id'] = api.USER.id_to_uid(ref['user_id']) - elif hasattr(ref, 'user_id'): - ref.user_id = api.USER.id_to_uid(ref.user_id) - - return Token(id=ref.id, user_id=ref.user_id, expires=ref.expires, - tenant_id=ref.tenant_id) - - @staticmethod - def to_model_list(refs): - return [TokenAPI.to_model(ref) for ref in refs] - - def create(self, values): - data = values.copy() - TokenAPI.transpose(data) - token_ref = models.Token() - token_ref.update(data) - token_ref.save() - return TokenAPI.to_model(token_ref) - - def get(self, id, session=None): - result = self._get(id, session) - - return TokenAPI.to_model(result) - - @staticmethod - def _get(id, session=None): - if id is None: - return None - - session = session or get_session() - - result = session.query(models.Token).filter_by(id=id).first() - - return result - - @staticmethod - def update(id, values, session=None): - if not session: - session = get_session() - - TokenAPI.transpose(values) - - with session.begin(): - ref = session.query(models.Token).filter_by(id=id).first() - ref.update(values) - ref.save(session=session) - - def delete(self, id, session=None): - if not session: - session = get_session() - - with session.begin(): - token_ref = self._get(id, session) - session.delete(token_ref) - - def get_for_user(self, user_id, session=None): - if not session: - session = get_session() - - if hasattr(api.USER, 'uid_to_id'): - user_id = api.USER.uid_to_id(user_id) - - result = session.query(models.Token).filter_by( - user_id=user_id, tenant_id=None).order_by("expires desc").first() - - return TokenAPI.to_model(result) - - def get_for_user_by_tenant(self, user_id, tenant_id, session=None): - if not session: - session = get_session() - - if hasattr(api.USER, 'uid_to_id'): - user_id = api.USER.uid_to_id(user_id) - - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - result = session.query(models.Token).\ - filter_by(user_id=user_id, tenant_id=tenant_id).\ - order_by("expires desc").\ - first() - - return TokenAPI.to_model(result) - - def get_all(self, session=None): - if not session: - session = get_session() - - results = session.query(models.Token).all() - - return TokenAPI.to_model_list(results) - - -def get(): - return TokenAPI() diff --git a/keystone/backends/sqlalchemy/api/user.py b/keystone/backends/sqlalchemy/api/user.py deleted file mode 100755 index f5a64b18..00000000 --- a/keystone/backends/sqlalchemy/api/user.py +++ /dev/null @@ -1,458 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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 uuid - -import keystone.backends.backendutils as utils -from keystone.backends.sqlalchemy import get_session, models, aliased, \ - joinedload -from keystone.backends import api -from keystone.models import User - - -# pylint: disable=E1103,W0221,W0223 -class UserAPI(api.BaseUserAPI): - def __init__(self, *args, **kw): - super(UserAPI, self).__init__(*args, **kw) - - @staticmethod - def transpose(ref): - """ Transposes field names from domain to sql model""" - if 'id' in ref: - ref['uid'] = ref.pop('id') - - if hasattr(api.TENANT, 'uid_to_id'): - if 'tenant_id' in ref: - ref['tenant_id'] = api.TENANT.uid_to_id(ref['tenant_id']) - elif hasattr(ref, 'tenant_id'): - ref.tenant_id = api.TENANT.uid_to_id(ref.tenant_id) - - @staticmethod - def to_model(ref): - """ Returns Keystone model object based on SQLAlchemy model""" - if ref: - if hasattr(api.TENANT, 'uid_to_id'): - if 'tenant_id' in ref: - ref['tenant_id'] = api.TENANT.id_to_uid(ref['tenant_id']) - elif hasattr(ref, 'tenant_id'): - ref.tenant_id = api.TENANT.id_to_uid(ref.tenant_id) - - return User(id=ref.uid, password=ref.password, name=ref.name, - tenant_id=ref.tenant_id, email=ref.email, - enabled=bool(ref.enabled)) - - @staticmethod - def to_model_list(refs): - return [UserAPI.to_model(ref) for ref in refs] - - # pylint: disable=W0221 - def get_all(self, session=None): - if not session: - session = get_session() - - results = session.query(models.User) - - return UserAPI.to_model_list(results) - - def create(self, values): - data = values.copy() - UserAPI.transpose(data) - utils.set_hashed_password(data) - if 'uid' not in data or data['uid'] is None: - data['uid'] = uuid.uuid4().hex - user_ref = models.User() - user_ref.update(data) - user_ref.save() - return UserAPI.to_model(user_ref) - - def get(self, id, session=None): - if id is None: - return None - - session = session or get_session() - - result = session.query(models.User).filter_by(uid=str(id)).first() - - return UserAPI.to_model(result) - - @staticmethod - def _get_by_id(id, session=None): - """Only for use by the sql backends - - - Queries by PK ID - - Doesn't wrap result with domain layer models - """ - if id is None: - return None - - session = session or get_session() - - # TODO(dolph): user ID's are NOT strings... why is this is being cast? - return session.query(models.User).filter_by(id=str(id)).first() - - @staticmethod - def id_to_uid(id, session=None): - if id is None: - return None - - session = session or get_session() - user = session.query(models.User).filter_by(id=str(id)).first() - return user.uid if user else None - - @staticmethod - def uid_to_id(uid, session=None): - if uid is None: - return None - - session = session or get_session() - user = session.query(models.User).filter_by(uid=str(uid)).first() - return user.id if user else None - - def get_by_name(self, name, session=None): - if not session: - session = get_session() - - result = session.query(models.User).filter_by(name=name).first() - - return UserAPI.to_model(result) - - def get_by_email(self, email, session=None): - if not session: - session = get_session() - - result = session.query(models.User).filter_by(email=email).first() - - return UserAPI.to_model(result) - - def get_page(self, marker, limit, session=None): - if not session: - session = get_session() - - if marker: - results = session.query(models.User).filter("id>:marker").params( - marker='%s' % marker).order_by( - models.User.id.desc()).limit(int(limit)).all() - else: - results = session.query(models.User).order_by( - models.User.id.desc()).limit(int(limit)).all() - - return UserAPI.to_model_list(results) - - # pylint: disable=R0912 - def get_page_markers(self, marker, limit, session=None): - if not session: - session = get_session() - - first = session.query(models.User).order_by( - models.User.id).first() - last = session.query(models.User).order_by( - models.User.id.desc()).first() - if first is None: - return (None, None) - if marker is None: - marker = first.id - next_page = session.query(models.User).filter("id > :marker").params( - marker='%s' % marker).order_by( - models.User.id).limit(int(limit)).all() - prev_page = session.query(models.User).filter("id < :marker").params( - marker='%s' % marker).order_by( - models.User.id.desc()).limit(int(limit)).all() - if len(next_page) == 0: - next_page = last - else: - for t in next_page: - next_page = t - if len(prev_page) == 0: - prev_page = first - else: - for t in prev_page: - prev_page = t - if prev_page.id == marker: - prev_page = None - else: - prev_page = prev_page.id - if next_page.id == last.id: - next_page = None - else: - next_page = next_page.id - return (prev_page, next_page) - - def user_roles_by_tenant(self, user_id, tenant_id, session=None): - if not session: - session = get_session() - - if hasattr(api.USER, 'uid_to_id'): - user_id = api.USER.uid_to_id(user_id) - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - results = session.query(models.UserRoleAssociation).\ - filter_by(user_id=user_id, tenant_id=tenant_id).\ - options(joinedload('roles')) - - for result in results: - if hasattr(api.USER, 'id_to_uid'): - result.user_id = api.USER.id_to_uid(result.user_id) - if hasattr(api.TENANT, 'id_to_uid'): - result.tenant_id = api.TENANT.id_to_uid(result.tenant_id) - - return results - - def update(self, id, values, session=None): - if not session: - session = get_session() - - UserAPI.transpose(values) - - with session.begin(): - user_ref = session.query(models.User).filter_by(uid=id).first() - utils.set_hashed_password(values) - user_ref.update(values) - user_ref.save(session=session) - - def delete(self, id, session=None): - if not session: - session = get_session() - - with session.begin(): - user_ref = session.query(models.User).filter_by(uid=id).first() - session.delete(user_ref) - - def get_by_tenant(self, id, tenant_id, session=None): - if not session: - session = get_session() - - uid = id - - if hasattr(api.USER, 'uid_to_id'): - id = api.USER.uid_to_id(uid) - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - # Most common use case: user lives in tenant - user = session.query(models.User).\ - filter_by(id=id, tenant_id=tenant_id).first() - if user: - return UserAPI.to_model(user) - - # Find user through grants to this tenant - result = session.query(models.UserRoleAssociation).\ - filter_by(tenant_id=tenant_id, user_id=id).first() - if result: - return self.get(uid, session) - else: - return None - - def users_get_by_tenant(self, user_id, tenant_id, session=None): - if not session: - session = get_session() - - if hasattr(api.USER, 'uid_to_id'): - user_id = api.USER.uid_to_id(user_id) - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - results = session.query(models.User).filter_by(id=user_id, - tenant_id=tenant_id) - return UserAPI.to_model_list(results) - - def user_role_add(self, values): - if hasattr(api.USER, 'uid_to_id'): - values['user_id'] = api.USER.uid_to_id(values['user_id']) - if hasattr(api.TENANT, 'uid_to_id'): - values['tenant_id'] = api.TENANT.uid_to_id(values['tenant_id']) - - user_rolegrant = models.UserRoleAssociation() - user_rolegrant.update(values) - user_rolegrant.save() - - if hasattr(api.USER, 'id_to_uid'): - user_rolegrant.user_id = api.USER.id_to_uid(user_rolegrant.user_id) - if hasattr(api.TENANT, 'id_to_uid'): - user_rolegrant.tenant_id = api.TENANT.id_to_uid( - user_rolegrant.tenant_id) - - return user_rolegrant - - def users_get_page(self, marker, limit, session=None): - if not session: - session = get_session() - - user = aliased(models.User) - if marker: - results = session.query(user).\ - filter("id>=:marker").params( - marker='%s' % marker).order_by( - "id").limit(int(limit)).all() - else: - results = session.query(user).\ - order_by("id").limit(int(limit)).all() - - return UserAPI.to_model_list(results) - - # pylint: disable=R0912 - def users_get_page_markers(self, marker, limit, session=None): - if not session: - session = get_session() - - user = aliased(models.User) - first = session.query(user).\ - order_by(user.id).first() - last = session.query(user).\ - order_by(user.id.desc()).first() - if first is None: - return (None, None) - if marker is None: - marker = first.id - next_page = session.query(user).\ - filter("id > :marker").params( - marker='%s' % marker).order_by(user.id).\ - limit(int(limit)).all() - prev_page = session.query(user).\ - filter("id < :marker").params( - marker='%s' % marker).order_by( - user.id.desc()).limit(int(limit)).all() - next_len = len(next_page) - prev_len = len(prev_page) - - if next_len == 0: - next_page = last - else: - for t in next_page: - next_page = t - if prev_len == 0: - prev_page = first - else: - for t in prev_page: - prev_page = t - if first.id == marker: - prev_page = None - else: - prev_page = prev_page.id - if marker == last.id: - next_page = None - else: - next_page = next_page.id - return (prev_page, next_page) - - def users_get_by_tenant_get_page(self, tenant_id, role_id, marker, limit, - session=None): - # This is broken. If a user has more than one role per project - # shit hits the fan because we're limiting the wrong model. - # Also the user lookup is nasty and potentially injectiable. - if not session: - session = get_session() - - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - user = aliased(models.UserRoleAssociation) - query = session.query(user).\ - filter("tenant_id = :tenant_id").\ - params(tenant_id='%s' % tenant_id) - - if role_id: - query = query.filter( - user.role_id == role_id) - - if marker: - rv = query.filter("id>=:marker").\ - params(marker='%s' % marker).\ - order_by("id").\ - limit(int(limit)).\ - all() - else: - rv = query.\ - order_by("id").\ - limit(int(limit)).\ - all() - - user_ids = set([str(assoc.user_id) for assoc in rv]) - users = session.query(models.User).\ - filter("id in ('%s')" % "','".join(user_ids)).\ - all() - - for usr in users: - usr.tenant_roles = set() - for role in usr.roles: - if role.tenant_id == tenant_id: - usr.tenant_roles.add(role.role_id) - - return UserAPI.to_model_list(users) - - # pylint: disable=R0912 - def users_get_by_tenant_get_page_markers(self, tenant_id, \ - role_id, marker, limit, session=None): - if not session: - session = get_session() - - if hasattr(api.TENANT, 'uid_to_id'): - tenant_id = api.TENANT.uid_to_id(tenant_id) - - user = aliased(models.UserRoleAssociation) - query = session.query(user).\ - filter(user.tenant_id == tenant_id) - if role_id: - query = query.filter( - user.role_id == role_id) - first = query.\ - order_by(user.id).first() - last = query.\ - order_by(user.id.desc()).first() - if first is None: - return (None, None) - if marker is None: - marker = first.id - next_page = query.\ - filter("id > :marker").params( - marker='%s' % marker).order_by(user.id).\ - limit(int(limit)).all() - prev_page = query.\ - filter("id < :marker").params( - marker='%s' % marker).order_by( - user.id.desc()).limit(int(limit)).all() - next_len = len(next_page) - prev_len = len(prev_page) - - if next_len == 0: - next_page = last - else: - for t in next_page: - next_page = t - if prev_len == 0: - prev_page = first - else: - for t in prev_page: - prev_page = t - if first.id == marker: - prev_page = None - else: - prev_page = prev_page.id - if marker == last.id: - next_page = None - else: - next_page = next_page.id - return (prev_page, next_page) - - def check_password(self, user_id, password): - user = self.get(user_id) - return utils.check_password(password, user.password) - # pylint: enable=W0221 - - -def get(): - return UserAPI() diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/001_initial_migration.py b/keystone/backends/sqlalchemy/migrate_repo/versions/001_initial_migration.py deleted file mode 100644 index 45b69685..00000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/001_initial_migration.py +++ /dev/null @@ -1,196 +0,0 @@ -# pylint: disable=C0103,R0801 - - -import sqlalchemy - - -meta = sqlalchemy.MetaData() - - -# services - -service = {} -service['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, - primary_key=True, autoincrement=True) -service['name'] = sqlalchemy.Column('name', sqlalchemy.String(255), - unique=True) -service['type'] = sqlalchemy.Column('type', sqlalchemy.String(255)) -service['desc'] = sqlalchemy.Column('desc', sqlalchemy.String(255)) -services = sqlalchemy.Table('services', meta, *service.values()) - -sqlalchemy.UniqueConstraint(service['name']) - - -# roles - -role = {} -role['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, - primary_key=True, autoincrement=True) -role['name'] = sqlalchemy.Column('name', sqlalchemy.String(255)) -role['desc'] = sqlalchemy.Column('desc', sqlalchemy.String(255)) -role['service_id'] = sqlalchemy.Column('service_id', sqlalchemy.Integer) -roles = sqlalchemy.Table('roles', meta, *role.values()) - -sqlalchemy.UniqueConstraint(role['name'], role['service_id']) - -sqlalchemy.ForeignKeyConstraint( - [role['service_id']], - [service['id']]) - - -# tenants - -tenant = {} -tenant['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - autoincrement=True) -tenant['name'] = sqlalchemy.Column('name', sqlalchemy.String(255), unique=True) -tenant['desc'] = sqlalchemy.Column('desc', sqlalchemy.String(255)) -tenant['enabled'] = sqlalchemy.Column('enabled', sqlalchemy.Integer) -tenants = sqlalchemy.Table('tenants', meta, *tenant.values()) - -sqlalchemy.UniqueConstraint(tenant['name']) - - -# users - -user = {} -user['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - autoincrement=True) -user['name'] = sqlalchemy.Column('name', sqlalchemy.String(255), unique=True) -user['password'] = sqlalchemy.Column('password', sqlalchemy.String(255)) -user['email'] = sqlalchemy.Column('email', sqlalchemy.String(255)) -user['enabled'] = sqlalchemy.Column('enabled', sqlalchemy.Integer) -user['tenant_id'] = sqlalchemy.Column('tenant_id', sqlalchemy.Integer) -users = sqlalchemy.Table('users', meta, *user.values()) - -sqlalchemy.UniqueConstraint(user['name']) - -sqlalchemy.ForeignKeyConstraint( - [user['tenant_id']], - [tenant['id']]) - - -# credentials - -credential = {} -credential['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, - primary_key=True, autoincrement=True) -credential['user_id'] = sqlalchemy.Column('user_id', sqlalchemy.Integer) -credential['tenant_id'] = sqlalchemy.Column('tenant_id', sqlalchemy.Integer, - nullable=True) -credential['type'] = sqlalchemy.Column('type', sqlalchemy.String(20)) -credential['key'] = sqlalchemy.Column('key', sqlalchemy.String(255)) -credential['secret'] = sqlalchemy.Column('secret', sqlalchemy.String(255)) -credentials = sqlalchemy.Table('credentials', meta, *credential.values()) - -sqlalchemy.ForeignKeyConstraint( - [credential['user_id']], - [user['id']]) -sqlalchemy.ForeignKeyConstraint( - [credential['tenant_id']], - [tenant['id']]) - - -# tokens - -token = {} -token['id'] = sqlalchemy.Column('id', sqlalchemy.String(255), primary_key=True, - unique=True) -token['user_id'] = sqlalchemy.Column('user_id', sqlalchemy.Integer) -token['tenant_id'] = sqlalchemy.Column('tenant_id', sqlalchemy.Integer) -token['expires'] = sqlalchemy.Column('expires', sqlalchemy.DateTime) -tokens = sqlalchemy.Table('token', meta, *token.values()) - - -# endpoint_templates - -endpoint_template = {} -endpoint_template['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, - primary_key=True) -endpoint_template['region'] = sqlalchemy.Column('region', - sqlalchemy.String(255)) -endpoint_template['service_id'] = sqlalchemy.Column('service_id', - sqlalchemy.Integer) -endpoint_template['public_url'] = sqlalchemy.Column('public_url', - sqlalchemy.String(2000)) -endpoint_template['admin_url'] = sqlalchemy.Column('admin_url', - sqlalchemy.String(2000)) -endpoint_template['internal_url'] = sqlalchemy.Column('internal_url', - sqlalchemy.String(2000)) -endpoint_template['enabled'] = sqlalchemy.Column('enabled', - sqlalchemy.Boolean) -endpoint_template['is_global'] = sqlalchemy.Column('is_global', - sqlalchemy.Boolean) -endpoint_templates = sqlalchemy.Table('endpoint_templates', meta, - *endpoint_template.values()) - -sqlalchemy.ForeignKeyConstraint( - [endpoint_template['service_id']], [service['id']]) - - -# endpoints - -endpoint = {} -endpoint['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True) -endpoint['tenant_id'] = sqlalchemy.Column('tenant_id', sqlalchemy.Integer) -endpoint['endpoint_template_id'] = sqlalchemy.Column('endpoint_template_id', - sqlalchemy.Integer) -endpoints = sqlalchemy.Table('endpoints', meta, *endpoint.values()) - -sqlalchemy.UniqueConstraint( - endpoint['endpoint_template_id'], endpoint['tenant_id']) - -sqlalchemy.ForeignKeyConstraint( - [endpoint['endpoint_template_id']], - [endpoint_template['id']]) - - -# user_roles - -user_role = {} -user_role['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True) -user_role['user_id'] = sqlalchemy.Column('user_id', sqlalchemy.Integer) -user_role['role_id'] = sqlalchemy.Column('role_id', sqlalchemy.Integer) -user_role['tenant_id'] = sqlalchemy.Column('tenant_id', sqlalchemy.Integer) -user_roles = sqlalchemy.Table('user_roles', meta, *user_role.values()) - -sqlalchemy.UniqueConstraint( - user_role['user_id'], user_role['role_id'], user_role['tenant_id']) - -sqlalchemy.ForeignKeyConstraint( - [user_role['user_id']], - [user['id']]) -sqlalchemy.ForeignKeyConstraint( - [user_role['role_id']], - [role['id']]) -sqlalchemy.ForeignKeyConstraint( - [user_role['tenant_id']], - [tenant['id']]) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - - user_roles.create() - endpoints.create() - roles.create() - services.create() - tenants.create() - users.create() - credentials.create() - tokens.create() - endpoint_templates.create() - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - - user_roles.drop() - endpoints.drop() - roles.drop() - services.drop() - tenants.drop() - users.drop() - credentials.drop() - tokens.drop() - endpoint_templates.drop() diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/002_rename_token_table.py b/keystone/backends/sqlalchemy/migrate_repo/versions/002_rename_token_table.py deleted file mode 100644 index e350b2a4..00000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/002_rename_token_table.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -Addresses bug 854425 - -Renames the 'token' table to 'tokens', -in order to appear more consistent with -other table names. -""" -# pylint: disable=C0103,R0801 - - -import sqlalchemy - - -meta = sqlalchemy.MetaData() - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - # pylint: disable=E1101 - sqlalchemy.Table('token', meta).rename('tokens') - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - # pylint: disable=E1101 - sqlalchemy.Table('tokens', meta).rename('token') diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/003_add_endpoint_template_versions.py b/keystone/backends/sqlalchemy/migrate_repo/versions/003_add_endpoint_template_versions.py deleted file mode 100644 index be5bde24..00000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/003_add_endpoint_template_versions.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -Adds support for versioning endpoint templates -""" -# pylint: disable=C0103,R0801 - - -import sqlalchemy -import migrate - - -meta = sqlalchemy.MetaData() - -endpoint_template = {} -endpoint_template['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, - primary_key=True) -endpoint_template['region'] = sqlalchemy.Column('region', - sqlalchemy.String(255)) -endpoint_template['service_id'] = sqlalchemy.Column('service_id', - sqlalchemy.Integer) -endpoint_template['public_url'] = sqlalchemy.Column('public_url', - sqlalchemy.String(2000)) -endpoint_template['admin_url'] = sqlalchemy.Column('admin_url', - sqlalchemy.String(2000)) -endpoint_template['internal_url'] = sqlalchemy.Column('internal_url', - sqlalchemy.String(2000)) -endpoint_template['enabled'] = sqlalchemy.Column('enabled', - sqlalchemy.Boolean) -endpoint_template['is_global'] = sqlalchemy.Column('is_global', - sqlalchemy.Boolean) -endpoint_templates = sqlalchemy.Table('endpoint_templates', meta, - *endpoint_template.values()) - -version_id = sqlalchemy.Column('version_id', sqlalchemy.String(20), - nullable=True) -version_list = sqlalchemy.Column('version_list', sqlalchemy.String(2000), - nullable=True) -version_info = sqlalchemy.Column('version_info', sqlalchemy.String(500), - nullable=True) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - - migrate.create_column(version_id, endpoint_templates) - assert endpoint_templates.c.version_id is version_id - - migrate.create_column(version_list, endpoint_templates) - assert endpoint_templates.c.version_list is version_list - - migrate.create_column(version_info, endpoint_templates) - assert endpoint_templates.c.version_info is version_info - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - - migrate.drop_column(version_id, endpoint_templates) - assert not hasattr(endpoint_templates.c, 'version_id') - - migrate.drop_column(version_list, endpoint_templates) - assert not hasattr(endpoint_templates.c, 'version_list') - - migrate.drop_column(version_info, endpoint_templates) - assert not hasattr(endpoint_templates.c, 'version_info') diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/004_add_service_owner.py b/keystone/backends/sqlalchemy/migrate_repo/versions/004_add_service_owner.py deleted file mode 100644 index 4bf02c8e..00000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/004_add_service_owner.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -Adds support for owner id in services -""" -# pylint: disable=C0103,R0801 - - -import sqlalchemy -import migrate - - -meta = sqlalchemy.MetaData() - -service = {} -service['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, - primary_key=True, autoincrement=True) -service['name'] = sqlalchemy.Column('name', sqlalchemy.String(255), - unique=True) -service['type'] = sqlalchemy.Column('type', sqlalchemy.String(255)) -service['desc'] = sqlalchemy.Column('desc', sqlalchemy.String(255)) -services = sqlalchemy.Table('services', meta, *service.values()) - -owner_id = sqlalchemy.Column('owner_id', sqlalchemy.Integer, - nullable=True) - -sqlalchemy.UniqueConstraint(service['name']) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - - migrate.create_column(owner_id, services) - assert services.c.owner_id is owner_id - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - - migrate.drop_column(owner_id, services) - assert not hasattr(services.c, 'owner_id') diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/005_add_tenants_uid.py b/keystone/backends/sqlalchemy/migrate_repo/versions/005_add_tenants_uid.py deleted file mode 100644 index ab8e7305..00000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/005_add_tenants_uid.py +++ /dev/null @@ -1,38 +0,0 @@ -# pylint: disable=C0103,R0801 - - -import sqlalchemy -import migrate - - -meta = sqlalchemy.MetaData() - - -# define the previous state of tenants - -tenant = {} -tenant['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - autoincrement=True) -tenant['name'] = sqlalchemy.Column('name', sqlalchemy.String(255), unique=True) -tenant['desc'] = sqlalchemy.Column('desc', sqlalchemy.String(255)) -tenant['enabled'] = sqlalchemy.Column('enabled', sqlalchemy.Integer) -tenants = sqlalchemy.Table('tenants', meta, *tenant.values()) - - -# this column will become unique/non-nullable after populating it -tenant_uid = sqlalchemy.Column('uid', sqlalchemy.String(255), - unique=False, nullable=True) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - - migrate.create_column(tenant_uid, tenants) - assert tenants.c.uid is tenant_uid - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - - migrate.drop_column(tenant_uid, tenants) - assert not hasattr(tenants.c, 'uid') diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/006_populate_tenants_uid.py b/keystone/backends/sqlalchemy/migrate_repo/versions/006_populate_tenants_uid.py deleted file mode 100644 index 8e5ea2d4..00000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/006_populate_tenants_uid.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -Data migration to populate tenants.uid with existing tenants.id values. -""" -# pylint: disable=C0103,R0801 - - -import sqlalchemy - - -meta = sqlalchemy.MetaData() - - -# define the previous state of tenants - -tenant = {} -tenant['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - autoincrement=True) -tenant['uid'] = sqlalchemy.Column('uid', sqlalchemy.String(255), unique=False, - nullable=True) -tenant['name'] = sqlalchemy.Column('name', sqlalchemy.String(255), unique=True) -tenant['desc'] = sqlalchemy.Column('desc', sqlalchemy.String(255)) -tenant['enabled'] = sqlalchemy.Column('enabled', sqlalchemy.Integer) -tenants = sqlalchemy.Table('tenants', meta, *tenant.values()) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - - dtenants = tenants.select().execute() - for dtenant in dtenants: - whereclause = "`id`='%s'" % (dtenant.id) - values = {'uid': str(dtenant.id)} - - tenants.update(whereclause=whereclause, values=values).execute() - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - - tenants.update(values={'uid': None}).execute() diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/007_make_tenants_uid_unique.py b/keystone/backends/sqlalchemy/migrate_repo/versions/007_make_tenants_uid_unique.py deleted file mode 100644 index 555d3c7f..00000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/007_make_tenants_uid_unique.py +++ /dev/null @@ -1,59 +0,0 @@ -""" -Schema migration to enforce uniqueness on tenants.uid -""" -# pylint: disable=C0103,R0801 - - -import sqlalchemy -import migrate -from migrate.changeset import constraint - - -meta = sqlalchemy.MetaData() - - -# define the previous state of tenants - -tenant = {} -tenant['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - autoincrement=True) -tenant['uid'] = sqlalchemy.Column('uid', sqlalchemy.String(255), unique=False, - nullable=True) -tenant['name'] = sqlalchemy.Column('name', sqlalchemy.String(255), unique=True) -tenant['desc'] = sqlalchemy.Column('desc', sqlalchemy.String(255)) -tenant['enabled'] = sqlalchemy.Column('enabled', sqlalchemy.Integer) -tenants = sqlalchemy.Table('tenants', meta, *tenant.values()) - - -unique_constraint = constraint.UniqueConstraint(tenant['uid']) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - - tenant['uid'].alter(nullable=False) - assert not tenants.c.uid.nullable - - unique_constraint.create() - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - - try: - # this is NOT supported in sqlite! - # but let's try anyway, in case it is - unique_constraint.drop() - except migrate.exceptions.NotSupportedError, e: - if migrate_engine.name == 'sqlite': - # skipping the constraint drop doesn't seem to cause any issues - # *in sqlite* - # as constraints are only checked on row insert/update, - # and don't apply to nulls. - print 'WARNING: Skipping dropping unique constraint ' \ - 'from `tenants`, UNIQUE (uid)' - else: - raise e - - tenant['uid'].alter(nullable=True) - assert tenants.c.uid.nullable diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/008_add_users_uid.py b/keystone/backends/sqlalchemy/migrate_repo/versions/008_add_users_uid.py deleted file mode 100644 index c634cae9..00000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/008_add_users_uid.py +++ /dev/null @@ -1,40 +0,0 @@ -# pylint: disable=C0103,R0801 - - -import sqlalchemy -import migrate - - -meta = sqlalchemy.MetaData() - - -# define the previous state of users - -user = {} -user['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - autoincrement=True) -user['name'] = sqlalchemy.Column('name', sqlalchemy.String(255), unique=True) -user['password'] = sqlalchemy.Column('password', sqlalchemy.String(255)) -user['email'] = sqlalchemy.Column('email', sqlalchemy.String(255)) -user['enabled'] = sqlalchemy.Column('enabled', sqlalchemy.Integer) -user['tenant_id'] = sqlalchemy.Column('tenant_id', sqlalchemy.Integer) -users = sqlalchemy.Table('users', meta, *user.values()) - - -# this column will become unique/non-nullable after populating it -user_uid = sqlalchemy.Column('uid', sqlalchemy.String(255), - unique=False, nullable=True) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - - migrate.create_column(user_uid, users) - assert users.c.uid is user_uid - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - - migrate.drop_column(user_uid, users) - assert not hasattr(users.c, 'uid') diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/009_populate_users_uid.py b/keystone/backends/sqlalchemy/migrate_repo/versions/009_populate_users_uid.py deleted file mode 100644 index c769eaa5..00000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/009_populate_users_uid.py +++ /dev/null @@ -1,42 +0,0 @@ -""" -Data migration to populate users.uid with existing users.id values. -""" -# pylint: disable=C0103,R0801 - - -import sqlalchemy - - -meta = sqlalchemy.MetaData() - - -# define the previous state of users - -user = {} -user['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - autoincrement=True) -user['uid'] = sqlalchemy.Column('uid', sqlalchemy.String(255), unique=False, - nullable=True) -user['name'] = sqlalchemy.Column('name', sqlalchemy.String(255), unique=True) -user['password'] = sqlalchemy.Column('password', sqlalchemy.String(255)) -user['email'] = sqlalchemy.Column('email', sqlalchemy.String(255)) -user['enabled'] = sqlalchemy.Column('enabled', sqlalchemy.Integer) -user['tenant_id'] = sqlalchemy.Column('tenant_id', sqlalchemy.Integer) -users = sqlalchemy.Table('users', meta, *user.values()) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - - dusers = users.select().execute() - for duser in dusers: - whereclause = "`id`='%s'" % (duser.id) - values = {'uid': str(duser.id)} - - users.update(whereclause=whereclause, values=values).execute() - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - - users.update(values={'uid': None}).execute() diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/010_make_users_uid_unique.py b/keystone/backends/sqlalchemy/migrate_repo/versions/010_make_users_uid_unique.py deleted file mode 100644 index 164be7d4..00000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/010_make_users_uid_unique.py +++ /dev/null @@ -1,60 +0,0 @@ -""" -Schema migration to enforce uniqueness on users.uid -""" -# pylint: disable=C0103,R0801 - - -import sqlalchemy -import migrate -from migrate.changeset import constraint - - -meta = sqlalchemy.MetaData() - - -# define the previous state of users - -user = {} -user['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - autoincrement=True) -user['uid'] = sqlalchemy.Column('uid', sqlalchemy.String(255), unique=False, - nullable=True) -user['name'] = sqlalchemy.Column('name', sqlalchemy.String(255), unique=True) -user['password'] = sqlalchemy.Column('password', sqlalchemy.String(255)) -user['email'] = sqlalchemy.Column('email', sqlalchemy.String(255)) -user['enabled'] = sqlalchemy.Column('enabled', sqlalchemy.Integer) -user['tenant_id'] = sqlalchemy.Column('tenant_id', sqlalchemy.Integer) -users = sqlalchemy.Table('users', meta, *user.values()) - -unique_constraint = constraint.UniqueConstraint(user['uid']) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - - user['uid'].alter(nullable=False) - assert not users.c.uid.nullable - - unique_constraint.create() - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - - try: - # this is NOT supported in sqlite! - # but let's try anyway, in case it is - unique_constraint.drop() - except migrate.exceptions.NotSupportedError, e: - if migrate_engine.name == 'sqlite': - # skipping the constraint drop doesn't seem to cause any issues - # *in sqlite* - # as constraints are only checked on row insert/update, - # and don't apply to nulls. - print 'WARNING: Skipping dropping unique constraint ' \ - 'from `users`, UNIQUE (uid)' - else: - raise e - - user['uid'].alter(nullable=True) - assert users.c.uid.nullable diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/011_is_enabled_boolean.py b/keystone/backends/sqlalchemy/migrate_repo/versions/011_is_enabled_boolean.py deleted file mode 100644 index 47b4a0bf..00000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/011_is_enabled_boolean.py +++ /dev/null @@ -1,58 +0,0 @@ -""" -Change 'enabled' columns to boolean types -""" -# pylint: disable=C0103,R0801 - - -import sqlalchemy -from migrate.changeset import constraint - - -meta = sqlalchemy.MetaData() - - -# define the previous state of users - -user = {} -user['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - autoincrement=True) -user['uid'] = sqlalchemy.Column('uid', sqlalchemy.String(255), unique=False, - nullable=False) -user['name'] = sqlalchemy.Column('name', sqlalchemy.String(255), unique=True) -user['password'] = sqlalchemy.Column('password', sqlalchemy.String(255)) -user['email'] = sqlalchemy.Column('email', sqlalchemy.String(255)) -user['enabled'] = sqlalchemy.Column('enabled', sqlalchemy.Integer) -user['tenant_id'] = sqlalchemy.Column('tenant_id', sqlalchemy.Integer) -users = sqlalchemy.Table('users', meta, *user.values()) -constraint.UniqueConstraint(user['uid']) - -tenant = {} -tenant['id'] = sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True, - autoincrement=True) -tenant['uid'] = sqlalchemy.Column('uid', sqlalchemy.String(255), unique=False, - nullable=False) -tenant['name'] = sqlalchemy.Column('name', sqlalchemy.String(255), unique=True) -tenant['desc'] = sqlalchemy.Column('desc', sqlalchemy.String(255)) -tenant['enabled'] = sqlalchemy.Column('enabled', sqlalchemy.Integer) -tenants = sqlalchemy.Table('tenants', meta, *tenant.values()) -constraint.UniqueConstraint(tenant['uid']) - - -def upgrade(migrate_engine): - meta.bind = migrate_engine - - user['enabled'].alter(type=sqlalchemy.Boolean) - assert users.c.enabled is user['enabled'] - - tenant['enabled'].alter(type=sqlalchemy.Boolean) - assert tenants.c.enabled is tenant['enabled'] - - -def downgrade(migrate_engine): - meta.bind = migrate_engine - - user['enabled'].alter(type=sqlalchemy.Integer) - assert users.c.enabled is user['enabled'] - - tenant['enabled'].alter(type=sqlalchemy.Integer) - assert tenants.c.enabled is tenant['enabled'] diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/011_postgresql_downgrade.sql b/keystone/backends/sqlalchemy/migrate_repo/versions/011_postgresql_downgrade.sql deleted file mode 100644 index 0d0bf99a..00000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/011_postgresql_downgrade.sql +++ /dev/null @@ -1,5 +0,0 @@ -ALTER TABLE users ALTER COLUMN enabled DROP DEFAULT; -ALTER TABLE users ALTER COLUMN enabled TYPE integer USING CASE enabled WHEN true THEN 1 ELSE 0 END; - -ALTER TABLE tenants ALTER COLUMN enabled DROP DEFAULT; -ALTER TABLE tenants ALTER COLUMN enabled TYPE integer USING CASE enabled WHEN true THEN 1 ELSE 0 END; diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/011_postgresql_upgrade.sql b/keystone/backends/sqlalchemy/migrate_repo/versions/011_postgresql_upgrade.sql deleted file mode 100644 index f0eafb7e..00000000 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/011_postgresql_upgrade.sql +++ /dev/null @@ -1,5 +0,0 @@ -ALTER TABLE users ALTER COLUMN enabled DROP DEFAULT; -ALTER TABLE users ALTER COLUMN enabled TYPE boolean USING CASE enabled WHEN '1' THEN true ELSE '0' END; - -ALTER TABLE tenants ALTER COLUMN enabled DROP DEFAULT; -ALTER TABLE tenants ALTER COLUMN enabled TYPE boolean USING CASE enabled WHEN '1' THEN true ELSE '0' END; diff --git a/keystone/backends/sqlalchemy/migration.py b/keystone/backends/sqlalchemy/migration.py deleted file mode 100644 index 62219272..00000000 --- a/keystone/backends/sqlalchemy/migration.py +++ /dev/null @@ -1,172 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# 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 logging -import os - -from migrate.versioning import api as versioning_api -# See LP bug #719834. sqlalchemy-migrate changed location of -# exceptions.py after 0.6.0. -try: - # pylint: disable=E0611 - from migrate.versioning import exceptions as versioning_exceptions -except ImportError: - from migrate import exceptions as versioning_exceptions - -from keystone.logic.types import fault - - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -def get_migrate_repo_path(): - """Get the path for the migrate repository.""" - path = os.path.join(os.path.abspath(os.path.dirname(__file__)), - 'migrate_repo') - assert os.path.exists(path) - return path - - -def get_migrate_repo(repo_path): - return versioning_api.repository.Repository(repo_path) - - -def get_schema(engine, repo_path): - return versioning_api.schema.ControlledSchema(engine, repo_path) - - -def get_repo_version(repo_path): - return get_migrate_repo(repo_path).latest - - -def get_db_version(engine, repo_path): - return get_schema(engine, repo_path).version - - -def db_goto_version(sql_connection, version): - """ - Jump to a specific database version without performing migrations. - - :param sql_connection: sqlalchemy connection string - :param version: version to jump to - """ - - # pylint: disable=W0613 - @versioning_api.with_engine - def set_db_version(url, repository, old_v, new_v, **opts): - engine = opts.pop('engine') - schema = get_schema(engine, repo_path) - schema.update_repository_table(old_v, new_v) - return True - - repo_path = get_migrate_repo_path() - new_version = int(version) - try: - old_version = versioning_api.db_version(sql_connection, repo_path) - if new_version != old_version: - return set_db_version(sql_connection, repo_path, old_version, - new_version) - except versioning_exceptions.DatabaseNotControlledError: - msg = (_("database '%(sql_connection)s' is not under " - "migration control") % locals()) - raise fault.DatabaseMigrationError(msg) - - -def db_version(sql_connection): - """ - Return the database's current migration number - - :param sql_connection: sqlalchemy connection string - :retval version number - """ - repo_path = get_migrate_repo_path() - try: - return versioning_api.db_version(sql_connection, repo_path) - except versioning_exceptions.DatabaseNotControlledError: - msg = (_("database '%(sql_connection)s' is not under " - "migration control") % locals()) - raise fault.DatabaseMigrationError(msg) - - -def upgrade(sql_connection, version=None): - """ - Upgrade the database's current migration level - - :param sql_connection: sqlalchemy connection string - :param version: version to upgrade (defaults to latest) - :retval version number - """ - db_version(sql_connection) # Ensure db is under migration control - repo_path = get_migrate_repo_path() - version_str = version or 'latest' # pylint: disable=W0612 - logger.info(_("Upgrading %(sql_connection)s to version %(version_str)s") % - locals()) - return versioning_api.upgrade(sql_connection, repo_path, version) - - -def downgrade(sql_connection, version): - """ - Downgrade the database's current migration level - - :param sql_connection: sqlalchemy connection string - :param version: version to downgrade to - :retval version number - """ - db_version(sql_connection) # Ensure db is under migration control - repo_path = get_migrate_repo_path() - logger.info(_("Downgrading %(sql_connection)s to version %(version)s") % - locals()) - return versioning_api.downgrade(sql_connection, repo_path, version) - - -def version_control(sql_connection): - """ - Place a database under migration control - - :param sql_connection: sqlalchemy connection string - """ - try: - _version_control(sql_connection) - except versioning_exceptions.DatabaseAlreadyControlledError: - msg = (_("database '%(sql_connection)s' is already under migration " - "control") % locals()) - raise fault.DatabaseMigrationError(msg) - - -def _version_control(sql_connection): - """ - Place a database under migration control - - :param sql_connection: sqlalchemy connection string - """ - repo_path = get_migrate_repo_path() - return versioning_api.version_control(sql_connection, repo_path) - - -def db_sync(sql_connection, version=None): - """ - Place a database under migration control and perform an upgrade - - :param sql_connection: sqlalchemy connection string - :retval version number - """ - try: - _version_control(sql_connection) - except versioning_exceptions.DatabaseAlreadyControlledError: - pass - - upgrade(sql_connection, version=version) diff --git a/keystone/backends/sqlalchemy/models.py b/keystone/backends/sqlalchemy/models.py deleted file mode 100755 index 7efe059c..00000000 --- a/keystone/backends/sqlalchemy/models.py +++ /dev/null @@ -1,187 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-2011 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 sqlalchemy import Column, String, Integer, ForeignKey, \ - UniqueConstraint, Boolean, DateTime -from sqlalchemy.exc import IntegrityError -from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import relationship, object_mapper - -# pylint: disable=C0103 -Base = declarative_base() - - -class KeystoneBase(object): - """Base class for Keystone Models.""" - __api__ = None - # pylint: disable=C0103 - _i = None - - def save(self, session=None): - """Save this object.""" - - if not session: - from keystone.backends.sqlalchemy import get_session - session = get_session() - session.add(self) - try: - session.flush() - except IntegrityError: - raise - - def delete(self, session=None): - """Delete this object.""" - self.save(session=session) - - def __setitem__(self, key, value): - setattr(self, key, value) - - def __getitem__(self, key): - return getattr(self, key) - - def get(self, key, default=None): - return getattr(self, key, default) - - def __iter__(self): - self._i = iter(object_mapper(self).columns) - return self - - def next(self): - n = self._i.next().name - return n, getattr(self, n) - - def update(self, values): - """Make the model object behave like a dict""" - for k, v in values.iteritems(): - setattr(self, k, v) - - def iteritems(self): - """Make the model object behave like a dict. - - Includes attributes from joins.""" - local = dict(self) - joined = dict([(k, v) for k, v in self.__dict__.iteritems() - if not k[0] == '_']) - local.update(joined) - return local.iteritems() - - def copy(self): - """Make the model object behave like a dict.""" - return dict(self).copy() - - -# Define associations first -class UserRoleAssociation(Base, KeystoneBase): - __tablename__ = 'user_roles' - id = Column(Integer, primary_key=True) - user_id = Column(Integer, ForeignKey('users.id')) - role_id = Column(Integer, ForeignKey('roles.id')) - tenant_id = Column(Integer, ForeignKey('tenants.id')) - __table_args__ = (UniqueConstraint("user_id", "role_id", "tenant_id"), {}) - - user = relationship('User') - role = relationship('Role') - - -class Endpoints(Base, KeystoneBase): - __tablename__ = 'endpoints' - id = Column(Integer, primary_key=True) - tenant_id = Column(Integer) - endpoint_template_id = Column(Integer, ForeignKey('endpoint_templates.id')) - __table_args__ = ( - UniqueConstraint("endpoint_template_id", "tenant_id"), {}) - - -# Define objects -class Role(Base, KeystoneBase): - __tablename__ = 'roles' - __api__ = 'role' - id = Column(Integer, primary_key=True, autoincrement=True) - name = Column(String(255)) - desc = Column(String(255)) - service_id = Column(Integer, ForeignKey('services.id')) - __table_args__ = ( - UniqueConstraint("name", "service_id"), {}) - - -class Service(Base, KeystoneBase): - __tablename__ = 'services' - __api__ = 'service' - id = Column(Integer, primary_key=True, autoincrement=True) - name = Column(String(255), unique=True) - type = Column(String(255)) - desc = Column(String(255)) - owner_id = Column(Integer, ForeignKey('users.id')) - - -class Tenant(Base, KeystoneBase): - __tablename__ = 'tenants' - __api__ = 'tenant' - id = Column(Integer, primary_key=True, autoincrement=True) - uid = Column(String(255), unique=True, nullable=False) - name = Column(String(255), unique=True) - desc = Column(String(255)) - enabled = Column(Boolean) - - -class User(Base, KeystoneBase): - __tablename__ = 'users' - __api__ = 'user' - id = Column(Integer, primary_key=True, autoincrement=True) - uid = Column(String(255), unique=True, nullable=False) - name = Column(String(255), unique=True) - password = Column(String(255)) - email = Column(String(255)) - enabled = Column(Boolean) - tenant_id = Column(Integer, ForeignKey('tenants.id')) - roles = relationship(UserRoleAssociation, cascade="all") - credentials = relationship('Credentials', backref='user', cascade="all") - - -class Credentials(Base, KeystoneBase): - __tablename__ = 'credentials' - __api__ = 'credentials' - id = Column(Integer, primary_key=True, autoincrement=True) - user_id = Column(Integer, ForeignKey('users.id')) - tenant_id = Column(Integer, ForeignKey('tenants.id'), nullable=True) - type = Column(String(20)) # ('Password','APIKey','EC2') - key = Column(String(255)) - secret = Column(String(255)) - - -class Token(Base, KeystoneBase): - __tablename__ = 'tokens' - __api__ = 'token' - id = Column(String(255), primary_key=True, unique=True) - user_id = Column(Integer) - tenant_id = Column(Integer) - expires = Column(DateTime) - - -class EndpointTemplates(Base, KeystoneBase): - __tablename__ = 'endpoint_templates' - __api__ = 'endpoint_template' - id = Column(Integer, primary_key=True) - region = Column(String(255)) - service_id = Column(Integer, ForeignKey('services.id')) - public_url = Column(String(2000)) - admin_url = Column(String(2000)) - internal_url = Column(String(2000)) - enabled = Column(Boolean) - is_global = Column(Boolean) - version_id = Column(String(20)) - version_list = Column(String(2000)) - version_info = Column(String(500)) diff --git a/keystone/catalog/__init__.py b/keystone/catalog/__init__.py new file mode 100644 index 00000000..95af1256 --- /dev/null +++ b/keystone/catalog/__init__.py @@ -0,0 +1 @@ +from keystone.catalog.core import * diff --git a/keystone/backends/sqlalchemy/api/__init__.py b/keystone/catalog/backends/__init__.py index e69de29b..e69de29b 100755..100644 --- a/keystone/backends/sqlalchemy/api/__init__.py +++ b/keystone/catalog/backends/__init__.py diff --git a/keystone/catalog/backends/kvs.py b/keystone/catalog/backends/kvs.py new file mode 100644 index 00000000..8ee781ba --- /dev/null +++ b/keystone/catalog/backends/kvs.py @@ -0,0 +1,39 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + + +from keystone.common import kvs + + +class Catalog(kvs.Base): + # Public interface + def get_catalog(self, user_id, tenant_id, metadata=None): + return self.db.get('catalog-%s-%s' % (tenant_id, user_id)) + + def get_service(self, service_id): + return self.db.get('service-%s' % service_id) + + def list_services(self): + return self.db.get('service_list', []) + + def create_service(self, service_id, service): + self.db.set('service-%s' % service_id, service) + service_list = set(self.db.get('service_list', [])) + service_list.add(service_id) + self.db.set('service_list', list(service_list)) + return service + + def update_service(self, service_id, service): + self.db.set('service-%s' % service_id, service) + return service + + def delete_service(self, service_id): + self.db.delete('service-%s' % service_id) + service_list = set(self.db.get('service_list', [])) + service_list.remove(service_id) + self.db.set('service_list', list(service_list)) + return None + + # Private interface + def _create_catalog(self, user_id, tenant_id, data): + self.db.set('catalog-%s-%s' % (tenant_id, user_id), data) + return data diff --git a/keystone/catalog/backends/templated.py b/keystone/catalog/backends/templated.py new file mode 100644 index 00000000..b6e7036f --- /dev/null +++ b/keystone/catalog/backends/templated.py @@ -0,0 +1,95 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +from keystone import config +from keystone.common import logging +from keystone.catalog.backends import kvs + + +CONF = config.CONF +config.register_str('template_file', group='catalog') + + +def parse_templates(template_lines): + o = {} + for line in template_lines: + if ' = ' not in line: + continue + + k, v = line.strip().split(' = ') + if not k.startswith('catalog.'): + continue + + parts = k.split('.') + + region = parts[1] + # NOTE(termie): object-store insists on having a dash + service = parts[2].replace('_', '-') + key = parts[3] + + region_ref = o.get(region, {}) + service_ref = region_ref.get(service, {}) + service_ref[key] = v + + region_ref[service] = service_ref + o[region] = region_ref + + return o + + +class TemplatedCatalog(kvs.Catalog): + """A backend that generates endpoints for the Catalog based on templates. + + It is usually configured via config entries that look like: + + catalog.$REGION.$SERVICE.$key = $value + + and is stored in a similar looking hierarchy. Where a value can contain + values to be interpolated by standard python string interpolation that look + like (the % is replaced by a $ due to paste attmepting to interpolate on + its own: + + http://localhost:$(public_port)s/ + + When expanding the template it will pass in a dict made up of the conf + instance plus a few additional key-values, notably tenant_id and user_id. + + It does not care what the keys and values are but it is worth noting that + keystone_compat will expect certain keys to be there so that it can munge + them into the output format keystone expects. These keys are: + + name - the name of the service, most likely repeated for all services of + the same type, across regions. + + adminURL - the url of the admin endpoint + + publicURL - the url of the public endpoint + + internalURL - the url of the internal endpoint + + """ + + def __init__(self, templates=None): + if templates: + self.templates = templates + else: + self._load_templates(CONF.catalog.template_file) + super(TemplatedCatalog, self).__init__() + + def _load_templates(self, template_file): + self.templates = parse_templates(open(template_file)) + + def get_catalog(self, user_id, tenant_id, metadata=None): + d = dict(CONF.iteritems()) + d.update({'tenant_id': tenant_id, + 'user_id': user_id}) + + o = {} + for region, region_ref in self.templates.iteritems(): + o[region] = {} + for service, service_ref in region_ref.iteritems(): + o[region][service] = {} + for k, v in service_ref.iteritems(): + v = v.replace('$(', '%(') + o[region][service][k] = v % d + + return o diff --git a/keystone/catalog/core.py b/keystone/catalog/core.py new file mode 100644 index 00000000..93c0de20 --- /dev/null +++ b/keystone/catalog/core.py @@ -0,0 +1,57 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +"""Main entry point into the Catalog service.""" + +import uuid + +import webob.exc + +from keystone import config +from keystone.common import manager +from keystone.common import wsgi + + +CONF = config.CONF + + +class Manager(manager.Manager): + """Default pivot point for the Catalog 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.catalog.driver) + + +class ServiceController(wsgi.Application): + def __init__(self): + self.catalog_api = Manager() + super(ServiceController, self).__init__() + + # CRUD extensions + # NOTE(termie): this OS-KSADM stuff is not very consistent + def get_services(self, context): + service_list = self.catalog_api.list_services(context) + service_refs = [self.catalog_api.get_service(context, x) + for x in service_list] + return {'OS-KSADM:services': service_refs} + + def get_service(self, context, service_id): + service_ref = self.catalog_api.get_service(context, service_id) + if not service_ref: + raise webob.exc.HTTPNotFound() + return {'OS-KSADM:service': service_ref} + + def delete_service(self, context, service_id): + service_ref = self.catalog_api.delete_service(context, service_id) + + def create_service(self, context, OS_KSADM_service): + service_id = uuid.uuid4().hex + service_ref = OS_KSADM_service.copy() + service_ref['id'] = service_id + new_service_ref = self.catalog_api.create_service( + context, service_id, service_ref) + return {'OS-KSADM:service': new_service_ref} diff --git a/keystone/cli.py b/keystone/cli.py new file mode 100644 index 00000000..b6b5abb8 --- /dev/null +++ b/keystone/cli.py @@ -0,0 +1,129 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +from __future__ import absolute_import + +import json +import logging +import sys +import StringIO +import textwrap + +import cli.app +import cli.log + +from keystone import config +from keystone.common import utils + + +CONF = config.CONF +CONF.set_usage('%prog COMMAND [key1=value1 key2=value2 ...]') + + +class BaseApp(cli.log.LoggingApp): + def __init__(self, *args, **kw): + kw.setdefault('name', self.__class__.__name__.lower()) + super(BaseApp, self).__init__(*args, **kw) + + def add_default_params(self): + for args, kw in DEFAULT_PARAMS: + self.add_param(*args, **kw) + + def _parse_keyvalues(self, args): + kv = {} + for x in args: + key, value = x.split('=', 1) + # make lists if there are multiple values + if key.endswith('[]'): + key = key[:-2] + existing = kv.get(key, []) + existing.append(value) + kv[key] = existing + else: + kv[key] = value + return kv + + +class DbSync(BaseApp): + """Sync the database.""" + + name = 'db_sync' + + def __init__(self, *args, **kw): + super(DbSync, self).__init__(*args, **kw) + + def main(self): + for k in ['identity', 'catalog', 'policy', 'token']: + driver = utils.import_object(getattr(CONF, k).driver) + if hasattr(driver, 'db_sync'): + driver.db_sync() + + +class ImportLegacy(BaseApp): + """Import a legacy database.""" + + name = 'import_legacy' + + def __init__(self, *args, **kw): + super(ImportLegacy, self).__init__(*args, **kw) + self.add_param('old_db', nargs=1) + + def main(self): + from keystone.common.sql import legacy + old_db = self.params.old_db[0] + migration = legacy.LegacyMigration(old_db) + migration.migrate_all() + + +class ExportLegacyCatalog(BaseApp): + """Export the service catalog from a legacy database.""" + + name = 'export_legacy_catalog' + + def __init__(self, *args, **kw): + super(ExportLegacyCatalog, self).__init__(*args, **kw) + self.add_param('old_db', nargs=1) + + def main(self): + from keystone.common.sql import legacy + old_db = self.params.old_db[0] + migration = legacy.LegacyMigration(old_db) + print '\n'.join(migration.dump_catalog()) + + +CMDS = {'db_sync': DbSync, + 'import_legacy': ImportLegacy, + 'export_legacy_catalog': ExportLegacyCatalog, + } + + +def print_commands(cmds): + print + print 'Available commands:' + o = [] + max_length = max([len(k) for k in cmds]) + 2 + for k, cmd in sorted(cmds.iteritems()): + initial_indent = '%s%s: ' % (' ' * (max_length - len(k)), k) + tw = textwrap.TextWrapper(initial_indent=initial_indent, + subsequent_indent=' ' * (max_length + 2), + width=80) + o.extend(tw.wrap( + (cmd.__doc__ and cmd.__doc__ or 'no docs').strip().split('\n')[0])) + print '\n'.join(o) + + +def run(cmd, args): + return CMDS[cmd](argv=args).run() + + +def main(argv=None, config_files=None): + CONF.reset() + args = CONF(config_files=config_files, args=argv) + + if len(args) < 2: + CONF.print_help() + print_commands(CMDS) + sys.exit(1) + + cmd = args[1] + if cmd in CMDS: + return run(cmd, (args[:1] + args[2:])) diff --git a/keystone/client.py b/keystone/client.py deleted file mode 100644 index 2e4a075d..00000000 --- a/keystone/client.py +++ /dev/null @@ -1,193 +0,0 @@ -# Copyright (C) 2011 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. - -"""Python HTTP clients for accessing Keystone's Service and Admin APIs.""" - -import httplib -import json -import logging - -import keystone.common.exception - -LOG = logging.getLogger(__name__) - - -class ServiceClient(object): - """Keystone v2.0 HTTP API client for normal service function. - - Provides functionality for retrieving new tokens and for retrieving - a list of tenants which the supplied token has access to. - - """ - - _default_port = 5000 - - def __init__(self, host, port=None, is_ssl=False, cert_file=None): - """Initialize client. - - :param host: The hostname or IP of the Keystone service to use - :param port: The port of the Keystone service to use - - """ - self.host = host - self.port = port or self._default_port - self.is_ssl = is_ssl - self.cert_file = cert_file - - def _http_request(self, verb, path, body=None, headers=None): - """Perform an HTTP request and return the HTTP response. - - :param verb: HTTP verb (e.g. GET, POST, etc.) - :param path: HTTP path (e.g. /v2.0/tokens) - :param body: HTTP Body content - :param headers: Dictionary of HTTP headers - :returns: httplib.HTTPResponse object - - """ - LOG.debug("Connecting to %s" % self.auth_address) - if (self.is_ssl): - connection = httplib.HTTPSConnection(self.auth_address, - cert_file=self.cert_file) - else: - connection = httplib.HTTPConnection(self.auth_address) - connection.request(verb, path, body=body, headers=headers) - response = connection.getresponse() - response.body = response.read() - status_int = int(response.status) - connection.close() - - if status_int < 200 or status_int >= 300: - msg = "Client received HTTP %d" % status_int - raise keystone.common.exception.ClientError(msg) - - return response - - @property - def auth_address(self): - """Return a host:port combination string.""" - return "%s:%d" % (self.host, self.port) - - def get_token(self, username, password): - """Retrieve a token from Keystone for a given user/password. - - :param username: The user name to authenticate with - :param password: The password to authenticate with - :returns: A string token - - """ - body = json.dumps({ - "auth": { - "passwordCredentials": { - "username": username, - "password": password, - }, - }, - }) - - headers = { - "Accept": "application/json", - "Content-Type": "application/json", - } - - response = self._http_request("POST", "/v2.0/tokens", body, headers) - token_id = json.loads(response.body)["access"]["token"]["id"] - - return token_id - - -class AdminClient(ServiceClient): - """Keystone v2.0 HTTP API client for administrative functions. - - Provides functionality for retrieving new tokens, validating existing - tokens, and retrieving user information from valid tokens. - - """ - - _default_port = 35357 - _default_admin_name = "admin" - _default_admin_pass = "password" - - # pylint: disable=R0913 - def __init__(self, host, port=None, is_ssl=False, cert_file=None, - admin_name=None, admin_pass=None): - """Initialize client. - - :param host: The hostname or IP of the Keystone service to use - :param port: The port of the Keystone service to use - :param admin_name: The username to use for admin purposes - :param admin_pass: The password to use for the admin account - - """ - super(AdminClient, self).__init__(host, port=port, is_ssl=is_ssl, - cert_file=cert_file) - self.admin_name = admin_name or self._default_admin_name - self.admin_pass = admin_pass or self._default_admin_pass - self._admin_token = None - - @property - def admin_token(self): - """Retrieve a valid admin token. - - If a token has already been retrieved, ensure that it is still valid - and then return it. If it has not already been retrieved or the token - is found to be invalid, retrieve a new token and return it. - - """ - token = self._admin_token - - if token is None or not self.check_token(token, token): - token = self.get_token(self.admin_name, self.admin_pass) - - self._admin_token = token - return self._admin_token - - def validate_token(self, token): - """Validate a token, returning details about the user. - - :param token: A token string - :returns: Object representing the user the token belongs to, or None - if the token is not valid. - - """ - url = "/v2.0/tokens/%s" % token - - headers = { - "Accept": "application/json", - "X-Auth-Token": self.admin_token, - } - - try: - response = self._http_request("GET", url, headers=headers) - except keystone.common.exception.ClientError: - return None - - return json.loads(response.body) - - def check_token(self, token, admin_token=None): - """Check to see if given token is valid. - - :param token: A token string - :param admin_token: The administrative token to use - :returns: True if token is valid, otherwise False - - """ - url = "/v2.0/tokens/%s" % token - headers = {"X-Auth-Token": admin_token or self.admin_token} - - try: - self._http_request("HEAD", url, headers=headers) - except keystone.common.exception.ClientError: - return False - - return True diff --git a/keystone/common/bufferedhttp.py b/keystone/common/bufferedhttp.py index db64175d..95caa4f4 100644 --- a/keystone/common/bufferedhttp.py +++ b/keystone/common/bufferedhttp.py @@ -1,3 +1,5 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + # Copyright (c) 2010-2011 OpenStack, LLC. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -30,20 +32,13 @@ from urllib import quote import logging import time -# pylint: disable=E0611 from eventlet.green.httplib import CONTINUE, HTTPConnection, HTTPMessage, \ HTTPResponse, HTTPSConnection, _UNKNOWN -DEFAULT_TIMEOUT = 30 - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - -# pylint: disable=R0902 class BufferedHTTPResponse(HTTPResponse): """HTTPResponse class that buffers reading of headers""" - # pylint: disable=C0103 def __init__(self, sock, debuglevel=0, strict=0, method=None): # pragma: no cover self.sock = sock @@ -64,7 +59,6 @@ class BufferedHTTPResponse(HTTPResponse): self.length = _UNKNOWN # number of bytes left in response self.will_close = _UNKNOWN # conn will close at end of response - # pylint: disable=E1101,E0203,W0201 def expect_response(self): self.fp = self.sock.makefile('rb', 0) version, status, reason = self._read_status() @@ -79,24 +73,20 @@ class BufferedHTTPResponse(HTTPResponse): self.msg.fp = None -# pylint: disable=W0232 class BufferedHTTPConnection(HTTPConnection): """HTTPConnection class that uses BufferedHTTPResponse""" response_class = BufferedHTTPResponse - # pylint: disable=W0201 def connect(self): self._connected_time = time.time() return HTTPConnection.connect(self) - # pylint: disable=W0201 def putrequest(self, method, url, skip_host=0, skip_accept_encoding=0): self._method = method self._path = url return HTTPConnection.putrequest(self, method, url, skip_host, skip_accept_encoding) - # pylint: disable=E1101 def getexpect(self): response = BufferedHTTPResponse(self.sock, strict=self.strict, method=self._method) @@ -105,17 +95,15 @@ class BufferedHTTPConnection(HTTPConnection): def getresponse(self): response = HTTPConnection.getresponse(self) - logger.debug(("HTTP PERF: %(time).5f seconds to %(method)s " - "%(host)s:%(port)s %(path)s)"), + logging.debug(('HTTP PERF: %(time).5f seconds to %(method)s ' + '%(host)s:%(port)s %(path)s)'), {'time': time.time() - self._connected_time, 'method': self._method, 'host': self.host, 'port': self.port, 'path': self._path}) return response -# pylint: disable=R0913 def http_connect(ipaddr, port, device, partition, method, path, - headers=None, query_string=None, ssl=False, key_file=None, - cert_file=None, timeout=None): + headers=None, query_string=None, ssl=False): """ Helper function to create an HTTPConnection object. If ssl is set True, HTTPSConnection will be used. However, if ssl=False, BufferedHTTPConnection @@ -130,21 +118,26 @@ def http_connect(ipaddr, port, device, partition, method, path, :param headers: dictionary of headers :param query_string: request query string :param ssl: set True if SSL should be used (default: False) - :param key_file: Private key file (not needed if cert_file has private key) - :param cert_file: Certificate file (Keystore) :returns: HTTPConnection object """ + if ssl: + conn = HTTPSConnection('%s:%s' % (ipaddr, port)) + else: + conn = BufferedHTTPConnection('%s:%s' % (ipaddr, port)) path = quote('/' + device + '/' + str(partition) + path) - # pylint: disable=E1121, E1124 - return http_connect_raw(ipaddr, port, device, partition, method, path, - headers, query_string, ssl, key_file, cert_file, - timeout=timeout) + if query_string: + path += '?' + query_string + conn.path = path + conn.putrequest(method, path) + if headers: + for header, value in headers.iteritems(): + conn.putheader(header, value) + conn.endheaders() + return conn -# pylint: disable=W0201 def http_connect_raw(ipaddr, port, method, path, headers=None, - query_string=None, ssl=False, key_file=None, - cert_file=None, timeout=None): + query_string=None, ssl=False): """ Helper function to create an HTTPConnection object. If ssl is set True, HTTPSConnection will be used. However, if ssl=False, BufferedHTTPConnection @@ -157,26 +150,18 @@ def http_connect_raw(ipaddr, port, method, path, headers=None, :param headers: dictionary of headers :param query_string: request query string :param ssl: set True if SSL should be used (default: False) - :param key_file: Private key file (not needed if cert_file has private key) - :param cert_file: Certificate file (Keystore) :returns: HTTPConnection object """ - if timeout is None: - timeout = DEFAULT_TIMEOUT if ssl: - conn = HTTPSConnection('%s:%s' % (ipaddr, port), key_file=key_file, - cert_file=cert_file, timeout=timeout) + conn = HTTPSConnection('%s:%s' % (ipaddr, port)) else: - conn = BufferedHTTPConnection('%s:%s' % (ipaddr, port), - timeout=timeout) + conn = BufferedHTTPConnection('%s:%s' % (ipaddr, port)) if query_string: path += '?' + query_string conn.path = path conn.putrequest(method, path) if headers: - # pylint: disable=E1103 for header, value in headers.iteritems(): conn.putheader(header, value) - # pylint: disable=E1103 conn.endheaders() return conn diff --git a/keystone/cfg.py b/keystone/common/cfg.py index da91356f..91c9546c 100644 --- a/keystone/cfg.py +++ b/keystone/common/cfg.py @@ -17,7 +17,7 @@ r""" Configuration options which may be set on the command line or in config files. -The schema for each option is defined using the Opt sub-classes e.g. +The schema for each option is defined using the Opt sub-classes e.g.:: common_opts = [ cfg.StrOpt('bind_host', @@ -28,7 +28,7 @@ The schema for each option is defined using the Opt sub-classes e.g. help='Port number to listen on') ] -Options can be strings, integers, floats, booleans, lists or 'multi strings': +Options can be strings, integers, floats, booleans, lists or 'multi strings':: enabled_apis_opt = \ cfg.ListOpt('enabled_apis', @@ -43,7 +43,7 @@ Options can be strings, integers, floats, booleans, lists or 'multi strings': default=DEFAULT_EXTENSIONS) Option schemas are registered with with the config manager at runtime, but -before the option is referenced: +before the option is referenced:: class ExtensionManager(object): @@ -59,7 +59,7 @@ before the option is referenced: .... A common usage pattern is for each option schema to be defined in the module or -class which uses the option: +class which uses the option:: opts = ... @@ -74,7 +74,7 @@ class which uses the option: An option may optionally be made available via the command line. Such options must registered with the config manager before the command line is parsed (for -the purposes of --help and CLI arg validation): +the purposes of --help and CLI arg validation):: cli_opts = [ cfg.BoolOpt('verbose', @@ -90,7 +90,7 @@ the purposes of --help and CLI arg validation): def add_common_opts(conf): conf.register_cli_opts(cli_opts) -The config manager has a single CLI option defined by default, --config-file: +The config manager has a single CLI option defined by default, --config-file:: class ConfigOpts(object): @@ -104,7 +104,7 @@ The config manager has a single CLI option defined by default, --config-file: Option values are parsed from any supplied config files using SafeConfigParser. If none are specified, a default set is used e.g. glance-api.conf and -glance-common.conf: +glance-common.conf:: glance-api.conf: [DEFAULT] @@ -119,7 +119,7 @@ are parsed in order, with values in later files overriding those in earlier files. The parsing of CLI args and config files is initiated by invoking the config -manager e.g. +manager e.g.:: conf = ConfigOpts() conf.register_opt(BoolOpt('verbose', ...)) @@ -127,7 +127,7 @@ manager e.g. if conf.verbose: ... -Options can be registered as belonging to a group: +Options can be registered as belonging to a group:: rabbit_group = cfg.OptionGroup(name='rabbit', title='RabbitMQ options') @@ -154,7 +154,7 @@ Options can be registered as belonging to a group: conf.register_opt(rabbit_ssl_opt, group=rabbit_group) If no group is specified, options belong to the 'DEFAULT' section of config -files: +files:: glance-api.conf: [DEFAULT] @@ -175,7 +175,7 @@ Command-line options in a group are automatically prefixed with the group name: Option values in the default group are referenced as attributes/properties on the config manager; groups are also attributes on the config manager, with -attributes for each of the options associated with the group: +attributes for each of the options associated with the group:: server.start(app, conf.bind_port, conf.bind_host, conf) @@ -184,7 +184,7 @@ attributes for each of the options associated with the group: port=conf.rabbit.port, ...) -Option values may reference other values using PEP 292 string substitution: +Option values may reference other values using PEP 292 string substitution:: opts = [ cfg.StrOpt('state_path', @@ -200,17 +200,13 @@ Option values may reference other values using PEP 292 string substitution: Note that interpolation can be avoided by using '$$'. """ -# pylint: disable=W0231,W0212,W0141,C0302,R0913,W0402 + +import sys import ConfigParser import copy -import logging import optparse import os -import re import string -import sys - -LOG = logging.getLogger(__name__) class Error(Exception): @@ -227,9 +223,9 @@ class ArgsAlreadyParsedError(Error): """Raised if a CLI opt is registered after parsing.""" def __str__(self): - ret = "arguments already parsed" + ret = 'arguments already parsed' if self.msg: - ret += ": " + self.msg + ret += ': ' + self.msg return ret @@ -242,9 +238,9 @@ class NoSuchOptError(Error): def __str__(self): if self.group is None: - return "no such option: %s" % self.opt_name + return 'no such option: %s' % self.opt_name else: - return "no such option in group %s: %s" % (self.group.name, + return 'no such option in group %s: %s' % (self.group.name, self.opt_name) @@ -255,7 +251,7 @@ class NoSuchGroupError(Error): self.group_name = group_name def __str__(self): - return "no such group: %s" % self.group_name + return 'no such group: %s' % self.group_name class DuplicateOptError(Error): @@ -265,14 +261,14 @@ class DuplicateOptError(Error): self.opt_name = opt_name def __str__(self): - return "duplicate option: %s" % self.opt_name + return 'duplicate option: %s' % self.opt_name class TemplateSubstitutionError(Error): """Raised if an error occurs substituting a variable in an opt value.""" def __str__(self): - return "template substitution error: %s" % self.msg + return 'template substitution error: %s' % self.msg class ConfigFilesNotFoundError(Error): @@ -445,8 +441,7 @@ class Opt(object): prefix = self._get_optparse_prefix('', group) self._add_to_optparse(container, self.name, self.short, kwargs, prefix) - @staticmethod - def _add_to_optparse(container, name, short, kwargs, prefix=''): + def _add_to_optparse(self, container, name, short, kwargs, prefix=''): """Add an option to an optparse parser or group. :param container: an optparse.OptionContainer object @@ -464,8 +459,7 @@ class Opt(object): raise DuplicateOptError(a) container.add_option(*args, **kwargs) - @staticmethod - def _get_optparse_container(parser, group): + def _get_optparse_container(self, parser, group): """Returns an optparse.OptionContainer. :param parser: an optparse.OptionParser @@ -497,8 +491,7 @@ class Opt(object): }) return kwargs - @staticmethod - def _get_optparse_prefix(prefix, group): + def _get_optparse_prefix(self, prefix, group): """Build a prefix for the CLI option name, if required. CLI options in a group are prefixed with the group's name in order @@ -603,7 +596,6 @@ class ListOpt(Opt): callback=self._parse_list, **kwargs) - # pylint: disable=W0613 def _parse_list(self, option, opt, value, parser): """An optparse callback for parsing an option value into a list.""" setattr(parser.values, self.dest, value.split(',')) @@ -689,7 +681,6 @@ class OptGroup(object): return self._optparse_group -# pylint: disable=R0902 class ConfigOpts(object): """ @@ -764,11 +755,16 @@ class ConfigOpts(object): :raises: SystemExit, ConfigFilesNotFoundError, ConfigFileParseError """ self.reset() + self._args = args + (values, args) = self._oparser.parse_args(self._args) + self._cli_values = vars(values) + if self.config_file: - self._parse_config_files() + self._parse_config_files(self.config_file) + return args def __getattr__(self, name): @@ -934,16 +930,9 @@ class ConfigOpts(object): info = self._get_opt_info(name, group) default, opt, override = map(lambda k: info[k], sorted(info.keys())) - # Explicit code overrides always trump others. if override is not None: return override - # CLI values override configuration settings. - name = name if group is None else "%s_%s" % (group.name, name) - value = self._cli_values.get(name, None) - if value is not None: - return value - if self._cparser is not None: section = group.name if group is not None else 'DEFAULT' try: @@ -954,6 +943,11 @@ class ConfigOpts(object): except ValueError, ve: raise ConfigFileValueError(str(ve)) + name = name if group is None else group.name + '_' + name + value = self._cli_values.get(name, None) + if value is not None: + return value + if default is not None: return default @@ -1017,60 +1011,11 @@ class ConfigOpts(object): return opts[opt_name] - # pylint: disable=R0914,R0201 - def _update_config_format(self, config_files): - """ - Update the config files in case they contain keys with dashes instead - of underscores. If a file needs updating, write it to a temp files and - replace the original name with the temp file. - NOTE: warnings are issued if any old formats are found. - """ - ret = config_files[:] - bad_settings = [] - for pos, cf in enumerate(config_files): - warn = False - out = [] - with file(cf) as config: - for ln in config: - try: - kk, vv = ln.split("=") - except ValueError: - out.append(ln) - continue - # Replace dashes with underscores - newk = kk.replace("-", "_") - # Replace periods with colons *only* if not part of a - # numeric value. - newk = re.sub(r"([^\d])\.([^\d])", r"\1:\2", newk) - if newk != kk and not kk.startswith("paste."): - warn = True - bad_settings.append("%s -> %s" % (kk, newk)) - out.append("=".join((newk, vv))) - out_text = "\n".join(out) - if warn: - # Need to write out a new tmp file and print a warning - changes = "\n\t".join(bad_settings) - msg = "\n\nWarning: your configuration may be using an old"\ - " format. Please update the following settings by"\ - " replacing hyphens with underscores, and periods"\ - " with colons:\n\t%s\n\n" % changes - # TODO(ziad): verify if that is what other projects are moving - # to? If so, make this a warning - LOG.debug(msg) - # Importing here to avoid circular imports. - from keystone import utils - new_name = utils.write_temp_file(out_text) - ret[pos] = new_name - return ret - - def _parse_config_files(self, config_files=None): + def _parse_config_files(self, config_files): """Parse the supplied configuration files. :raises: ConfigFilesNotFoundError, ConfigFileParseError """ - if config_files is None: - config_files = self.config_file - config_files = self._update_config_format(config_files) self._cparser = ConfigParser.SafeConfigParser() try: diff --git a/keystone/common/config.py b/keystone/common/config.py deleted file mode 100755 index dbfad235..00000000 --- a/keystone/common/config.py +++ /dev/null @@ -1,386 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# 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. - -""" -Routines for configuring OpenStack Service -""" - -import logging.config -from logging import FileHandler -import optparse -import os -from paste import deploy -import sys -import ConfigParser -from keystone.common.wsgi import add_console_handler - -DEFAULT_LOG_FORMAT = "%(asctime)s %(levelname)8s [%(name)s] %(message)s" -DEFAULT_LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S" -DEFAULT_LOG_DIR = "/var/log/keystone" -DEFAULT_LOG_FILE = "keystone.log" - - -def parse_options(parser, cli_args=None): - """ - Returns the parsed CLI options, command to run and its arguments, merged - with any same-named options found in a configuration file. - - The function returns a tuple of (options, args), where options is a - mapping of option key/str(value) pairs, and args is the set of arguments - (not options) supplied on the command-line. - - The reason that the option values are returned as strings only is that - ConfigParser and paste.deploy only accept string values... - - :param parser: The option parser - :param cli_args: (Optional) Set of arguments to process. If not present, - sys.argv[1:] is used. - :returns: tuple of (options, args) - - """ - - (options, args) = parser.parse_args(cli_args) - - return (vars(options), args) - - -def add_common_options(parser): - """ - Given a supplied optparse.OptionParser, adds an OptionGroup that - represents all common configuration options. - - :param parser: optparse.OptionParser - """ - help_text = "The following configuration options are common to "\ - "all keystone programs." - - group = optparse.OptionGroup(parser, "Common Options", help_text) - group.add_option('-v', '--verbose', default=False, dest="verbose", - action="store_true", - help="Print more verbose output") - group.add_option('-d', '--debug', default=False, dest="debug", - action="store_true", - help="Print debugging output to console") - group.add_option('-c', '--config-file', default=None, metavar="PATH", - help="""Path to the config file to use. When not \ -specified (the default), we generally look at the first argument specified to \ -be a config file, and if that is also missing, we search standard directories \ -for a config file.""") - group.add_option('-p', '--port', '--bind-port', - dest="bind_port", - help="specifies port to listen on") - group.add_option('--host', '--bind-host', - default=None, dest="bind_host", - help="specifies host address to listen on "\ - "(default is all or 0.0.0.0)") - # This one is handled by keystone/tools/tracer.py (if loaded) - group.add_option('-t', '--trace-calls', default=False, - dest="trace_calls", - action="store_true", - help="Turns on call tracing for troubleshooting") - - parser.add_option_group(group) - return group - - -def add_log_options(parser): - """ - Given a supplied optparse.OptionParser, adds an OptionGroup that - represents all the configuration options around logging. - - :param parser: optparse.OptionParser - """ - help_text = "The following configuration options are specific to logging "\ - "functionality for this program." - - group = optparse.OptionGroup(parser, "Logging Options", help_text) - group.add_option('--log-config', default=None, metavar="PATH", - help="""If this option is specified, the logging \ -configuration file specified is used and overrides any other logging options \ -specified. Please see the Python logging module documentation for details on \ -logging configuration files.""") - group.add_option('--log-date-format', metavar="FORMAT", - default=DEFAULT_LOG_DATE_FORMAT, - help="Format string for %(asctime)s in log records. "\ - "Default: %default") - group.add_option('--log-file', default=None, metavar="PATH", - help="(Optional) Name of log file to output to. "\ - "If not set, logging will go to stdout.") - group.add_option("--log-dir", default=None, - help="(Optional) The directory to keep log files in "\ - "(will be prepended to --logfile)") - - parser.add_option_group(group) - return group - - -def setup_logging(options, conf): - """ - Sets up the logging options for a log with supplied name - - :param options: Mapping of typed option key/values - :param conf: Mapping of untyped key/values from config file - """ - if options.get('log_config', None): - # Use a logging configuration file for all settings... - if os.path.exists(options['log_config']): - logging.config.fileConfig(options['log_config']) - return - else: - raise RuntimeError("Unable to locate specified logging "\ - "config file: %s" % options['log_config']) - - # If either the CLI option or the conf value - # is True, we set to True - debug = options.get('debug') or conf.get('debug', False) - debug = debug in [True, "True", "1"] - verbose = options.get('verbose') or conf.get('verbose', False) - verbose = verbose in [True, "True", "1"] - root_logger = logging.root - root_logger.setLevel( - logging.DEBUG if debug else - logging.INFO if verbose else - logging.WARNING) - - # Set log configuration from options... - # Note that we use a hard-coded log format in the options - # because of Paste.Deploy bug #379 - # http://trac.pythonpaste.org/pythonpaste/ticket/379 - log_format = options.get('log_format', DEFAULT_LOG_FORMAT) - log_date_format = options.get('log_date_format', DEFAULT_LOG_DATE_FORMAT) - formatter = logging.Formatter(log_format, log_date_format) - - # grab log_file and log_dir from config; set to defaults of not already - # defined - logfile = options.get('log_file') or conf.get('log_file', DEFAULT_LOG_FILE) - logdir = options.get('log_dir') or conf.get('log_dir', DEFAULT_LOG_DIR) - - # Add FileHandler if it doesn't exist - logfile = os.path.abspath(os.path.join(logdir, logfile)) - handlers = [handler for handler in root_logger.handlers - if isinstance(handler, FileHandler) - and handler.baseFilename == logfile] - - if not handlers: - logfile = logging.FileHandler(logfile) - logfile.setFormatter(formatter) - root_logger.addHandler(logfile) - - # Mirror to console if verbose or debug - if debug or verbose: - add_console_handler(root_logger, logging.DEBUG) - else: - add_console_handler(root_logger, logging.INFO) - - -def find_config_file(options, args): - """ - Return the first config file found. - - We search for the paste config file in the following order: - * If --config-file option is used, use that - * If args[0] is a file, use that - * Search for keystone.conf in standard directories: - - * . - * ~.keystone/ - * ~ - * /etc/keystone - * /etc - - If no config file is given get from possible_topdir/etc/keystone.conf - - :returns: Full path to config file, or None if no config file found - """ - POSSIBLE_TOPDIR = os.path.normpath(os.path.join(\ - os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) - fix_path = lambda p: os.path.abspath(os.path.expanduser(p)) - cfg_file = options.get('config_file') - if cfg_file: - if isinstance(cfg_file, list): - cfg_file = cfg_file[0] - if os.path.exists(cfg_file): - return fix_path(cfg_file) - elif args: - if os.path.exists(args[0]): - return fix_path(args[0]) - - # Handle standard directory search for keystone.conf - config_file_dirs = [fix_path(os.getcwd()), - fix_path(os.path.join('~', '.keystone')), - fix_path('~'), - '/etc/keystone/', - '/etc'] - - for cfg_dir in config_file_dirs: - cfg_file = os.path.join(cfg_dir, 'keystone.conf') - if os.path.exists(cfg_file): - return cfg_file - else: - if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'etc', \ - 'keystone.conf')): - # For debug only - config_file = os.path.join(POSSIBLE_TOPDIR, 'etc', \ - 'keystone.conf') - return config_file - - -def load_paste_config(app_name, options, args): - """ - Looks for a config file to use for an app and returns the - config file path and a configuration mapping from a paste config file. - - We search for the paste config file in the following order: - - * If --config-file option is used, use that - * If args[0] is a file, use that - * Search for keystone.conf in standard directories: - - * . - * ~.keystone/ - * ~ - * /etc/keystone - * /etc - - :param app_name: Name of the application to load config for, or None. - None signifies to only load the [DEFAULT] section of - the config file. - :param options: Set of typed options returned from parse_options() - :param args: Command line arguments from argv[1:] - :returns: Tuple of (conf_file, conf) - :raises: RuntimeError when config file cannot be located or there was a - problem loading the configuration file. - """ - conf_file = find_config_file(options, args) - if not conf_file: - raise RuntimeError("Unable to locate any configuration file. "\ - "Cannot load application %s" % app_name) - try: - conf = deploy.appconfig("config:%s" % conf_file, name=app_name) - conf.global_conf.update(get_non_paste_configs(conf_file)) - return conf_file, conf - except Exception, e: - raise RuntimeError("Error loading config %s: %s" % (conf_file, e)) - - -def get_non_paste_configs(conf_file): - load_config_files(conf_file) - complete_conf = load_config_files(conf_file) - #Add Non Paste global sections.Need to find a better way. - global_conf = {} - if complete_conf is not None: - for section in complete_conf.sections(): - if not (section.startswith('filter:') or \ - section.startswith('app:') or \ - section.startswith('pipeline:')): - section_items = complete_conf.items(section) - section_items_dict = {} - for section_item in section_items: - section_items_dict[section_item[0]] = section_item[1] - global_conf[section] = section_items_dict - return global_conf - - -def load_config_files(config_files): - '''Load the config files.''' - config = ConfigParser.ConfigParser() - if config_files is not None: - config.read(config_files) - return config - - -def load_paste_app(app_name, options, args): - """ - Builds and returns a WSGI app from a paste config file. - - We search for the paste config file in the following order: - * If --config-file option is used, use that - * If args[0] is a file, use that - * Search for keystone.conf in standard directories: - - * . - * ~.keystone/ - * ~ - * /etc/keystone - * /etc - - :param app_name: Name of the application to load (server, admin, proxy, ..) - :param options: Set of typed options returned from parse_options() - :param args: Command line arguments from argv[1:] - :raises: RuntimeError when config file cannot be located or application - cannot be loaded from config file - """ - conf_file, conf = load_paste_config(app_name, options, args) - - try: - # Setup logging early, supplying both the CLI options and the - # configuration mapping from the config file - if not conf.get('log_file'): - options['log_file'] = "%s.log" % app_name - setup_logging(options, conf) - - # We only update the conf dict for the verbose and debug - # flags. Everything else must be set up in the conf file... - debug = options.get('debug') or conf.get('debug', False) - debug = debug in [True, "True", "1"] - verbose = options.get('verbose') or conf.get('verbose', False) - verbose = verbose in [True, "True", "1"] - conf['debug'] = debug - conf['verbose'] = verbose - # Log the options used when starting if we're in debug mode... - if debug: - logger = logging.getLogger(app_name) - logger.info("*" * 50) - logger.info("Configuration options gathered from config file:") - logger.info(conf_file) - logger.info("================================================") - items = dict([(k, v) for k, v in conf.items() - if k not in ('__file__', 'here')]) - for key, value in sorted(items.items()): - logger.info("%(key)-20s %(value)s" % locals()) - logger.info("*" * 50) - app = deploy.loadapp("config:%s" % conf_file, name=app_name, - global_conf=conf.global_conf) - except (LookupError, ImportError) as e: - raise RuntimeError("Unable to load %(app_name)s from " - "configuration file %(conf_file)s." - "\nGot: %(e)r" % locals()) - return conf, app - - -def get_option(options, option, **kwargs): - if option in options: - value = options[option] - type_ = kwargs.get('type', 'str') - if type_ == 'bool': - if hasattr(value, 'lower'): - return value.lower() == 'true' - else: - return value - elif type_ == 'int': - return int(value) - elif type_ == 'float': - return float(value) - else: - return value - elif 'default' in kwargs: - return kwargs['default'] - else: - raise KeyError("option '%s' not found" % option) diff --git a/keystone/common/crypt.py b/keystone/common/crypt.py deleted file mode 100644 index bb25620d..00000000 --- a/keystone/common/crypt.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# 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. - -""" -Routines for URL-safe encrypting/decrypting - -Keep this file in sync with all copies: -- glance/common/crypt.py -- keystone/middleware/crypt.py -- keystone/common/crypt.py - -""" - -import base64 - -from Crypto.Cipher import AES -from Crypto import Random -from Crypto.Random import random - - -def urlsafe_encrypt(key, plaintext, blocksize=16): - """ - Encrypts plaintext. Resulting ciphertext will contain URL-safe characters - :param key: AES secret key - :param plaintext: Input text to be encrypted - :param blocksize: Non-zero integer multiple of AES blocksize in bytes (16) - - :returns : Resulting ciphertext - """ - def pad(text): - """ - Pads text to be encrypted - """ - pad_length = (blocksize - len(text) % blocksize) - sr = random.StrongRandom() - pad = ''.join(chr(sr.randint(1, 0xFF)) for i in range(pad_length - 1)) - # We use chr(0) as a delimiter between text and padding - return text + chr(0) + pad - - # random initial 16 bytes for CBC - init_vector = Random.get_random_bytes(16) - cypher = AES.new(key, AES.MODE_CBC, init_vector) - padded = cypher.encrypt(pad(str(plaintext))) - return base64.urlsafe_b64encode(init_vector + padded) - - -def urlsafe_decrypt(key, ciphertext): - """ - Decrypts URL-safe base64 encoded ciphertext - :param key: AES secret key - :param ciphertext: The encrypted text to decrypt - - :returns : Resulting plaintext - """ - # Cast from unicode - ciphertext = base64.urlsafe_b64decode(str(ciphertext)) - cypher = AES.new(key, AES.MODE_CBC, ciphertext[:16]) - padded = cypher.decrypt(ciphertext[16:]) - return padded[:padded.rfind(chr(0))] diff --git a/keystone/common/exception.py b/keystone/common/exception.py deleted file mode 100755 index e734e94f..00000000 --- a/keystone/common/exception.py +++ /dev/null @@ -1,100 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# 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. - -""" -OpenStack base exception handling, including decorator for re-raising -OpenStack-type exceptions. SHOULD include dedicated exception logging. -""" - -import logging - - -class ProcessExecutionError(IOError): - def __init__(self, stdout=None, stderr=None, exit_code=None, cmd=None, - description=None): - if description is None: - description = "Unexpected error while running command." - if exit_code is None: - exit_code = '-' - message = "%s\nCommand: %s\nExit code: %s\nStdout: %r\nStderr: %r" % ( - description, cmd, exit_code, stdout, stderr) - IOError.__init__(self, message) - - -class Error(Exception): - def __init__(self, message=None): - super(Error, self).__init__(message) - - -class ApiError(Error): - def __init__(self, message='Unknown', code='Unknown'): - self.message = message - self.code = code - super(ApiError, self).__init__('%s: %s' % (code, message)) - - -class NotFound(Error): - pass - - -class Duplicate(Error): - pass - - -class NotAuthorized(Error): - pass - - -class NotEmpty(Error): - pass - - -class Invalid(Error): - pass - - -class BadInputError(Exception): - """Error resulting from a client sending bad input to a server""" - pass - - -class MissingArgumentError(Error): - pass - - -class DatabaseMigrationError(Error): - pass - - -class ClientError(Error): - pass - - -def wrap_exception(f): - def _wrap(*args, **kw): - try: - return f(*args, **kw) - except Exception, e: - if not isinstance(e, Error): - #exc_type, exc_value, exc_traceback = sys.exc_info() - logging.exception('Uncaught exception') - #logging.error(traceback.extract_stack(exc_traceback)) - raise Error(str(e)) - raise - _wrap.func_name = f.func_name - return _wrap diff --git a/keystone/common/kvs.py b/keystone/common/kvs.py new file mode 100644 index 00000000..795c2f70 --- /dev/null +++ b/keystone/common/kvs.py @@ -0,0 +1,24 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + + +class DictKvs(dict): + def set(self, key, value): + if type(value) is type({}): + self[key] = value.copy() + else: + self[key] = value[:] + + def delete(self, key): + del self[key] + + +INMEMDB = DictKvs() + + +class Base(object): + def __init__(self, db=None): + if db is None: + db = INMEMDB + elif type(db) is type({}): + db = DictKvs(db) + self.db = db diff --git a/keystone/common/logging.py b/keystone/common/logging.py new file mode 100644 index 00000000..a9aaccfc --- /dev/null +++ b/keystone/common/logging.py @@ -0,0 +1,55 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +"""Wrapper for built-in logging module.""" + +from __future__ import absolute_import + +import functools +import logging +import pprint + +from logging.handlers import SysLogHandler +from logging.handlers import WatchedFileHandler + +# A list of things we want to replicate from logging. +# levels +CRITICAL = logging.CRITICAL +FATAL = logging.FATAL +ERROR = logging.ERROR +WARNING = logging.WARNING +WARN = logging.WARN +INFO = logging.INFO +DEBUG = logging.DEBUG +NOTSET = logging.NOTSET + + +# methods +getLogger = logging.getLogger +debug = logging.debug +info = logging.info +warning = logging.warning +warn = logging.warn +error = logging.error +exception = logging.exception +critical = logging.critical +log = logging.log + +# classes +root = logging.root +Formatter = logging.Formatter + +# handlers +StreamHandler = logging.StreamHandler +WatchedFileHandler = WatchedFileHandler +SysLogHandler = SysLogHandler + + +def log_debug(f): + @functools.wraps(f) + def wrapper(*args, **kw): + logging.debug('%s(%s, %s) ->', f.func_name, str(args), str(kw)) + rv = f(*args, **kw) + logging.debug(pprint.pformat(rv, indent=2)) + logging.debug('') + return rv + return wrapper diff --git a/keystone/common/manager.py b/keystone/common/manager.py new file mode 100644 index 00000000..17c7d5e5 --- /dev/null +++ b/keystone/common/manager.py @@ -0,0 +1,36 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import functools + +from keystone import config +from keystone.common import utils + + +class Manager(object): + """Base class for intermediary request layer. + + The Manager layer exists to support additional logic that applies to all + or some of the methods exposed by a service that are not specific to the + HTTP interface. + + It also provides a stable entry point to dynamic backends. + + An example of a probable use case is logging all the calls. + + """ + + def __init__(self, driver_name): + self.driver = utils.import_object(driver_name) + + def __getattr__(self, name): + """Forward calls to the underlying driver.""" + # NOTE(termie): context is the first argument, we're going to strip + # that for now, in the future we'll probably do some + # logging and whatnot in this class + f = getattr(self.driver, name) + + @functools.wraps(f) + def _wrapper(context, *args, **kw): + return f(*args, **kw) + setattr(self, name, _wrapper) + return _wrapper diff --git a/keystone/common/sql/__init__.py b/keystone/common/sql/__init__.py new file mode 100644 index 00000000..ae31c702 --- /dev/null +++ b/keystone/common/sql/__init__.py @@ -0,0 +1 @@ +from keystone.common.sql.core import * diff --git a/keystone/common/sql/core.py b/keystone/common/sql/core.py new file mode 100644 index 00000000..cb621865 --- /dev/null +++ b/keystone/common/sql/core.py @@ -0,0 +1,119 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +"""SQL backends for the various services.""" + + +import json + +import eventlet.db_pool +import sqlalchemy as sql +from sqlalchemy import types as sql_types +from sqlalchemy.ext import declarative +import sqlalchemy.orm +import sqlalchemy.pool +import sqlalchemy.engine.url + +from keystone import config + + +CONF = config.CONF + + +ModelBase = declarative.declarative_base() + + +# For exporting to other modules +Column = sql.Column +String = sql.String +ForeignKey = sql.ForeignKey +DateTime = sql.DateTime + + +# Special Fields +class JsonBlob(sql_types.TypeDecorator): + impl = sql.Text + + def process_bind_param(self, value, dialect): + return json.dumps(value) + + def process_result_value(self, value, dialect): + return json.loads(value) + + +class DictBase(object): + def to_dict(self): + return dict(self.iteritems()) + + def __setitem__(self, key, value): + setattr(self, key, value) + + def __getitem__(self, key): + return getattr(self, key) + + def get(self, key, default=None): + return getattr(self, key, default) + + def __iter__(self): + self._i = iter(sqlalchemy.orm.object_mapper(self).columns) + return self + + def next(self): + n = self._i.next().name + return n + + def update(self, values): + """Make the model object behave like a dict.""" + for k, v in values.iteritems(): + setattr(self, k, v) + + def iteritems(self): + """Make the model object behave like a dict. + + Includes attributes from joins. + + """ + return dict([(k, getattr(self, k)) for k in self]) + #local = dict(self) + #joined = dict([(k, v) for k, v in self.__dict__.iteritems() + # if not k[0] == '_']) + #local.update(joined) + #return local.iteritems() + + +# Backends +class Base(object): + _MAKER = None + _ENGINE = None + + def get_session(self, autocommit=True, expire_on_commit=False): + """Return a SQLAlchemy session.""" + if self._MAKER is None or self._ENGINE is None: + self._ENGINE = self.get_engine() + self._MAKER = self.get_maker(self._ENGINE, + autocommit, + expire_on_commit) + + session = self._MAKER() + # TODO(termie): we may want to do something similar + #session.query = nova.exception.wrap_db_error(session.query) + #session.flush = nova.exception.wrap_db_error(session.flush) + return session + + def get_engine(self): + """Return a SQLAlchemy engine.""" + connection_dict = sqlalchemy.engine.url.make_url(CONF.sql.connection) + + engine_args = {'pool_recycle': CONF.sql.idle_timeout, + 'echo': False, + } + + if 'sqlite' in connection_dict.drivername: + engine_args['poolclass'] = sqlalchemy.pool.NullPool + + return sql.create_engine(CONF.sql.connection, **engine_args) + + def get_maker(self, engine, autocommit=True, expire_on_commit=False): + """Return a SQLAlchemy sessionmaker using the given engine.""" + return sqlalchemy.orm.sessionmaker(bind=engine, + autocommit=autocommit, + expire_on_commit=expire_on_commit) diff --git a/keystone/common/sql/legacy.py b/keystone/common/sql/legacy.py new file mode 100644 index 00000000..d8bbb2c5 --- /dev/null +++ b/keystone/common/sql/legacy.py @@ -0,0 +1,141 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import re + +import sqlalchemy + +from keystone.identity.backends import sql as identity_sql + + +def export_db(db): + table_names = db.table_names() + + migration_data = {} + for table_name in table_names: + query = db.execute("select * from %s" % table_name) + table_data = [] + for row in query.fetchall(): + entry = {} + for k in row.keys(): + entry[k] = row[k] + table_data.append(entry) + + migration_data[table_name] = table_data + + return migration_data + + +def _translate_replacements(s): + if '%' not in str(s): + return s + return re.sub(r'%([\w_]+)%', r'$(\1)s', s) + + +class LegacyMigration(object): + def __init__(self, db_string): + self.db = sqlalchemy.create_engine(db_string) + self.identity_driver = identity_sql.Identity() + self.identity_driver.db_sync() + self._data = {} + self._user_map = {} + self._tenant_map = {} + self._role_map = {} + + def migrate_all(self): + self._export_legacy_db() + self._migrate_tenants() + self._migrate_users() + self._migrate_roles() + self._migrate_user_roles() + self._migrate_tokens() + + def dump_catalog(self): + """Generate the contents of a catalog templates file.""" + self._export_legacy_db() + + services_by_id = dict((x['id'], x) for x in self._data['services']) + template = 'catalog.%(region)s.%(service_type)s.%(key)s = %(value)s' + + o = [] + for row in self._data['endpoint_templates']: + service = services_by_id[row['service_id']] + d = {'service_type': service['type'], + 'region': row['region']} + + for x in ['internal_url', 'public_url', 'admin_url', 'enabled']: + d['key'] = x.replace('_u', 'U') + d['value'] = _translate_replacements(row[x]) + o.append(template % d) + + d['key'] = 'name' + d['value'] = service['desc'] + o.append(template % d) + + return o + + def _export_legacy_db(self): + self._data = export_db(self.db) + + def _migrate_tenants(self): + for x in self._data['tenants']: + # map + new_dict = {'description': x.get('desc', ''), + 'id': x.get('uid', x.get('id')), + 'enabled': x.get('enabled', True)} + new_dict['name'] = x.get('name', new_dict.get('id')) + # track internal ids + self._tenant_map[x.get('id')] = new_dict['id'] + # create + #print 'create_tenant(%s, %s)' % (new_dict['id'], new_dict) + self.identity_driver.create_tenant(new_dict['id'], new_dict) + + def _migrate_users(self): + for x in self._data['users']: + # map + new_dict = {'email': x.get('email', ''), + 'password': x.get('password', None), + 'id': x.get('uid', x.get('id')), + 'enabled': x.get('enabled', True)} + if x.get('tenant_id'): + new_dict['tenant_id'] = self._tenant_map.get(x['tenant_id']) + new_dict['name'] = x.get('name', new_dict.get('id')) + # track internal ids + self._user_map[x.get('id')] = new_dict['id'] + # create + #print 'create_user(%s, %s)' % (new_dict['id'], new_dict) + self.identity_driver.create_user(new_dict['id'], new_dict) + if new_dict.get('tenant_id'): + self.identity_driver.add_user_to_tenant(new_dict['tenant_id'], + new_dict['id']) + + def _migrate_roles(self): + for x in self._data['roles']: + # map + new_dict = {'id': x['id'], + 'name': x.get('name', x['id'])} + # track internal ids + self._role_map[x.get('id')] = new_dict['id'] + # create + self.identity_driver.create_role(new_dict['id'], new_dict) + + def _migrate_user_roles(self): + for x in self._data['user_roles']: + # map + if (not x.get('user_id') + or not x.get('tenant_id') + or not x.get('role_id')): + continue + user_id = self._user_map[x['user_id']] + tenant_id = self._tenant_map[x['tenant_id']] + role_id = self._role_map[x['role_id']] + + try: + self.identity_driver.add_user_to_tenant(tenant_id, user_id) + except Exception: + pass + + self.identity_driver.add_role_to_user_and_tenant( + user_id, tenant_id, role_id) + + def _migrate_tokens(self): + pass diff --git a/keystone/backends/sqlalchemy/migrate_repo/README b/keystone/common/sql/migrate_repo/README index 6218f8ca..6218f8ca 100644 --- a/keystone/backends/sqlalchemy/migrate_repo/README +++ b/keystone/common/sql/migrate_repo/README diff --git a/keystone/backends/sqlalchemy/migrate_repo/__init__.py b/keystone/common/sql/migrate_repo/__init__.py index e69de29b..e69de29b 100644 --- a/keystone/backends/sqlalchemy/migrate_repo/__init__.py +++ b/keystone/common/sql/migrate_repo/__init__.py diff --git a/keystone/backends/sqlalchemy/migrate_repo/manage.py b/keystone/common/sql/migrate_repo/manage.py index 2a928c84..39fa3892 100755..100644 --- a/keystone/backends/sqlalchemy/migrate_repo/manage.py +++ b/keystone/common/sql/migrate_repo/manage.py @@ -1,3 +1,5 @@ #!/usr/bin/env python from migrate.versioning.shell import main -main(debug='False') + +if __name__ == '__main__': + main(debug='False') diff --git a/keystone/backends/sqlalchemy/migrate_repo/migrate.cfg b/keystone/common/sql/migrate_repo/migrate.cfg index 42986cf7..a8be6089 100644 --- a/keystone/backends/sqlalchemy/migrate_repo/migrate.cfg +++ b/keystone/common/sql/migrate_repo/migrate.cfg @@ -1,7 +1,7 @@ [db_settings] # Used to identify which repository this database is versioned under. # You can use the name of your project. -repository_id=Keystone +repository_id=keystone # The name of the database table used to track the schema version. # This name shouldn't already be used by your project. @@ -18,3 +18,8 @@ version_table=migrate_version # be using to ensure your updates to that database work properly. # This must be a list; example: ['postgres','sqlite'] required_dbs=[] + +# When creating new change scripts, Migrate will stamp the new script with +# a version number. By default this is latest_version + 1. You can set this +# to 'true' to tell Migrate to use the UTC timestamp instead. +use_timestamp_numbering=False diff --git a/keystone/common/sql/migrate_repo/versions/001_add_initial_tables.py b/keystone/common/sql/migrate_repo/versions/001_add_initial_tables.py new file mode 100644 index 00000000..ae54b476 --- /dev/null +++ b/keystone/common/sql/migrate_repo/versions/001_add_initial_tables.py @@ -0,0 +1,20 @@ +from sqlalchemy import * +from migrate import * + +from keystone.common import sql + +# these are to make sure all the models we care about are defined +import keystone.identity.backends.sql +import keystone.token.backends.sql +import keystone.contrib.ec2.backends.sql + + +def upgrade(migrate_engine): + # Upgrade operations go here. Don't create your own engine; bind + # migrate_engine to your metadata + sql.ModelBase.metadata.create_all(migrate_engine) + + +def downgrade(migrate_engine): + # Operations to reverse the above upgrade go here. + pass diff --git a/keystone/backends/sqlalchemy/migrate_repo/versions/__init__.py b/keystone/common/sql/migrate_repo/versions/__init__.py index e69de29b..e69de29b 100644 --- a/keystone/backends/sqlalchemy/migrate_repo/versions/__init__.py +++ b/keystone/common/sql/migrate_repo/versions/__init__.py diff --git a/keystone/common/sql/migration.py b/keystone/common/sql/migration.py new file mode 100644 index 00000000..0ea9cd12 --- /dev/null +++ b/keystone/common/sql/migration.py @@ -0,0 +1,80 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# 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 os +import sys + +import sqlalchemy +from migrate.versioning import api as versioning_api + +from keystone import config + + +CONF = config.CONF + + +try: + from migrate.versioning import exceptions as versioning_exceptions +except ImportError: + try: + # python-migration changed location of exceptions after 1.6.3 + # See LP Bug #717467 + from migrate import exceptions as versioning_exceptions + except ImportError: + sys.exit('python-migrate is not installed. Exiting.') + + +def db_sync(version=None): + if version is not None: + try: + version = int(version) + except ValueError: + raise Exception('version should be an integer') + + current_version = db_version() + repo_path = _find_migrate_repo() + if version is None or version > current_version: + return versioning_api.upgrade( + CONF.sql.connection, repo_path, version) + else: + return versioning_api.downgrade( + CONF.sql.connection, repo_path, version) + + +def db_version(): + repo_path = _find_migrate_repo() + try: + return versioning_api.db_version( + CONF.sql.connection, repo_path) + except versioning_exceptions.DatabaseNotControlledError: + return db_version_control(0) + + +def db_version_control(version=None): + repo_path = _find_migrate_repo() + versioning_api.version_control( + CONF.sql.connection, repo_path, version) + return version + + +def _find_migrate_repo(): + """Get the path for the migrate repository.""" + path = os.path.join(os.path.abspath(os.path.dirname(__file__)), + 'migrate_repo') + assert os.path.exists(path) + return path diff --git a/keystone/common/sql/util.py b/keystone/common/sql/util.py new file mode 100644 index 00000000..a181c284 --- /dev/null +++ b/keystone/common/sql/util.py @@ -0,0 +1,18 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import os + +from keystone import config +from keystone.common.sql import migration + + +CONF = config.CONF + + +def setup_test_database(): + # TODO(termie): be smart about this + try: + os.unlink('bla.db') + except Exception: + pass + migration.db_sync() diff --git a/keystone/common/template.py b/keystone/common/template.py deleted file mode 100644 index 27073e70..00000000 --- a/keystone/common/template.py +++ /dev/null @@ -1,373 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# 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. -# -# Template library copied from bottle: http://bottlepy.org/ -# -# Copyright (c) 2011, Marcel Hellkamp. -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# - - -import cgi -import re -import os -import functools -import time -import tokenize -import mimetypes -from webob import Response -from paste.util.template import TemplateError -# from paste.util.datetimeutil import parse_date -import datetime - -import keystone.logic.types.fault as fault -from keystone.logic.types.fault import ForbiddenFault - -TEMPLATES = {} -DEBUG = False -TEMPLATE_PATH = ['./', './views/'] - - -class BaseTemplate(object): - """ Base class and minimal API for template adapters """ - extentions = ['tpl', 'html', 'thtml', 'stpl'] - settings = {} # used in prepare() - defaults = {} # used in render() - - def __init__(self, source=None, name=None, lookup=None, encoding='utf8', - **settings): - """ Create a new template. - If the source parameter (str or buffer) is missing, the name argument - is used to guess a template filename. Subclasses can assume that - self.source and/or self.filename are set. Both are strings. - The lookup, encoding and settings parameters are stored as instance - variables. - The lookup parameter stores a list containing directory paths. - The encoding parameter should be used to decode byte strings or files. - The settings parameter contains a dict for engine-specific settings. - """ - lookup = lookup or [] - - self.name = name - self.source = source.read() if hasattr(source, 'read') else source - self.filename = source.filename \ - if hasattr(source, 'filename') \ - else None - self.lookup = [os.path.abspath(path) for path in lookup] - self.encoding = encoding - self.settings = self.settings.copy() # Copy from class variable - self.settings.update(settings) # Apply - if not self.source and self.name: - self.filename = self.search(self.name, self.lookup) - if not self.filename: - raise TemplateError('Template %s not found' % repr(name), - (0, 0), None) - if not self.source and not self.filename: - raise TemplateError('No template specified', (0, 0), None) - self.prepare(**self.settings) - - @classmethod - def search(cls, name, lookup=None): - """ Search name in all directories specified in lookup. - First without, then with common extensions. Return first hit. """ - lookup = lookup or [] - - if os.path.isfile(name): - return name - for spath in lookup: - fname = os.path.join(spath, name) - if os.path.isfile(fname): - return fname - for ext in cls.extentions: - if os.path.isfile('%s.%s' % (fname, ext)): - return '%s.%s' % (fname, ext) - - @classmethod - def global_config(cls, key, *args): - '''This reads or sets the global settings stored in class.settings.''' - if args: - cls.settings[key] = args[0] - else: - return cls.settings[key] - - def prepare(self, **options): - """Run preparations (parsing, caching, ...). - It should be possible to call this again to refresh a template or to - update settings. - """ - raise NotImplementedError - - def render(self, **args): - """Render the template with the specified local variables and return - a single byte or unicode string. If it is a byte string, the encoding - must match self.encoding. This method must be thread-safe! - """ - raise NotImplementedError - - -class SimpleTemplate(BaseTemplate): - blocks = ('if', 'elif', 'else', 'try', 'except', 'finally', 'for', 'while', - 'with', 'def', 'class') - dedent_blocks = ('elif', 'else', 'except', 'finally') - cache = None - code = None - compiled = None - _str = None - _escape = None - - def prepare(self, escape_func=cgi.escape, noescape=False): - self.cache = {} - if self.source: - self.code = self.translate(self.source) - self.compiled = compile(self.code, '<string>', 'exec') - else: - self.code = self.translate(open(self.filename).read()) - self.compiled = compile(self.code, self.filename, 'exec') - enc = self.encoding - touni = functools.partial(unicode, encoding=self.encoding) - self._str = lambda x: touni(x, enc) - self._escape = lambda x: escape_func(touni(x)) - if noescape: - self._str, self._escape = self._escape, self._str - - def translate(self, template): - stack = [] # Current Code indentation - lineno = 0 # Current line of code - ptrbuffer = [] # Buffer for printable strings and token tuples - codebuffer = [] # Buffer for generated python code - functools.partial(unicode, encoding=self.encoding) - multiline = dedent = False - - def yield_tokens(line): - for i, part in enumerate(re.split(r'\{\{(.*?)\}\}', line)): - if i % 2: - if part.startswith('!'): - yield 'RAW', part[1:] - else: - yield 'CMD', part - else: - yield 'TXT', part - - def split_comment(codeline): - """ Removes comments from a line of code. """ - line = codeline.splitlines()[0] - try: - tokens = list(tokenize.generate_tokens(iter(line).next)) - except tokenize.TokenError: - return line.rsplit('#', 1) if '#' in line else (line, '') - for token in tokens: - if token[0] == tokenize.COMMENT: - start, end = token[2][1], token[3][1] - return ( - codeline[:start] + codeline[end:], - codeline[start:end]) - return line, '' - - def flush(): - """Flush the ptrbuffer""" - if not ptrbuffer: - return - cline = '' - for line in ptrbuffer: - for token, value in line: - if token == 'TXT': - cline += repr(value) - elif token == 'RAW': - cline += '_str(%s)' % value - elif token == 'CMD': - cline += '_escape(%s)' % value - cline += ', ' - cline = cline[:-2] + '\\\n' - cline = cline[:-2] - if cline[:-1].endswith('\\\\\\\\\\n'): - cline = cline[:-7] + cline[-1] # 'nobr\\\\\n' --> 'nobr' - cline = '_printlist([' + cline + '])' - del ptrbuffer[:] # Do this before calling code() again - code(cline) - - def code(stmt): - for line in stmt.splitlines(): - codebuffer.append(' ' * len(stack) + line.strip()) - - for line in template.splitlines(True): - lineno += 1 - line = line if isinstance(line, unicode)\ - else unicode(line, encoding=self.encoding) - if lineno <= 2: - m = re.search(r"%.*coding[:=]\s*([-\w\.]+)", line) - if m: - self.encoding = m.group(1) - if m: - line = line.replace('coding', 'coding (removed)') - if line.strip()[:2].count('%') == 1: - line = line.split('%', 1)[1].lstrip() # Rest of line after % - cline = split_comment(line)[0].strip() - cmd = re.split(r'[^a-zA-Z0-9_]', cline)[0] - flush() # encodig (TODO: why?) - if cmd in self.blocks or multiline: - cmd = multiline or cmd - dedent = cmd in self.dedent_blocks # "else:" - if dedent and not multiline: - cmd = stack.pop() - code(line) - oneline = not cline.endswith(':') # "if 1: pass" - multiline = cmd if cline.endswith('\\') else False - if not oneline and not multiline: - stack.append(cmd) - elif cmd == 'end' and stack: - code('#end(%s) %s' % (stack.pop(), line.strip()[3:])) - elif cmd == 'include': - p = cline.split(None, 2)[1:] - if len(p) == 2: - code("_=_include(%s, _stdout, %s)" % - (repr(p[0]), p[1])) - elif p: - code("_=_include(%s, _stdout)" % repr(p[0])) - else: # Empty %include -> reverse of %rebase - code("_printlist(_base)") - elif cmd == 'rebase': - p = cline.split(None, 2)[1:] - if len(p) == 2: - code("globals()['_rebase']=(%s, dict(%s))" % ( - repr(p[0]), p[1])) - elif p: - code("globals()['_rebase']=(%s, {})" % repr(p[0])) - else: - code(line) - else: # Line starting with text (not '%') or '%%' (escaped) - if line.strip().startswith('%%'): - line = line.replace('%%', '%', 1) - ptrbuffer.append(yield_tokens(line)) - flush() - return '\n'.join(codebuffer) + '\n' - - def subtemplate(self, _name, _stdout, **args): - if _name not in self.cache: - self.cache[_name] = self.__class__(name=_name, lookup=self.lookup) - return self.cache[_name].execute(_stdout, **args) - - def execute(self, _stdout, **args): - env = self.defaults.copy() - env.update({'_stdout': _stdout, '_printlist': _stdout.extend, - '_include': self.subtemplate, '_str': self._str, - '_escape': self._escape}) - env.update(args) - eval(self.compiled, env) - if '_rebase' in env: - subtpl, rargs = env['_rebase'] - subtpl = self.__class__(name=subtpl, lookup=self.lookup) - rargs['_base'] = _stdout[:] # copy stdout - del _stdout[:] # clear stdout - return subtpl.execute(_stdout, **rargs) - return env - - def render(self, **args): - """ Render the template using keyword arguments as local variables. """ - stdout = [] - self.execute(stdout, **args) - return ''.join(stdout) - - -def static_file(resp, req, filename, root, guessmime=True, mimetype=None, - download=False): - """ Opens a file in a safe way and returns a HTTPError object with status - code 200, 305, 401 or 404. Sets Content-Type, Content-Length and - Last-Modified header. Obeys If-Modified-Since header and HEAD requests. - """ - root = os.path.abspath(root) + os.sep - filename = os.path.abspath(os.path.join(root, filename.strip('/\\'))) - if not filename.startswith(root): - return ForbiddenFault("Access denied.") - if not os.path.exists(filename) or not os.path.isfile(filename): - return fault.ItemNotFoundFault("File does not exist.") - if not os.access(filename, os.R_OK): - return ForbiddenFault( - "You do not have permission to access this file.") - - if not mimetype and guessmime: - resp.content_type = mimetypes.guess_type(filename)[0] - else: - resp.content_type = mimetype or 'text/plain' - - if download: - download = os.path.basename(filename) - resp.content_disposition = 'attachment; filename="%s"' % download - - stats = os.stat(filename) - lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", - time.gmtime(stats.st_mtime)) - resp.last_modified = lm - ims = req.environ.get('HTTP_IF_MODIFIED_SINCE') - if ims: - ims = ims.split(";")[0].strip() # IE sends "<date>; length=146" - try: - ims = datetime.datetime.fromtimestamp(stats.st_mtime) - ims = datetime.datetime.ctime(ims) - filetime = datetime.datetime.fromtimestamp(stats.st_mtime) - if ims is not None and ims >= filetime: - resp.date = time.strftime( - "%a, %d %b %Y %H:%M:%S GMT", time.gmtime()) - return Response(body=None, status=304, - headerlist=resp.headerlist) - except: - # TODO(Ziad): handle this better - pass - resp.content_length = stats.st_size - if req.method == 'HEAD': - return Response(body=None, status=200, headerlist=resp.headerlist) - else: - return Response(body=open(filename).read(), status=200, - headerlist=resp.headerlist) - - -def template(tpl, template_adapter=SimpleTemplate, **kwargs): - ''' - Get a rendered template as a string iterator. - You can use a name, a filename or a template string as first parameter. - ''' - if tpl not in TEMPLATES or DEBUG: - settings = kwargs.get('template_settings', {}) - lookup = kwargs.get('template_lookup', TEMPLATE_PATH) - if isinstance(tpl, template_adapter): - TEMPLATES[tpl] = tpl - if settings: - TEMPLATES[tpl].prepare(**settings) - elif "\n" in tpl or "{" in tpl or "%" in tpl or '$' in tpl: - TEMPLATES[tpl] = template_adapter(source=tpl, lookup=lookup, - **settings) - else: - TEMPLATES[tpl] = template_adapter(name=tpl, lookup=lookup, - **settings) - return TEMPLATES[tpl].render(**kwargs) diff --git a/keystone/common/utils.py b/keystone/common/utils.py new file mode 100644 index 00000000..96e595bb --- /dev/null +++ b/keystone/common/utils.py @@ -0,0 +1,227 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# Copyright 2011 - 2012 Justin Santa Barbara +# All Rights Reserved. +# +# 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 base64 +import hashlib +import hmac +import json +import subprocess +import sys +import time +import urllib + +import passlib.hash + +from keystone import config +from keystone.common import logging + + +CONF = config.CONF +config.register_int('crypt_strength', default=40000) + + +ISO_TIME_FORMAT = '%Y-%m-%dT%H:%M:%SZ' + + +def import_class(import_str): + """Returns a class from a string including module and class.""" + mod_str, _sep, class_str = import_str.rpartition('.') + try: + __import__(mod_str) + return getattr(sys.modules[mod_str], class_str) + except (ImportError, ValueError, AttributeError), exc: + logging.debug('Inner Exception: %s', exc) + raise + + +def import_object(import_str, *args, **kw): + """Returns an object including a module or module and class.""" + try: + __import__(import_str) + return sys.modules[import_str] + except ImportError: + cls = import_class(import_str) + return cls(*args, **kw) + + +class SmarterEncoder(json.JSONEncoder): + """Help for JSON encoding dict-like objects.""" + def default(self, obj): + if not isinstance(obj, dict) and hasattr(obj, 'iteritems'): + return dict(obj.iteritems()) + return super(SmarterEncoder, self).default(obj) + + +class Ec2Signer(object): + """Hacked up code from boto/connection.py""" + + def __init__(self, secret_key): + secret_key = secret_key.encode() + self.hmac = hmac.new(secret_key, digestmod=hashlib.sha1) + if hashlib.sha256: + self.hmac_256 = hmac.new(secret_key, digestmod=hashlib.sha256) + + def generate(self, credentials): + """Generate auth string according to what SignatureVersion is given.""" + if credentials['params']['SignatureVersion'] == '0': + return self._calc_signature_0(credentials['params']) + if credentials['params']['SignatureVersion'] == '1': + return self._calc_signature_1(credentials['params']) + if credentials['params']['SignatureVersion'] == '2': + return self._calc_signature_2(credentials['params'], + credentials['verb'], + credentials['host'], + credentials['path']) + raise Exception('Unknown Signature Version: %s' % + credentials['params']['SignatureVersion']) + + @staticmethod + def _get_utf8_value(value): + """Get the UTF8-encoded version of a value.""" + if not isinstance(value, str) and not isinstance(value, unicode): + value = str(value) + if isinstance(value, unicode): + return value.encode('utf-8') + else: + return value + + def _calc_signature_0(self, params): + """Generate AWS signature version 0 string.""" + s = params['Action'] + params['Timestamp'] + self.hmac.update(s) + return base64.b64encode(self.hmac.digest()) + + def _calc_signature_1(self, params): + """Generate AWS signature version 1 string.""" + keys = params.keys() + keys.sort(cmp=lambda x, y: cmp(x.lower(), y.lower())) + for key in keys: + self.hmac.update(key) + val = self._get_utf8_value(params[key]) + self.hmac.update(val) + return base64.b64encode(self.hmac.digest()) + + def _calc_signature_2(self, params, verb, server_string, path): + """Generate AWS signature version 2 string.""" + logging.debug('using _calc_signature_2') + string_to_sign = '%s\n%s\n%s\n' % (verb, server_string, path) + if self.hmac_256: + current_hmac = self.hmac_256 + params['SignatureMethod'] = 'HmacSHA256' + else: + current_hmac = self.hmac + params['SignatureMethod'] = 'HmacSHA1' + keys = params.keys() + keys.sort() + pairs = [] + for key in keys: + val = self._get_utf8_value(params[key]) + val = urllib.quote(val, safe='-_~') + pairs.append(urllib.quote(key, safe='') + '=' + val) + qs = '&'.join(pairs) + logging.debug('query string: %s', qs) + string_to_sign += qs + logging.debug('string_to_sign: %s', string_to_sign) + current_hmac.update(string_to_sign) + b64 = base64.b64encode(current_hmac.digest()) + logging.debug('len(b64)=%d', len(b64)) + logging.debug('base64 encoded digest: %s', b64) + return b64 + + +def hash_password(password): + """Hash a password. Hard.""" + password_utf8 = password.encode('utf-8') + if passlib.hash.sha512_crypt.identify(password_utf8): + return password_utf8 + h = passlib.hash.sha512_crypt.encrypt(password_utf8, + rounds=CONF.crypt_strength) + return h + + +def check_password(password, hashed): + """Check that a plaintext password matches hashed. + + hashpw returns the salt value concatenated with the actual hash value. + It extracts the actual salt if this value is then passed as the salt. + + """ + if password is None: + return False + password_utf8 = password.encode('utf-8') + return passlib.hash.sha512_crypt.verify(password_utf8, hashed) + + +# From python 2.7 +def check_output(*popenargs, **kwargs): + r"""Run command with arguments and return its output as a byte string. + + If the exit code was non-zero it raises a CalledProcessError. The + CalledProcessError object will have the return code in the returncode + attribute and output in the output attribute. + + The arguments are the same as for the Popen constructor. Example: + + >>> check_output(['ls', '-l', '/dev/null']) + 'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n' + + The stdout argument is not allowed as it is used internally. + To capture standard error in the result, use stderr=STDOUT. + + >>> check_output(['/bin/sh', '-c', + ... 'ls -l non_existent_file ; exit 0'], + ... stderr=STDOUT) + 'ls: non_existent_file: No such file or directory\n' + """ + if 'stdout' in kwargs: + raise ValueError('stdout argument not allowed, it will be overridden.') + logging.debug(' '.join(popenargs[0])) + process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs) + output, unused_err = process.communicate() + retcode = process.poll() + if retcode: + cmd = kwargs.get('args') + if cmd is None: + cmd = popenargs[0] + raise subprocess.CalledProcessError(retcode, cmd) + return output + + +def git(*args): + return check_output(['git'] + list(args)) + + +def isotime(dt_obj): + """Format datetime object as ISO compliant string. + + :param dt_obj: datetime.datetime object + :returns: string representation of datetime object + + """ + return dt_obj.strftime(ISO_TIME_FORMAT) + + +def unixtime(dt_obj): + """Format datetime object as unix timestamp + + :param dt_obj: datetime.datetime object + :returns: float + + """ + return time.mktime(dt_obj.utctimetuple()) diff --git a/keystone/common/wsgi.py b/keystone/common/wsgi.py index c3ce7975..7b6926d0 100755..100644 --- a/keystone/common/wsgi.py +++ b/keystone/common/wsgi.py @@ -17,102 +17,60 @@ # License for the specific language governing permissions and limitations # under the License. -# pylint: disable=W0613 -""" -Utility methods for working with WSGI servers -""" +"""Utility methods for working with WSGI servers.""" import json import logging import sys -import datetime -import ssl +import eventlet import eventlet.wsgi -eventlet.patcher.monkey_patch(all=False, socket=True) +eventlet.patcher.monkey_patch(all=False, socket=True, time=True) +import routes import routes.middleware -from webob import Response +import webob import webob.dec +import webob.exc -LOG = logging.getLogger(__name__) - - -def find_console_handler(logger): - """Returns a stream handler, if any""" - for handler in logger.handlers: - if isinstance(handler, logging.StreamHandler) and \ - handler.stream == sys.stderr: - return handler - - -def add_console_handler(logger, level=logging.INFO): - """ - Add a Handler which writes log messages to sys.stderr (usually the console) - """ - console = find_console_handler(logger) - - if not console: - console = logging.StreamHandler() - console.setLevel(level) - # set a format which is simpler for console use - formatter = logging.Formatter( - "%(name)-12s: %(levelname)-8s %(message)s") - # tell the handler to use this format - console.setFormatter(formatter) - # add the handler to the root logger - LOG.debug("Adding console handler at level %s" % level) - logger.addHandler(console) - elif console.level != level: - LOG.debug("Setting console handler level to %s from %s" % (level, - console.level)) - console.setLevel(level) - return console +from keystone import exception +from keystone.common import utils class WritableLogger(object): """A thin wrapper that responds to `write` and logs.""" - def __init__(self, logger, level=logging.INFO): + def __init__(self, logger, level=logging.DEBUG): self.logger = logger self.level = level - if level == logging.DEBUG: - add_console_handler(logger, level) def write(self, msg): - self.logger.log(self.level, msg.strip("\n")) - - -def run_server(application, port): - """Run a WSGI server with the given application.""" - LOG.debug("Running WSGI server on 0.0.0.0:%s" % port) - sock = eventlet.listen(('0.0.0.0', port)) - eventlet.wsgi.server(sock, application) + self.logger.log(self.level, msg) class Server(object): """Server class to manage multiple WSGI sockets and applications.""" - started = False - def __init__(self, application=None, port=None, threads=1000): + def __init__(self, application, port, threads=1000): self.application = application self.port = port self.pool = eventlet.GreenPool(threads) self.socket_info = {} - self.threads = {} + self.greenthread = None - def start(self, application=None, port=None, host='0.0.0.0', key=None, - backlog=128): + def start(self, host='0.0.0.0', key=None, backlog=128): """Run a WSGI server with the given application.""" - if application is not None: - self.application = application - if port is not None: - self.port = port - LOG.debug("start server '%s' on %s:%s" % (key, host, self.port)) + logging.debug('Starting %(arg0)s on %(host)s:%(port)s' % \ + {'arg0': sys.argv[0], + 'host': host, + 'port': self.port}) socket = eventlet.listen((host, self.port), backlog=backlog) - thread = self.pool.spawn(self._run, self.application, socket) + self.greenthread = self.pool.spawn(self._run, self.application, socket) if key: - self.socket_info[key] = socket - self.threads[key] = thread + self.socket_info[key] = socket.getsockname() + + def kill(self): + if self.greenthread: + self.greenthread.kill() def wait(self): """Wait until all servers have completed running.""" @@ -123,50 +81,189 @@ class Server(object): def _run(self, application, socket): """Start a WSGI server in a new green thread.""" - LOG.debug("_run called") - eventlet_logger = logging.getLogger('eventlet.wsgi.server') + logger = logging.getLogger('eventlet.wsgi.server') eventlet.wsgi.server(socket, application, custom_pool=self.pool, - log=WritableLogger(eventlet_logger, logging.root.level)) - - -class SslServer(Server): - """SSL Server class to manage multiple WSGI sockets and applications.""" - # pylint: disable=W0221,R0913 - def start(self, application, port, host='0.0.0.0', backlog=128, - certfile=None, keyfile=None, ca_certs=None, - cert_required='True', key=None): - """Run a 2-way SSL WSGI server with the given application.""" - LOG.debug("start SSL server '%s' on %s:%s" % (key, host, port)) - socket = eventlet.listen((host, port), backlog=backlog) - if cert_required == 'True': - cert_reqs = ssl.CERT_REQUIRED - else: - cert_reqs = ssl.CERT_NONE - sslsocket = eventlet.wrap_ssl(socket, certfile=certfile, - keyfile=keyfile, - server_side=True, cert_reqs=cert_reqs, - ca_certs=ca_certs) - thread = self.pool.spawn(self._run, application, sslsocket) - if key: - self.socket_info[key] = sslsocket - self.threads[key] = thread + log=WritableLogger(logger)) -class Middleware(object): - """ - Base WSGI middleware wrapper. These classes require an application to be +class Request(webob.Request): + pass + + +class BaseApplication(object): + """Base WSGI application wrapper. Subclasses need to implement __call__.""" + + @classmethod + def factory(cls, global_config, **local_config): + """Used for paste app factories in paste.deploy config files. + + Any local configuration (that is, values under the [app:APPNAME] + section of the paste config) will be passed into the `__init__` method + as kwargs. + + A hypothetical configuration would look like: + + [app:wadl] + latest_version = 1.3 + paste.app_factory = nova.api.fancy_api:Wadl.factory + + which would result in a call to the `Wadl` class as + + import nova.api.fancy_api + fancy_api.Wadl(latest_version='1.3') + + You could of course re-implement the `factory` method in subclasses, + but using the kwarg passing it shouldn't be necessary. + + """ + return cls() + + def __call__(self, environ, start_response): + r"""Subclasses will probably want to implement __call__ like this: + + @webob.dec.wsgify(RequestClass=Request) + def __call__(self, req): + # Any of the following objects work as responses: + + # Option 1: simple string + res = 'message\n' + + # Option 2: a nicely formatted HTTP exception page + res = exc.HTTPForbidden(detail='Nice try') + + # Option 3: a webob Response object (in case you need to play with + # headers, or you want to be treated like an iterable, or or or) + res = Response(); + res.app_iter = open('somefile') + + # Option 4: any wsgi app to be run next + res = self.application + + # Option 5: you can get a Response object for a wsgi app, too, to + # play with headers etc + res = req.get_response(self.application) + + # You can then just return your response... + return res + # ... or set req.response and return None. + req.response = res + + See the end of http://pythonpaste.org/webob/modules/dec.html + for more info. + + """ + raise NotImplementedError('You must implement __call__') + + +class Application(BaseApplication): + @webob.dec.wsgify + def __call__(self, req): + arg_dict = req.environ['wsgiorg.routing_args'][1] + action = arg_dict.pop('action') + del arg_dict['controller'] + logging.debug('arg_dict: %s', arg_dict) + + # allow middleware up the stack to provide context & params + context = req.environ.get('openstack.context', {}) + context['query_string'] = dict(req.params.iteritems()) + params = req.environ.get('openstack.params', {}) + params.update(arg_dict) + + # TODO(termie): do some basic normalization on methods + method = getattr(self, action) + + # NOTE(vish): make sure we have no unicode keys for py2.6. + params = self._normalize_dict(params) + + try: + result = method(context, **params) + except exception.Error as e: + logging.warning(e) + return render_exception(e) + + if result is None or type(result) is str or type(result) is unicode: + return result + elif isinstance(result, webob.exc.WSGIHTTPException): + return result + + response = webob.Response() + self._serialize(response, result) + return response + + def _serialize(self, response, result): + response.content_type = 'application/json' + response.body = json.dumps(result, cls=utils.SmarterEncoder) + + def _normalize_arg(self, arg): + return str(arg).replace(':', '_').replace('-', '_') + + def _normalize_dict(self, d): + return dict([(self._normalize_arg(k), v) + for (k, v) in d.iteritems()]) + + def assert_admin(self, context): + if not context['is_admin']: + try: + user_token_ref = self.token_api.get_token( + context=context, token_id=context['token_id']) + except exception.TokenNotFound: + raise exception.Unauthorized() + creds = user_token_ref['metadata'].copy() + creds['user_id'] = user_token_ref['user'].get('id') + creds['tenant_id'] = user_token_ref['tenant'].get('id') + # NOTE(vish): this is pretty inefficient + creds['roles'] = [self.identity_api.get_role(context, role)['name'] + for role in creds.get('roles', [])] + # Accept either is_admin or the admin role + assert self.policy_api.can_haz(context, + ('is_admin:1', 'roles:admin'), + creds) + + +class Middleware(Application): + """Base WSGI middleware. + + These classes require an application to be initialized that will be called next. By default the middleware will simply call its wrapped app, or you can override __call__ to customize its behavior. + """ + @classmethod + def factory(cls, global_config, **local_config): + """Used for paste app factories in paste.deploy config files. + + Any local configuration (that is, values under the [filter:APPNAME] + section of the paste config) will be passed into the `__init__` method + as kwargs. + + A hypothetical configuration would look like: + + [filter:analytics] + redis_host = 127.0.0.1 + paste.filter_factory = nova.api.analytics:Analytics.factory + + which would result in a call to the `Analytics` class as + + import nova.api.analytics + analytics.Analytics(app_from_paste, redis_host='127.0.0.1') + + You could of course re-implement the `factory` method in subclasses, + but using the kwarg passing it shouldn't be necessary. + + """ + def _factory(app): + conf = global_config.copy() + conf.update(local_config) + return cls(app) + return _factory + def __init__(self, application): self.application = application - @staticmethod - def process_request(req): - """ - Called on each request. + def process_request(self, req): + """Called on each request. If this returns None, the next application down the stack will be executed. If it returns a response then that response will be returned @@ -175,13 +272,11 @@ class Middleware(object): """ return None - @staticmethod - def process_response(response): + def process_response(self, response): """Do whatever you'd like to the response.""" return response - # pylint: disable=W1111 - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=Request) def __call__(self, req): response = self.process_request(req) if response: @@ -191,23 +286,30 @@ class Middleware(object): class Debug(Middleware): - """ - Helper class that can be inserted into any WSGI application chain - to get information about the request and response. + """Helper class for debugging a WSGI application. + + Can be inserted into any WSGI application chain to get information + about the request and response. + """ - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=Request) def __call__(self, req): - print ("*" * 40) + " REQUEST ENVIRON" + logging.debug('%s %s %s', ('*' * 20), 'REQUEST ENVIRON', ('*' * 20)) for key, value in req.environ.items(): - print key, "=", value - print + logging.debug('%s = %s', key, value) + logging.debug('') + logging.debug('%s %s %s', ('*' * 20), 'REQUEST BODY', ('*' * 20)) + for line in req.body_file: + logging.debug(line) + logging.debug('') + resp = req.get_response(self.application) - print ("*" * 40) + " RESPONSE HEADERS" + logging.debug('%s %s %s', ('*' * 20), 'RESPONSE HEADERS', ('*' * 20)) for (key, value) in resp.headers.iteritems(): - print key, "=", value - print + logging.debug('%s = %s', key, value) + logging.debug('') resp.app_iter = self.print_generator(resp.app_iter) @@ -215,283 +317,158 @@ class Debug(Middleware): @staticmethod def print_generator(app_iter): - """ - Iterator that prints the contents of a wrapper string iterator - when iterated. - """ - print ("*" * 40) + " BODY" + """Iterator that prints the contents of a wrapper string.""" + logging.debug('%s %s %s', ('*' * 20), 'RESPONSE BODY', ('*' * 20)) for part in app_iter: - sys.stdout.write(part) - sys.stdout.flush() + #sys.stdout.write(part) + logging.debug(part) + #sys.stdout.flush() yield part print -def debug_filter_factory(global_conf): - """Filter factor to easily insert a debugging middleware into the - paste.deploy pipeline""" - def filter(app): - return Debug(app) - return filter - - class Router(object): - """ - WSGI middleware that maps incoming requests to WSGI apps. - """ + """WSGI middleware that maps incoming requests to WSGI apps.""" def __init__(self, mapper): - """ - Create a router for the given routes.Mapper. + """Create a router for the given routes.Mapper. Each route in `mapper` must specify a 'controller', which is a WSGI app to call. You'll probably want to specify an 'action' as - well and have your controller be a wsgi.Controller, who will route - the request to the action method. + well and have your controller be an object that can route + the request to the action-specific method. Examples: mapper = routes.Mapper() sc = ServerController() # Explicit mapping of one route to a controller+action - mapper.connect(None, "/svrlist", controller=sc, action="list") + mapper.connect(None, '/svrlist', controller=sc, action='list') # Actions are all implicitly defined - mapper.resource("server", "servers", controller=sc) + mapper.resource('server', 'servers', controller=sc) # Pointing to an arbitrary WSGI app. You can specify the # {path_info:.*} parameter so the target app can be handed just that # section of the URL. - mapper.connect(None, "/v2.0/{path_info:.*}", controller=TheApp()) + mapper.connect(None, '/v1.0/{path_info:.*}', controller=BlogApp()) + """ self.map = mapper self._router = routes.middleware.RoutesMiddleware(self._dispatch, self.map) - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=Request) def __call__(self, req): - """ - Route the incoming request to a controller based on self.map. + """Route the incoming request to a controller based on self.map. + If no match, return a 404. + """ return self._router @staticmethod - @webob.dec.wsgify + @webob.dec.wsgify(RequestClass=Request) def _dispatch(req): - """ + """Dispatch the request to the appropriate controller. + Called by self._router after matching the incoming request to a route - and putting the information into req.environ. Returns the routed - WSGI app's response or an Accept-appropriate 404. + and putting the information into req.environ. Either returns 404 + or the routed WSGI app's response. + """ - return req.environ['wsgiorg.routing_args'][1].get('controller') \ - or HTTPNotFound() + match = req.environ['wsgiorg.routing_args'][1] + if not match: + return webob.exc.HTTPNotFound() + app = match['controller'] + return app -class Controller(object): - """ - WSGI app that reads routing information supplied by RoutesMiddleware - and calls the requested action method upon itself. All action methods - must, in addition to their normal parameters, accept a 'req' argument - which is the incoming webob.Request. They raise a webob.exc exception, - or return a dict which will be serialized by requested content type. - """ +class ComposingRouter(Router): + def __init__(self, mapper=None, routers=None): + if mapper is None: + mapper = routes.Mapper() + if routers is None: + routers = [] + for router in routers: + router.add_routes(mapper) + super(ComposingRouter, self).__init__(mapper) - @webob.dec.wsgify - def __call__(self, req): - """ - Call the method specified in req.environ by RoutesMiddleware. - """ - arg_dict = req.environ['wsgiorg.routing_args'][1] - action = arg_dict['action'] - method = getattr(self, action) - del arg_dict['controller'] - del arg_dict['action'] - arg_dict['req'] = req - result = method(**arg_dict) - if isinstance(result, dict): - return self._serialize(result, req) - else: - return result - def _serialize(self, data, request): - """ - Serialize the given dict to the response type requested in request. - Uses self._serialization_metadata if it exists, which is a dict mapping - MIME types to information needed to serialize to that type. - """ - _metadata = getattr(type(self), "_serialization_metadata", {}) - serializer = Serializer(request.environ, _metadata) - return serializer.to_content_type(data) +class ComposableRouter(Router): + """Router that supports use by ComposingRouter.""" + def __init__(self, mapper=None): + if mapper is None: + mapper = routes.Mapper() + self.add_routes(mapper) + super(ComposableRouter, self).__init__(mapper) -class Serializer(object): - """ - Serializes a dictionary to a Content Type specified by a WSGI environment. + def add_routes(self, mapper): + """Add routes to given mapper.""" + pass + + +class ExtensionRouter(Router): + """A router that allows extensions to supplement or overwrite routes. + + Expects to be subclassed. """ + def __init__(self, application, mapper=None): + if mapper is None: + mapper = routes.Mapper() + self.application = application + self.add_routes(mapper) + mapper.connect('{path_info:.*}', controller=self.application) + super(ExtensionRouter, self).__init__(mapper) - def __init__(self, environ, metadata=None): - """ - Create a serializer based on the given WSGI environment. - 'metadata' is an optional dict mapping MIME types to information - needed to serialize a dictionary to that type. - """ - if metadata is None: - metadata = {} - self.environ = environ - self.metadata = metadata - self._methods = { - 'application/json': self._to_json, - 'application/xml': self._to_xml} - - def to_content_type(self, data): - """ - Serialize a dictionary into a string. The format of the string - will be decided based on the Content Type requested in self.environ: - by Accept: header, or by URL suffix. - """ - # FIXME(sirp): for now, supporting json only - #mimetype = 'application/xml' - mimetype = 'application/json' - LOG.debug("serializing: mimetype=%s" % mimetype) - # TODO(gundlach): determine mimetype from request - return self._methods.get(mimetype, repr)(data) + def add_routes(self, mapper): + pass - @staticmethod - def _to_json(data): - def sanitizer(obj): - if isinstance(obj, datetime.datetime): - return obj.isoformat() - return obj - - return json.dumps(data, default=sanitizer) - - def _to_xml(self, data): - metadata = self.metadata.get('application/xml', {}) - # We expect data to contain a single key which is the XML root. - root_key = data.keys()[0] - from xml.dom import minidom - doc = minidom.Document() - node = self._to_xml_node(doc, metadata, root_key, data[root_key]) - return node.toprettyxml(indent=' ') - - def _to_xml_node(self, doc, metadata, nodename, data): - """Recursive method to convert data members to XML nodes.""" - result = doc.createElement(nodename) - if isinstance(data, list): - singular = metadata.get('plurals', {}).get(nodename, None) - if singular is None: - if nodename.endswith('s'): - singular = nodename[:-1] - else: - singular = 'item' - for item in data: - node = self._to_xml_node(doc, metadata, singular, item) - result.appendChild(node) - elif isinstance(data, dict): - attrs = metadata.get('attributes', {}).get(nodename, {}) - for k, v in data.items(): - if k in attrs: - result.setAttribute(k, str(v)) - else: - node = self._to_xml_node(doc, metadata, k, v) - result.appendChild(node) - else: # atom - node = doc.createTextNode(str(data)) - result.appendChild(node) - return result - - -class WSGIHTTPException(Response, webob.exc.HTTPException): - """Returned when no matching route can be identified""" - - code = None - label = None - title = None - explanation = None - - xml_template = """\ -<?xml version="1.0" encoding="UTF-8"?> -<%s xmlns="http://docs.openstack.org/identity/api/v2.0" code="%s"> - <message>%s</message> - <details>%s</details> -</%s>""" - - def __init__(self, code, label, title, explanation, **kw): - self.code = code - self.label = label - self.title = title - self.explanation = explanation - - Response.__init__(self, status='%s %s' % (self.code, self.title), **kw) - webob.exc.HTTPException.__init__(self, self.explanation, self) - - def xml_body(self): - """Generate a XML body string using the available data""" - return self.xml_template % ( - self.label, self.code, self.title, self.explanation, self.label) - - def json_body(self): - """Generate a JSON body string using the available data""" - json_dict = {self.label: {}} - json_dict[self.label]['message'] = self.title - json_dict[self.label]['details'] = self.explanation - json_dict[self.label]['code'] = self.code - - return json.dumps(json_dict) - - def generate_response(self, environ, start_response): - """Returns a response to the given environment""" - if self.content_length is not None: - del self.content_length - - headerlist = list(self.headerlist) - - accept = environ.get('HTTP_ACCEPT', '') - - # Return JSON by default - if accept and 'xml' in accept: - content_type = 'application/xml' - body = self.xml_body() - else: - content_type = 'application/json' - body = self.json_body() - - extra_kw = {} - - if isinstance(body, unicode): - extra_kw.update(charset='utf-8') - - resp = Response(body, - status=self.status, - headerlist=headerlist, - content_type=content_type, - **extra_kw) - - # Why is this repeated? - resp.content_type = content_type - - return resp(environ, start_response) + @classmethod + def factory(cls, global_config, **local_config): + """Used for paste app factories in paste.deploy config files. - def __call__(self, environ, start_response): - if environ['REQUEST_METHOD'] == 'HEAD': - start_response(self.status, self.headerlist) - return [] - if not self.body: - return self.generate_response(environ, start_response) - return webob.Response.__call__(self, environ, start_response) - - def exception(self): - """Returns self as an exception response""" - return webob.exc.HTTPException(self.explanation, self) - - exception = property(exception) - - -class HTTPNotFound(WSGIHTTPException): - """Represents a 404 Not Found webob response exception""" - def __init__(self, code=404, label='itemNotFound', title='Item not found.', - explanation='Error Details...', **kw): - """Build a 404 WSGI response""" - super(HTTPNotFound, self).__init__(code, label, title, explanation, - **kw) + Any local configuration (that is, values under the [filter:APPNAME] + section of the paste config) will be passed into the `__init__` method + as kwargs. + + A hypothetical configuration would look like: + + [filter:analytics] + redis_host = 127.0.0.1 + paste.filter_factory = nova.api.analytics:Analytics.factory + + which would result in a call to the `Analytics` class as + + import nova.api.analytics + analytics.Analytics(app_from_paste, redis_host='127.0.0.1') + + You could of course re-implement the `factory` method in subclasses, + but using the kwarg passing it shouldn't be necessary. + + """ + def _factory(app): + conf = global_config.copy() + conf.update(local_config) + return cls(app) + return _factory + + +def render_exception(error): + """Forms a WSGI response based on the current error.""" + resp = webob.Response() + resp.status = '%s %s' % (error.code, error.title) + resp.headerlist = [('Content-Type', 'application/json')] + + body = { + 'error': { + 'code': error.code, + 'title': error.title, + 'message': str(error), + } + } + + resp.body = json.dumps(body) + + return resp diff --git a/keystone/config.py b/keystone/config.py index 977c3df3..e95efe42 100644 --- a/keystone/config.py +++ b/keystone/config.py @@ -1,41 +1,22 @@ -#!/usr/bin/env python - -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# 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. - -""" -Routines for configuring OpenStack Service -""" +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + import gettext -import logging -import logging.config -import logging.handlers import sys import os -from keystone import cfg +from keystone.common import cfg +from keystone.common import logging -gettext.install("keystone", unicode=1) +gettext.install('keystone', unicode=1) -class Config(cfg.CommonConfigOpts): - def __call__(self, config_files=None): +class ConfigMixin(object): + def __call__(self, config_files=None, *args, **kw): if config_files is not None: - self._opts["config_file"]["opt"].default = config_files - return super(Config, self).__call__() + self._opts['config_file']['opt'].default = config_files + kw.setdefault('args', []) + return super(ConfigMixin, self).__call__(*args, **kw) def __getitem__(self, key, default=None): return getattr(self, key, default) @@ -44,21 +25,23 @@ class Config(cfg.CommonConfigOpts): return setattr(self, key, value) def iteritems(self): - for key in self._opts: - yield (key, getattr(self, key)) - - def to_dict(self): - """ Returns a representation of the CONF settings as a dict.""" - ret = {} - for key, val in self.iteritems(): - if val is not None: - ret[key] = val - for grp_name in self._groups: - ret[grp_name] = grp_dict = {} - grp = self._get_group(grp_name) - for opt in grp._opts: # pylint: disable=W0212 - grp_dict[opt] = self._get(opt, grp_name) - return ret + for k in self._opts: + yield (k, getattr(self, k)) + + def print_help(self): + self._oparser.print_help() + + def set_usage(self, usage): + self.usage = usage + self._oparser.usage = usage + + +class Config(ConfigMixin, cfg.ConfigOpts): + pass + + +class CommonConfig(ConfigMixin, cfg.CommonConfigOpts): + pass def setup_logging(conf): @@ -67,15 +50,16 @@ def setup_logging(conf): :param conf: a cfg.ConfOpts object """ + if conf.log_config: # Use a logging configuration file for all settings... - for location in (sys.argv[0], "."): - pth = os.path.join(location, "etc", conf.log_config) - if os.path.exists(pth): - logging.config.fileConfig(pth) - return - raise RuntimeError("Unable to locate specified logging " - "config file: %s" % conf.log_config) + if os.path.exists(conf.log_config): + logging.config.fileConfig(conf.log_config) + return + else: + raise RuntimeError('Unable to locate specified logging ' + 'config file: %s' % conf.log_config) + root_logger = logging.root if conf.debug: root_logger.setLevel(logging.DEBUG) @@ -83,94 +67,92 @@ def setup_logging(conf): root_logger.setLevel(logging.INFO) else: root_logger.setLevel(logging.WARNING) + formatter = logging.Formatter(conf.log_format, conf.log_date_format) if conf.use_syslog: try: - facility = getattr(logging.handlers.SysLogHandler, + facility = getattr(logging.SysLogHandler, conf.syslog_log_facility) except AttributeError: - raise ValueError(_("Invalid syslog facility")) + raise ValueError(_('Invalid syslog facility')) - handler = logging.handlers.SysLogHandler(address="/dev/log", + handler = logging.SysLogHandler(address='/dev/log', facility=facility) elif conf.log_file: logfile = conf.log_file if conf.log_dir: logfile = os.path.join(conf.log_dir, logfile) - handler = logging.handlers.WatchedFileHandler(logfile) + handler = logging.WatchedFileHandler(logfile) else: handler = logging.StreamHandler(sys.stdout) + handler.setFormatter(formatter) root_logger.addHandler(handler) def register_str(*args, **kw): - group = _ensure_group(kw) - return CONF.register_opt(cfg.StrOpt(*args, **kw), group=group) + conf = kw.pop('conf', CONF) + group = _ensure_group(kw, conf) + return conf.register_opt(cfg.StrOpt(*args, **kw), group=group) def register_cli_str(*args, **kw): - group = _ensure_group(kw) - return CONF.register_opt(cfg.StrOpt(*args, **kw), group=group) + conf = kw.pop('conf', CONF) + group = _ensure_group(kw, conf) + return conf.register_cli_opt(cfg.StrOpt(*args, **kw), group=group) def register_bool(*args, **kw): - group = _ensure_group(kw) - return CONF.register_opt(cfg.BoolOpt(*args, **kw), group=group) + conf = kw.pop('conf', CONF) + group = _ensure_group(kw, conf) + return conf.register_opt(cfg.BoolOpt(*args, **kw), group=group) def register_cli_bool(*args, **kw): - group = _ensure_group(kw) - return CONF.register_cli_opt(cfg.BoolOpt(*args, **kw), group=group) + conf = kw.pop('conf', CONF) + group = _ensure_group(kw, conf) + return conf.register_cli_opt(cfg.BoolOpt(*args, **kw), group=group) -def register_list(*args, **kw): - group = _ensure_group(kw) - return CONF.register_opt(cfg.ListOpt(*args, **kw), group=group) +def register_int(*args, **kw): + conf = kw.pop('conf', CONF) + group = _ensure_group(kw, conf) + return conf.register_opt(cfg.IntOpt(*args, **kw), group=group) -def register_multi_string(*args, **kw): - group = _ensure_group(kw) - return CONF.register_opt(cfg.MultiStrOpt(*args, **kw), group=group) +def register_cli_int(*args, **kw): + conf = kw.pop('conf', CONF) + group = _ensure_group(kw, conf) + return conf.register_cli_opt(cfg.IntOpt(*args, **kw), group=group) -def _ensure_group(kw): - group = kw.pop("group", None) +def _ensure_group(kw, conf): + group = kw.pop('group', None) if group: - CONF.register_group(cfg.OptGroup(name=group)) + conf.register_group(cfg.OptGroup(name=group)) return group -CONF = Config(project="keystone") - -register_str("default_store") -register_str("service_header_mappings") -register_list("extensions") -register_str("service_host") -register_str("service_port") -register_bool("service_ssl") -register_str("admin_host") -register_str("admin_port") -register_bool("admin_ssl") -register_str("bind_host") -register_str("bind_port") -register_str("certfile") -register_str("keyfile") -register_str("ca_certs") -register_bool("cert_required") -register_str("keystone_admin_role") -register_str("keystone_service_admin_role") -register_bool("hash_password") -register_str("backends") -register_str("global_service_id") -register_bool("disable_tokens_in_url") - -register_str("sql_connection", group="keystone.backends.sqlalchemy") -register_str("backend_entities", group="keystone.backends.sqlalchemy") -register_str("sql_idle_timeout", group="keystone.backends.sqlalchemy") -# May need to initialize other backends, too. -register_str("ldap_url", group="keystone.backends.ldap") -register_str("ldap_user", group="keystone.backends.ldap") -register_str("ldap_password", group="keystone.backends.ldap") -register_list("backend_entities", group="kkeystone.backends.ldap") +CONF = CommonConfig(project='keystone') + + +register_str('admin_token', default='ADMIN') +register_str('compute_port') +register_str('admin_port') +register_str('public_port') + + +# sql options +register_str('connection', group='sql') +register_str('idle_timeout', group='sql') +register_str('min_pool_size', group='sql') +register_str('maz_pool_size', group='sql') +register_str('pool_timeout', group='sql') + + +register_str('driver', group='catalog') +register_str('driver', group='identity') +register_str('driver', group='policy') +register_str('driver', group='token') +register_str('driver', group='ec2') diff --git a/keystone/content/admin/HP-IDM-admin-devguide.pdf b/keystone/content/admin/HP-IDM-admin-devguide.pdf Binary files differdeleted file mode 100644 index 7ab314ac..00000000 --- a/keystone/content/admin/HP-IDM-admin-devguide.pdf +++ /dev/null diff --git a/keystone/content/admin/HP-IDM-admin.wadl b/keystone/content/admin/HP-IDM-admin.wadl deleted file mode 100644 index 4fcb25d4..00000000 --- a/keystone/content/admin/HP-IDM-admin.wadl +++ /dev/null @@ -1,150 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!--*******************************************************--> -<!-- Import Common XML Entities --> -<!-- --> -<!-- You can resolve the entites with xmllint --> -<!-- --> -<!-- xmllint -noent HP-IDM-admin.wadl --> -<!--*******************************************************--> -<!DOCTYPE application [ -<!ENTITY % common SYSTEM "https://raw.github.com/openstack/keystone/master/keystone/content/common/common.ent"> - %common; -]> - -<application xmlns="http://wadl.dev.java.net/2009/02" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:HP-IDM="http://docs.openstack.org/identity/api/ext/HP-IDM/v1.0" - xmlns:capi="http://docs.openstack.org/common/api/v1.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0"> - - <grammars> - <include href="https://raw.github.com/openstack/keystone/master/keystone/content/common/xsd/api.xsd"/> - <include href="https://raw.github.com/openstack/keystone/master/keystone/content/common/xsd/api-common.xsd"/> - </grammars> - - <!--*******************************************************--> - <!-- All Resources --> - <!--*******************************************************--> - - <!-- We should use SSL in production --> - <resources base="http://localhost:35357"> - <resource id="version" path="v2.0"> - <resource id="tokens" path="tokens"> - <resource id="tokenById" path="{tokenId}"> - <param name="X-Auth-Token" style="header" type="xsd:string" required="true"> - <doc>You need a valid admin token for access.</doc> - </param> - <param name="tokenId" style="template" type="xsd:string" required="true"/> - <param name="belongsTo" style="query" type="xsd:string" required="false"/> - <param name="HP-IDM-serviceId" style="query" type="xsd:string" required="false"/> - <method href="#validateToken"/> - <method href="#checkToken"/> - </resource> - </resource> - </resource> - </resources> - - <!--*******************************************************--> - <!-- All Methods --> - <!--*******************************************************--> - - - <!-- Token Operations --> - <method name="GET" id="validateToken"> - <doc xml:lang="EN" title="Validate Token"> - <p xmlns="http://www.w3.org/1999/xhtml" class="shortdesc"> - Check that a token is valid and that it belongs to a supplied tenant - and services and return the permissions relevant to a particular client. - </p> - <p xmlns="http://www.w3.org/1999/xhtml"> - Valid tokens will exist in the - <code>/tokens/{tokenId}</code> path and invalid - tokens will not. In other words, a user should expect an - itemNotFound (<code>404</code>) fault for an - invalid token. - </p> - <p xmlns="http://www.w3.org/1999/xhtml"> - If 'HP-IDM-serviceId' is provided, it must be a comma-separated string of - service IDs. If any of the service IDs is invalid or if there are no - roles associated with the service IDs, a user should expect a 401. - </p> - </doc> - <request> - <param name="belongsTo" style="query" required="false" type="xsd:string"> - <doc xml:lang="EN"> - <p xmlns="http://www.w3.org/1999/xhtml"> - Validates a token has the supplied tenant in scope. - </p> - </doc> - </param> - <param name="HP-IDM-serviceId" style="query" required="false" type="xsd:string"> - <doc xml:lang="EN"> - <p xmlns="http://www.w3.org/1999/xhtml"> - If provided, filter the roles to be returned by the given service IDs. - </p> - </doc> - </param> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:access"> - <doc> - <xsdxt:code href="../samples/validatetoken.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc> - <xsdxt:code href="../samples/validatetoken.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - <method name="HEAD" id="checkToken"> - <doc xml:lang="EN" title="Check Token"> - <p xmlns="http://www.w3.org/1999/xhtml" class="shortdesc"> - Check that a token is valid and that it belongs to a particular tenant and services - (For performance). - </p> - <p xmlns="http://www.w3.org/1999/xhtml"> - Valid tokens will exist in the - <code>/tokens/{tokenId}</code> path and invalid - tokens will not. In other words, a user should expect an - itemNotFound (<code>404</code>) fault for an - invalid token. - </p> - <p xmlns="http://www.w3.org/1999/xhtml"> - If `belongsTo` is provided, validates that a token has a specific tenant in scope. - </p> - <p xmlns="http://www.w3.org/1999/xhtml"> - If 'HP-IDM-serviceId' is provided, it must be a comma-separated string of - service IDs. If any of the service ID is invalid or if there are no - roles associated with the service IDs, a user should expect a 401. - </p> - <p xmlns="http://www.w3.org/1999/xhtml"> - No response body is returned for this method. - </p> - </doc> - <request> - <param name="belongsTo" style="query" required="false" type="xsd:string"> - <doc xml:lang="EN"> - <p xmlns="http://www.w3.org/1999/xhtml"> - Validates a token has the supplied tenant in scope. (for performance). - </p> - </doc> - </param> - <param name="HP-IDM-serviceId" style="query" required="false" type="xsd:string"> - <doc xml:lang="EN"> - <p xmlns="http://www.w3.org/1999/xhtml"> - Check the roles against the given service IDs. - </p> - </doc> - </param> - </request> - <response status="200 203"/> - &commonFaults; - &getFaults; - </method> -</application> diff --git a/keystone/content/admin/OS-KSADM-admin-devguide.pdf b/keystone/content/admin/OS-KSADM-admin-devguide.pdf Binary files differdeleted file mode 100644 index 7a433dc7..00000000 --- a/keystone/content/admin/OS-KSADM-admin-devguide.pdf +++ /dev/null diff --git a/keystone/content/admin/OS-KSADM-admin.wadl b/keystone/content/admin/OS-KSADM-admin.wadl deleted file mode 100644 index 3f034427..00000000 --- a/keystone/content/admin/OS-KSADM-admin.wadl +++ /dev/null @@ -1,793 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- (C) 2011 OpenStack LLC., All Rights Reserved --> -<!--*******************************************************--> -<!-- Import Common XML Entities --> -<!-- --> -<!-- You can resolve the entites with xmllint --> -<!-- --> -<!-- xmllint -noent OS-KSADM-admin.wadl --> -<!--*******************************************************--> -<!DOCTYPE application [ -<!ENTITY % common SYSTEM "../common/common.ent"> -%common; -]> - -<application xmlns="http://wadl.dev.java.net/2009/02" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:OS-KSADM="http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0" - xmlns:capi="http://docs.openstack.org/common/api/v1.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xsi:schemaLocation="http://docs.openstack.org/identity/api/v2.0 ../common/xsd/api.xsd - http://docs.openstack.org/common/api/v1.0 ../common/xsd/api-common.xsd - http://wadl.dev.java.net/2009/02 http://www.w3.org/Submission/wadl/wadl.xsd - http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0 ../common/xsd/OS-KSADM.xsd - "> - - <grammars> - <include href="../common/xsd/api.xsd"/> - <include href="../common/xsd/api-common.xsd"/> - <include href="../common/xsd/OS-KSADM.xsd" /> - </grammars> - <!--*******************************************************--> - <!-- All Resources --> - <!--*******************************************************--> - - <!-- We should use SSL in production --> - <resources base="http://localhost:35357"> - <resource id="version" path="v2.0"> - <param name="X-Auth-Token" style="header" type="xsd:string" required="true"> - <doc>You need a valid admin token for access.</doc> - </param> - <resource id="users" path="users"> - <method href="#listUsers"/> - <method href="#addUser"/> - <resource id="userById" path="{userId}"> - <param name="userId" style="template" required="true" type="xsd:string"/> - <method href="#updateUser"/> - <method href="#deleteUser"/> - - <resource id="userRoles" path="roles"> - <resource id="user-roles-OS-KSADM" path="OS-KSADM"> - <resource id="userRoleById" path="{roleId}"> - <param name="roleId" style="template" type="xsd:string"/> - <method href="#addUserRole"/> - <method href="#deleteUserRole"/> - </resource> - </resource> - </resource> - - <resource id="user-OS-KSADM" path="OS-KSADM"> - <resource id="enabled" path="enabled"> - <method href="#setUserEnabled"/> - </resource> - - <resource id="userCredentials" path="credentials"> - <method href="#addUserCredential"/> - <method href="#listCredentials"/> - <resource id="userCredentialsByType" path="{credential-type}"> - <param name="credentialType" style="template" type="OS-KSADM:extensibleCredentialsType" required="true"/> - <method href="#updateUserCredential"/> - <method href="#deleteUserCredential"/> - <method href="#getUserCredential"/> - </resource> - </resource> - </resource> - </resource> - </resource> - - <resource id="tenants" path="tenants"> - <method href="#addTenant"/> - <resource id="tenantById" path="{tenantId}"> - <param name="tenantId" style="template" type="xsd:string"/> - <method href="#updateTenant"/> - <method href="#deleteTenant"/> - <resource id="usersForTenant" path="users"> - <method href="#listUsersForTenant"/> - <resource id="userForTenant" path="{userId}"> - <param name="userId" style="template" type="xsd:string"/> - <resource id="userRolesForTenant" path="roles"> - <resource id="tenant-user-role-OS-KSADM" path="OS-KSADM"> - <resource id="userSpecificRoleForTenant" path="{roleId}"> - <param name="roleId" style="template" type="xsd:string"/> - <method href="#addRolesToUserOnTenant"/> - <method href="#deleteRoleFromUserOnTenant"/> - </resource> - </resource> - </resource> - </resource> - </resource> - </resource> - </resource> - - <resource id="role-service-OS-KSADM" path="OS-KSADM"> - <resource id="roles" path="roles"> - <method href="#getRoleByName"/> - <method href="#listRoles"/> - <method href="#addRole"/> - <resource id="roleId" path="{roleId}"> - <param name="roleId" style="template" type="xsd:string"/> - <method href="#getRole"/> - <method href="#deleteRole"/> - </resource> - </resource> - <resource id="services" path="services"> - <method href="#listServices"/> - <method href="#addService"/> - <method href="#getServiceByName"/> - <resource id="serviceId" path="{serviceId}"> - <param name="serviceId" style="template" type="xsd:string"/> - <method href="#getService"/> - <method href="#deleteService"/> - </resource> - </resource> - </resource> - </resource> - </resources> - - <!--*******************************************************--> - <!-- All Methods --> - <!--*******************************************************--> - - <!-- Tenant Operations --> - <method name="POST" id="addTenant"> - <doc xml:lang="EN" title="Add Tenant"> - <p xmlns="http://www.w3.org/1999/xhtml" class="shortdesc"> - Creates a tenant. - </p> - <p xmlns="http://www.w3.org/1999/xhtml">This call creates a tenant.</p> - </doc> - <request> - <representation mediaType="application/xml" element="identity:tenant"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/tenantwithoutid.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/tenantwithoutid.json"/> - </doc> - </representation> - </request> - <response status="201"> - <representation mediaType="application/xml" element="identity:tenant"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/tenant.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/tenant.json"/> - </doc> - </representation> - </response> - &commonFaults; - &postPutFaults; - </method> - - <method name="POST" id="updateTenant"> - <doc xml:lang="EN" title="Update Tenant"> - <p xmlns="http://www.w3.org/1999/xhtml" class="shortdesc"> - Updates a tenant. - </p> - <p xmlns="http://www.w3.org/1999/xhtml">This call updates a tenant.</p> - </doc> - <request> - <representation mediaType="application/xml" element="identity:tenant"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/tenant.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/tenant.json"/> - </doc> - </representation> - </request> - <response status="200"> - <representation mediaType="application/xml" element="identity:tenant"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/tenant.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/tenant.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - &postPutFaults; - </method> - - <method name="DELETE" id="deleteTenant"> - <doc xml:lang="EN" title="Delete a Tenant"> - <p xmlns="http://www.w3.org/1999/xhtml" class="shortdesc"> - Deletes a tenant. - </p> - <p xmlns="http://www.w3.org/1999/xhtml">This call deletes a tenant.</p> - </doc> - <response status="204"/> - &commonFaults; - &getFaults; - </method> - - <method name="GET" id="listUsersForTenant"> - <doc xml:lang="EN" title="List users for a Tenant."> - <p xmlns="http://www.w3.org/1999/xhtml" class="shortdesc">Lists all the users for a tenant.</p> - <p xmlns="http://www.w3.org/1999/xhtml">Lists all the users for a tenant.</p> - - </doc> - <request> - <param name="marker" style="query" required="false" type="xsd:string"/> - <param name="limit" style="query" required="false" type="xsd:int"/> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:users"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/users.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/users.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - - <method name="PUT" id="addRolesToUserOnTenant"> - <doc xml:lang="EN" title="Add roles to a user on a tenant."> - <p xmlns="http://www.w3.org/1999/xhtml">Adds a specific role to a user for a tenant.</p> - </doc> - <response status="201"/> - &commonFaults; - &postPutFaults; - &getFaults; - </method> - - <method name="DELETE" id="deleteRoleFromUserOnTenant"> - <doc xml:lang="EN" title="Delete roles from a user on tenant."> - <p xmlns="http://www.w3.org/1999/xhtml">Deletes a specific role from a user for a tenant.</p> - </doc> - <response status="204"/> - &commonFaults; - &getFaults; - </method> - - <!--User Operations--> - <method name="GET" id="listUsers"> - <doc xml:lang="EN" title="List users"> - <p xmlns="http://www.w3.org/1999/xhtml">List users.</p> - - </doc> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:userss"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/users.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/users.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - - - <method name="POST" id="addUser"> - <doc xml:lang="EN" title="Add user"> - <p xmlns="http://www.w3.org/1999/xhtml">Adds a user.</p> - </doc> - <request> - <representation mediaType="application/xml" element="identity:user"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/userwithoutid.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/userwithoutid.json"/> - </doc> - </representation> - </request> - <response status="201"> - <representation mediaType="application/xml" element="identity:user"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/user.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/user.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - &postPutFaults; - </method> - - <method name="POST" id="updateUser"> - <doc xml:lang="EN" title="Update user"> - <p xmlns="http://www.w3.org/1999/xhtml">Update a user.</p> - </doc> - <request> - <representation mediaType="application/xml" element="identity:user"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/user.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/user.json"/> - </doc> - </representation> - </request> - <response status="200"> - <representation mediaType="application/xml" element="identity:user"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/user.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/user.json"/> - </doc> - </representation> - </response> - &commonFaults; - &postPutFaults; - &getFaults; - </method> - - <method name="DELETE" id="deleteUser"> - <doc xml:lang="EN" title="Delete user"> - <p xmlns="http://www.w3.org/1999/xhtml">Delete a user.</p> - </doc> - <response status="204"/> - &commonFaults; - &getFaults; - </method> - - <method name="PUT" id="setUserEnabled"> - <doc xml:lang="EN" title="Set user enabled."> - <p xmlns="http://www.w3.org/1999/xhtml">Enable user.</p> - - - </doc> - <request> - <representation mediaType="application/xml" element="identity:user"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/userwithenabledonly.xml"/> - </doc> - <param name="user" style="plain" path="/" type="OS-KSADM:UserWithOnlyEnabled"/></representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/userwithenabledonly.json"/> - </doc> - </representation> - </request> - <response status="200"> - <representation mediaType="application/xml" element="identity:user"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/user.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/user.json"/> - </doc> - </representation> - </response> - &commonFaults; - &postPutFaults; - &getFaults; - </method> - - <!--User Roles--> - <method name="PUT" id="addUserRole"> - <doc xml:lang="EN" title="Add Global roles to a user."> - <p xmlns="http://www.w3.org/1999/xhtml">Adds a specific global role to a user.</p> - </doc> - <response status="201"/> - &commonFaults; - &postPutFaults; - &getFaults; - </method> - - <method name="DELETE" id="deleteUserRole"> - <doc xml:lang="EN" title="Delete Global Roles on User."> - <p xmlns="http://www.w3.org/1999/xhtml">Deletes a specific global role from a user.</p> - </doc> - <response status="204"/> - &commonFaults; - &getFaults; - </method> - - - <!-- User Credentials--> - <method name="POST" id="addUserCredential"> - <doc xml:lang="EN" title="Add user Credential."> - <p xmlns="http://www.w3.org/1999/xhtml">Adds a credential to a user.</p> - </doc> - <request> - <representation mediaType="application/xml" element="identity:credential"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/passwordcredentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/passwordcredentials.json"/> - </doc> - </representation> - </request> - <response status="201"> - <representation mediaType="application/xml" element="identity:credential"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/passwordcredentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/passwordcredentials.json"/> - </doc> - </representation> - </response> - &commonFaults; - &postPutFaults; - &getFaults; - </method> - - <method name="GET" id="listCredentials"> - <doc xml:lang="EN" title="List Credentials"> - <p xmlns="http://www.w3.org/1999/xhtml">List credentials.</p> - - </doc> - <request> - <param name="marker" style="query" required="false" type="xsd:string"/> - <param name="limit" style="query" required="false" type="xsd:int"/> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:credentials"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/credentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/credentials.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - - <method name="GET" id="listCredentialsByType"> - <doc xml:lang="EN" title="List Credentials by type"> - <p xmlns="http://www.w3.org/1999/xhtml">List credentials by type.</p> - - </doc> - <request> - <param name="marker" style="query" required="false" type="xsd:string"/> - <param name="limit" style="query" required="false" type="xsd:int"/> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:credentials"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/credentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/credentials.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - - <method name="POST" id="updateUserCredential"> - <doc xml:lang="EN" title="Update user credential"> - <p xmlns="http://www.w3.org/1999/xhtml">Update credentials.</p> - - </doc> - <request> - <representation mediaType="application/xml" element="identity:credential"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/passwordcredentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/passwordcredentials.json"/> - </doc> - </representation> - </request> - <response status="200"> - <representation mediaType="application/xml" element="identity:credential"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/passwordcredentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/passwordcredentials.json"/> - </doc> - </representation> - </response> - &commonFaults; - &postPutFaults; - &getFaults; - </method> - - <method name="DELETE" id="deleteUserCredential"> - <doc xml:lang="EN" title="Delete user credential"> - <p xmlns="http://www.w3.org/1999/xhtml">Delete User credentials.</p> - </doc> - <response status="204"/> - &commonFaults; - &postPutFaults; - &getFaults; - </method> - - <method name="GET" id="getUserCredential"> - <doc xml:lang="EN" title="Get user Credentials"> - <p xmlns="http://www.w3.org/1999/xhtml">Get user credentials.</p> - - </doc> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:credential"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/passwordcredentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/passwordcredentials.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - - <!--Roles--> - <method name="GET" id="listRoles"> - <doc xml:lang="EN" title="List Roles"> - <p xmlns="http://www.w3.org/1999/xhtml">List roles.</p> - - </doc> - <request> - <param name="serviceId" style="query" required="false" type="xsd:string"/> - <param name="marker" style="query" required="false" type="xsd:string"/> - <param name="limit" style="query" required="false" type="xsd:int"/> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:roles"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/roles.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/roles.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - - <method name="POST" id="addRole"> - <doc xml:lang="EN" title="Add Role"> - <p xmlns="http://www.w3.org/1999/xhtml">Add a Role.</p> - </doc> - <request> - <representation mediaType="application/xml" element="identity:role"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/role.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/role.json"/> - </doc> - </representation> - </request> - <response status="201"> - <representation mediaType="application/xml" element="identity:role"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/role.xml"/> - </doc> - <param name="Location" type="xsd:anyURI" style="header"/> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/role.json"/> - </doc> - <param name="Location" type="xsd:anyURI" style="header"/> - </representation> - </response> - &commonFaults; - &postPutFaults; - &getFaults; - </method> - - <method name="GET" id="getRoleByName"> - <doc xml:lang="EN" title="Get Role By Name"> - <p xmlns="http://www.w3.org/1999/xhtml">Get a role by Name.</p> - </doc> - <request> - <param name="name" style="query" type="xsd:string" required="true"/> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:role"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/role.xml"/> - </doc> - <param name="Location" type="xsd:anyURI" style="header"/> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/role.json"/> - </doc> - <param name="Location" type="xsd:anyURI" style="header"/> - </representation> - </response> - &commonFaults; - &postPutFaults; - &getFaults; - </method> - - - <method name="GET" id="getRole"> - <doc xml:lang="EN" title="Get Role"> - <p xmlns="http://www.w3.org/1999/xhtml">Get a role.</p> - </doc> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:role"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/role.xml"/> - </doc> - <param name="Location" type="xsd:anyURI" style="header"/> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/role.json"/> - </doc> - <param name="Location" type="xsd:anyURI" style="header"/> - </representation> - </response> - &commonFaults; - &postPutFaults; - &getFaults; - </method> - - <method name="DELETE" id="deleteRole"> - <doc xml:lang="EN" title="Delete Role"> - <p xmlns="http://www.w3.org/1999/xhtml">Delete a role.</p> - </doc> - <response status="204"/> - &commonFaults; - &getFaults; - </method> - - <!-- Service Operations --> - <method name="GET" id="listServices"> - <doc xml:lang="EN" title="List Services"> - <p xmlns="http://www.w3.org/1999/xhtml">List services.</p> - </doc> - <request> - <param name="marker" style="query" required="false" type="xsd:string"/> - <param name="limit" style="query" required="false" type="xsd:int"/> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="OS-KSADM:services"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/services.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/services.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - <method name="GET" id="getServiceByName"> - <doc xml:lang="EN" title="Get Service"> - <p xmlns="http://www.w3.org/1999/xhtml">Get a service by name.</p> - </doc> - <request> - <param name="name" style="query" type="xsd:string" required="true"/> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="OS-KSADM:service"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/service.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/service.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - <method name="GET" id="getService"> - <doc xml:lang="EN" title="Get Service"> - <p xmlns="http://www.w3.org/1999/xhtml">Get a service.</p> - </doc> - <response status="200 203"> - <representation mediaType="application/xml" element="OS-KSADM:service"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/service.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/service.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - <method name="POST" id="addService"> - <doc xml:lang="EN" title="Add Service"> - <p xmlns="http://www.w3.org/1999/xhtml">Add a service.</p> - </doc> - <request> - <representation mediaType="application/xml" element="OS-KSADM:service"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/service.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/service.json"/> - </doc> - </representation> - </request> - <response status="201"> - <representation mediaType="application/xml" element="OS-KSADM:service"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/service.xml"/> - </doc> - <param name="Location" type="xsd:anyURI" style="header"/> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/service.json"/> - </doc> - <param name="Location" type="xsd:anyURI" style="header"/> - </representation> - </response> - &commonFaults; - &postPutFaults; - &getFaults; - </method> - <method name="DELETE" id="deleteService"> - <doc xml:lang="EN" title="Delete Service"> - <p xmlns="http://www.w3.org/1999/xhtml">Delete a service.</p> - </doc> - <response status="204"/> - &commonFaults; - &getFaults; - </method> -</application> diff --git a/keystone/content/admin/OS-KSCATALOG-admin-devguide.pdf b/keystone/content/admin/OS-KSCATALOG-admin-devguide.pdf Binary files differdeleted file mode 100644 index fef083bb..00000000 --- a/keystone/content/admin/OS-KSCATALOG-admin-devguide.pdf +++ /dev/null diff --git a/keystone/content/admin/OS-KSCATALOG-admin.wadl b/keystone/content/admin/OS-KSCATALOG-admin.wadl deleted file mode 100644 index 2ed3558c..00000000 --- a/keystone/content/admin/OS-KSCATALOG-admin.wadl +++ /dev/null @@ -1,295 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- (C) 2011 OpenStack LLC., All Rights Reserved --> -<!--*******************************************************--> -<!-- Import Common XML Entities --> -<!-- --> -<!-- You can resolve the entites with xmllint --> -<!-- --> -<!-- xmllint -noent OS-KSCATALOG-admin.wadl --> -<!--*******************************************************--> -<!DOCTYPE application [ -<!ENTITY % common SYSTEM "../common/common.ent"> -%common; -]> - -<application xmlns="http://wadl.dev.java.net/2009/02" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:OS-KSCATALOG="http://docs.openstack.org/identity/api/ext/OS-KSCATALOG/v1.0" - xmlns:capi="http://docs.openstack.org/common/api/v1.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xsi:schemaLocation="http://docs.openstack.org/identity/api/v2.0 ../common/xsd/api.xsd - http://docs.openstack.org/common/api/v1.0 ../common/xsd/api-common.xsd - http://wadl.dev.java.net/2009/02 http://www.w3.org/Submission/wadl/wadl.xsd - http://docs.openstack.org/identity/api/ext/OS-KSCATALOG/v1.0 ../common/xsd/OS-KSCATALOG.xsd - "> - - <grammars> - <include href="../common/xsd/api.xsd"/> - <include href="../common/xsd/api-common.xsd"/> - <include href="../common/xsd/OS-KSCATALOG.xsd"/> - </grammars> - <!--*******************************************************--> - <!-- All Resources --> - <!--*******************************************************--> - - <!-- We should use SSL in production --> - <resources base="http://localhost:35357"> - <resource id="version" path="v2.0"> - - <resource id="tenants" path="tenants"> - <param name="X-Auth-Token" style="header" type="xsd:string" required="true"/> - <resource id="tenantId" path="{tenantId}"> - <param name="tenantId" style="template" type="xsd:string"/> - <resource id="endpoints-OS-KSCATALOG" path="OS-KSCATALOG"> - <resource id="endpoints" path="endpoints"> - <method href="#listEndpoints"/> - <method href="#addEndpoint"/> - <resource id="endpoint" path="{endpointId}"> - <param name="endpointId" style="template" type="xsd:string"/> - <method href="#getEndpoint"/> - <method href="#deleteEndpoint"/> - </resource> - </resource> - </resource> - </resource> - </resource> - - <resource id="role-service-OS-KSCATALOG" path="OS-KSCATALOG"> - <resource id="endpointTemplates" path="endpointTemplates"> - <param name="X-Auth-Token" style="header" type="xsd:string" required="true"/> - <method href="#listEndpointTemplates"/> - <method href="#addEndpointTemplate"/> - <resource id="endpointTemplateId" path="{endpointTemplateId}"> - <param name="X-Auth-Token" style="header" type="xsd:string" required="true"/> - <param name="endpointTemplateId" style="template" type="xsd:string"/> - <method href="#getEndpointTemplate"/> - <method href="#updateEndpointTemplate"/> - <method href="#deleteEndpointTemplate"/> - </resource> - </resource> - </resource> - </resource> - </resources> - - <!--*******************************************************--> - <!-- All Methods --> - <!--*******************************************************--> - - <!-- EndPoint Templates --> - - <method name="GET" id="listEndpointTemplates"> - <doc xml:lang="EN" title="List Endpoint Templates"> - <p xmlns="http://www.w3.org/1999/xhtml">List Endpoint Templates.</p> - - </doc> - <request> - <param name="serviceId" style="query" required="false" type="xsd:string"/> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="OS-KSCATALOG:endpointTemplates"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/endpointTemplates.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/endpointTemplates.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - - <method name="GET" id="getEndpointTemplate"> - <doc xml:lang="EN" title="Get Endpoint Template"> - <p xmlns="http://www.w3.org/1999/xhtml">Get Endpoint Template.</p> - - </doc> - <response status="200 203"> - <representation mediaType="application/xml" element="OS-KSCATALOG:endpointTemplate"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/endpointTemplate.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/endpointTemplate.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - - <method name="POST" id="addEndpointTemplate"> - <doc xml:lang="EN" title="Add Endpoint Template"> - <p xmlns="http://www.w3.org/1999/xhtml">Add Endpoint Template.</p> - - - </doc> - <request> - <representation mediaType="application/xml" element="OS-KSCATALOG:endpointTemplate"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/endpointTemplate.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/endpointTemplate.json"/> - </doc> - </representation> - </request> - <response status="201"> - <representation mediaType="application/xml" element="OS-KSCATALOG:endpointTemplate"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/endpointTemplate.xml"/> - </doc> - <param name="Location" type="xsd:anyURI" style="header"/> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/endpointTemplate.json"/> - </doc> - <param name="Location" type="xsd:anyURI" style="header"/> - </representation> - </response> - &commonFaults; - &getFaults; - &postPutFaults; - </method> - - <method name="PUT" id="updateEndpointTemplate"> - <doc xml:lang="EN" title="Update Endpoint Template"> - <p xmlns="http://www.w3.org/1999/xhtml">Update Endpoint Template.</p> - </doc> - <request> - <representation mediaType="application/xml" element="OS-KSCATALOG:endpointTemplate"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/endpointTemplate.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/endpointTemplate.json"/> - </doc> - </representation> - </request> - <response status="200"> - <representation mediaType="application/xml" element="OS-KSCATALOG:endpointTemplate"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/endpointTemplate.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/endpointTemplate.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - &postPutFaults; - </method> - - <method name="DELETE" id="deleteEndpointTemplate"> - <doc xml:lang="EN" title="Delete Endpoint Template."> - <p xmlns="http://www.w3.org/1999/xhtml">Delete a Endpoint Template.</p> - </doc> - <response status="204"/> - &commonFaults; - &getFaults; - </method> - - <method name="POST" id="addEndpoint"> - <doc xml:lang="EN" title="Add Endpoint"> - <p xmlns="http://www.w3.org/1999/xhtml">Add Endpoint to a tenant.</p> - - - </doc> - <request> - <representation mediaType="application/xml" element="OS-KSCATALOG:endpointTemplate"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/endpointTemplateWithOnlyId.xml"/> - </doc> - <param name="endpoint" style="plain" path="/" - type="OS-KSCATALOG:EndpointTemplateWithOnlyId"/> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/endpointTemplateWithOnlyId.json"/> - </doc> - </representation> - </request> - <response status="201"> - <representation mediaType="application/xml" element="identity:endpoint"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/endpoint.xml"/> - </doc> - <param name="Location" type="xsd:anyURI" style="header"/> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/endpoint.json"/> - </doc> - <param name="Location" type="xsd:anyURI" style="header"/> - </representation> - </response> - &commonFaults; - &getFaults; - &postPutFaults; - </method> - - <method name="GET" id="listEndpoints"> - <doc xml:lang="EN" title="List Endpoints"> - <p xmlns="http://www.w3.org/1999/xhtml">List Endpoints of a Tenant.</p> - - </doc> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:endpoints"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/endpoints.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/endpoints.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - - <method name="GET" id="getEndpoint"> - <doc xml:lang="EN" title="Get Endpoint"> - <p xmlns="http://www.w3.org/1999/xhtml">Get Endpoint of a Tenant.</p> - - </doc> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:endpoint"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/endpoint.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/endpoint.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - - <method name="DELETE" id="deleteEndpoint"> - <doc xml:lang="EN" title="Delete Endpoint."> - <p xmlns="http://www.w3.org/1999/xhtml">Delete a Endpoint from a Tenant.</p> - </doc> - <response status="204"/> - &commonFaults; - &getFaults; - </method> -</application> diff --git a/keystone/content/admin/OS-KSEC2-admin-devguide.pdf b/keystone/content/admin/OS-KSEC2-admin-devguide.pdf Binary files differdeleted file mode 100644 index 084a26c4..00000000 --- a/keystone/content/admin/OS-KSEC2-admin-devguide.pdf +++ /dev/null diff --git a/keystone/content/admin/OS-KSEC2-admin.wadl b/keystone/content/admin/OS-KSEC2-admin.wadl deleted file mode 100644 index 8c2f62bd..00000000 --- a/keystone/content/admin/OS-KSEC2-admin.wadl +++ /dev/null @@ -1,212 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- (C) 2011 OpenStack LLC., All Rights Reserved --> -<!--*******************************************************--> -<!-- Import Common XML Entities --> -<!-- --> -<!-- You can resolve the entites with xmllint --> -<!-- --> -<!-- xmllint -noent OS-KSEC2-admin.wadl --> -<!--*******************************************************--> -<!DOCTYPE application [ -<!ENTITY % common SYSTEM "../common/common.ent"> -%common; -]> - -<application xmlns="http://wadl.dev.java.net/2009/02" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:OS-KSEC2="http://docs.openstack.org/identity/api/ext/OS-KSEC2/v1.0" - xmlns:capi="http://docs.openstack.org/common/api/v1.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xsi:schemaLocation="http://docs.openstack.org/identity/api/v2.0 ../common/xsd/api.xsd - http://docs.openstack.org/common/api/v1.0 ../common/xsd/api-common.xsd - http://wadl.dev.java.net/2009/02 http://www.w3.org/Submission/wadl/wadl.xsd - http://docs.openstack.org/identity/api/ext/OS-KSEC2/v1.0 ../common/xsd/OS-KSEC2-credentials.xsd - "> - - <grammars> - <include href="../common/xsd/api.xsd"/> - <include href="../common/xsd/api-common.xsd"/> - <include href="../common/xsd/OS-KSEC2-credentials.xsd" /> - </grammars> - <!--*******************************************************--> - <!-- All Resources --> - <!--*******************************************************--> - - <!-- We should use SSL in production --> - <resources base="http://localhost:35357"> - <resource id="version" path="v2.0"> - <param name="X-Auth-Token" style="header" type="xsd:string" required="true"> - <doc>You need a valid admin token for access.</doc> - </param> - <resource id="users" path="users"> - <resource id="userById" path="{userId}"> - <param name="userId" style="template" required="true" type="xsd:string"/> - <resource id="user-OS-KSADM" path="OS-KSADM"> - <resource id="userCredentials" path="credentials"> - <method href="#addUserCredential"/> - <method href="#listCredentials"/> - <resource id="userCredentialsByType" path="OS-KSEC2:ec2Credentials"> - <method href="#updateUserCredential"/> - <method href="#deleteUserCredential"/> - <method href="#getUserCredential"/> - </resource> - </resource> - </resource> - </resource> - </resource> - </resource> - </resources> - - <!--*******************************************************--> - <!-- All Methods --> - <!--*******************************************************--> - - - - <!-- User Credentials--> - <method name="POST" id="addUserCredential"> - <doc xml:lang="EN" title="Add user Credential."> - <p xmlns="http://www.w3.org/1999/xhtml">Adds a credential to a user.</p> - </doc> - <request> - <representation mediaType="application/xml" element="OS-KSEC2:ec2Credentials"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/ec2Credentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/ec2Credentials.json"/> - </doc> - </representation> - </request> - <response status="201"> - <representation mediaType="application/xml" element="OS-KSEC2:ec2Credentials"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/ec2Credentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/ec2Credentials.json"/> - </doc> - </representation> - </response> - &commonFaults; - &postPutFaults; - &getFaults; - </method> - - <method name="GET" id="listCredentials"> - <doc xml:lang="EN" title="List Credentials"> - <p xmlns="http://www.w3.org/1999/xhtml">List credentials.</p> - </doc> - <request> - <param name="marker" style="query" required="false" type="xsd:string"/> - <param name="limit" style="query" required="false" type="xsd:int"/> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:credentials"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/credentialswithec2.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/credentialswithec2.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - - <method name="GET" id="listCredentialsByType"> - <doc xml:lang="EN" title="List Credentials by type"> - <p xmlns="http://www.w3.org/1999/xhtml">List credentials by type.</p> - </doc> - <request> - <param name="marker" style="query" required="false" type="xsd:string"/> - <param name="limit" style="query" required="false" type="xsd:int"/> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:credentials"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/credentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/credentials.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - - <method name="POST" id="updateUserCredential"> - <doc xml:lang="EN" title="Update user credential"> - <p xmlns="http://www.w3.org/1999/xhtml">Update credentials.</p> - </doc> - <request> - <representation mediaType="application/xml" element="OS-KSEC2:ec2Credentials"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/ec2Credentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/ec2Credentials.json"/> - </doc> - </representation> - </request> - <response status="200"> - <representation mediaType="application/xml" element="OS-KSEC2:ec2Credentials"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/ec2Credentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/ec2Credentials.json"/> - </doc> - </representation> - </response> - &commonFaults; - &postPutFaults; - &getFaults; - </method> - - <method name="DELETE" id="deleteUserCredential"> - <doc xml:lang="EN" title="Delete user credential"> - <p xmlns="http://www.w3.org/1999/xhtml">Delete User credentials.</p> - </doc> - <response status="204"/> - &commonFaults; - &postPutFaults; - &getFaults; - </method> - - <method name="GET" id="getUserCredential"> - <doc xml:lang="EN" title="Get user Credentials"> - <p xmlns="http://www.w3.org/1999/xhtml">Get user credentials.</p> - </doc> - <response status="200 203"> - <representation mediaType="application/xml" element="OS-KSEC2:ec2Credentials"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/ec2Credentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/ec2Credentials.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> -</application> diff --git a/keystone/content/admin/OS-KSS3-admin.wadl b/keystone/content/admin/OS-KSS3-admin.wadl deleted file mode 100644 index fcffb6eb..00000000 --- a/keystone/content/admin/OS-KSS3-admin.wadl +++ /dev/null @@ -1,212 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- (C) 2011 OpenStack LLC., All Rights Reserved --> -<!--*******************************************************--> -<!-- Import Common XML Entities --> -<!-- --> -<!-- You can resolve the entites with xmllint --> -<!-- --> -<!-- xmllint -noent OS-KSEC2-admin.wadl --> -<!--*******************************************************--> -<!DOCTYPE application [ -<!ENTITY % common SYSTEM "../common/common.ent"> -%common; -]> - -<application xmlns="http://wadl.dev.java.net/2009/02" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:OS-KSEC2="http://docs.openstack.org/identity/api/ext/OS-KSEC2/v1.0" - xmlns:capi="http://docs.openstack.org/common/api/v1.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xsi:schemaLocation="http://docs.openstack.org/identity/api/v2.0 ../common/xsd/api.xsd - http://docs.openstack.org/common/api/v1.0 ../common/xsd/api-common.xsd - http://wadl.dev.java.net/2009/02 http://www.w3.org/Submission/wadl/wadl.xsd - http://docs.openstack.org/identity/api/ext/OS-KSEC2/v1.0 ../common/xsd/OS-KSEC2-credentials.xsd - "> - - <grammars> - <include href="../common/xsd/api.xsd"/> - <include href="../common/xsd/api-common.xsd"/> - <include href="../common/xsd/OS-KSEC2-credentials.xsd" /> - </grammars> - <!--*******************************************************--> - <!-- All Resoruces --> - <!--*******************************************************--> - - <!-- We should use SSL in production --> - <resources base="http://localhost:35357"> - <resource id="version" path="v2.0"> - <param name="X-Auth-Token" style="header" type="xsd:string" required="true"> - <doc>You need a valid admin token for access.</doc> - </param> - <resource id="users" path="users"> - <resource id="userById" path="{userId}"> - <param name="userId" style="template" required="true" type="xsd:string"/> - <resource id="user-OS-KSADM" path="OS-KSADM"> - <resource id="userCredentials" path="credentials"> - <method href="#addUserCredential"/> - <method href="#listCredentials"/> - <resource id="userCredentialsByType" path="OS-KSEC2:s3Credentials"> - <method href="#updateUserCredential"/> - <method href="#deleteUserCredential"/> - <method href="#getUserCredential"/> - </resource> - </resource> - </resource> - </resource> - </resource> - </resource> - </resources> - - <!--*******************************************************--> - <!-- All Methods --> - <!--*******************************************************--> - - - - <!-- User Credentials--> - <method name="POST" id="addUserCredential"> - <doc xml:lang="EN" title="Add user Credential."> - <p xmlns="http://www.w3.org/1999/xhtml">Adds a credential to a user.</p> - </doc> - <request> - <representation mediaType="application/xml" element="OS-KSS3:s3credentials"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/s3Credentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/s3Credentials.json"/> - </doc> - </representation> - </request> - <response status="201"> - <representation mediaType="application/xml" element="OS-KSS3:s3credentials"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/s3Credentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/s3Credentials.json"/> - </doc> - </representation> - </response> - &commonFaults; - &postPutFaults; - &getFaults; - </method> - - <method name="GET" id="listCredentials"> - <doc xml:lang="EN" title="List Credentials"> - <p xmlns="http://www.w3.org/1999/xhtml">List credentials.</p> - </doc> - <request> - <param name="marker" style="query" required="false" type="xsd:string"/> - <param name="limit" style="query" required="false" type="xsd:int"/> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:credentials"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/credentialswiths3.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/credentialswiths3.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - - <method name="GET" id="listCredentialsByType"> - <doc xml:lang="EN" title="List Credentials by type"> - <p xmlns="http://www.w3.org/1999/xhtml">List credentials by type.</p> - </doc> - <request> - <param name="marker" style="query" required="false" type="xsd:string"/> - <param name="limit" style="query" required="false" type="xsd:int"/> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:credentials"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/credentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/credentials.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - - <method name="POST" id="updateUserCredential"> - <doc xml:lang="EN" title="Update user credential"> - <p xmlns="http://www.w3.org/1999/xhtml">Update credentials.</p> - </doc> - <request> - <representation mediaType="application/xml" element="OS-KSS3:s3credentials"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/s3Credentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/s3Credentials.json"/> - </doc> - </representation> - </request> - <response status="200"> - <representation mediaType="application/xml" element="OS-KSS3:s3credentials"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/s3Credentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/s3Credentials.json"/> - </doc> - </representation> - </response> - &commonFaults; - &postPutFaults; - &getFaults; - </method> - - <method name="DELETE" id="deleteUserCredential"> - <doc xml:lang="EN" title="Delete user credential"> - <p xmlns="http://www.w3.org/1999/xhtml">Delete User credentials.</p> - </doc> - <response status="204"/> - &commonFaults; - &postPutFaults; - &getFaults; - </method> - - <method name="GET" id="getUserCredential"> - <doc xml:lang="EN" title="Get user Credentials"> - <p xmlns="http://www.w3.org/1999/xhtml">Get user credentials.</p> - </doc> - <response status="200 203"> - <representation mediaType="application/xml" element="OS-KSS3:s3credentials"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/s3Credentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/s3Credentials.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> -</application> diff --git a/keystone/content/admin/OS-KSVALIDATE-admin.wadl b/keystone/content/admin/OS-KSVALIDATE-admin.wadl deleted file mode 100644 index c477131d..00000000 --- a/keystone/content/admin/OS-KSVALIDATE-admin.wadl +++ /dev/null @@ -1,192 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!--*******************************************************--> -<!-- Import Common XML Entities --> -<!-- --> -<!-- You can resolve the entites with xmllint --> -<!-- --> -<!-- xmllint -noent OS-KSVALIDATE-admin.wadl --> -<!--*******************************************************--> -<!DOCTYPE application [ -<!ENTITY % common SYSTEM "https://raw.github.com/openstack/keystone/master/keystone/content/common/common.ent"> - %common; -]> - -<application xmlns="http://wadl.dev.java.net/2009/02" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:OS-KSVALIDATE="http://docs.openstack.org/identity/api/ext/OS-KSVALIDATE/v1.0" - xmlns:capi="http://docs.openstack.org/common/api/v1.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xsi:schemaLocation="http://docs.openstack.org/identity/api/v2.0 ../common/xsd/api.xsd - http://docs.openstack.org/common/api/v1.0 ../common/xsd/api-common.xsd - http://wadl.dev.java.net/2009/02 http://www.w3.org/Submission/wadl/wadl.xsd - "> - - <grammars> - <include href="https://raw.github.com/openstack/keystone/master/keystone/content/common/xsd/api.xsd"/> - <include href="https://raw.github.com/openstack/keystone/master/keystone/content/common/xsd/api-common.xsd"/> - </grammars> - - <!--*******************************************************--> - <!-- All Resources --> - <!--*******************************************************--> - - <!-- We should use SSL in production --> - <resources base="http://localhost:35357"> - <resource id="version" path="v2.0"> - <resource id="extension" path="OS-KSVALIDATE"> - <resource id="token" path="token"> - <resource id="validate" path="validate"> - <param name="X-Auth-Token" style="header" type="xsd:string" required="true"> - <doc>You need a valid admin token for access.</doc> - </param> - <param name="X-Subject-Token" style="header" type="xsd:string" required="true"> - <doc>You need to supply a token to validate.</doc> - </param> - <param name="belongsTo" style="query" type="xsd:string" required="false"/> - <param name="HP-IDM-serviceId" style="query" type="xsd:string" required="false"/> - <method href="#validateToken"/> - <method href="#checkToken"/> - </resource> - - <resource id="endpointsForToken" path="endpoints"> - <param name="X-Auth-Token" style="header" type="xsd:string" required="true"> - <doc>You need a valid admin token for access.</doc> - </param> - <param name="X-Subject-Token" style="header" type="xsd:string" required="true"> - <doc>You need to supply a token to validate.</doc> - </param> - <param name="HP-IDM-serviceId" style="query" type="xsd:string" required="false"/> - <method href="#listEndpointsForToken"/> - </resource> - </resource> - </resource> - </resource> - </resources> - - <!--*******************************************************--> - <!-- All Methods --> - <!--*******************************************************--> - - - <!-- Token Operations --> - <method name="GET" id="validateToken"> - <doc xml:lang="EN" title="Validate Token"> - <p xmlns="http://www.w3.org/1999/xhtml" class="shortdesc"> - Check that a token is valid and that it belongs to a supplied tenant - and services and return the permissions relevant to a particular client. - </p> - <p xmlns="http://www.w3.org/1999/xhtml"> - Behaviour is similar to <code>/tokens/{tokenId}</code>. In - other words, a user should expect an - itemNotFound (<code>404</code>) fault for an - invalid token. - </p> - <p xmlns="http://www.w3.org/1999/xhtml"> - 'X-Subject-Token' is encrypted, but can still be used for - caching. This extension will basically decrypt this header and - internally call Keystone's normal validation, passing along all - headers and query parameters. It should therefore support - all exsting calls on <code>/tokens/{tokenId}</code>, including - extensions such as HP-IDM. - </p> - </doc> - <request> - <param name="belongsTo" style="query" required="false" type="xsd:string"> - <doc xml:lang="EN"> - <p xmlns="http://www.w3.org/1999/xhtml"> - Validates a token has the supplied tenant in scope. - </p> - </doc> - </param> - <param name="OS-KSVALIDATE-serviceId" style="query" required="false" type="xsd:string"> - <doc xml:lang="EN"> - <p xmlns="http://www.w3.org/1999/xhtml"> - If provided, filter the roles to be returned by the given service IDs. - </p> - </doc> - </param> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:access"> - <doc> - <xsdxt:code href="../samples/validatetoken.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc> - <xsdxt:code href="../samples/validatetoken.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - <method name="HEAD" id="checkToken"> - <doc xml:lang="EN" title="Check Token"> - <p xmlns="http://www.w3.org/1999/xhtml" class="shortdesc"> - Check that a token is valid and that it belongs to a particular - tenant and services (For performance). - </p> - <p xmlns="http://www.w3.org/1999/xhtml"> - Behaviour is similar to <code>/tokens/{tokenId}</code>. In - other words, a user should expect an - itemNotFound (<code>404</code>) fault for an - invalid token. - </p> - <p xmlns="http://www.w3.org/1999/xhtml"> - 'X-Subject-Token' is encrypted, but can still be used for - caching. This extension will basically decrypt this header and - internally call Keystone's normal validation, passing along all - headers and query parameters. It should therefore support - all exsting calls on <code>/tokens/{tokenId}</code>, including - extensions such as HP-IDM. - </p> - <p xmlns="http://www.w3.org/1999/xhtml"> - No response body is returned for this method. - </p> - </doc> - <request> - <param name="belongsTo" style="query" required="false" type="xsd:string"> - <doc xml:lang="EN"> - <p xmlns="http://www.w3.org/1999/xhtml"> - Validates a token has the supplied tenant in scope. (for performance). - </p> - </doc> - </param> - <param name="OS-KSVALIDATE-serviceId" style="query" required="false" type="xsd:string"> - <doc xml:lang="EN"> - <p xmlns="http://www.w3.org/1999/xhtml"> - Check the roles against the given service IDs. - </p> - </doc> - </param> - </request> - <response status="200 203"/> - &commonFaults; - &getFaults; - </method> - <method name="GET" id="listEndpointsForToken"> - <doc xml:lang="EN" title="List Endoints for a Token"> - <p xmlns="http://www.w3.org/1999/xhtml"> - Returns a list of endpoints associated with a specific token. - </p> - </doc> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:endpoints"> - <doc> - <xsdxt:code href="../common/samples/endpoints.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc> - <xsdxt:code href="../common/samples/endpoints.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - -</application> diff --git a/keystone/content/admin/RAX-KSGRP-admin.wadl b/keystone/content/admin/RAX-KSGRP-admin.wadl deleted file mode 100644 index 01548029..00000000 --- a/keystone/content/admin/RAX-KSGRP-admin.wadl +++ /dev/null @@ -1,74 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- (C) 2011 OpenStack LLC., All Rights Reserved --> -<!--*******************************************************--> -<!-- Import Common XML Entities --> -<!-- --> -<!-- You can resolve the entites with xmllint --> -<!-- --> -<!-- xmllint -noent RAX-KSGRP-admin.wadl --> -<!--*******************************************************--> -<!DOCTYPE application [ -<!ENTITY % common SYSTEM "../common/common.ent"> -%common; -]> -<application xmlns="http://wadl.dev.java.net/2009/02" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:RAX-KSGRP="http://docs.openstack.org/identity/api/ext/RAX-KSGRP/v1.0" - xmlns:capi="http://docs.openstack.org/common/api/v1.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xsi:schemaLocation="http://docs.openstack.org/identity/api/v2.0 ../common/xsd/api.xsd - http://docs.openstack.org/common/api/v1.0 ../common/xsd/api-common.xsd - http://wadl.dev.java.net/2009/02 http://www.w3.org/Submission/wadl/wadl.xsd - http://docs.openstack.org/identity/api/ext/RAX-KSGRP/v1.0 ../common/xsd/RAX-KSGRP-groups.xsd - "> - - <grammars> - <include href="../common/xsd/api.xsd"/> - <include href="../common/xsd/api-common.xsd"/> - <include href="../common/xsd/RAX-KSGRP-groups.xsd"/> - </grammars> - <!--*******************************************************--> - <!-- All Resources --> - <!--*******************************************************--> - - <!-- We should use SSL in production --> - <resources base="http://localhost:35357"> - <resource id="version" path="v2.0"> - <param name="X-Auth-Token" style="header" type="xsd:string" required="true"> - <doc>You need a valid admin token for access.</doc> - </param> - <resource id="users" path="users"> - <resource id="userById" path="{userId}"> - <param name="userId" style="template" type="xsd:string"/> - <resource id="user-groups-RAX-KSGRP" path="RAX-KSGRP"> - <method href="#listUserGroups"/> - </resource> - </resource> - </resource> - </resource> - </resources> - - <method name="GET" id="listUserGroups"> - <doc xml:lang="EN" title="List Groups for a User"> - <p xmlns="http://www.w3.org/1999/xhtml">List all the groups for a user.</p> - - </doc> - <response status="200 203"> - <representation mediaType="application/xml" element="RAX-KSGRP:groups"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/RAX-KSGRP-groups.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/RAX-KSGRP-groups.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - </application> - diff --git a/keystone/content/admin/RAX-KSKEY-admin-devguide.pdf b/keystone/content/admin/RAX-KSKEY-admin-devguide.pdf Binary files differdeleted file mode 100644 index 224cd999..00000000 --- a/keystone/content/admin/RAX-KSKEY-admin-devguide.pdf +++ /dev/null diff --git a/keystone/content/admin/RAX-KSKEY-admin.wadl b/keystone/content/admin/RAX-KSKEY-admin.wadl deleted file mode 100644 index 3bebb1b3..00000000 --- a/keystone/content/admin/RAX-KSKEY-admin.wadl +++ /dev/null @@ -1,212 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- (C) 2011 OpenStack LLC., All Rights Reserved --> -<!--*******************************************************--> -<!-- Import Common XML Entities --> -<!-- --> -<!-- You can resolve the entites with xmllint --> -<!-- --> -<!-- xmllint -noent RAX-KSKEY-admin.wadl --> -<!--*******************************************************--> -<!DOCTYPE application [ -<!ENTITY % common SYSTEM "../common/common.ent"> -%common; -]> - -<application xmlns="http://wadl.dev.java.net/2009/02" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:RAX-KSKEY="http://docs.openstack.org/identity/api/ext/RAX-KSKEY/v1.0" - xmlns:capi="http://docs.openstack.org/common/api/v1.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xsi:schemaLocation="http://docs.openstack.org/identity/api/v2.0 ../common/xsd/api.xsd - http://docs.openstack.org/common/api/v1.0 ../common/xsd/api-common.xsd - http://wadl.dev.java.net/2009/02 http://www.w3.org/Submission/wadl/wadl.xsd - http://docs.rackspace.com/identity/api/ext/RAX-KSKEY/v1.0 ../common/xsd/RAX-KSKEY-credentials.xsd - "> - - <grammars> - <include href="../common/xsd/api.xsd"/> - <include href="../common/xsd/api-common.xsd"/> - <include href="../common/xsd/RAX-KSKEY-credentials.xsd" /> - </grammars> - <!--*******************************************************--> - <!-- All Resources --> - <!--*******************************************************--> - - <!-- We should use SSL in production --> - <resources base="http://localhost:35357"> - <resource id="version" path="v2.0"> - <param name="X-Auth-Token" style="header" type="xsd:string" required="true"> - <doc>You need a valid admin token for access.</doc> - </param> - <resource id="users" path="users"> - <resource id="userById" path="{userId}"> - <param name="userId" style="template" required="true" type="xsd:string"/> - <resource id="user-OS-KSADM" path="OS-KSADM"> - <resource id="userCredentials" path="credentials"> - <method href="#addUserCredential"/> - <method href="#listCredentials"/> - <resource id="userCredentialsByType" path="RAX-KSKEY:apiKeyCredentials"> - <method href="#updateUserCredential"/> - <method href="#deleteUserCredential"/> - <method href="#getUserCredential"/> - </resource> - </resource> - </resource> - </resource> - </resource> - </resource> - </resources> - - <!--*******************************************************--> - <!-- All Methods --> - <!--*******************************************************--> - - <!-- User Credentials--> - <method name="POST" id="addUserCredential"> - <doc xml:lang="EN" title="Add user Credential."> - <p xmlns="http://www.w3.org/1999/xhtml">Adds a credential to a user.</p> - </doc> - <request> - <representation mediaType="application/xml" element="RAX-KSKEY:apiKeyCredentials"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/apiKeyCredentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/apiKeyCredentials.json"/> - </doc> - </representation> - </request> - <response status="201"> - <representation mediaType="application/xml" element="RAX-KSKEY:apiKeyCredentials"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/apiKeyCredentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/apiKeyCredentials.json"/> - </doc> - </representation> - </response> - &commonFaults; - &postPutFaults; - &getFaults; - </method> - - <method name="GET" id="listCredentials"> - <doc xml:lang="EN" title="List Credentials"> - <p xmlns="http://www.w3.org/1999/xhtml">List credentials.</p> - </doc> - <request> - <param name="marker" style="query" required="false" type="xsd:string"/> - <param name="limit" style="query" required="false" type="xsd:int"/> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:credentials"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/credentialswithapikey.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/credentialswithapikey.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - - <method name="GET" id="listCredentialsByType"> - <doc xml:lang="EN" title="List Credentials by type"> - <p xmlns="http://www.w3.org/1999/xhtml">List credentials by type.</p> - - </doc> - <request> - <param name="marker" style="query" required="false" type="xsd:string"/> - <param name="limit" style="query" required="false" type="xsd:int"/> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:credentials"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/credentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/credentials.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - - <method name="POST" id="updateUserCredential"> - <doc xml:lang="EN" title="Update user credential"> - <p xmlns="http://www.w3.org/1999/xhtml">Update credentials.</p> - </doc> - <request> - <representation mediaType="application/xml" element="RAX-KSKEY:apiKeyCredentials"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/apiKeyCredentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/apiKeyCredentials.json"/> - </doc> - </representation> - </request> - <response status="200"> - <representation mediaType="application/xml" element="RAX-KSKEY:apiKeyCredentials"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/apiKeyCredentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/apiKeyCredentials.json"/> - </doc> - </representation> - </response> - &commonFaults; - &postPutFaults; - &getFaults; - </method> - - <method name="DELETE" id="deleteUserCredential"> - <doc xml:lang="EN" title="Delete user credential"> - <p xmlns="http://www.w3.org/1999/xhtml">Delete User credentials.</p> - </doc> - <response status="204"/> - &commonFaults; - &postPutFaults; - &getFaults; - </method> - - <method name="GET" id="getUserCredential"> - <doc xml:lang="EN" title="Get user Credentials"> - <p xmlns="http://www.w3.org/1999/xhtml">Get user credentials.</p> - </doc> - <response status="200 203"> - <representation mediaType="application/xml" element="RAX-KSKEY:apiKeyCredentials"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/apiKeyCredentials.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc xml:lang="EN"> - <xsdxt:code href="../common/samples/apiKeyCredentials.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> -</application> - diff --git a/keystone/content/admin/RAX-KSQA-admin.wadl b/keystone/content/admin/RAX-KSQA-admin.wadl deleted file mode 100644 index 23d201fd..00000000 --- a/keystone/content/admin/RAX-KSQA-admin.wadl +++ /dev/null @@ -1,106 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?>
-<!-- (C) 2011 OpenStack LLC., All Rights Reserved -->
-<!--*******************************************************-->
-<!-- Import Common XML Entities -->
-<!-- -->
-<!-- You can resolve the entites with xmllint -->
-<!-- -->
-<!-- xmllint -noent RAX-KSQA-admin.wadl -->
-<!--*******************************************************-->
-<!DOCTYPE application [
-<!ENTITY % common SYSTEM "../common/common.ent">
-%common;
-]>
-<application xmlns="http://wadl.dev.java.net/2009/02"
- xmlns:identity="http://docs.openstack.org/identity/api/v2.0"
- xmlns:RAX-KSQA="http://docs.openstack.org/identity/api/ext/RAX-KSQA/v1.0"
- xmlns:capi="http://docs.openstack.org/common/api/v1.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:xsd="http://www.w3.org/2001/XMLSchema"
- xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0"
- xsi:schemaLocation="http://docs.openstack.org/identity/api/v2.0 ../common/xsd/api.xsd
- http://docs.openstack.org/common/api/v1.0 ../common/xsd/api-common.xsd
- http://wadl.dev.java.net/2009/02 http://www.w3.org/Submission/wadl/wadl.xsd
- http://docs.openstack.org/identity/api/ext/RAX-KSQA/v1.0 ../common/xsd/RAX-KSQA-secretQA.xsd
- ">
-
- <grammars>
- <include href="../common/xsd/api.xsd"/>
- <include href="../common/xsd/api-common.xsd"/>
- <include href="../common/xsd/RAX-KSQA-secretQA.xsd"/>
- </grammars>
- <!--*******************************************************-->
- <!-- All Resources -->
- <!--*******************************************************-->
-
- <!-- We should use SSL in production -->
- <resources base="http://localhost:35357">
- <resource id="version" path="v2.0">
- <param name="X-Auth-Token" style="header" type="xsd:string" required="true">
- <doc>You need a valid admin token for access.</doc>
- </param>
- <resource id="users" path="users">
- <resource id="userById" path="{userId}">
- <param name="userId" style="template" type="xsd:string"/>
- <resource id="user-RAX-KSQA" path="RAX-KSQA">
- <resource id="secretqa" path="secretqa">
- <method href="#getUserSecretQA"/>
- <method href="#updateUserSecretQA"/>
- </resource>
- </resource>
- </resource>
- </resource>
- </resource>
- </resources>
-
- <method name="GET" id="getUserSecretQA">
- <doc xml:lang="EN" title="Get User SecretQA">
- <p xmlns="http://www.w3.org/1999/xhtml">Gets a User secret Question and Answer.</p>
- </doc>
- <response status="200 203">
- <representation mediaType="application/xml" element="RAX-KSQA:secretQA">
- <doc xml:lang="EN">
- <xsdxt:code href="../common/samples/RAX-KSQA-secretQA.xml"/>
- </doc>
- </representation>
- <representation mediaType="application/json">
- <doc xml:lang="EN">
- <xsdxt:code href="../common/samples/RAX-KSQA-secretQA.json"/>
- </doc>
- </representation>
- </response>
- &commonFaults;
- &getFaults;
- </method>
- <method name="PUT" id="updateUserSecretQA">
- <doc xml:lang="EN" title="Update User SecretQA">
- <p xmlns="http://www.w3.org/1999/xhtml">Updates a User secret Question and Answer.</p>
- </doc>
- <request>
- <representation mediaType="application/xml" element="RAX-KSQA:secretQA">
- <doc xml:lang="EN">
- <xsdxt:code href="../common/samples/RAX-KSQA-secretQA.xml"/>
- </doc>
- </representation>
- <representation mediaType="application/json">
- <doc xml:lang="EN">
- <xsdxt:code href="../common/samples/RAX-KSQA-secretQA.json"/>
- </doc>
- </representation>
- </request>
- <response status="200">
- <representation mediaType="application/xml" element="RAX-KSQA:secretQA">
- <doc xml:lang="EN">
- <xsdxt:code href="../common/samples/RAX-KSQA-secretQA.xml"/>
- </doc>
- </representation>
- <representation mediaType="application/json">
- <doc xml:lang="EN">
- <xsdxt:code href="../common/samples/RAX-KSQA-secretQA.json"/>
- </doc>
- </representation>
- </response>
- &commonFaults;
- &postPutFaults;
- </method>
- </application>
diff --git a/keystone/content/admin/extensions.json b/keystone/content/admin/extensions.json deleted file mode 100644 index 7a514fd7..00000000 --- a/keystone/content/admin/extensions.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extensions": { - "values": [] - } -} diff --git a/keystone/content/admin/extensions.xml b/keystone/content/admin/extensions.xml deleted file mode 100644 index ed5ee9c6..00000000 --- a/keystone/content/admin/extensions.xml +++ /dev/null @@ -1,5 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<extensions xmlns="http://docs.openstack.org/common/api/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom"> -</extensions> diff --git a/keystone/content/admin/identity-admin.wadl b/keystone/content/admin/identity-admin.wadl deleted file mode 100644 index a6ded2ac..00000000 --- a/keystone/content/admin/identity-admin.wadl +++ /dev/null @@ -1,508 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!--*******************************************************--> -<!-- Import Common XML Entities --> -<!-- --> -<!-- You can resolve the entites with xmllint --> -<!-- --> -<!-- xmllint -noent identity-admin.wadl --> -<!--*******************************************************--> -<!DOCTYPE application [ - <!ENTITY % common SYSTEM "../common/common.ent"> - %common; -]> - -<application xmlns="http://wadl.dev.java.net/2009/02" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:capi="http://docs.openstack.org/common/api/v1.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xsi:schemaLocation="http://docs.openstack.org/identity/api/v2.0 ../common/xsd/api.xsd - http://docs.openstack.org/common/api/v1.0 ../common/xsd/api-common.xsd - http://wadl.dev.java.net/2009/02 http://www.w3.org/Submission/wadl/wadl.xsd - "> - - <grammars> - <include href="../common/xsd/api.xsd"/> - <include href="../common/xsd/api-common.xsd"/> - </grammars> - - <!--*******************************************************--> - <!-- All Resources --> - <!--*******************************************************--> - - <!-- We should use SSL in production --> - <resources base="http://localhost:35357"> - <resource id="version" path="v2.0"> - <method href="#getVersionInfo"/> - - <resource id="extensions" path="extensions"> - <method href="#listExtensions"/> - - <resource id="extension" path="{alias}"> - <param name="alias" style="template" type="xsd:string"/> - <method href="#getExtension"/> - </resource> - </resource> - - <resource id="tokens" path="tokens"> - <method href="#authenticate"/> - <resource id="tokenById" path="{tokenId}"> - <param name="X-Auth-Token" style="header" type="xsd:string" required="true"> - <doc>You need a valid admin token for access.</doc> - </param> - <param name="tokenId" style="template" type="xsd:string" required="true"/> - <param name="belongsTo" style="query" type="xsd:string" required="false"/> - <method href="#validateToken"/> - <method href="#checkToken"/> - <resource id="endpointsForToken" path="endpoints"> - <method href="#listEndpointsForToken"/> - </resource> - </resource> - </resource> - - <resource id="users" path="users"> - <param name="X-Auth-Token" style="header" type="xsd:string" required="true"> - <doc>You need a valid admin token for access.</doc> - </param> - <method href="#getUserByName"/> - <resource id="userid" path="{user_id}"> - <param name="user_id" style="template" type="xsd:string" required="true"/> - <method href="#getUserById"/> - - <resource id="userRoles" path="roles"> - <method href="#listUserGlobalRoles"/> - </resource> - </resource> - </resource> - - <resource id="tenants" path="tenants"> - <param name="X-Auth-Token" style="header" type="xsd:string" required="true"> - <doc> - <p xmlns="http://www.w3.org/1999/xhtml"> - You need a valid admin token for access. - </p> - </doc> - </param> - <method href="#listTenants"/> - <method href="#getTenantByName"/> - <resource id="tenantById" path="{tenantId}"> - <param name="tenantId" style="template" type="xsd:string" required="true"/> - <method href="#getTenantById"/> - - <resource id="usersForTenant" path="users"> - <resource id="getTenantUser" path="{user_id}"> - <param name="user_id" style="template" type="xsd:string" required="true"/> - - <resource id="userRolesForTenant" path="roles"> - <method href="#listRolesForUserOnTenant"/> - </resource> - </resource> - </resource> - </resource> - </resource> - </resource> - </resources> - - <!--*******************************************************--> - <!-- Resource Types --> - <!--*******************************************************--> - - <resource_type id="VersionDetails"> - <method href="#getVersionInfo"/> - </resource_type> - - <resource_type id="ExtensionList"> - <doc xml:lang="EN" title="Extension List"> - <p xmlns="http://www.w3.org/1999/xhtml"> - A list of supported extensions. - </p> - </doc> - <method href="#listExtensions"/> - </resource_type> - - <!--*******************************************************--> - <!-- All Methods --> - <!--*******************************************************--> - - <!-- Version --> - - <method name="GET" id="getVersionInfo"> - <doc xml:lang="EN" title="Version Details"> - <p xmlns="http://www.w3.org/1999/xhtml"> - Returns detailed information about this specific version of the API. - </p> - </doc> - <response status="200 203"> - <representation mediaType="application/xml" element="capi:version"> - <param name="location" style="plain" type="xsd:anyURI" required="true" path="/capi:version/atom:link[@rel='self']/@href"> - <link resource_type="#VersionDetails" rel="self"/> - </param> - </representation> - <representation mediaType="application/json"/> - </response> - &commonFaults; - &getFaults; - </method> - - <!-- Extensions --> - - <method name="GET" id="listExtensions"> - <doc xml:lang="EN" title="List Extensions"> - <p xmlns="http://www.w3.org/1999/xhtml"> - Lists supported extensions. - </p> - </doc> - <response status="200 203"> - <representation mediaType="application/xml" element="capi:extensions"> - <param name="next" style="plain" type="xsd:anyURI" path="/capi:extensions/atom:link[@rel='next']/@href"> - <link resource_type="#ExtensionList" rel="next"/> - </param> - <param name="previous" style="plain" type="xsd:anyURI" path="/capi:extensions/atom:link[@rel='previous']/@href"> - <link resource_type="#ExtensionList" rel="previous"/> - </param> - </representation> - <representation mediaType="application/json"/> - </response> - &commonFaults; - </method> - <method name="GET" id="getExtension"> - <doc xml:lang="EN" title="Get Extension Details"> - <p xmlns="http://www.w3.org/1999/xhtml"> - Gets details about a specific extension. - </p> - </doc> - <response status="200 203"> - <representation mediaType="application/xml" element="capi:extension"/> - <representation mediaType="application/json"/> - </response> - &commonFaults; - &getFaults; - </method> - - <!-- Token Operations --> - - <method name="POST" id="authenticate"> - <doc xml:lang="EN" title="Authenticate for Service API"> - <p xmlns="http://www.w3.org/1999/xhtml" class="shortdesc"> - Authenticate to generate a token. - </p> - <p xmlns="http://www.w3.org/1999/xhtml"> - This call will return a token if successful. Each ReST request against other services (or other - calls on Keystone such as the GET /tenants call) - requires the inclusion of a specific authorization token HTTP x-header, defined as X-Auth-Token. - Clients obtain - this token, along with the URL to other service APIs, by first authenticating against the - Keystone Service and supplying valid credentials. - </p> - <p xmlns="http://www.w3.org/1999/xhtml"> - Client authentication is provided via a ReST interface using the POST method, - with v2.0/tokens supplied as the path. A payload of credentials must be included - in the body. - </p> - <p xmlns="http://www.w3.org/1999/xhtml"> - The Keystone Service is a ReSTful web service. It is the entry point to all service APIs. - To access the Keystone Service, you must know URL of the Keystone service. - </p> - </doc> - <request> - <representation mediaType="application/xml" element="identity:auth"> - <doc> - <xsdxt:code href="../common/samples/auth_credentials.xml"/> - <xsdxt:code href="../common/samples/auth_with_token.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc> - <xsdxt:code href="../common/samples/auth_credentials.json"/> - <xsdxt:code href="../common/samples/auth_with_token.json"/> - </doc> - </representation> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:access"> - <doc> - <xsdxt:code href="../common/samples/auth.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc> - <xsdxt:code href="../common/samples/auth.json"/> - </doc> - </representation> - </response> - <response status="403"> - <representation mediaType="application/xml" element="identity:userDisabled"/> - <representation mediaType="application/json"/> - </response> - &commonFaults; - &getFaults; - </method> - <method name="GET" id="validateToken"> - <doc xml:lang="EN" title="Validate Token"> - <p xmlns="http://www.w3.org/1999/xhtml" class="shortdesc"> - Check that a token is valid and that it belongs to a supplied tenant - and return the permissions relevant to a particular client. - </p> - <p xmlns="http://www.w3.org/1999/xhtml"> - Valid tokens will exist in the - <code>/tokens/{tokenId}</code> path and invalid - tokens will not. In other words, a user should expect an - itemNotFound (<code>404</code>) fault for an - invalid token. - </p> - </doc> - <request> - <param name="belongsTo" style="query" required="false" type="xsd:string"> - <doc xml:lang="EN"> - <p xmlns="http://www.w3.org/1999/xhtml"> - Validates a token has the supplied tenant in scope. - </p> - </doc> - </param> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:access"> - <doc> - <xsdxt:code href="../common/samples/validatetoken.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc> - <xsdxt:code href="../common/samples/validatetoken.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - <method name="HEAD" id="checkToken"> - <doc xml:lang="EN" title="Check Token"> - <p xmlns="http://www.w3.org/1999/xhtml" class="shortdesc"> - Check that a token is valid and that it belongs to a particular tenant - (For performance). - </p> - </doc> - <request> - <param name="belongsTo" style="query" required="false" type="xsd:string"> - <doc xml:lang="EN"> - <p xmlns="http://www.w3.org/1999/xhtml"> - Validates a token has the supplied tenant in scope. (for performance). - </p> - <p xmlns="http://www.w3.org/1999/xhtml"> - Valid tokens will exist in the - <code>/tokens/{tokenId}</code> path and invalid - tokens will not. In other words, a user should expect an - itemNotFound (<code>404</code>) fault for an - invalid token. - </p> - <p xmlns="http://www.w3.org/1999/xhtml"> - If `belongsTo` is provided, validates that a token has a specific tenant in scope. - </p> - <p xmlns="http://www.w3.org/1999/xhtml"> - No response body is returned for this method. - </p> - </doc> - </param> - </request> - <response status="200 203"/> - &commonFaults; - &getFaults; - </method> - - <!--User Operations--> - <method name="GET" id="getUserByName"> - <doc xml:lang="EN" title="Get a User by Name"> - <p xmlns="http://www.w3.org/1999/xhtml"> - Returns detailed information about a specific user, by user name. - </p> - </doc> - <request> - <param name="name" style="query" type="xsd:string" required="true"/> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:user"> - <doc> - <xsdxt:code href="../common/samples/user.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc> - <xsdxt:code href="../common/samples/user.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - <method name="GET" id="getUserById"> - <doc xml:lang="EN" title="Get a User by ID"> - <p xmlns="http://www.w3.org/1999/xhtml"> - Returns detailed information about a specific user, by user id. - </p> - </doc> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:user"> - <doc> - <xsdxt:code href="../common/samples/user.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc> - <xsdxt:code href="../common/samples/user.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - <method name="GET" id="listUserGlobalRoles"> - <doc xml:lang="EN" title="List User Global Roles"> - <p xmlns="http://www.w3.org/1999/xhtml" class="shortdesc"> - Returns global roles for a specific user (excludes tenant roles). - </p> - <p xmlns="http://www.w3.org/1999/xhtml">Returns a list of global roles associated with a specific - user (excludes tenant roles).</p> - </doc> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:roles"> - <doc> - <xsdxt:code href="../common/samples/roles.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc> - <xsdxt:code href="../common/samples/roles.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - - <!-- Tenant Operations --> - - <method name="GET" id="listTenants"> - <doc xml:lang="EN" title="Get Tenants"> - <p xmlns="http://www.w3.org/1999/xhtml" class="shortdesc"> - Get a list of tenants. - </p> - <p xmlns="http://www.w3.org/1999/xhtml"> - The operation returns a list of tenants which the supplied token provides - access to. This call must be authenticated, so a valid token must - be passed in as a header. - </p> - <xsdxt:samples> - <xsdxt:sample xmlns="http://docs.rackspace.com/api" title="Tenants Request with Auth Token"> - <xsdxt:code href="../common/samples/tenants-request.txt" language="text"/> - </xsdxt:sample> - </xsdxt:samples> - </doc> - <request> - <param name="marker" style="query" required="false" type="xsd:string"/> - <param name="limit" style="query" required="false" type="xsd:int"/> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:tenants"> - <doc> - <xsdxt:code href="../common/samples/tenants.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc> - <xsdxt:code href="../common/samples/tenants.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - <method name="GET" id="getTenantByName"> - <doc xml:lang="EN" title="Get tenants by name"> - <p xmlns="http://www.w3.org/1999/xhtml" class="shortdesc"> - Returns detailed information about a tenant, by name. - </p> - </doc> - <request> - <param name="name" style="query" type="xsd:string" required="true"/> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:tenant"> - <doc> - <xsdxt:code href="../common/samples/tenant.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc> - <xsdxt:code href="../common/samples/tenant.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - <method name="GET" id="getTenantById"> - <doc xml:lang="EN" title="Get Tenants by ID"> - <p xmlns="http://www.w3.org/1999/xhtml" class="shortdesc"> - Returns detailed information about a tenant, by id. - </p> - </doc> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:tenant"> - <doc> - <xsdxt:code href="../common/samples/tenant.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc> - <xsdxt:code href="../common/samples/tenant.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - <method name="GET" id="listEndpointsForToken"> - <doc xml:lang="EN" title="List Endoints for a Token"> - <p xmlns="http://www.w3.org/1999/xhtml"> - Returns a list of endpoints associated with a specific token. - </p> - </doc> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:endpoints"> - <doc> - <xsdxt:code href="../common/samples/endpoints.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc> - <xsdxt:code href="../common/samples/endpoints.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> - - <method name="GET" id="listRolesForUserOnTenant"> - <doc xml:lang="EN" title="List Roles for User on Tenant"> - <p xmlns="http://www.w3.org/1999/xhtml"> - Returns roles for a specific user on a specific tenant (excludes global roles). - </p> - </doc> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:roles"> - <doc> - <xsdxt:code href="../common/samples/roles.xml"/> - </doc> - </representation> - <representation mediaType="application/json"> - <doc> - <xsdxt:code href="../common/samples/roles.json"/> - </doc> - </representation> - </response> - &commonFaults; - &getFaults; - </method> -</application> diff --git a/keystone/content/admin/identityadminguide.pdf b/keystone/content/admin/identityadminguide.pdf Binary files differdeleted file mode 100644 index 5229e5d4..00000000 --- a/keystone/content/admin/identityadminguide.pdf +++ /dev/null diff --git a/keystone/content/admin/version.atom.tpl b/keystone/content/admin/version.atom.tpl deleted file mode 100644 index e39250d5..00000000 --- a/keystone/content/admin/version.atom.tpl +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<feed xmlns="http://www.w3.org/2005/Atom"> - <title type="text">Available API Versions</title> - <updated>2010-12-22T00:00:00.00Z</updated> - <id>http://identity.api.openstack.org/</id> - <author><name>OpenStack</name><uri>http://www.openstack.org/</uri></author> - <link rel="self" href="http://identity.api.openstack.org/"/> - <entry> - <id>http://identity.api.openstack.org/v2.0/</id> - <title type="text">Version v2.0</title> - <updated>2011-09-30T00:00:00.00Z</updated> - <link rel="self" href="http://identity.api.openstack.org/v2.0/"/> - <content type="text">Version v2.0 BETA (2011-09-30T00:00:00.00Z)</content> - </entry> - <entry> - <id>http://identity.api.openstack.org/v1.1/</id> - <title type="text">Version v1.1</title> - <updated>2011-01-21T11:33:21-06:00</updated> - <link rel="self" href="http://identity.api.openstack.org/v1.1/"/> - <content type="text">Version v1.1 CURRENT (2011-01-21T11:33:21-06:00)</content> - </entry> - <entry> - <id>http://identity.api.openstack.org/v1.0/</id> - <title type="text">Version v1.0</title> - <updated>2009-10-09T11:30:00Z</updated> - <link rel="self" href="http://identity.api.openstack.org/v1.0/"/> - <content type="text">Version v1.0 DEPRECATED (2009-10-09T11:30:00Z)</content> - </entry> -</feed> diff --git a/keystone/content/admin/version.json.tpl b/keystone/content/admin/version.json.tpl deleted file mode 100644 index 112ba1b8..00000000 --- a/keystone/content/admin/version.json.tpl +++ /dev/null @@ -1,32 +0,0 @@ -{ - "versions": { - "values": [{ - "id": "v{{API_VERSION}}", - "status": "{{API_VERSION_STATUS}}", - "updated": "{{API_VERSION_DATE}}", - "links": [{ - "rel": "self", - "href": "http://{{HOST}}:{{PORT}}/v{{API_VERSION}}/" - }, { - "rel": "describedby", - "type": "text/html", - "href": "http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/content/" - }, { - "rel": "describedby", - "type": "application/pdf", - "href": "http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/identity-dev-guide-{{API_VERSION}}.pdf" - }, { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "http://{{HOST}}:{{PORT}}/v2.0/identity-admin.wadl" - }], - "media-types": [{ - "base": "application/xml", - "type": "application/vnd.openstack.identity-v{{API_VERSION}}+xml" - }, { - "base": "application/json", - "type": "application/vnd.openstack.identity-v{{API_VERSION}}+json" - }] - }] - } -}
\ No newline at end of file diff --git a/keystone/content/admin/version.xml.tpl b/keystone/content/admin/version.xml.tpl deleted file mode 100644 index b62c9b6a..00000000 --- a/keystone/content/admin/version.xml.tpl +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<versions xmlns="http://docs.openstack.org/common/api/v2.0" - xmlns:atom="http://www.w3.org/2005/Atom"> - - <version id="v{{API_VERSION}}" status="{{API_VERSION_STATUS}}" updated="{{API_VERSION_DATE}}"> - - <media-types> - <media-type base="application/xml" - type="application/vnd.openstack.identity-v{{API_VERSION}}+xml"/> - <media-type base="application/json" - type="application/vnd.openstack.identity-v{{API_VERSION}}+json"/> - </media-types> - - <atom:link rel="self" - href="http://{{HOST}}:{{PORT}}/v{{API_VERSION}}/"/> - - <atom:link rel="describedby" - type="text/html" - href="http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/content/" /> - - <atom:link rel="describedby" - type="application/pdf" - href="http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/identity-dev-guide-{{API_VERSION}}.pdf" /> - - <atom:link rel="describedby" - type="application/vnd.sun.wadl+xml" - href="http://{{HOST}}:{{PORT}}/v2.0/identity-admin.wadl" /> - </version> -</versions>
\ No newline at end of file diff --git a/keystone/content/common/common.ent b/keystone/content/common/common.ent deleted file mode 100644 index b492c5d2..00000000 --- a/keystone/content/common/common.ent +++ /dev/null @@ -1,56 +0,0 @@ - - <!-- - A collection of common faults, these are pretty much expected - in every request. - --> - <!ENTITY commonFaults - ' - <response xmlns="http://wadl.dev.java.net/2009/02"> - <representation mediaType="application/xml" element="identity:identityFault"/> - <representation mediaType="application/json"/> - </response> - <response status="400" xmlns="http://wadl.dev.java.net/2009/02"> - <representation mediaType="application/xml" element="identity:badRequest"/> - <representation mediaType="application/json"/> - </response> - <response status="401" xmlns="http://wadl.dev.java.net/2009/02"> - <representation mediaType="application/xml" element="identity:unauthorized"/> - <representation mediaType="application/json"/> - </response> - <response status="403" xmlns="http://wadl.dev.java.net/2009/02"> - <representation mediaType="application/xml" element="identity:forbidden"/> - <representation mediaType="application/json"/> - </response> - <response status="405" xmlns="http://wadl.dev.java.net/2009/02"> - <representation mediaType="application/xml" element="identity:badMethod"/> - <representation mediaType="application/json"/> - </response> - <response status="413" xmlns="http://wadl.dev.java.net/2009/02"> - <representation mediaType="application/xml" element="identity:overLimit"/> - <representation mediaType="application/json"/> - </response> - <response status="503" xmlns="http://wadl.dev.java.net/2009/02"> - <representation mediaType="application/xml" element="identity:serviceUnavailable"/> - <representation mediaType="application/json"/> - </response> - '> - <!-- - Faults on GET - --> - <!ENTITY getFaults - ' - <response status="404" xmlns="http://wadl.dev.java.net/2009/02"> - <representation mediaType="application/xml" element="identity:itemNotFound"/> - <representation mediaType="application/json"/> - </response> - '> - <!-- - Faults on POST/PUT - --> - <!ENTITY postPutFaults - ' - <response status="415" xmlns="http://wadl.dev.java.net/2009/02"> - <representation mediaType="application/xml" element="identity:badMediaType"/> - <representation mediaType="application/json"/> - </response> - '> diff --git a/keystone/content/common/js/shjs/sh_java.js b/keystone/content/common/js/shjs/sh_java.js deleted file mode 100644 index 731fc9f3..00000000 --- a/keystone/content/common/js/shjs/sh_java.js +++ /dev/null @@ -1,337 +0,0 @@ -if (! this.sh_languages) { - this.sh_languages = {}; -} -sh_languages['java'] = [ - [ - [ - /\b(?:import|package)\b/g, - 'sh_preproc', - -1 - ], - [ - /\/\/\//g, - 'sh_comment', - 1 - ], - [ - /\/\//g, - 'sh_comment', - 7 - ], - [ - /\/\*\*/g, - 'sh_comment', - 8 - ], - [ - /\/\*/g, - 'sh_comment', - 9 - ], - [ - /\b[+-]?(?:(?:0x[A-Fa-f0-9]+)|(?:(?:[\d]*\.)?[\d]+(?:[eE][+-]?[\d]+)?))u?(?:(?:int(?:8|16|32|64))|L)?\b/g, - 'sh_number', - -1 - ], - [ - /"/g, - 'sh_string', - 10 - ], - [ - /'/g, - 'sh_string', - 11 - ], - [ - /(\b(?:class|interface))([ \t]+)([$A-Za-z0-9_]+)/g, - ['sh_keyword', 'sh_normal', 'sh_classname'], - -1 - ], - [ - /\b(?:abstract|assert|break|case|catch|class|const|continue|default|do|else|extends|false|final|finally|for|goto|if|implements|instanceof|interface|native|new|null|private|protected|public|return|static|strictfp|super|switch|synchronized|throw|throws|true|this|transient|try|volatile|while)\b/g, - 'sh_keyword', - -1 - ], - [ - /\b(?:int|byte|boolean|char|long|float|double|short|void)\b/g, - 'sh_type', - -1 - ], - [ - /~|!|%|\^|\*|\(|\)|-|\+|=|\[|\]|\\|:|;|,|\.|\/|\?|&|<|>|\|/g, - 'sh_symbol', - -1 - ], - [ - /\{|\}/g, - 'sh_cbracket', - -1 - ], - [ - /(?:[A-Za-z]|_)[A-Za-z0-9_]*(?=[ \t]*\()/g, - 'sh_function', - -1 - ], - [ - /([A-Za-z](?:[^`~!@#$%&*()_=+{}|;:",<.>\/?'\\[\]\^\-\s]|[_])*)((?:<.*>)?)(\s+(?=[*&]*[A-Za-z][^`~!@#$%&*()_=+{}|;:",<.>\/?'\\[\]\^\-\s]*\s*[`~!@#$%&*()_=+{}|;:",<.>\/?'\\[\]\^\-\[\]]+))/g, - ['sh_usertype', 'sh_usertype', 'sh_normal'], - -1 - ] - ], - [ - [ - /$/g, - null, - -2 - ], - [ - /(?:<?)[A-Za-z0-9_\.\/\-_~]+@[A-Za-z0-9_\.\/\-_~]+(?:>?)|(?:<?)[A-Za-z0-9_]+:\/\/[A-Za-z0-9_\.\/\-_~]+(?:>?)/g, - 'sh_url', - -1 - ], - [ - /<\?xml/g, - 'sh_preproc', - 2, - 1 - ], - [ - /<!DOCTYPE/g, - 'sh_preproc', - 4, - 1 - ], - [ - /<!--/g, - 'sh_comment', - 5 - ], - [ - /<(?:\/)?[A-Za-z](?:[A-Za-z0-9_:.-]*)(?:\/)?>/g, - 'sh_keyword', - -1 - ], - [ - /<(?:\/)?[A-Za-z](?:[A-Za-z0-9_:.-]*)/g, - 'sh_keyword', - 6, - 1 - ], - [ - /&(?:[A-Za-z0-9]+);/g, - 'sh_preproc', - -1 - ], - [ - /<(?:\/)?[A-Za-z][A-Za-z0-9]*(?:\/)?>/g, - 'sh_keyword', - -1 - ], - [ - /<(?:\/)?[A-Za-z][A-Za-z0-9]*/g, - 'sh_keyword', - 6, - 1 - ], - [ - /@[A-Za-z]+/g, - 'sh_type', - -1 - ], - [ - /(?:TODO|FIXME|BUG)(?:[:]?)/g, - 'sh_todo', - -1 - ] - ], - [ - [ - /\?>/g, - 'sh_preproc', - -2 - ], - [ - /([^=" \t>]+)([ \t]*)(=?)/g, - ['sh_type', 'sh_normal', 'sh_symbol'], - -1 - ], - [ - /"/g, - 'sh_string', - 3 - ] - ], - [ - [ - /\\(?:\\|")/g, - null, - -1 - ], - [ - /"/g, - 'sh_string', - -2 - ] - ], - [ - [ - />/g, - 'sh_preproc', - -2 - ], - [ - /([^=" \t>]+)([ \t]*)(=?)/g, - ['sh_type', 'sh_normal', 'sh_symbol'], - -1 - ], - [ - /"/g, - 'sh_string', - 3 - ] - ], - [ - [ - /-->/g, - 'sh_comment', - -2 - ], - [ - /<!--/g, - 'sh_comment', - 5 - ] - ], - [ - [ - /(?:\/)?>/g, - 'sh_keyword', - -2 - ], - [ - /([^=" \t>]+)([ \t]*)(=?)/g, - ['sh_type', 'sh_normal', 'sh_symbol'], - -1 - ], - [ - /"/g, - 'sh_string', - 3 - ] - ], - [ - [ - /$/g, - null, - -2 - ] - ], - [ - [ - /\*\//g, - 'sh_comment', - -2 - ], - [ - /(?:<?)[A-Za-z0-9_\.\/\-_~]+@[A-Za-z0-9_\.\/\-_~]+(?:>?)|(?:<?)[A-Za-z0-9_]+:\/\/[A-Za-z0-9_\.\/\-_~]+(?:>?)/g, - 'sh_url', - -1 - ], - [ - /<\?xml/g, - 'sh_preproc', - 2, - 1 - ], - [ - /<!DOCTYPE/g, - 'sh_preproc', - 4, - 1 - ], - [ - /<!--/g, - 'sh_comment', - 5 - ], - [ - /<(?:\/)?[A-Za-z](?:[A-Za-z0-9_:.-]*)(?:\/)?>/g, - 'sh_keyword', - -1 - ], - [ - /<(?:\/)?[A-Za-z](?:[A-Za-z0-9_:.-]*)/g, - 'sh_keyword', - 6, - 1 - ], - [ - /&(?:[A-Za-z0-9]+);/g, - 'sh_preproc', - -1 - ], - [ - /<(?:\/)?[A-Za-z][A-Za-z0-9]*(?:\/)?>/g, - 'sh_keyword', - -1 - ], - [ - /<(?:\/)?[A-Za-z][A-Za-z0-9]*/g, - 'sh_keyword', - 6, - 1 - ], - [ - /@[A-Za-z]+/g, - 'sh_type', - -1 - ], - [ - /(?:TODO|FIXME|BUG)(?:[:]?)/g, - 'sh_todo', - -1 - ] - ], - [ - [ - /\*\//g, - 'sh_comment', - -2 - ], - [ - /(?:<?)[A-Za-z0-9_\.\/\-_~]+@[A-Za-z0-9_\.\/\-_~]+(?:>?)|(?:<?)[A-Za-z0-9_]+:\/\/[A-Za-z0-9_\.\/\-_~]+(?:>?)/g, - 'sh_url', - -1 - ], - [ - /(?:TODO|FIXME|BUG)(?:[:]?)/g, - 'sh_todo', - -1 - ] - ], - [ - [ - /"/g, - 'sh_string', - -2 - ], - [ - /\\./g, - 'sh_specialchar', - -1 - ] - ], - [ - [ - /'/g, - 'sh_string', - -2 - ], - [ - /\\./g, - 'sh_specialchar', - -1 - ] - ] -]; diff --git a/keystone/content/common/js/shjs/sh_javascript.js b/keystone/content/common/js/shjs/sh_javascript.js deleted file mode 100644 index ae4fa0ec..00000000 --- a/keystone/content/common/js/shjs/sh_javascript.js +++ /dev/null @@ -1,347 +0,0 @@ -if (! this.sh_languages) { - this.sh_languages = {}; -} -sh_languages['javascript'] = [ - [ - [ - /\/\/\//g, - 'sh_comment', - 1 - ], - [ - /\/\//g, - 'sh_comment', - 7 - ], - [ - /\/\*\*/g, - 'sh_comment', - 8 - ], - [ - /\/\*/g, - 'sh_comment', - 9 - ], - [ - /\b(?:abstract|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|final|finally|for|function|goto|if|implements|in|instanceof|interface|native|new|null|private|protected|prototype|public|return|static|super|switch|synchronized|throw|throws|this|transient|true|try|typeof|var|volatile|while|with)\b/g, - 'sh_keyword', - -1 - ], - [ - /(\+\+|--|\)|\])(\s*)(\/=?(?![*\/]))/g, - ['sh_symbol', 'sh_normal', 'sh_symbol'], - -1 - ], - [ - /(0x[A-Fa-f0-9]+|(?:[\d]*\.)?[\d]+(?:[eE][+-]?[\d]+)?)(\s*)(\/(?![*\/]))/g, - ['sh_number', 'sh_normal', 'sh_symbol'], - -1 - ], - [ - /([A-Za-z$_][A-Za-z0-9$_]*\s*)(\/=?(?![*\/]))/g, - ['sh_normal', 'sh_symbol'], - -1 - ], - [ - /\/(?:\\.|[^*\\\/])(?:\\.|[^\\\/])*\/[gim]*/g, - 'sh_regexp', - -1 - ], - [ - /\b[+-]?(?:(?:0x[A-Fa-f0-9]+)|(?:(?:[\d]*\.)?[\d]+(?:[eE][+-]?[\d]+)?))u?(?:(?:int(?:8|16|32|64))|L)?\b/g, - 'sh_number', - -1 - ], - [ - /"/g, - 'sh_string', - 10 - ], - [ - /'/g, - 'sh_string', - 11 - ], - [ - /~|!|%|\^|\*|\(|\)|-|\+|=|\[|\]|\\|:|;|,|\.|\/|\?|&|<|>|\|/g, - 'sh_symbol', - -1 - ], - [ - /\{|\}/g, - 'sh_cbracket', - -1 - ], - [ - /\b(?:Math|Infinity|NaN|undefined|arguments)\b/g, - 'sh_predef_var', - -1 - ], - [ - /\b(?:Array|Boolean|Date|Error|EvalError|Function|Number|Object|RangeError|ReferenceError|RegExp|String|SyntaxError|TypeError|URIError|decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|isNaN|parseFloat|parseInt)\b/g, - 'sh_predef_func', - -1 - ], - [ - /(?:[A-Za-z]|_)[A-Za-z0-9_]*(?=[ \t]*\()/g, - 'sh_function', - -1 - ] - ], - [ - [ - /$/g, - null, - -2 - ], - [ - /(?:<?)[A-Za-z0-9_\.\/\-_~]+@[A-Za-z0-9_\.\/\-_~]+(?:>?)|(?:<?)[A-Za-z0-9_]+:\/\/[A-Za-z0-9_\.\/\-_~]+(?:>?)/g, - 'sh_url', - -1 - ], - [ - /<\?xml/g, - 'sh_preproc', - 2, - 1 - ], - [ - /<!DOCTYPE/g, - 'sh_preproc', - 4, - 1 - ], - [ - /<!--/g, - 'sh_comment', - 5 - ], - [ - /<(?:\/)?[A-Za-z](?:[A-Za-z0-9_:.-]*)(?:\/)?>/g, - 'sh_keyword', - -1 - ], - [ - /<(?:\/)?[A-Za-z](?:[A-Za-z0-9_:.-]*)/g, - 'sh_keyword', - 6, - 1 - ], - [ - /&(?:[A-Za-z0-9]+);/g, - 'sh_preproc', - -1 - ], - [ - /<(?:\/)?[A-Za-z][A-Za-z0-9]*(?:\/)?>/g, - 'sh_keyword', - -1 - ], - [ - /<(?:\/)?[A-Za-z][A-Za-z0-9]*/g, - 'sh_keyword', - 6, - 1 - ], - [ - /@[A-Za-z]+/g, - 'sh_type', - -1 - ], - [ - /(?:TODO|FIXME|BUG)(?:[:]?)/g, - 'sh_todo', - -1 - ] - ], - [ - [ - /\?>/g, - 'sh_preproc', - -2 - ], - [ - /([^=" \t>]+)([ \t]*)(=?)/g, - ['sh_type', 'sh_normal', 'sh_symbol'], - -1 - ], - [ - /"/g, - 'sh_string', - 3 - ] - ], - [ - [ - /\\(?:\\|")/g, - null, - -1 - ], - [ - /"/g, - 'sh_string', - -2 - ] - ], - [ - [ - />/g, - 'sh_preproc', - -2 - ], - [ - /([^=" \t>]+)([ \t]*)(=?)/g, - ['sh_type', 'sh_normal', 'sh_symbol'], - -1 - ], - [ - /"/g, - 'sh_string', - 3 - ] - ], - [ - [ - /-->/g, - 'sh_comment', - -2 - ], - [ - /<!--/g, - 'sh_comment', - 5 - ] - ], - [ - [ - /(?:\/)?>/g, - 'sh_keyword', - -2 - ], - [ - /([^=" \t>]+)([ \t]*)(=?)/g, - ['sh_type', 'sh_normal', 'sh_symbol'], - -1 - ], - [ - /"/g, - 'sh_string', - 3 - ] - ], - [ - [ - /$/g, - null, - -2 - ] - ], - [ - [ - /\*\//g, - 'sh_comment', - -2 - ], - [ - /(?:<?)[A-Za-z0-9_\.\/\-_~]+@[A-Za-z0-9_\.\/\-_~]+(?:>?)|(?:<?)[A-Za-z0-9_]+:\/\/[A-Za-z0-9_\.\/\-_~]+(?:>?)/g, - 'sh_url', - -1 - ], - [ - /<\?xml/g, - 'sh_preproc', - 2, - 1 - ], - [ - /<!DOCTYPE/g, - 'sh_preproc', - 4, - 1 - ], - [ - /<!--/g, - 'sh_comment', - 5 - ], - [ - /<(?:\/)?[A-Za-z](?:[A-Za-z0-9_:.-]*)(?:\/)?>/g, - 'sh_keyword', - -1 - ], - [ - /<(?:\/)?[A-Za-z](?:[A-Za-z0-9_:.-]*)/g, - 'sh_keyword', - 6, - 1 - ], - [ - /&(?:[A-Za-z0-9]+);/g, - 'sh_preproc', - -1 - ], - [ - /<(?:\/)?[A-Za-z][A-Za-z0-9]*(?:\/)?>/g, - 'sh_keyword', - -1 - ], - [ - /<(?:\/)?[A-Za-z][A-Za-z0-9]*/g, - 'sh_keyword', - 6, - 1 - ], - [ - /@[A-Za-z]+/g, - 'sh_type', - -1 - ], - [ - /(?:TODO|FIXME|BUG)(?:[:]?)/g, - 'sh_todo', - -1 - ] - ], - [ - [ - /\*\//g, - 'sh_comment', - -2 - ], - [ - /(?:<?)[A-Za-z0-9_\.\/\-_~]+@[A-Za-z0-9_\.\/\-_~]+(?:>?)|(?:<?)[A-Za-z0-9_]+:\/\/[A-Za-z0-9_\.\/\-_~]+(?:>?)/g, - 'sh_url', - -1 - ], - [ - /(?:TODO|FIXME|BUG)(?:[:]?)/g, - 'sh_todo', - -1 - ] - ], - [ - [ - /"/g, - 'sh_string', - -2 - ], - [ - /\\./g, - 'sh_specialchar', - -1 - ] - ], - [ - [ - /'/g, - 'sh_string', - -2 - ], - [ - /\\./g, - 'sh_specialchar', - -1 - ] - ] -]; diff --git a/keystone/content/common/js/shjs/sh_main.js b/keystone/content/common/js/shjs/sh_main.js deleted file mode 100644 index 1fe3ea07..00000000 --- a/keystone/content/common/js/shjs/sh_main.js +++ /dev/null @@ -1,538 +0,0 @@ -/*
-SHJS - Syntax Highlighting in JavaScript
-Copyright (C) 2007, 2008 gnombat@users.sourceforge.net
-License: http://shjs.sourceforge.net/doc/gplv3.html
-*/
-
-if (! this.sh_languages) {
- this.sh_languages = {};
-}
-var sh_requests = {};
-
-function sh_isEmailAddress(url) {
- if (/^mailto:/.test(url)) {
- return false;
- }
- return url.indexOf('@') !== -1;
-}
-
-function sh_setHref(tags, numTags, inputString) {
- var url = inputString.substring(tags[numTags - 2].pos, tags[numTags - 1].pos);
- if (url.length >= 2 && url.charAt(0) === '<' && url.charAt(url.length - 1) === '>') {
- url = url.substr(1, url.length - 2);
- }
- if (sh_isEmailAddress(url)) {
- url = 'mailto:' + url;
- }
- tags[numTags - 2].node.href = url;
-}
-
-/*
-Konqueror has a bug where the regular expression /$/g will not match at the end
-of a line more than once:
-
- var regex = /$/g;
- var match;
-
- var line = '1234567890';
- regex.lastIndex = 10;
- match = regex.exec(line);
-
- var line2 = 'abcde';
- regex.lastIndex = 5;
- match = regex.exec(line2); // fails
-*/
-function sh_konquerorExec(s) {
- var result = [''];
- result.index = s.length;
- result.input = s;
- return result;
-}
-
-/**
-Highlights all elements containing source code in a text string. The return
-value is an array of objects, each representing an HTML start or end tag. Each
-object has a property named pos, which is an integer representing the text
-offset of the tag. Every start tag also has a property named node, which is the
-DOM element started by the tag. End tags do not have this property.
-@param inputString a text string
-@param language a language definition object
-@return an array of tag objects
-*/
-function sh_highlightString(inputString, language) {
- if (/Konqueror/.test(navigator.userAgent)) {
- if (! language.konquered) {
- for (var s = 0; s < language.length; s++) {
- for (var p = 0; p < language[s].length; p++) {
- var r = language[s][p][0];
- if (r.source === '$') {
- r.exec = sh_konquerorExec;
- }
- }
- }
- language.konquered = true;
- }
- }
-
- var a = document.createElement('a');
- var span = document.createElement('span');
-
- // the result
- var tags = [];
- var numTags = 0;
-
- // each element is a pattern object from language
- var patternStack = [];
-
- // the current position within inputString
- var pos = 0;
-
- // the name of the current style, or null if there is no current style
- var currentStyle = null;
-
- var output = function(s, style) {
- var length = s.length;
- // this is more than just an optimization - we don't want to output empty <span></span> elements
- if (length === 0) {
- return;
- }
- if (! style) {
- var stackLength = patternStack.length;
- if (stackLength !== 0) {
- var pattern = patternStack[stackLength - 1];
- // check whether this is a state or an environment
- if (! pattern[3]) {
- // it's not a state - it's an environment; use the style for this environment
- style = pattern[1];
- }
- }
- }
- if (currentStyle !== style) {
- if (currentStyle) {
- tags[numTags++] = {pos: pos};
- if (currentStyle === 'sh_url') {
- sh_setHref(tags, numTags, inputString);
- }
- }
- if (style) {
- var clone;
- if (style === 'sh_url') {
- clone = a.cloneNode(false);
- }
- else {
- clone = span.cloneNode(false);
- }
- clone.className = style;
- tags[numTags++] = {node: clone, pos: pos};
- }
- }
- pos += length;
- currentStyle = style;
- };
-
- var endOfLinePattern = /\r\n|\r|\n/g;
- endOfLinePattern.lastIndex = 0;
- var inputStringLength = inputString.length;
- while (pos < inputStringLength) {
- var start = pos;
- var end;
- var startOfNextLine;
- var endOfLineMatch = endOfLinePattern.exec(inputString);
- if (endOfLineMatch === null) {
- end = inputStringLength;
- startOfNextLine = inputStringLength;
- }
- else {
- end = endOfLineMatch.index;
- startOfNextLine = endOfLinePattern.lastIndex;
- }
-
- var line = inputString.substring(start, end);
-
- var matchCache = [];
- for (;;) {
- var posWithinLine = pos - start;
-
- var stateIndex;
- var stackLength = patternStack.length;
- if (stackLength === 0) {
- stateIndex = 0;
- }
- else {
- // get the next state
- stateIndex = patternStack[stackLength - 1][2];
- }
-
- var state = language[stateIndex];
- var numPatterns = state.length;
- var mc = matchCache[stateIndex];
- if (! mc) {
- mc = matchCache[stateIndex] = [];
- }
- var bestMatch = null;
- var bestPatternIndex = -1;
- for (var i = 0; i < numPatterns; i++) {
- var match;
- if (i < mc.length && (mc[i] === null || posWithinLine <= mc[i].index)) {
- match = mc[i];
- }
- else {
- var regex = state[i][0];
- regex.lastIndex = posWithinLine;
- match = regex.exec(line);
- mc[i] = match;
- }
- if (match !== null && (bestMatch === null || match.index < bestMatch.index)) {
- bestMatch = match;
- bestPatternIndex = i;
- if (match.index === posWithinLine) {
- break;
- }
- }
- }
-
- if (bestMatch === null) {
- output(line.substring(posWithinLine), null);
- break;
- }
- else {
- // got a match
- if (bestMatch.index > posWithinLine) {
- output(line.substring(posWithinLine, bestMatch.index), null);
- }
-
- var pattern = state[bestPatternIndex];
-
- var newStyle = pattern[1];
- var matchedString;
- if (newStyle instanceof Array) {
- for (var subexpression = 0; subexpression < newStyle.length; subexpression++) {
- matchedString = bestMatch[subexpression + 1];
- output(matchedString, newStyle[subexpression]);
- }
- }
- else {
- matchedString = bestMatch[0];
- output(matchedString, newStyle);
- }
-
- switch (pattern[2]) {
- case -1:
- // do nothing
- break;
- case -2:
- // exit
- patternStack.pop();
- break;
- case -3:
- // exitall
- patternStack.length = 0;
- break;
- default:
- // this was the start of a delimited pattern or a state/environment
- patternStack.push(pattern);
- break;
- }
- }
- }
-
- // end of the line
- if (currentStyle) {
- tags[numTags++] = {pos: pos};
- if (currentStyle === 'sh_url') {
- sh_setHref(tags, numTags, inputString);
- }
- currentStyle = null;
- }
- pos = startOfNextLine;
- }
-
- return tags;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// DOM-dependent functions
-
-function sh_getClasses(element) {
- var result = [];
- var htmlClass = element.className;
- if (htmlClass && htmlClass.length > 0) {
- var htmlClasses = htmlClass.split(' ');
- for (var i = 0; i < htmlClasses.length; i++) {
- if (htmlClasses[i].length > 0) {
- result.push(htmlClasses[i]);
- }
- }
- }
- return result;
-}
-
-function sh_addClass(element, name) {
- var htmlClasses = sh_getClasses(element);
- for (var i = 0; i < htmlClasses.length; i++) {
- if (name.toLowerCase() === htmlClasses[i].toLowerCase()) {
- return;
- }
- }
- htmlClasses.push(name);
- element.className = htmlClasses.join(' ');
-}
-
-/**
-Extracts the tags from an HTML DOM NodeList.
-@param nodeList a DOM NodeList
-@param result an object with text, tags and pos properties
-*/
-function sh_extractTagsFromNodeList(nodeList, result) {
- var length = nodeList.length;
- for (var i = 0; i < length; i++) {
- var node = nodeList.item(i);
- switch (node.nodeType) {
- case 1:
- if (node.nodeName.toLowerCase() === 'br') {
- var terminator;
- if (/MSIE/.test(navigator.userAgent)) {
- terminator = '\r';
- }
- else {
- terminator = '\n';
- }
- result.text.push(terminator);
- result.pos++;
- }
- else {
- result.tags.push({node: node.cloneNode(false), pos: result.pos});
- sh_extractTagsFromNodeList(node.childNodes, result);
- result.tags.push({pos: result.pos});
- }
- break;
- case 3:
- case 4:
- result.text.push(node.data);
- result.pos += node.length;
- break;
- }
- }
-}
-
-/**
-Extracts the tags from the text of an HTML element. The extracted tags will be
-returned as an array of tag objects. See sh_highlightString for the format of
-the tag objects.
-@param element a DOM element
-@param tags an empty array; the extracted tag objects will be returned in it
-@return the text of the element
-@see sh_highlightString
-*/
-function sh_extractTags(element, tags) {
- var result = {};
- result.text = [];
- result.tags = tags;
- result.pos = 0;
- sh_extractTagsFromNodeList(element.childNodes, result);
- return result.text.join('');
-}
-
-/**
-Merges the original tags from an element with the tags produced by highlighting.
-@param originalTags an array containing the original tags
-@param highlightTags an array containing the highlighting tags - these must not overlap
-@result an array containing the merged tags
-*/
-function sh_mergeTags(originalTags, highlightTags) {
- var numOriginalTags = originalTags.length;
- if (numOriginalTags === 0) {
- return highlightTags;
- }
-
- var numHighlightTags = highlightTags.length;
- if (numHighlightTags === 0) {
- return originalTags;
- }
-
- var result = [];
- var originalIndex = 0;
- var highlightIndex = 0;
-
- while (originalIndex < numOriginalTags && highlightIndex < numHighlightTags) {
- var originalTag = originalTags[originalIndex];
- var highlightTag = highlightTags[highlightIndex];
-
- if (originalTag.pos <= highlightTag.pos) {
- result.push(originalTag);
- originalIndex++;
- }
- else {
- result.push(highlightTag);
- if (highlightTags[highlightIndex + 1].pos <= originalTag.pos) {
- highlightIndex++;
- result.push(highlightTags[highlightIndex]);
- highlightIndex++;
- }
- else {
- // new end tag
- result.push({pos: originalTag.pos});
-
- // new start tag
- highlightTags[highlightIndex] = {node: highlightTag.node.cloneNode(false), pos: originalTag.pos};
- }
- }
- }
-
- while (originalIndex < numOriginalTags) {
- result.push(originalTags[originalIndex]);
- originalIndex++;
- }
-
- while (highlightIndex < numHighlightTags) {
- result.push(highlightTags[highlightIndex]);
- highlightIndex++;
- }
-
- return result;
-}
-
-/**
-Inserts tags into text.
-@param tags an array of tag objects
-@param text a string representing the text
-@return a DOM DocumentFragment representing the resulting HTML
-*/
-function sh_insertTags(tags, text) {
- var doc = document;
-
- var result = document.createDocumentFragment();
- var tagIndex = 0;
- var numTags = tags.length;
- var textPos = 0;
- var textLength = text.length;
- var currentNode = result;
-
- // output one tag or text node every iteration
- while (textPos < textLength || tagIndex < numTags) {
- var tag;
- var tagPos;
- if (tagIndex < numTags) {
- tag = tags[tagIndex];
- tagPos = tag.pos;
- }
- else {
- tagPos = textLength;
- }
-
- if (tagPos <= textPos) {
- // output the tag
- if (tag.node) {
- // start tag
- var newNode = tag.node;
- currentNode.appendChild(newNode);
- currentNode = newNode;
- }
- else {
- // end tag
- currentNode = currentNode.parentNode;
- }
- tagIndex++;
- }
- else {
- // output text
- currentNode.appendChild(doc.createTextNode(text.substring(textPos, tagPos)));
- textPos = tagPos;
- }
- }
-
- return result;
-}
-
-/**
-Highlights an element containing source code. Upon completion of this function,
-the element will have been placed in the "sh_sourceCode" class.
-@param element a DOM <pre> element containing the source code to be highlighted
-@param language a language definition object
-*/
-function sh_highlightElement(element, language) {
- sh_addClass(element, 'sh_sourceCode');
- var originalTags = [];
- var inputString = sh_extractTags(element, originalTags);
- var highlightTags = sh_highlightString(inputString, language);
- var tags = sh_mergeTags(originalTags, highlightTags);
- var documentFragment = sh_insertTags(tags, inputString);
- while (element.hasChildNodes()) {
- element.removeChild(element.firstChild);
- }
- element.appendChild(documentFragment);
-}
-
-function sh_getXMLHttpRequest() {
- if (window.ActiveXObject) {
- return new ActiveXObject('Msxml2.XMLHTTP');
- }
- else if (window.XMLHttpRequest) {
- return new XMLHttpRequest();
- }
- throw 'No XMLHttpRequest implementation available';
-}
-
-function sh_load(language, element, prefix, suffix) {
- if (language in sh_requests) {
- sh_requests[language].push(element);
- return;
- }
- sh_requests[language] = [element];
- var request = sh_getXMLHttpRequest();
- var url = prefix + 'sh_' + language + suffix;
- request.open('GET', url, true);
- request.onreadystatechange = function () {
- if (request.readyState === 4) {
- try {
- if (! request.status || request.status === 200) {
- eval(request.responseText);
- var elements = sh_requests[language];
- for (var i = 0; i < elements.length; i++) {
- sh_highlightElement(elements[i], sh_languages[language]);
- }
- }
- else {
- throw 'HTTP error: status ' + request.status;
- }
- }
- finally {
- request = null;
- }
- }
- };
- request.send(null);
-}
-
-/**
-Highlights all elements containing source code on the current page. Elements
-containing source code must be "pre" elements with a "class" attribute of
-"sh_LANGUAGE", where LANGUAGE is a valid language identifier; e.g., "sh_java"
-identifies the element as containing "java" language source code.
-*/
-function sh_highlightDocument(prefix, suffix) {
- var nodeList = document.getElementsByTagName('pre');
- for (var i = 0; i < nodeList.length; i++) {
- var element = nodeList.item(i);
- var htmlClasses = sh_getClasses(element);
- for (var j = 0; j < htmlClasses.length; j++) {
- var htmlClass = htmlClasses[j].toLowerCase();
- if (htmlClass === 'sh_sourcecode') {
- continue;
- }
- if (htmlClass.substr(0, 3) === 'sh_') {
- var language = htmlClass.substring(3);
- if (language in sh_languages) {
- sh_highlightElement(element, sh_languages[language]);
- }
- else if (typeof(prefix) === 'string' && typeof(suffix) === 'string') {
- sh_load(language, element, prefix, suffix);
- }
- else {
- throw 'Found <pre> element with class="' + htmlClass + '", but no such language exists';
- }
- break;
- }
- }
- }
-}
diff --git a/keystone/content/common/js/shjs/sh_xml.js b/keystone/content/common/js/shjs/sh_xml.js deleted file mode 100644 index d6748ad4..00000000 --- a/keystone/content/common/js/shjs/sh_xml.js +++ /dev/null @@ -1,115 +0,0 @@ -if (! this.sh_languages) { - this.sh_languages = {}; -} -sh_languages['xml'] = [ - [ - [ - /<\?xml/g, - 'sh_preproc', - 1, - 1 - ], - [ - /<!DOCTYPE/g, - 'sh_preproc', - 3, - 1 - ], - [ - /<!--/g, - 'sh_comment', - 4 - ], - [ - /<(?:\/)?[A-Za-z](?:[A-Za-z0-9_:.-]*)(?:\/)?>/g, - 'sh_keyword', - -1 - ], - [ - /<(?:\/)?[A-Za-z](?:[A-Za-z0-9_:.-]*)/g, - 'sh_keyword', - 5, - 1 - ], - [ - /&(?:[A-Za-z0-9]+);/g, - 'sh_preproc', - -1 - ] - ], - [ - [ - /\?>/g, - 'sh_preproc', - -2 - ], - [ - /([^=" \t>]+)([ \t]*)(=?)/g, - ['sh_type', 'sh_normal', 'sh_symbol'], - -1 - ], - [ - /"/g, - 'sh_string', - 2 - ] - ], - [ - [ - /\\(?:\\|")/g, - null, - -1 - ], - [ - /"/g, - 'sh_string', - -2 - ] - ], - [ - [ - />/g, - 'sh_preproc', - -2 - ], - [ - /([^=" \t>]+)([ \t]*)(=?)/g, - ['sh_type', 'sh_normal', 'sh_symbol'], - -1 - ], - [ - /"/g, - 'sh_string', - 2 - ] - ], - [ - [ - /-->/g, - 'sh_comment', - -2 - ], - [ - /<!--/g, - 'sh_comment', - 4 - ] - ], - [ - [ - /(?:\/)?>/g, - 'sh_keyword', - -2 - ], - [ - /([^=" \t>]+)([ \t]*)(=?)/g, - ['sh_type', 'sh_normal', 'sh_symbol'], - -1 - ], - [ - /"/g, - 'sh_string', - 2 - ] - ] -]; diff --git a/keystone/content/common/js/trc/schema/controller.js b/keystone/content/common/js/trc/schema/controller.js deleted file mode 100644 index b72d4015..00000000 --- a/keystone/content/common/js/trc/schema/controller.js +++ /dev/null @@ -1,184 +0,0 @@ -/** - controller.js - - (C) 2009 Rackspace Hosting, All Rights Reserved - - This file definas a single object in global scope: - - trc.schema.controller - - The controller object is responsible for displaying a menu that - allows users to view schema source and jump to various definitions - in the schema. - **/ - - -// -// Initialization code... -// -(function() - { - // - // Make sure dependecies are defined in the global scope, throw - // an error if they are not. - // - if ((!window.trc) || - (!trc.util)) - { - throw new Error("Require trc/util.js to be loaded."); - } - - // - // We use YUI to build our controller menu make sure we have the - // proper dependecies loaded, call init when we do... - // - - function InitController() - { - trc.schema.controller._init(); - } - - trc.util.yui.loadYUIDeps (["menu"], InitController); - })(); - - -if (!trc.schema) -{ - trc.schema = new Object(); -} - -trc.schema.controller = { - // - // Internal and external links by type: - // - // type --> array of links - // - // possible types include: import, include, element, - // attribute, complextype, simpleType - // - // each link contains the following properties: - // name : the name of the link - // href : the link itself - // title : a description of the link - links : new Object(), - - // - // A single link that points to the schema index document. - // - index : null, - - // - // Our initialization function - // - _init : function() { - // - // Load the menu... - // - var controllerDiv = document.getElementById("Controller"); - var mainMenu = this._menuMarkup("mainmenu"); - - for (var linkType in this.links) - { - var subItem = this._menuItemMarkup(mainMenu, linkType, "#", null); - var subMenu = this._menuMarkup (linkType+"_subMenu"); - - var items = this.links[linkType]; - for (var i=0;i<items.length;i++) - { - this._menuItemMarkup (subMenu, - items[i].name, - items[i].href, - items[i].title); - } - subItem.item.appendChild (subMenu.main); - } - - // - // Toggle view source menu - // - this._menuItemMarkup (mainMenu, "toggle src view", - "javascript:trc.schema.sampleManager.toggleSrcView()", null); - - // - // Index schema document - // - if (this.index != null) - { - this._menuItemMarkup (mainMenu, this.index.name, - this.index.href, this.index.title); - } - - controllerDiv.appendChild (mainMenu.main); - var oMenu = new YAHOO.widget.Menu("mainmenu", {position: "static"}); - oMenu.render(); - oMenu.show(); - }, - - // - // Builds menu markup returns the associated divs in the - // properties main, body, header, footer, and list - // - _menuMarkup : function(id /*Id for main part*/) - { - // - // Build our menu div... - // - var mainDiv = document.createElement("div"); - var headerDiv = document.createElement("div"); - var bodyDiv = document.createElement("div"); - var footerDiv = document.createElement("div"); - var listDiv = document.createElement("ul"); - - mainDiv.setAttribute ("id", id); - trc.util.dom.setClassName (mainDiv, "yuimenu"); - trc.util.dom.setClassName (headerDiv, "hd"); - trc.util.dom.setClassName (bodyDiv, "bd"); - trc.util.dom.setClassName (footerDiv, "ft"); - - mainDiv.appendChild (headerDiv); - mainDiv.appendChild (bodyDiv); - mainDiv.appendChild (footerDiv); - bodyDiv.appendChild (listDiv); - - return { - main : mainDiv, - body : bodyDiv, - header : headerDiv, - footer : footerDiv, - list : listDiv - }; - }, - - // - // Adds a menu item to existing markup. - // - _menuItemMarkup : function (menu, /*Markup returned from _menuMarkup*/ - name, /* String, menu item name */ - href, /* String, menu item href */ - title /* String, title (tool tip)*/ - ) - { - var listItem = document.createElement ("li"); - var link = document.createElement ("a"); - - trc.util.dom.setClassName (listItem, "yuimenuitem"); - trc.util.dom.setClassName (link, "yuimenuitemlabel"); - - link.setAttribute ("href", href); - - if (title != null) - { - link.setAttribute ("title", title); - } - - link.appendChild (document.createTextNode(name)); - - listItem.appendChild (link); - menu.list.appendChild(listItem); - - return { - item : listItem, - anchor : link - }; - } -}; diff --git a/keystone/content/common/js/trc/schema/layoutManager.js b/keystone/content/common/js/trc/schema/layoutManager.js deleted file mode 100644 index f7a24b08..00000000 --- a/keystone/content/common/js/trc/schema/layoutManager.js +++ /dev/null @@ -1,137 +0,0 @@ -/** - layoutManager.js - - (C) 2009 Rackspace Hosting, All Rights Reserved - - This file contains code that adjusts the layout of a schema - document after a dom has been loaded. It does not modify the - global scope. -**/ - -(function() - { - // - // Make sure dependecies are defined in the global scope, throw - // an error if they are not. - // - if ((!window.trc) || - (!trc.util)) - { - throw new Error("Require trc/util.js to be loaded."); - } - - // - // This function should be called when the DOM is loaded so we - // can get to work adjusting things. - // - function InitLayoutManager() - { - layoutManager._init(); - } - trc.util.browser.addInitFunction (InitLayoutManager); - - var layoutManager={ - // - // Initialization function... - // - _init : function() - { - this._adjustMain(); - this._adjustSubElements(); - }, - - // - // Applies appropriate styles to body and other main content - // tags. - // - _adjustMain : function() - { - // - // Change the class name for the correct YUI skin name. - // - var bodyTags = document.getElementsByTagName("body"); - if (bodyTags.length == 0) - { - throw new Error ("Couldn't find body element, bad DOM?"); - } - else - { - trc.util.dom.setClassName(bodyTags[0], "yui-skin-sam"); - } - - // - // Setout the layout... - // - var docDiv = document.getElementById("doc"); - var mainDiv = document.getElementById("Main"); - - trc.util.dom.setClassName (docDiv, "yui-t1"); - docDiv.setAttribute ("id", "doc3"); - mainDiv.setAttribute ("id", "yui-main"); - - // - // Old IE browser hacks... - // - switch (trc.util.browser.detectIEVersion()) - { - // - // IE 6 does not support fixed positioning. The - // following is a little hack to get it to work. - // - // - case 6: - var controllerDiv = document.getElementById("Controller"); - controllerDiv.style.position="absolute"; - window.setInterval((function(){ - /* avoid leak by constantly querying for the - * controller. */ - var ctrlDiv = document.getElementById("Controller"); - ctrlDiv.style.top = document.documentElement.scrollTop+10; - }), 1000); - break; - - // - // The controler doesn't work **at all** in IE 7 - // don't even show it. - // - case 7: - var controllerDiv = document.getElementById("Controller"); - controllerDiv.style.display = "none"; - break; - } - }, - - // - // Adds appropriate classes for subElements... - // - _adjustSubElements : function() - { - var divs = document.getElementsByTagName("div"); - for (var i=0;i<divs.length;i++) - { - var currentClass = divs[i].getAttribute ("class"); - var newClassName = currentClass; - switch (currentClass) - { - case "SubItem" : - newClassName += " yui-gd"; - break; - case "SubItemProps" : - newClassName += " yui-gd first"; - break; - case "SubName" : - newClassName += " yui-u first"; - break; - case "SubAttributes" : - case "SubDocumentation" : - newClassName += " yui-u"; - break; - } - if (currentClass != newClassName) - { - trc.util.dom.setClassName (divs[i], newClassName); - } - } - } - }; - })(); diff --git a/keystone/content/common/js/trc/schema/sampleManager.js b/keystone/content/common/js/trc/schema/sampleManager.js deleted file mode 100644 index edb05b79..00000000 --- a/keystone/content/common/js/trc/schema/sampleManager.js +++ /dev/null @@ -1,342 +0,0 @@ -/** - schemaManager.js: - - (C) 2009 Rackspace Hosting, All Rights Reserved - - This file defines a single object in global scope: - - trc.schema.sampleManager - - The object is responsible for loading, formatting, and displaying - samples in schema files. It expects trc.util to be defined which is - provided in trc/util.js. - - Code highlighting is provided by SHJS - (http://shjs.sourceforge.net/). It should also be loaded before - this code is initialized. - - All methods/properties prepended with an underscore (_) are meant - for internal use. - **/ - -// -// Initialization code... -// -(function() - { - // - // Make sure dependecies are defined in the global scope, throw - // an error if they are not. - // - if ((!window.trc) || - (!trc.util)) - { - throw new Error("Require trc/util.js to be loaded."); - } - - // - // Make sure syntax highlighter scripts are loaded, if not then - // load them. - // - if (!window.sh_highlightDocument) - { - trc.util.dom.addStyle ("../style/shjs/sh_darkblue.css"); - - trc.util.dom.addScript ("../js/shjs/sh_main.js"); - trc.util.dom.addScript ("../js/shjs/sh_xml.js"); - trc.util.dom.addScript ("../js/shjs/sh_javascript.js"); - trc.util.dom.addScript ("../js/shjs/sh_java.js"); - } - - function InitSchemaSampleManager() - { - trc.schema.sampleManager._init(); - } - - trc.util.browser.addInitFunction(InitSchemaSampleManager); - })(); - -// -// Define trc.schema.sampleManager... -// -if (!trc.schema) -{ - trc.schema = new Object(); -} -trc.schema.sampleManager = { - // - // All sample data in an associative array: - // - // Select Element ID -> Array of sample ids. - // - samples : new Object(), - - // - // An array of code data.. - // - // Code data is defined as an object with the following - // properties: - // - // type: The mimetype of the code...href: The location of the code - // or null if it's inline - // - // id: The id of the pre that contains the code. - // - // The initial object is the source code for the current document. - // - codes : new Array({ - id : "SrcContentCode", - type : "application/xml", - href : (function() { - var ret = location.href; - if (location.hash && (location.hash.length != 0)) - { - ret = ret.replace (location.hash, ""); - } - return ret; - })() - }), - - // - // Sets up the manager, begins the loading process... - // - _init : function() { - // - // Setup an array to hold data items to load, this is used by - // the loadSample method. - // - this._toLoad = new Array(); - - for (var i=0;i<this.codes.length;i++) - { - if ((this.codes[i] != null) && - (this.codes[i].href != null)) - { - this._toLoad.push (this.codes[i]); - } - } - - // - // Loads the code text - // - this._loadCode(); - }, - - // - // Loads the next sample in the toLoad array. - // - _loadCode : function() { - if (this._toLoad.length == 0) - { - // - // All samples have been loaded, fire the loadComplete - // method. - // - this._loadComplete(); - return; - } - - var codeData = this._toLoad.pop(); - var request = trc.util.net.getHTTPRequest(); - var manager = this; - - request.onreadystatechange = function() { - if (request.readyState == 4 /* Ready */) { - if (request.status == 200 /* OKAY */) { - manager._setCodeText (codeData, request.responseText); - } - else - { - manager._setCodeText (codeData, "Could not load sample ("+request.status+") "+request.responseText); - } - manager._loadCode(); - } - }; - - request.open ("GET", codeData.href); - request.send(null); - }, - - // - // Called after all samples are loaded into the DOM. - // - _loadComplete : function() - { - // - // Normalize all code samples.. - // - this._normalizeCodeText(1, 1, 5); - - // - // Perform syntax highlighting... - // - sh_highlightDocument(); - - // - // All samples are initially hidden, show the selected - // samples... - // - for (var optionID in this.samples) - { - this.showSample(optionID); - } - - // - // We've adjusted the document, we need to setup the view so - // that we're still pointing to the hash target. - // - if (window.location.hash && - (window.location.hash.length != 0)) - { - window.location.href = window.location.hash; - } - }, - - // - // Sets code text replacing any text already existing there. - // - _setCodeText : function ( codeData /* Info of the code to set (code object) */, - code /* Code text to set (string) */) - { - // - // Preprocess the txt if nessesary... - // - var ieVersion = trc.util.browser.detectIEVersion(); - if ((ieVersion > -1) && - (ieVersion < 8)) - { - code = trc.util.text.unix2dos (code); - } - - var pre = document.getElementById(codeData.id); - var preNodes = pre.childNodes; - // - // Remove placeholder data... - // - while (preNodes.length != 0) - { - pre.removeChild (preNodes[0]); - } - - // - // Set the correct class type... - // - switch (codeData.type) - { - /* - Javascript mimetypes - */ - case 'application/json': - case 'application/javascript': - case 'application/x-javascript': - case 'application/ecmascript': - case 'text/ecmascript': - case 'text/javascript': - trc.util.dom.setClassName (pre, "sh_javascript"); - break; - /* - Not real mimetypes but this is what we'll use for Java. - */ - case 'application/java': - case 'text/java': - trc.util.dom.setClassName (pre, "sh_java"); - break; - default: - trc.util.dom.setClassName (pre, "sh_xml"); - break; - } - - // - // Add new code... - // - pre.appendChild (document.createTextNode (code)); - }, - - // - // Retrives source code text - // - _getCodeText : function (codeData /* Info for the code to get*/) - { - var pre = document.getElementById(codeData.id); - pre.normalize(); - // - // Should be a single text node after pre... - // - return pre.firstChild.nodeValue; - }, - - - // - // Normalizes text by ensuring that top, bottom, right indent - // levels are equal for all samples. - // - _normalizeCodeText : function (top, /* integer, top indent in lines */ - bottom, /* integer, bottom indent in lines */ - right /* integer, right indent in spaces */ - ) - { - for (var i=0;i<this.codes.length;i++) - { - if (this.codes[i] != null) - { - var code = this._getCodeText (this.codes[i]); - code = trc.util.text.setIndent (code, top, bottom, right); - this._setCodeText (this.codes[i], code); - } - } - }, - - // - // This event handler shows the appropriate sample given an ID - // to the select element. - // - showSample : function (selectID) /* ID of the Select element */ - { - // - // Get the selected value - // - var selected = document.getElementById(selectID); - var selectedValue = selected.options[selected.selectedIndex].value; - var samples = this.samples[selectID]; - - // - // Undisplay old samples, display selected ones. - // - for (var i=0;i<samples.length;i++) - { - if (samples[i] != null) - { - var sample = document.getElementById (samples[i]); - if (samples[i] == selectedValue) - { - sample.style.display = "block"; - } - else - { - sample.style.display = "none"; - } - } - } - }, - - // - // Toggles the current source view. If the source is displayed it - // undisplays it and vice versa. - // - toggleSrcView : function() - { - var content = document.getElementById ("Content"); - var src = document.getElementById ("SrcContent"); - - if (content.style.display != "none") - { - content.style.display = "none"; - src.style.display = "block"; - } - else - { - content.style.display = "block"; - src.style.display = "none"; - } - } -}; diff --git a/keystone/content/common/js/trc/util.js b/keystone/content/common/js/trc/util.js deleted file mode 100644 index 28267cf0..00000000 --- a/keystone/content/common/js/trc/util.js +++ /dev/null @@ -1,564 +0,0 @@ -/** - util.js: - - (C) 2009 Rackspace Hosting, All Rights Reserved - - This file defines a single object in global scope: - - trc.util - - The util object contains internal objects which contain useful - utility properties and methods. - - trc.util.browser: contains methods for browser detection. - - trc.util.dom: contains methods for manipulating the DOM. - - trc.util.text: contains methods and properties useful when working - with plain text. - - trc.util.net: contains methods for creating HTTP requests. - - trc.util.yui : contains methods for working with the YUI toolkit. - - All methods/properties prepended with an underscore (_) are meant - for internal use. -**/ - -// -// Define TRC -// -if (!window.trc) -{ - trc= new Object(); -} -trc.util = new Object(); -trc.util.browser = { - // - // Returns the current version of IE, or -1 if it's not an IE - // browser. This is one of the recomended ways of detecting IE - // see: - // - // http://msdn.microsoft.com/en-us/library/ms537509%28VS.85%29.aspx - // - detectIEVersion : function() { - var rv = -1; // Return value assumes failure. - if (navigator.appName == 'Microsoft Internet Explorer') - { - var ua = navigator.userAgent; - var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})"); - if (re.exec(ua) != null) - rv = parseFloat( RegExp.$1 ); - } - return rv; - }, - - // - // A list of functions to execute on init. - // - _initFuns : new Array(), - - // - // Has the init function event been set? - // - _initFunSet: false, - - // - // Function called when the DOM has loaded. It launches all init - // functions. - // - _onInit : function() - { - // - // Sort by order... - // - this._initFuns.sort(function(a, b){ return a.order - b.order; }); - for (var i=0;i<this._initFuns.length;i++) - { - this._initFuns[i](); - } - }, - - // - // Adds a function that should be executed when the dom is - // loaded. - // - addInitFunction : function(init, /*Function to call after dom - * is loaded*/ - - order /* An optional it specifing - * order. The bigger the int the - * later it will run. Default is - * 1.*/ - ) { - if (arguments.length < 2) - { - init.order = 1; - } - else - { - init.order = order; - } - this._initFuns.push (init); - - if (!this._initFunSet) - { - var butil = this; - function initFun() - { - return (function(){ butil._onInit(); }); - } - - // - // Try event listeners, attachEvent and if that fails use - // window.onload... - // - if (window.addEventListener) - { - window.addEventListener("load", initFun(), false); - } else if (window.attachEvent) - { - window.attachEvent ("onload", initFun()); - } else - { - window.onload = initFun(); - } - - this._initFunSet = true; - } - } -}; - -trc.util.dom = { - // - // Adds a new script tag to the current DOM. - // - addScript : function (src /* Script href */) - { - var scriptElement = document.createElement ("script"); - scriptElement.setAttribute ("type", "text/javascript"); - scriptElement.setAttribute ("src", src); - - this.addToHead (scriptElement); - }, - - // - // Adds a new stylesheet to the current DOM. - // - addStyle : function (src /* Stylesheet href */) - { - var linkElement = document.createElement ("link"); - linkElement.setAttribute ("rel", "stylesheet"); - linkElement.setAttribute ("type", "text/css"); - linkElement.setAttribute ("href", src); - - this.addToHead (linkElement); - }, - - // - // Adds a DOM node to the HTTP head element. The element is - // always added as the last child an error is thrown if the - // head element can't be found. - // - addToHead : function (node /* A DOM node */) - { - var headArray = document.getElementsByTagName("head"); - if (headArray.length == 0) - { - throw new Error("Couldn't find head element, bad DOM?"); - } - else - { - headArray[0].appendChild (node); - } - }, - - // - // Dum utility function for setting the class name of an - // element. Eventually we'll move completely to XHTML, but - // this will never work in IE 6, so for now we need this - // method for setting the class name. - // - setClassName : function (element, /* DOM Element*/ - name /* Class name to use */ - ) - { - var ieVersion = trc.util.browser.detectIEVersion(); - - if ((ieVersion > -1) && - (ieVersion < 7)) - { - element.className = name; - } - else - { - element.setAttribute ("class",name); - } - } -}; - -trc.util.text = { - // - // Useful RegExps - // - blank : new RegExp ("^\\s*$"), /* A blank string */ - indent : new RegExp ("^\\s+"), /* Line indent */ - lines : new RegExp ("$","m"), /* All lines */ - linechars : new RegExp ("(\n|\r)"), /* EOL line characters */ - tabs : new RegExp ("\t","g"), /* All tabs */ - - // - // We need this because microsoft browsers before IE 7, connot - // display pre-formatted text correctly win unix style line - // endings. - // - unix2dos : function(txt /* String */) { - //if already DOS... - if (txt.search(/\r\n/) != -1) - { - return txt; - } - return txt.replace (/\n/g, "\r\n"); - }, - - // - // Useful to normalize text. - // - dos2unix : function(txt /* String */) { - //if already unix... - if (txt.search(/\r\n/) == -1) - { - return txt; - } - - return txt.replace(/\r/g, ""); - }, - - // - // Create a string with a character repeated x times. - // - repString : function (length, /* integer, size of the string to create */ - ch /* string, The character to set the string to */ - ) - { - var ret = new String(); - for (var i=0;i<length;i++) {ret=ret.concat(ch);} - - return ret; - }, - - // - // Replace tabs in a text with strings. - // - replaceTabs : function (txt, /* String to modify */ - length /* integer, tab length in spaces */ - ) - { - var tabs = this.repString(length, " "); - return txt.replace (this.tabs, tabs); - }, - - // - // Given multi-line text returns Adjust top and bottom indent - // (in lines) and right indent (in spaces) - // - setIndent : function (txt, /* String */ - top, /* integer, top indent in lines */ - bottom, /* integer, bottom indent in lines */ - right /* integer, right indent in spaces */ - ) - { - // - // Can't indent an empty string.. - // - if (txt.length == 0) - { - return txt; - } - - // - // If not 0, bottom will be off by one... - // - if (bottom != 0) - { - bottom++; - } - - var head=this.repString (top, "\n"); - var tail=this.repString (bottom, "\n"); - var marg=this.repString (right, " "); - var ntxt = this.dos2unix(txt); - var ntxt = this.replaceTabs (ntxt, 8); - var lines = ntxt.split (this.lines); - var origIndent=Number.MAX_VALUE; - var origIndentStr; - - // - // Look up indent. - // - for (var i=0;i<lines.length;i++) - { - // - // Remove EOL characters... - // - lines[i] = lines[i].replace (this.linechars, ""); - - // - // Ignore blank lines - // - if (lines[i].match(this.blank) != null) - { - continue; - } - - // - // Detect the indent if any... - // - var result = lines[i].match(this.indent); - if (result == null) - { - origIndent = 0; - origIndentStr = ""; - } - else if (result[0].length < origIndent) - { - origIndent = result[0].length; - origIndentStr = result[0]; - } - } - - // - // This implys all line are blank...can't indent. - // - if (origIndent == Number.MAX_VALUE) - { - return txt; - } - - if (origIndent != 0) - { - var regExStr = "^"; - for (var i=0;i<origIndent;i++) - { - regExStr=regExStr.concat("\\s"); - } - var indent = new RegExp(regExStr); - for (var i=0;i<lines.length;i++) - { - lines[i] = lines[i].replace(indent,marg); - } - } - else - { - for (var i=0;i<lines.length;i++) - { - lines[i] = marg.concat (lines[i]); - } - } - - // - // Remove top... - // - while (lines.length != 0) - { - if (lines[0].match(this.blank)) - { - lines.shift(); - } - else - { - break; - } - } - - // - // Remove bottom... - // - while (lines.length != 0) - { - if (lines[lines.length-1].match(this.blank)) - { - lines.pop(); - } - else - { - break; - } - } - - var indented = lines.join("\n"); - indented=head.concat(indented, tail); - - return indented; - } -}; - -trc.util.net = { - // - // A list of possible factories for creating an XMLHTTPRequest - // - _HTTPReqFactories : - [ - function() { return new XMLHttpRequest(); }, - function() { return new ActiveXObject("Msxml2.XMLHTTP"); }, - function() { return new ActiveXObject("Microsoft.XMLHTTP"); } - ], - - // - // A cached XMLHTTPRequest factory that we know works in this - // browser - // - _HTTPReqFactory : null, - - // - // Provides a way of getting an HTTPRequest object in a - // platform independent manner - // - getHTTPRequest : function() - { - // - // Use cache if available.. - // - if (this._HTTPReqFactory != null) return this._HTTPReqFactory(); - - // - // Search for a factory.. - // - for (var i=0; i< this._HTTPReqFactories.length; i++) - { - try { - var factory = this._HTTPReqFactories[i]; - var request = factory(); - if (request != null) - { - this._HTTPReqFactory = factory; - return request; - } - } catch (e) { - continue; - } - } - - // - // Looks like we don't have support for XMLHttpRequest... - // - this._HTTPReqFactory = function() {throw new Error("XMLHttpRequest not supported");} - this._HTTPReqFactory(); - return; - } -}; - - -// -// Init code for trc.util.yui... -// -(function() - { - // - // Menu make sure we have the YUI loader as it's used by our - // init function to load YUI components. - // - if (!window.YAHOO) - { - // - // We are currently using YUI on YAHOO!'s servers we may - // want to change this. - // - var YUI_BASE="http://yui.yahooapis.com/2.7.0/"; - - trc.util.dom.addScript (YUI_BASE+"build/yuiloader/yuiloader-min.js"); - } - - function InitYUIUtil() - { - trc.util.yui._init(); - } - trc.util.browser.addInitFunction (InitYUIUtil); - })(); - -trc.util.yui = { - // - // A list of dependecies to be passed to the YUI loader. This is - // essentially a hash set: dep->dep. - // - _deps : new Object(), - - // - // An array of callback functions, these should be called when all - // dependecies are loaded. - // - _callbacks : new Array(), - - // - // The init function simply calls the YUI loader... - // - _init : function() { - var yuiUtil = this; - - // - // It takes safari a while to load the YUI Loader if it hasn't - // loaded yet keep trying at 1/4 second intervals - // - if (!window.YAHOO) - { - window.setTimeout (function() { - yuiUtil._init(); - }, 250); - return; - } - - // - // Collect requirements... - // - var required = new Array(); - for (var req in this._deps) - { - required.push (req); - } - - // - // Load YUI dependecies... - // - var loader = new YAHOO.util.YUILoader({ - require: required, - loadOptional: true, - filter: "RAW", - onSuccess: function() { - yuiUtil._depsLoaded(); - }, - timeout: 10000, - combine: true - }); - loader.insert(); - }, - - // - // Called after all dependecies have been loaded - // - _depsLoaded : function() { - // - // Dependecies are loaded let everyone know. - // - for (var i=0;i<this._callbacks.length;i++) - { - this._callbacks[i](); - } - }, - - // - // Request that one or more YUI dependecies are loaded. - // - loadYUIDeps : function (deps, /*An array of dep strings */ - callback /*A function to call when deps are loaded*/ - ) - { - for (var i=0;i<deps.length;i++) - { - this._deps[deps[i]] = deps[i]; - } - if (callback != null) - { - this._callbacks.push (callback); - } - } -}; diff --git a/keystone/content/common/samples/RAX-KSADM-userWithPassword.json b/keystone/content/common/samples/RAX-KSADM-userWithPassword.json deleted file mode 100644 index 3f852ebe..00000000 --- a/keystone/content/common/samples/RAX-KSADM-userWithPassword.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "user": { - "username": "jqsmith", - "password": "Secret0", - "email": "john.smith@example.org", - "enabled": true - } -}
\ No newline at end of file diff --git a/keystone/content/common/samples/RAX-KSADM-userWithPassword.xml b/keystone/content/common/samples/RAX-KSADM-userWithPassword.xml deleted file mode 100644 index 68b82692..00000000 --- a/keystone/content/common/samples/RAX-KSADM-userWithPassword.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<user xmlns="http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:type="UserWithPassword" - enabled="true" email="john.smith@example.org" - username="jqsmith" password="Secret0" />
\ No newline at end of file diff --git a/keystone/content/common/samples/RAX-KSGRP-groups.json b/keystone/content/common/samples/RAX-KSGRP-groups.json deleted file mode 100644 index 7e5ac29d..00000000 --- a/keystone/content/common/samples/RAX-KSGRP-groups.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "RAX-KSGRP:groups": [ - { - "id": "test_global_group_add", - "description": "A description ..." - } - ], - "RAX-KSGRP:groups_links": [] -} diff --git a/keystone/content/common/samples/RAX-KSGRP-groups.xml b/keystone/content/common/samples/RAX-KSGRP-groups.xml deleted file mode 100644 index 3a6a43ba..00000000 --- a/keystone/content/common/samples/RAX-KSGRP-groups.xml +++ /dev/null @@ -1,5 +0,0 @@ -<groups xmlns="http://docs.rackspace.com/identity/api/ext/RAX-KSGRP/v1.0"> - <group xmlns="http://docs.rackspace.com/identity/api/ext/RAX-KSGRP/v1.0" id="test_global_group_add"> - <description>A Description of the group</description> - </group> -</groups> diff --git a/keystone/content/common/samples/RAX-KSQA-secretQA.json b/keystone/content/common/samples/RAX-KSQA-secretQA.json deleted file mode 100644 index 6baf6e32..00000000 --- a/keystone/content/common/samples/RAX-KSQA-secretQA.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "RAX-KSQA:secretQA": { - "question": "What is the color of my eyes?", - "answer": "Leonardo Da Vinci" - } -} diff --git a/keystone/content/common/samples/RAX-KSQA-secretQA.xml b/keystone/content/common/samples/RAX-KSQA-secretQA.xml deleted file mode 100644 index c6570af6..00000000 --- a/keystone/content/common/samples/RAX-KSQA-secretQA.xml +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<secretQA xmlns="http://docs.rackspace.com/identity/api/ext/RAX-KSQA/v1.0" - question="What is the color of my eyes?" - answer="Leonardo Da Vinci" /> - - diff --git a/keystone/content/common/samples/apiKeyCredentials.json b/keystone/content/common/samples/apiKeyCredentials.json deleted file mode 100644 index 4b59ac3b..00000000 --- a/keystone/content/common/samples/apiKeyCredentials.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "RAX-KSKEY:apiKeyCredentials": { - "username": "test_user", - "apiKey": "aaaaa-bbbbb-ccccc-12345678" - } -} diff --git a/keystone/content/common/samples/apiKeyCredentials.xml b/keystone/content/common/samples/apiKeyCredentials.xml deleted file mode 100644 index 7a4b0fdb..00000000 --- a/keystone/content/common/samples/apiKeyCredentials.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<apiKeyCredentials - xmlns="http://docs.rackspace.com/identity/api/ext/RAX-KSKEY/v1.0" - username="testuser" - apiKey="aaaaa-bbbbb-ccccc-12345678"/> - diff --git a/keystone/content/common/samples/auth.json b/keystone/content/common/samples/auth.json deleted file mode 100644 index 6730360f..00000000 --- a/keystone/content/common/samples/auth.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "access": { - "token": { - "id": "ab48a9efdfedb23ty3494", - "expires": "2010-11-01T03:32:15-05:00", - "tenant": { - "id": "t1000", - "name": "My Project" - }, - "tenants": [{ - "id": "t1000", - "name": "My Project" - }] - }, - "user": { - "id": "u123", - "name": "jqsmith", - "roles": [ - { - "id": "100", - "name": "compute:admin" - }, - { - "id": "101", - "name": "object-store:admin", - "tenantId": "t1000" - } - ], - "roles_links": [] - }, - "serviceCatalog": [ - { - "name": "Cloud Servers", - "type": "compute", - "endpoints": [ - { - "id": "1", - "tenantId": "t1000", - "publicURL": "https://compute.north.host.com/v1/t1000", - "internalURL": "https://compute.north.internal/v1/t1000", - "region": "North", - "versionId": "1", - "versionInfo": "https://compute.north.host.com/v1/", - "versionList": "https://compute.north.host.com/" - }, - { - "id": "2", - "tenantId": "t1000", - "publicURL": "https://compute.north.host.com/v1.1/t1000", - "internalURL": "https://compute.north.internal/v1.1/t1000", - "region": "North", - "versionId": "1.1", - "versionInfo": "https://compute.north.host.com/v1.1/", - "versionList": "https://compute.north.host.com/" - } - ], - "endpoints_links": [] - }, - { - "name": "Cloud Files", - "type": "object-store", - "endpoints": [ - { - "tenantId": "t1000", - "publicURL": "https://storage.north.host.com/v1/t1000", - "internalURL": "https://storage.north.internal/v1/t1000", - "region": "North", - "versionId": "1", - "versionInfo": "https://storage.north.host.com/v1/", - "versionList": "https://storage.north.host.com/" - }, - { - "tenantId": "t1000", - "publicURL": "https://storage.south.host.com/v1/t1000", - "internalURL": "https://storage.south.internal/v1/t1000", - "region": "South", - "versionId": "1", - "versionInfo": "https://storage.south.host.com/v1/", - "versionList": "https://storage.south.host.com/" - } - ] - }, - { - "name": "DNS-as-a-Service", - "type": "dnsextension:dns", - "endpoints": [ - { - "tenantId": "t1000", - "publicURL": "https://dns.host.com/v2.0/t1000", - "versionId": "2.0", - "versionInfo": "https://dns.host.com/v2.0/", - "versionList": "https://dns.host.com/" - } - ] - } - ] - } -} diff --git a/keystone/content/common/samples/auth.xml b/keystone/content/common/samples/auth.xml deleted file mode 100644 index 21efa79d..00000000 --- a/keystone/content/common/samples/auth.xml +++ /dev/null @@ -1,75 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<access xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns="http://docs.openstack.org/identity/api/v2.0"> - <token id="ab48a9efdfedb23ty3494" expires="2010-11-01T03:32:15-05:00"> - <tenant id="t1000" name="My Project" /> - </token> - <user id="u123" name="jqsmith"> - <roles> - <role id="100" name="compute:admin"/> - <role id="101" name="object-store:admin" tenantId="t1000"/> - </roles> - </user> - <serviceCatalog> - <service type="compute" name="Cloud Servers"> - <endpoint - id="1" - tenantId="t1000" - region="North" - publicURL="https://compute.north.host.com/v1/t1000" - internalURL="https://compute.north.host.internal/v1/t1000"> - <version - id="1" - info="https://compute.north.host.com/v1/" - list="https://compute.north.host.com/" - /> - </endpoint> - <endpoint - id="2" - tenantId="t1000" - region="North" - publicURL="https://compute.north.host.com/v1.1/t1000" - internalURL="https://compute.north.host.internal/v1.1/t1000"> - <version - id="1.1" - info="https://compute.north.host.com/v1.1/" - list="https://compute.north.host.com/" /> - </endpoint> - </service> - <service type="object-store" name="Cloud Files"> - <endpoint - id="3" - tenantId="t1000" - region="North" - publicURL="https://storage.north.host.com/v1/t1000" - internalURL="https://storage.north.host.internal/v1/t1000"> - <version - id="1" - info="https://storage.north.host.com/v1/" - list="https://storage.north.host.com/" /> - </endpoint> - <endpoint - id="4" - tenantId="t1000" - region="South" - publicURL="https://storage.south.host.com/v1/t1000" - internalURL="https://storage.south.host.internal/v1/t1000"> - <version - id="1" - info="https://storage.south.host.com/v1/" - list="https://storage.south.host.com/" /> - </endpoint> - </service> - <service type="dnsextension:dns" name="DNS-as-a-Service"> - <endpoint - id="5" - tenantId="t1000" - publicURL="https://dns.host.com/v2.0/t1000"> - <version - id="2.0" - info="https://dns.host.com/v2.0/" - list="https://dns.host.com/" /> - </endpoint> - </service> - </serviceCatalog> -</access> diff --git a/keystone/content/common/samples/auth_credentials-OS-KSEC2.json b/keystone/content/common/samples/auth_credentials-OS-KSEC2.json deleted file mode 100644 index 45c987e6..00000000 --- a/keystone/content/common/samples/auth_credentials-OS-KSEC2.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "auth": { - "OS-KSEC2:ec2Credentials": { - "username": "test_user", - "secret": "aaaaa", - "signature": "bbb" - }, - "tenantId": "77654" - } -} diff --git a/keystone/content/common/samples/auth_credentials-OS-KSEC2.xml b/keystone/content/common/samples/auth_credentials-OS-KSEC2.xml deleted file mode 100644 index ec4f3314..00000000 --- a/keystone/content/common/samples/auth_credentials-OS-KSEC2.xml +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<auth - xmlns="http://docs.openstack.org/identity/api/v2.0" - tenantId="1234"> - <ec2Credentials - xmlns="http://docs.openstack.org/identity/api/ext/OS-KSEC2/v1.0" - username="testuser" - key="aaaaa" - signature="bbbbb"/> -</auth> diff --git a/keystone/content/common/samples/auth_credentials-RAX-KSKEY.json b/keystone/content/common/samples/auth_credentials-RAX-KSKEY.json deleted file mode 100644 index 29f78dcd..00000000 --- a/keystone/content/common/samples/auth_credentials-RAX-KSKEY.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "auth": { - "RAX-KSKEY:apiKeyCredentials": { - "username": "test_user", - "apiKey": "aaaaa-bbbbb-ccccc-12345678" - }, - "tenantId": "1234" - } -} diff --git a/keystone/content/common/samples/auth_credentials-RAX-KSKEY.xml b/keystone/content/common/samples/auth_credentials-RAX-KSKEY.xml deleted file mode 100644 index a4284f95..00000000 --- a/keystone/content/common/samples/auth_credentials-RAX-KSKEY.xml +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<auth xmlns="http://docs.openstack.org/identity/api/v2.0"> - <apiKeyCredentials - xmlns="http://docs.rackspace.com/identity/api/ext/RAX-KSKEY/v1.0" - username="testuser" - apiKey="aaaaa-bbbbb-ccccc-12345678"/> -</auth> diff --git a/keystone/content/common/samples/auth_credentials.json b/keystone/content/common/samples/auth_credentials.json deleted file mode 100644 index 00fb7826..00000000 --- a/keystone/content/common/samples/auth_credentials.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "auth": { - "passwordCredentials": { - "username": "test_user", - "password": "mypass" - }, - "tenantName": "customer-x" - } -} diff --git a/keystone/content/common/samples/auth_credentials.xml b/keystone/content/common/samples/auth_credentials.xml deleted file mode 100644 index 6621b83a..00000000 --- a/keystone/content/common/samples/auth_credentials.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<auth xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns="http://docs.openstack.org/identity/api/v2.0" - tenantName="customer-x"> - <passwordCredentials username="test_user" password="test"/> -</auth> diff --git a/keystone/content/common/samples/auth_with_token.json b/keystone/content/common/samples/auth_with_token.json deleted file mode 100644 index 7a765eba..00000000 --- a/keystone/content/common/samples/auth_with_token.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "auth": { - "tenantName": "customer-x", - "token": { - "id": "abcdefghijk" - } - } -} diff --git a/keystone/content/common/samples/auth_with_token.xml b/keystone/content/common/samples/auth_with_token.xml deleted file mode 100644 index 5190f62a..00000000 --- a/keystone/content/common/samples/auth_with_token.xml +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<auth xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns="http://docs.openstack.org/identity/api/v2.0" - tenantName="customer-x"> - <token id="abcdefghijk" /> -</auth> - diff --git a/keystone/content/common/samples/authwithgroups.json b/keystone/content/common/samples/authwithgroups.json deleted file mode 100644 index d364ebb4..00000000 --- a/keystone/content/common/samples/authwithgroups.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "access": { - "token": { - "id": "asdasdasd-adsasdads-asdasdasd-adsadsasd", - "expires": "2010-11-01T03:32:15-05:00" - }, - "user": { - "id": "123", - "name": "testName", - "roles": [ - { - "id": "234", - "name": "compute:admin" - }, - { - "id": "235", - "name": "object-store:admin", - "tenantId": "1" - } - ], - "roles_links": [], - "RAX-KSGRP:groups": [ - { - "id": "test_global_group_add", - "description": "A description ..." - } - ], - "RAX-KSGRP:groups_links": [] - }, - "serviceCatalog": [ - { - "name": "Cloud Servers", - "type": "compute", - "endpoints": [ - { - "publicURL": "https://compute.north.host/v1/1234", - "internalURL": "https://compute.north.host/v1/1234", - "region": "North", - "tenantId": "1234", - "versionId": "1.0", - "versionInfo": "https://compute.north.host/v1.0/", - "versionList": "https://compute.north.host/" - }, - { - "publicURL": "https://compute.north.host/v1.1/3456", - "internalURL": "https://compute.north.host/v1.1/3456", - "region": "North", - "tenantId": "3456", - "versionId": "1.1", - "versionInfo": "https://compute.north.host/v1.1/", - "versionList": "https://compute.north.host/" - } - ], - "endpoints_links": [] - }, - { - "name": "Cloud Files", - "type": "object-store", - "endpoints": [ - { - "publicURL": "https://compute.north.host/v1/blah-blah", - "internalURL": "https://compute.north.host/v1/blah-blah", - "region": "South", - "tenantId": "1234", - "versionId": "1.0", - "versionInfo": "uri", - "versionList": "uri" - }, - { - "publicURL": "https://compute.north.host/v1.1/blah-blah", - "internalURL": "https://compute.north.host/v1.1/blah-blah", - "region": "South", - "tenantId": "1234", - "versionId": "1.1", - "versionInfo": "https://compute.north.host/v1.1/", - "versionList": "https://compute.north.host/" - } - ], - "endpoints_links": [ - { - "rel": "next", - "href": "https://identity.north.host/v2.0/endpoints?marker=2" - } - ] - } - ], - "serviceCatalog_links": [ - { - "rel": "next", - "href": "https://identity.host/v2.0/endpoints?session=2hfh8Ar&marker=2" - } - ] - } -} diff --git a/keystone/content/common/samples/authwithgroups.xml b/keystone/content/common/samples/authwithgroups.xml deleted file mode 100644 index f8c6981b..00000000 --- a/keystone/content/common/samples/authwithgroups.xml +++ /dev/null @@ -1,70 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<access xmlns="http://docs.openstack.org/identity/api/v2.0"> - <token expires="2010-11-01T03:32:15-05:00" - id="ab48a9efdfedb23ty3494"/> - <serviceCatalog> - <service type="compute" name="Computers in the Cloud"> - <endpoint region="North" tenantId="1" - publicURL="https://north.compute.public.com/v2.0/1234" - internalURL="https://north.compute.internal.com/v2.0/1234"> - <version id="2.0" - info="https://north.compute.public.com/v2.0/" - list="https://north.compute.public.com/"/> - </endpoint> - <endpoint - region="South" - tenantId="3456" - publicURL="https://south.compute.public.com/v2.0/3456" - internalURL="https://south.compute.internal.com/v2.0/3456"> - <version - id="2.0" - info="https://south.compute.public.com/v2.0/" - list="https://south.compute.public.com/" /> - </endpoint> - </service> - <service type="object-store" name="HTTP Object Store"> - <endpoint - region="North" - tenantId="1234" - publicURL="https://north.object-store.public.com/v1/1234" - internalURL="https://north.object-store.internal.com/v1/1234"> - <version - id="1" - info="https://north.object-store.public.com/v1/" - list="https://north.object-store.public.com/" /> - </endpoint> - <endpoint - region="South" - tenantId="3456" - publicURL="https://south.object-store.public.com/v2.0/3456" - internalURL="https://south.object-store.internal.com/v2.0/3456"> - <version - id="2.0" - info="https://south.object-store.public.com/v1/" - list="https://south.object-store.public.com/" /> - </endpoint> - </service> - <service type="dns" name="DNS-as-a-Service"> - <endpoint - publicURL="https://dns.public.com/v2.0/blah-blah"> - <version - id="2.0" - info="https://dns.public.com/v2.0/" - list="https://dns.public.com/" /> - </endpoint> - </service> - </serviceCatalog> - <user xmlns="http://docs.openstack.org/identity/api/v2.0" - id="123" username="jqsmith"> - <roles xmlns="http://docs.openstack.org/identity/api/v2.0"> - <role id="123" name="Admin" tenantId="1234" description="All Access" /> - <role id="234" name="object-store:admin" tenantId="1"/> - - </roles> - <groups xmlns="http://docs.rackspace.com/identity/api/ext/RAX-KSGRP/v1.0"> - <group id="test_global_group_add"> - <description>A Description of the group</description> - </group> - </groups> - </user> -</auth> diff --git a/keystone/content/common/samples/choices.json b/keystone/content/common/samples/choices.json deleted file mode 100644 index 259b8c42..00000000 --- a/keystone/content/common/samples/choices.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "choices": [ - { - "id": "v1.0", - "status": "DEPRECATED", - "links": [ - { - "rel": "self", - "href": "http://identity.api.openstack.org/v1.0" - } - ], - "media-types": { - "values": [ - { - "base": "application/xml", - "type": "application/vnd.openstack.identity+xml;version=1.0" - }, - { - "base": "application/json", - "type": "application/vnd.openstack.identity+json;version=1.0" - } - ] - } - }, - { - "id": "v1.1", - "status": "CURRENT", - "links": [ - { - "rel": "self", - "href": "http://identity.api.openstack.org/v1.1" - } - ], - "media-types": { - "values": [ - { - "base": "application/xml", - "type": "application/vnd.openstack.identity+xml;version=1.1" - }, - { - "base": "application/json", - "type": "application/vnd.openstack.identity+json;version=1.1" - } - ] - } - }, - { - "id": "v2.0", - "status": "BETA", - "links": [ - { - "rel": "self", - "href": "http://identity.api.openstack.org/v2.0" - } - ], - "media-types": { - "values": [ - { - "base": "application/xml", - "type": "application/vnd.openstack.identity+xml;version=2.0" - }, - { - "base": "application/json", - "type": "application/vnd.openstack.identity+json;version=2.0" - } - ] - } - } - ], - "choices_links": "" -} diff --git a/keystone/content/common/samples/choices.xml b/keystone/content/common/samples/choices.xml deleted file mode 100644 index 834a1987..00000000 --- a/keystone/content/common/samples/choices.xml +++ /dev/null @@ -1,38 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<choices - xmlns="http://docs.openstack.org/common/api/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom"> - <version id="v1.0" status="DEPRECATED"> - <media-types> - <media-type - base="application/xml" - type="application/vnd.openstack.identity+xml;version=1.0" /> - <media-type - base="application/json" - type="application/vnd.openstack.identity+json;version=1.0" /> - </media-types> - <atom:link rel="self" href="http://identity.api.openstack.org/v1.0" /> - </version> - <version id="v1.1" status="CURRENT"> - <media-types> - <media-type - base="application/xml" - type="application/vnd.openstack.identity+xml;version=1.1" /> - <media-type - base="application/json" - type="application/vnd.openstack.identity+json;version=1.1" /> - </media-types> - <atom:link rel="self" href="http://identity.api.openstack.org/v1.1" /> - </version> - <version id="v2.0" status="BETA"> - <media-types> - <media-type - base="application/xml" - type="application/vnd.openstack.identity+xml;version=2.0" /> - <media-type - base="application/json" - type="application/vnd.openstack.identity+json;version=2.0" /> - </media-types> - <atom:link rel="self" href="http://identity.api.openstack.org/v2.0" /> - </version> -</choices> diff --git a/keystone/content/common/samples/credentials.json b/keystone/content/common/samples/credentials.json deleted file mode 100644 index 584fc3a1..00000000 --- a/keystone/content/common/samples/credentials.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "credentials": [ - { - "passwordCredentials": { - "username": "test_user", - "password": "mypass" - } - } - ], - "credentials_links": [] -} diff --git a/keystone/content/common/samples/credentials.xml b/keystone/content/common/samples/credentials.xml deleted file mode 100644 index 8efa9f2e..00000000 --- a/keystone/content/common/samples/credentials.xml +++ /dev/null @@ -1,5 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<credentials xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns="http://docs.openstack.org/identity/api/v2.0"> - <passwordCredentials username="test_user" password="test"/> -</credentials> diff --git a/keystone/content/common/samples/credentialswithapikey.json b/keystone/content/common/samples/credentialswithapikey.json deleted file mode 100644 index 8ab8f2cd..00000000 --- a/keystone/content/common/samples/credentialswithapikey.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "credentials": [ - { - "passwordCredentials": { - "username": "test_user", - "password": "mypass" - } - }, - { - "RAX-KSKEY:apiKeyCredentials": { - "username": "test_user", - "apiKey": "aaaaa-bbbbb-ccccc-12345678" - } - } - ], - "credentials_links": [] -} diff --git a/keystone/content/common/samples/credentialswithapikey.xml b/keystone/content/common/samples/credentialswithapikey.xml deleted file mode 100644 index 762016e8..00000000 --- a/keystone/content/common/samples/credentialswithapikey.xml +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<credentials xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns="http://docs.openstack.org/identity/api/v2.0"> - <passwordCredentials username="test_user" password="test"/> - <apiKeyCredentials - xmlns="http://docs.rackspace.com/identity/api/ext/RAX-KSKEY/v1.0" - username="testuser" - apiKey="aaaaa-bbbbb-ccccc-12345678"/> -</credentials> diff --git a/keystone/content/common/samples/credentialswithec2.json b/keystone/content/common/samples/credentialswithec2.json deleted file mode 100644 index 8da6844e..00000000 --- a/keystone/content/common/samples/credentialswithec2.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "credentials": [ - { - "passwordCredentials": { - "username": "test_user", - "password": "mypass" - } - }, - { - "OS-KSEC2:ec2Credentials": { - "username": "test_user", - "secret": "aaaaa", - "signature": "bbb" - } - } - ], - "credentials_links": [] -} diff --git a/keystone/content/common/samples/credentialswithec2.xml b/keystone/content/common/samples/credentialswithec2.xml deleted file mode 100644 index b6ded2a3..00000000 --- a/keystone/content/common/samples/credentialswithec2.xml +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<credentials xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns="http://docs.openstack.org/identity/api/v2.0"> - <passwordCredentials username="test_user" password="test"/> - <ec2Credentials xmlns="http://docs.openstack.org/identity/api/ext/OS-KSEC2/v1.0" - username="testuser" key="aaaaa" signature="bbbbb"/> -</credentials> diff --git a/keystone/content/common/samples/credentialswiths3.json b/keystone/content/common/samples/credentialswiths3.json deleted file mode 100644 index fb286205..00000000 --- a/keystone/content/common/samples/credentialswiths3.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "credentials":[{ - "passwordCredentials":{ - "username":"test_user", - "password":"mypass" - } - }, - { - "OS-KSS3:s3Credentials":{ - "username":"test_user", - "secret":"aaaaa", - "signature":"bbb" - } - } - ], - "credentials_links":[] -} diff --git a/keystone/content/common/samples/credentialswiths3.xml b/keystone/content/common/samples/credentialswiths3.xml deleted file mode 100644 index 4c482d8a..00000000 --- a/keystone/content/common/samples/credentialswiths3.xml +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<credentials xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns="http://docs.openstack.org/identity/api/v2.0"> - <passwordCredentials username="test_user" password="test"/> - <s3Credentials xmlns="http://docs.openstack.org/identity/api/ext/OS-KSS3/v1.0" - username="testuser" key="aaaaa" signature="bbbbb"/> -</credentials> diff --git a/keystone/content/common/samples/ec2Credentials.json b/keystone/content/common/samples/ec2Credentials.json deleted file mode 100644 index e72ee28c..00000000 --- a/keystone/content/common/samples/ec2Credentials.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "OS-KSEC2:ec2Credentials": { - "username": "test_user", - "secret": "aaaaa", - "signature": "bbb" - } -} diff --git a/keystone/content/common/samples/ec2Credentials.xml b/keystone/content/common/samples/ec2Credentials.xml deleted file mode 100644 index e36f231c..00000000 --- a/keystone/content/common/samples/ec2Credentials.xml +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - <ec2Credentials - xmlns="http://docs.openstack.org/identity/api/ext/OS-KSEC2/v1.0" - username="testuser" - key="aaaaa" - signature="bbbbb"/> - diff --git a/keystone/content/common/samples/endpoint.json b/keystone/content/common/samples/endpoint.json deleted file mode 100644 index aad05b62..00000000 --- a/keystone/content/common/samples/endpoint.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "endpoint": { - "id": 1, - "tenantId": 1, - "region": "North", - "type": "compute", - "publicURL": "https://compute.north.public.com/v1", - "internalURL": "https://compute.north.internal.com/v1", - "adminURL": "https://compute.north.internal.com/v1", - "versionId": "1", - "versionInfo": "https://compute.north.public.com/v1/", - "versionList": "https://compute.north.public.com/" - } -} diff --git a/keystone/content/common/samples/endpoint.xml b/keystone/content/common/samples/endpoint.xml deleted file mode 100644 index cd09b24a..00000000 --- a/keystone/content/common/samples/endpoint.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<endpoint - id="1" - tenantId="1" - type="compute" - name="Compute" - region="North" - publicURL="https://compute.north.public.com/v1" - internalURL="https://compute.north.internal.com/v1" - adminURL="https://compute.north.internal.com/v1" - xmlns="http://docs.openstack.org/identity/api/v2.0"> - <version - id="1" - info="https://compute.north.public.com/v1/" - list="https://compute.north.public.com/" - /> -</endpoint>
\ No newline at end of file diff --git a/keystone/content/common/samples/endpointTemplate.json b/keystone/content/common/samples/endpointTemplate.json deleted file mode 100644 index 4cf6c4d1..00000000 --- a/keystone/content/common/samples/endpointTemplate.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "OS-KSCATALOG:endpointTemplate": { - "id": 1, - "region": "North", - "global": true, - "name": "nova", - "type": "compute", - "publicURL": "https://compute.north.public.com/v1", - "internalURL": "https://compute.north.internal.com/v1", - "versionId": "1", - "versionInfo": "https://compute.north.public.com/v1/", - "versionList": "https://compute.north.public.com/", - "enabled": true - } -} diff --git a/keystone/content/common/samples/endpointTemplate.xml b/keystone/content/common/samples/endpointTemplate.xml deleted file mode 100644 index 37f98091..00000000 --- a/keystone/content/common/samples/endpointTemplate.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<endpointTemplate - xmlns="http://docs.openstack.org/identity/api/ext/OS-KSCATALOG/v1.0" - id="1" - region="North" - global="true" - type="compute" - name="Nova" - publicURL="https://service-public.com/v1" - internalURL="https://service-internal.com/v1" - enabled="true"> - <version - id="1" - info="https://compute.north.public.com/v1/" - list="https://compute.north.public.com/" - /> -</endpointTemplate>
\ No newline at end of file diff --git a/keystone/content/common/samples/endpointTemplateWithOnlyId.json b/keystone/content/common/samples/endpointTemplateWithOnlyId.json deleted file mode 100644 index e2fa47f7..00000000 --- a/keystone/content/common/samples/endpointTemplateWithOnlyId.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "OS-KSCATALOG:endpointTemplate": { - "id": 1 - } -} diff --git a/keystone/content/common/samples/endpointTemplateWithOnlyId.xml b/keystone/content/common/samples/endpointTemplateWithOnlyId.xml deleted file mode 100644 index 6379b3ae..00000000 --- a/keystone/content/common/samples/endpointTemplateWithOnlyId.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<endpointTemplate - xmlns="http://docs.openstack.org/identity/api/ext/OS-KSCATALOG/v1.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:type="EndpointTemplateWithOnlyId" - id="1"/> diff --git a/keystone/content/common/samples/endpointTemplates.json b/keystone/content/common/samples/endpointTemplates.json deleted file mode 100644 index ab683ae8..00000000 --- a/keystone/content/common/samples/endpointTemplates.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "OS-KSCATALOG:endpointsTemplates": [ - { - "id": 1, - "region": "North", - "global": true, - "type": "compute", - "publicURL": "https://compute.north.public.com/v1", - "internalURL": "https://compute.north.internal.com/v1", - "versionId": "1", - "versionInfo": "https://compute.north.public.com/v1/", - "versionList": "https://compute.north.public.com/", - "enabled": true - }, - { - "id": 2, - "region": "South", - "type": "compute", - "publicURL": "https://compute.south.public.com/v1", - "internalURL": "https://compute.south.internal.com/v1", - "versionId": "1", - "versionInfo": "https://compute.south.public.com/v1/", - "versionList": "https://compute.south.public.com/", - "enabled": false - }, - { - "id": 3, - "region": "North", - "global": true, - "type": "object-store", - "publicURL": "https://object-store.north.public.com/v1.0", - "versionId": "1.0", - "versionInfo": "https://object-store.north.public.com/v1.0/", - "versionList": "https://object-store.north.public.com/", - "enabled": true - }, - { - "id": 4, - "region": "South", - "type": "object-store", - "publicURL": "https://object-store.south.public.com/v2", - "versionId": "2", - "versionInfo": "https://object-store.south.public.com/v2/", - "versionList": "https://object-store.south.public.com/", - "enabled": true - }, - { - "id": 5, - "global": true, - "type": "OS-DNS:DNS", - "publicURL": "https://dns.public.com/v3.2", - "versionId": "1.0", - "versionInfo": "https://dns.public.com/v1.0/", - "versionList": "https://dns.public.com/", - "enabled": true - } - ], - "OS-KSCATALOG:endpointsTemplates_links": [] -} diff --git a/keystone/content/common/samples/endpointTemplates.xml b/keystone/content/common/samples/endpointTemplates.xml deleted file mode 100644 index 965be9be..00000000 --- a/keystone/content/common/samples/endpointTemplates.xml +++ /dev/null @@ -1,67 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<endpointTemplates xmlns="http://docs.openstack.org/identity/api/ext/OS-KSCATALOG/v1.0"> - <endpointTemplate - id="1" - region="North" - global="true" - type="compute" - name="Compute" - publicURL="https://compute.north.public.com/v1" - internalURL="https://compute.north.internal.com/v1" - enabled="true"> - <version - id="1" - list="https://compute.north.public.com/" - info="https://compute.north.public.com/v1"/> - </endpointTemplate> - <endpointTemplate - id="2" - region="south" - type="compute" - name="Compute" - publicURL="https://service2.public.com/v1" - internalURL="https://service2.internal.public.com/v1" - enabled="false"> - <version - id="1" - list="https://service1.public.com/" - info="https://service1.public.com/v1"/> - </endpointTemplate> - <endpointTemplate - id="3" - region="DFW" - global="true" - type="ext1:service1" - name="Compute" - publicURL="https://service1.public.com/v1" - enabled="true"> - <version - id="1" - list="https://service1.public.com/" - info="https://service1.public.com/v1"/> - </endpointTemplate> - <endpointTemplate - id="4" - region="ORD" - type="compute" - name="Compute" - publicURL="https://service2.public.com/v1" - enabled="true"> - <version - id="1" - list="https://service1.public.com/" - info="https://service1.public.com/v1"/> - </endpointTemplate> - <endpointTemplate - id="5" - global="true" - type="compute" - name="Compute" - publicURL="https://service3.public.com/v1"> - <version - id="1" - list="https://service1.public.com/" - info="https://service1.public.com/v1"/> - </endpointTemplate> -</endpointTemplates> diff --git a/keystone/content/common/samples/endpoints.json b/keystone/content/common/samples/endpoints.json deleted file mode 100644 index e10f05e3..00000000 --- a/keystone/content/common/samples/endpoints.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "endpoints": [ - { - "id": 1, - "tenantId": "1", - "region": "North", - "type": "compute", - "publicURL": "https://compute.north.public.com/v1", - "internalURL": "https://compute.north.internal.com/v1", - "adminURL": "https://compute.north.internal.com/v1", - "versionId": "1", - "versionInfo": "https://compute.north.public.com/v1/", - "versionList": "https://compute.north.public.com/" - }, - { - "id": 2, - "tenantId": "1", - "region": "South", - "type": "compute", - "publicURL": "https://compute.north.public.com/v1", - "internalURL": "https://compute.north.internal.com/v1", - "adminURL": "https://compute.north.internal.com/v1", - "versionId": "1", - "versionInfo": "https://compute.north.public.com/v1/", - "versionList": "https://compute.north.public.com/" - }, - { - "id": 3, - "tenantId": "1", - "region": "East", - "type": "compute", - "publicURL": "https://compute.north.public.com/v1", - "internalURL": "https://compute.north.internal.com/v1", - "adminURL": "https://compute.north.internal.com/v1", - "versionId": "1", - "versionInfo": "https://compute.north.public.com/v1/", - "versionList": "https://compute.north.public.com/" - }, - { - "id": 4, - "tenantId": "1", - "region": "West", - "type": "compute", - "publicURL": "https://compute.north.public.com/v1", - "internalURL": "https://compute.north.internal.com/v1", - "adminURL": "https://compute.north.internal.com/v1", - "versionId": "1", - "versionInfo": "https://compute.north.public.com/v1/", - "versionList": "https://compute.north.public.com/" - }, - { - "id": 5, - "tenantId": "1", - "region": "Global", - "type": "compute", - "publicURL": "https://compute.north.public.com/v1", - "internalURL": "https://compute.north.internal.com/v1", - "adminURL": "https://compute.north.internal.com/v1", - "versionId": "1", - "versionInfo": "https://compute.north.public.com/v1/", - "versionList": "https://compute.north.public.com/" - } - ], - "endpoints_links": [] -} diff --git a/keystone/content/common/samples/endpoints.xml b/keystone/content/common/samples/endpoints.xml deleted file mode 100644 index b09819a1..00000000 --- a/keystone/content/common/samples/endpoints.xml +++ /dev/null @@ -1,76 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<endpoints - xmlns="http://docs.openstack.org/identity/api/v2.0"> - <endpoint - id="1" - tenantId="1" - type="compute" - name="Compute" - region="North" - publicURL="https://compute.north.public.com/v1" - internalURL="https://compute.north.internal.com/v1" - adminURL="https://compute.north.internal.com/v1"> - <version - id="1" - info="https://compute.north.public.com/v1/" - list="https://compute.north.public.com/" - /> - </endpoint> - <endpoint - id="2" - tenantId="2" - type="compute" - name="Compute" - region="South" - publicURL="https://compute.north.public.com/v1" - internalURL="https://compute.north.internal.com/v1" - adminURL="https://compute.north.internal.com/v1"> - <version - id="1" - info="https://compute.north.public.com/v1/" - list="https://compute.north.public.com/" - /> - </endpoint> - <endpoint - id="3" - tenantId="1" - type="compute" - name="Compute" - region="East" - publicURL="https://compute.north.public.com/v1" - internalURL="https://compute.north.internal.com/v1" - adminURL="https://compute.north.internal.com/v1" - tenantId="1" - /> - <endpoint - id="4" - tenantId="1" - type="compute" - name="Compute" - region="West" - publicURL="https://compute.north.public.com/v1" - internalURL="https://compute.north.internal.com/v1" - adminURL="https://compute.north.internal.com/v1"> - <version - id="1" - info="https://compute.north.public.com/v1/" - list="https://compute.north.public.com/" - /> - </endpoint> - <endpoint - id="5" - tenantId="1" - type="compute" - name="Compute" - region="Global" - publicURL="https://compute.north.public.com/v1" - internalURL="https://compute.north.internal.com/v1" - adminURL="https://compute.north.internal.com/v1"> - <version - id="1" - info="https://compute.north.public.com/v1/" - list="https://compute.north.public.com/" - /> - </endpoint> -</endpoints> diff --git a/keystone/content/common/samples/ext-getuser.json b/keystone/content/common/samples/ext-getuser.json deleted file mode 100644 index f4071ccf..00000000 --- a/keystone/content/common/samples/ext-getuser.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "user": { - "roles": [ - { - "tenantId": "1234", - "id": "Admin" - } - ], - "roles_links": [], - "id": "1000", - "username": "jqsmith", - "email": "john.smith@example.org", - "enabled": true, - "RS-META:metadata": { - "values": { - "MetaKey1": "MetaValue1", - "MetaKey2": "MetaValue2" - } - } - } -} diff --git a/keystone/content/common/samples/ext-getuser.xml b/keystone/content/common/samples/ext-getuser.xml deleted file mode 100644 index b155904a..00000000 --- a/keystone/content/common/samples/ext-getuser.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<user xmlns="http://docs.openstack.org/identity/api/v2.0" - enabled="true" email="john.smith@example.org" - id="u1000" username="jqsmith"> - <roles> - <role tenantId="1234" id="Admin"/> - </roles> - <metadata - xmlns="http://docs.rackspacecloud.com/identity/api/ext/meta/v2.0"> - <meta key="MetaKey1">MetaValue1</meta> - <meta key="MetaKey2">MetaValue2</meta> - </metadata> -</user> diff --git a/keystone/content/common/samples/extension.json b/keystone/content/common/samples/extension.json deleted file mode 100644 index bb66a50c..00000000 --- a/keystone/content/common/samples/extension.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "extension": { - "name": "User Metadata Extension", - "namespace": "http://docs.rackspacecloud.com/identity/api/ext/meta/v2.0", - "alias": "RS-META", - "updated": "2011-01-12T11:22:33-06:00", - "description": "Allows associating arbritrary metadata with a user.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "http://docs.rackspacecloud.com/identity/api/ext/identity-meta-20111201.pdf" - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "http://docs.rackspacecloud.com/identity/api/ext/identity-cbs.wadl" - } - ] - } -} diff --git a/keystone/content/common/samples/extension.xml b/keystone/content/common/samples/extension.xml deleted file mode 100644 index 056d7e96..00000000 --- a/keystone/content/common/samples/extension.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<extension xmlns="http://docs.openstack.org/common/api/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom" - name="User Metadata Extension" - namespace="http://docs.rackspacecloud.com/identity/api/ext/meta/v2.0" - alias="RS-META" - updated="2011-01-12T11:22:33-06:00"> - - <description> - Allows associating arbritrary metadata with a user. - </description> - - <atom:link rel="describedby" - type="application/pdf" - href="http://docs.rackspacecloud.com/identity/api/ext/identity-meta-20111201.pdf"/> - <atom:link rel="describedby" - type="application/vnd.sun.wadl+xml" - href="http://docs.rackspacecloud.com/identity/api/ext/identity-meta.wadl"/> - -</extension> - - diff --git a/keystone/content/common/samples/extensions.json b/keystone/content/common/samples/extensions.json deleted file mode 100644 index ca46f941..00000000 --- a/keystone/content/common/samples/extensions.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "extensions": { - "values": [ - { - "name": "Reset Password Extension", - "namespace": "http://docs.rackspacecloud.com/identity/api/ext/rpe/v2.0", - "alias": "RS-RPE", - "updated": "2011-01-22T13:25:27-06:00", - "description": "Adds the capability to reset a user's password. The user is emailed when the password has been reset.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "http://docs.rackspacecloud.com/identity/api/ext/identity-rpe-20111111.pdf" - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "http://docs.rackspacecloud.com/identity/api/ext/identity-rpe.wadl" - } - ] - }, - { - "name": "User Metadata Extension", - "namespace": "http://docs.rackspacecloud.com/identity/api/ext/meta/v2.0", - "alias": "RS-META", - "updated": "2011-01-12T11:22:33-06:00", - "description": "Allows associating arbritrary metadata with a user.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "http://docs.rackspacecloud.com/identity/api/ext/identity-meta-20111201.pdf" - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "http://docs.rackspacecloud.com/identity/api/ext/identity-meta.wadl" - } - ] - } - ]}, - "extensions_links": [] -} diff --git a/keystone/content/common/samples/extensions.xml b/keystone/content/common/samples/extensions.xml deleted file mode 100644 index c11b06d7..00000000 --- a/keystone/content/common/samples/extensions.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<extensions xmlns="http://docs.openstack.org/common/api/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom"> - <extension - name="Reset Password Extension" - namespace="http://docs.rackspacecloud.com/identity/api/ext/rpe/v1.0" - alias="RS-RPE" - updated="2011-01-22T13:25:27-06:00"> - - <description> - Adds the capability to reset a user's password. The user is - emailed when the password has been reset. - </description> - - <atom:link rel="describedby" - type="application/pdf" - href="http://docs.rackspacecloud.com/identity/api/ext/identity-rpe-20111111.pdf"/> - <atom:link rel="describedby" - type="application/vnd.sun.wadl+xml" - href="http://docs.rackspacecloud.com/identity/api/ext/identity-rpe.wadl"/> - </extension> - <extension - name="User Metadata Extension" - namespace="http://docs.rackspacecloud.com/identity/api/ext/meta/v2.0" - alias="RS-META" - updated="2011-01-12T11:22:33-06:00"> - <description> - Allows associating arbritrary metadata with a user. - </description> - - <atom:link rel="describedby" - type="application/pdf" - href="http://docs.rackspacecloud.com/identity/api/ext/identity-meta-20111201.pdf"/> - <atom:link rel="describedby" - type="application/vnd.sun.wadl+xml" - href="http://docs.rackspacecloud.com/identity/api/ext/identity-meta.wadl"/> - </extension> -</extensions> diff --git a/keystone/content/common/samples/getuser-1.json b/keystone/content/common/samples/getuser-1.json deleted file mode 100644 index 741312cc..00000000 --- a/keystone/content/common/samples/getuser-1.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "user": { - "roles": [ - { - "tenantId": "1234", - "id": "Admin" - }, - { - "tenantId": "1234", - "id": "DBUser" - } - ], - "roles_links": [ - { - "rel": "next", - "href": "http://identity.api.openstack.org/v2.0/tenants/1234/users/u1000/roles?marker=Super" - } - ], - "id": "u1000", - "username": "jqsmith", - "email": "john.smith@example.org", - "enabled": true - } -} diff --git a/keystone/content/common/samples/getuser-1.xml b/keystone/content/common/samples/getuser-1.xml deleted file mode 100644 index 531a229f..00000000 --- a/keystone/content/common/samples/getuser-1.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<user xmlns="http://docs.openstack.org/identity/api/v2.0" - xmlns:atom="http://www.w3.org/2005/Atom" - enabled="true" email="john.smith@example.org" - username="jqsmith" id="u1000"> - <roles> - <role tenantId="1234" id="Admin"/> - <role tenantId="1234" id="DBUser"/> - <atom:link - rel="next" - href="http://identity.api.openstack.org/v2.0/tenants/1234/users/u1000/groups?marker=Super"/> - </roles> -</user> diff --git a/keystone/content/common/samples/identity_fault.json b/keystone/content/common/samples/identity_fault.json deleted file mode 100644 index 9968eec2..00000000 --- a/keystone/content/common/samples/identity_fault.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "identityFault": { - "message": "Fault", - "details": "Error Details...", - "code": 500 - } -} diff --git a/keystone/content/common/samples/identity_fault.xml b/keystone/content/common/samples/identity_fault.xml deleted file mode 100644 index 6787af21..00000000 --- a/keystone/content/common/samples/identity_fault.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<identityFault xmlns="http://docs.openstack.org/identity/api/v2.0" - code="500"> - <message>Fault</message> - <details>Error Details...</details> -</identityFault> diff --git a/keystone/content/common/samples/item_not_found.json b/keystone/content/common/samples/item_not_found.json deleted file mode 100644 index 8ba8c207..00000000 --- a/keystone/content/common/samples/item_not_found.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "itemNotFound": { - "message": "Item not found.", - "details": "Error Details...", - "code": 404 - } -} diff --git a/keystone/content/common/samples/item_not_found.xml b/keystone/content/common/samples/item_not_found.xml deleted file mode 100644 index 3f78b498..00000000 --- a/keystone/content/common/samples/item_not_found.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<itemNotFound xmlns="http://docs.openstack.org/identity/api/v2.0" - code="404"> - <message>Item not found.</message> - <details>Error Details...</details> -</itemNotFound> diff --git a/keystone/content/common/samples/norequestbody.txt b/keystone/content/common/samples/norequestbody.txt deleted file mode 100644 index c6a777d5..00000000 --- a/keystone/content/common/samples/norequestbody.txt +++ /dev/null @@ -1 +0,0 @@ -This operation does not require a request body. diff --git a/keystone/content/common/samples/noresponsebody.txt b/keystone/content/common/samples/noresponsebody.txt deleted file mode 100644 index 96e583f9..00000000 --- a/keystone/content/common/samples/noresponsebody.txt +++ /dev/null @@ -1 +0,0 @@ -This operation does not return a response body. diff --git a/keystone/content/common/samples/passwordcredentials.json b/keystone/content/common/samples/passwordcredentials.json deleted file mode 100644 index c57309ba..00000000 --- a/keystone/content/common/samples/passwordcredentials.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "passwordCredentials": { - "username": "test_user", - "password": "mypass" - } -} diff --git a/keystone/content/common/samples/passwordcredentials.xml b/keystone/content/common/samples/passwordcredentials.xml deleted file mode 100644 index 86e4d9fe..00000000 --- a/keystone/content/common/samples/passwordcredentials.xml +++ /dev/null @@ -1,4 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - <passwordCredentials xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns="http://docs.openstack.org/identity/api/v2.0" username="test_user" password="test"/> - diff --git a/keystone/content/common/samples/role.json b/keystone/content/common/samples/role.json deleted file mode 100644 index a1b8109d..00000000 --- a/keystone/content/common/samples/role.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "role": { - "id": "123", - "name": "Guest", - "description": "Guest Access" - } -} diff --git a/keystone/content/common/samples/role.xml b/keystone/content/common/samples/role.xml deleted file mode 100644 index dd25cfff..00000000 --- a/keystone/content/common/samples/role.xml +++ /dev/null @@ -1,4 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<role xmlns="http://docs.openstack.org/identity/api/v2.0" - id="123" name="Admin" description="All Access" /> diff --git a/keystone/content/common/samples/roles.json b/keystone/content/common/samples/roles.json deleted file mode 100644 index 2504a2b1..00000000 --- a/keystone/content/common/samples/roles.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "roles": [ - { - "id": "123", - "name": "compute:admin", - "description": "Nova Administrator" - } - ], - "roles_links": [] -} diff --git a/keystone/content/common/samples/roles.xml b/keystone/content/common/samples/roles.xml deleted file mode 100644 index 30596f92..00000000 --- a/keystone/content/common/samples/roles.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<roles xmlns="http://docs.openstack.org/identity/api/v2.0"> - <role id="123" name="Admin" description="All Access" /> - <role id="234" name="Guest" description="Guest Access" /> -</roles> diff --git a/keystone/content/common/samples/s3Credentials.json b/keystone/content/common/samples/s3Credentials.json deleted file mode 100644 index f4b59aa5..00000000 --- a/keystone/content/common/samples/s3Credentials.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "OS-KSS3:s3Credentials":{ - "username":"test_user", - "secret":"aaaaa", - "signature":"bbb" - } -} diff --git a/keystone/content/common/samples/s3Credentials.xml b/keystone/content/common/samples/s3Credentials.xml deleted file mode 100644 index a6941392..00000000 --- a/keystone/content/common/samples/s3Credentials.xml +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - <s3Credentials - xmlns="http://docs.openstack.org/identity/api/ext/OS-KSS3/v1.0" - username="testuser" - key="aaaaa" - signature="bbbbb"/> - diff --git a/keystone/content/common/samples/samplerequestheader.txt b/keystone/content/common/samples/samplerequestheader.txt deleted file mode 100644 index 5641d874..00000000 --- a/keystone/content/common/samples/samplerequestheader.txt +++ /dev/null @@ -1,4 +0,0 @@ -POST /v2.0/tokens HTTP/1.1 -Host: identity.api.openstack.org -Content-Type: application/json -Accept: application/xml
\ No newline at end of file diff --git a/keystone/content/common/samples/sampleresponseheader.txt b/keystone/content/common/samples/sampleresponseheader.txt deleted file mode 100644 index aee1205a..00000000 --- a/keystone/content/common/samples/sampleresponseheader.txt +++ /dev/null @@ -1,4 +0,0 @@ -HTTP/1.1 200 OKAY -Date: Mon, 12 Nov 2010 15:55:01 GMT -Content-Length: -Content-Type: application/xml; charset=UTF-8
\ No newline at end of file diff --git a/keystone/content/common/samples/service.json b/keystone/content/common/samples/service.json deleted file mode 100644 index f2a783f2..00000000 --- a/keystone/content/common/samples/service.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "OS-KSADM:service": { - "id": "123", - "name": "nova", - "type": "compute", - "description": "OpenStack Compute Service" - } -} diff --git a/keystone/content/common/samples/service.xml b/keystone/content/common/samples/service.xml deleted file mode 100644 index d1c98cec..00000000 --- a/keystone/content/common/samples/service.xml +++ /dev/null @@ -1,4 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<service xmlns="http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0" - id="123" name="nova" type="compute" description="OpenStack Compute Service" /> diff --git a/keystone/content/common/samples/services.json b/keystone/content/common/samples/services.json deleted file mode 100644 index dd8dea4f..00000000 --- a/keystone/content/common/samples/services.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "OS-KSADM:services": [ - { - "id": "123", - "name": "nova", - "type": "compute", - "description": "OpenStack Compute Service" - }, - { - "id": "234", - "name": "glance", - "type": "image", - "description": "OpenStack Image Service" - } - ], - "OS-KSADM:services_links": [] -} diff --git a/keystone/content/common/samples/services.xml b/keystone/content/common/samples/services.xml deleted file mode 100644 index 12947c3a..00000000 --- a/keystone/content/common/samples/services.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<services xmlns="http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0"> - <service id="123" name="nova" type="compute" description="Openstack Compute Service" /> - <service id="234" name="glance" type="image" description="Openstack Image Service" /> -</services>
\ No newline at end of file diff --git a/keystone/content/common/samples/tenant.json b/keystone/content/common/samples/tenant.json deleted file mode 100644 index 794a61ce..00000000 --- a/keystone/content/common/samples/tenant.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "tenant": { - "id": "1234", - "name": "ACME corp", - "description": "A description ...", - "enabled": true - } -} diff --git a/keystone/content/common/samples/tenant.xml b/keystone/content/common/samples/tenant.xml deleted file mode 100644 index ce0137be..00000000 --- a/keystone/content/common/samples/tenant.xml +++ /dev/null @@ -1,5 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<tenant xmlns="http://docs.openstack.org/identity/api/v2.0" - enabled="true" id="1234" name="ACME Corp"> - <description>A description...</description> -</tenant> diff --git a/keystone/content/common/samples/tenantlock.json b/keystone/content/common/samples/tenantlock.json deleted file mode 100644 index 611e4adb..00000000 --- a/keystone/content/common/samples/tenantlock.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "tenant": { - "description": "A NEW description..." - } -} diff --git a/keystone/content/common/samples/tenantlock.xml b/keystone/content/common/samples/tenantlock.xml deleted file mode 100644 index 06a68a83..00000000 --- a/keystone/content/common/samples/tenantlock.xml +++ /dev/null @@ -1,4 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<tenant xmlns="http://docs.openstack.org/identity/api/v2.0"> - <description>A NEW description...</description> -</tenant> diff --git a/keystone/content/common/samples/tenants-1.json b/keystone/content/common/samples/tenants-1.json deleted file mode 100644 index b3f42349..00000000 --- a/keystone/content/common/samples/tenants-1.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "tenants": [ - { - "id": "1234", - "name": "ACME corp", - "description": "A description ...", - "enabled": true - } - ], - "tenants_links": [ - { - "rel": "next", - "href": "http://identity.api.openstack.org/v2.0/tenants?limit=1&marker=1234" - } - ] -} diff --git a/keystone/content/common/samples/tenants-1.xml b/keystone/content/common/samples/tenants-1.xml deleted file mode 100644 index e486649e..00000000 --- a/keystone/content/common/samples/tenants-1.xml +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<tenants xmlns="http://docs.openstack.org/identity/api/v2.0" - xmlns:atom="http://www.w3.org/2005/Atom"> - <tenant enabled="true" id="1234" name="ACME Corp"> - <description>A description...</description> - </tenant> - <atom:link - rel="next" - href="http://identity.api.openstack.org/v2.0/tenants?limit=1&marker=1234"/> -</tenants> diff --git a/keystone/content/common/samples/tenants-2.json b/keystone/content/common/samples/tenants-2.json deleted file mode 100644 index c464a446..00000000 --- a/keystone/content/common/samples/tenants-2.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "tenants": [ - { - "id": "3645", - "name": "Iron Works", - "description": "A description ...", - "enabled": true - } - ], - "tenants_links": [ - { - "rel": "next", - "href": "http://identity.api.openstack.org/v2.0/tenants?limit=1&marker=3645" - }, - { - "rel": "previous", - "href": "http://identity.api.openstack.org/v2.0/tenants?limit=1" - } - ] -} diff --git a/keystone/content/common/samples/tenants-2.xml b/keystone/content/common/samples/tenants-2.xml deleted file mode 100644 index 7b049c40..00000000 --- a/keystone/content/common/samples/tenants-2.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<tenants xmlns="http://docs.openstack.org/identity/api/v2.0" - xmlns:atom="http://www.w3.org/2005/Atom"> - <tenant enabled="true" id="3645" name="Iron Works"> - <description>A description...</description> - </tenant> - <atom:link - rel="previous" - href="http://identity.api.openstack.org/v2.0/tenants?limit=1"/> - <atom:link - rel="next" - href="http://identity.api.openstack.org/v2.0/tenants?limit=1&marker=3645"/> -</tenants> diff --git a/keystone/content/common/samples/tenants-3.json b/keystone/content/common/samples/tenants-3.json deleted file mode 100644 index 5b62bd7e..00000000 --- a/keystone/content/common/samples/tenants-3.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "tenants": [ - { - "id": "9999", - "name": "Bigz", - "description": "A description ...", - "enabled": true - } - ], - "tenants_links": [ - { - "rel": "previous", - "href": "http://identity.api.openstack.org/v2.0/tenants?limit=1&marker=1234" - } - ] -} diff --git a/keystone/content/common/samples/tenants-3.xml b/keystone/content/common/samples/tenants-3.xml deleted file mode 100644 index a0edcadb..00000000 --- a/keystone/content/common/samples/tenants-3.xml +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<tenants xmlns="http://docs.openstack.org/identity/api/v2.0" - xmlns:atom="http://www.w3.org/2005/Atom"> - <tenant enabled="true" id="9999" name="Bigz"> - <description>A description...</description> - </tenant> - <atom:link - rel="previous" - href="http://identity.api.openstack.org/v2.0/tenants?limit=1&marker=1234"/> -</tenants> diff --git a/keystone/content/common/samples/tenants-request.txt b/keystone/content/common/samples/tenants-request.txt deleted file mode 100644 index 9dbf85e5..00000000 --- a/keystone/content/common/samples/tenants-request.txt +++ /dev/null @@ -1,5 +0,0 @@ -GET /v2.0/tenants HTTP/1.1 -Host: identity.api.openstack.org -Content-Type: application/json -X-Auth-Token: fa8426a0-8eaf-4d22-8e13-7c1b16a9370c -Accept: application/json
\ No newline at end of file diff --git a/keystone/content/common/samples/tenants.json b/keystone/content/common/samples/tenants.json deleted file mode 100644 index a249472f..00000000 --- a/keystone/content/common/samples/tenants.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "tenants": [ - { - "id": "1234", - "name": "ACME Corp", - "description": "A description ...", - "enabled": true - }, - { - "id": "3456", - "name": "Iron Works", - "description": "A description ...", - "enabled": true - } - ], - "tenants_links": [] -} diff --git a/keystone/content/common/samples/tenants.xml b/keystone/content/common/samples/tenants.xml deleted file mode 100644 index ac5fa2d9..00000000 --- a/keystone/content/common/samples/tenants.xml +++ /dev/null @@ -1,14 +0,0 @@ -HTTP/1.1 200 OK -Content-Type: application/xml; charset=UTF-8 -Content-Length: 200 -Date: Sun, 1 Jan 2011 9:00:00 GMT - -<?xml version="1.0" encoding="UTF-8"?> -<tenants xmlns="http://docs.openstack.org/identity/api/v2.0"> - <tenant enabled="true" id="1234" name="ACME Corp"> - <description>A description...</description> - </tenant> - <tenant enabled="true" id="3645" name="Iron Works"> - <description>A description...</description> - </tenant> -</tenants> diff --git a/keystone/content/common/samples/tenantwithoutid.json b/keystone/content/common/samples/tenantwithoutid.json deleted file mode 100644 index 63faba9c..00000000 --- a/keystone/content/common/samples/tenantwithoutid.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "tenant": { - "name": "ACME corp", - "description": "A description ...", - "enabled": true - } -} diff --git a/keystone/content/common/samples/tenantwithoutid.xml b/keystone/content/common/samples/tenantwithoutid.xml deleted file mode 100644 index 3983684b..00000000 --- a/keystone/content/common/samples/tenantwithoutid.xml +++ /dev/null @@ -1,5 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<tenant xmlns="http://docs.openstack.org/identity/api/v2.0" - enabled="true" name="ACME Corp"> - <description>A description...</description> -</tenant> diff --git a/keystone/content/common/samples/updatedtenant.json b/keystone/content/common/samples/updatedtenant.json deleted file mode 100644 index 2acf53d1..00000000 --- a/keystone/content/common/samples/updatedtenant.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "tenant": { - "id": "1234", - "name": "ACME Corp", - "description": "A NEW description...", - "enabled": true - } -} diff --git a/keystone/content/common/samples/updatedtenant.xml b/keystone/content/common/samples/updatedtenant.xml deleted file mode 100644 index 5b36701a..00000000 --- a/keystone/content/common/samples/updatedtenant.xml +++ /dev/null @@ -1,5 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<tenant xmlns="http://docs.openstack.org/identity/api/v2.0" - enabled="true" id="1234" name="ACME Corp"> - <description>A NEW description...</description> -</tenant> diff --git a/keystone/content/common/samples/user.json b/keystone/content/common/samples/user.json deleted file mode 100644 index 75d1508b..00000000 --- a/keystone/content/common/samples/user.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "user": { - "id": "u1000", - "username": "jqsmith", - "email": "john.smith@example.org", - "enabled": true - } -} diff --git a/keystone/content/common/samples/user.xml b/keystone/content/common/samples/user.xml deleted file mode 100644 index ccaf7ec4..00000000 --- a/keystone/content/common/samples/user.xml +++ /dev/null @@ -1,4 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<user xmlns="http://docs.openstack.org/identity/api/v2.0" - enabled="true" email="john.smith@example.org" - username="jqsmith" id="u1000"/> diff --git a/keystone/content/common/samples/users.json b/keystone/content/common/samples/users.json deleted file mode 100644 index 3fc104d7..00000000 --- a/keystone/content/common/samples/users.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "users": [ - { - "id": "u1000", - "username": "jqsmith", - "email": "john.smith@example.org", - "enabled": true - }, - { - "id": "u1001", - "username": "jqsmith", - "email": "john.smith@example.org", - "enabled": true - } - ], - "users_links": [] -} diff --git a/keystone/content/common/samples/users.xml b/keystone/content/common/samples/users.xml deleted file mode 100644 index 01b321da..00000000 --- a/keystone/content/common/samples/users.xml +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<users xmlns="http://docs.openstack.org/identity/api/v2.0"> - <user xmlns="http://docs.openstack.org/identity/api/v2.0" - enabled="true" email="john.smith@example.org" - username="jqsmith" id="u1000"/> - <user xmlns="http://docs.openstack.org/identity/api/v2.0" - enabled="true" email="john.smith@example.org" - username="jqsmith" id="u1001"/> -</users> diff --git a/keystone/content/common/samples/userwithenabledonly.json b/keystone/content/common/samples/userwithenabledonly.json deleted file mode 100644 index fdd74562..00000000 --- a/keystone/content/common/samples/userwithenabledonly.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "user": { - "enabled": true - } -} diff --git a/keystone/content/common/samples/userwithenabledonly.xml b/keystone/content/common/samples/userwithenabledonly.xml deleted file mode 100644 index 0176b9eb..00000000 --- a/keystone/content/common/samples/userwithenabledonly.xml +++ /dev/null @@ -1,5 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<user xmlns="http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:type="UserWithOnlyEnabled" - enabled="true"/> diff --git a/keystone/content/common/samples/userwithoutid.json b/keystone/content/common/samples/userwithoutid.json deleted file mode 100644 index 8d15f3af..00000000 --- a/keystone/content/common/samples/userwithoutid.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "user": { - "username": "jqsmith", - "email": "john.smith@example.org", - "enabled": true - } -} diff --git a/keystone/content/common/samples/userwithoutid.xml b/keystone/content/common/samples/userwithoutid.xml deleted file mode 100644 index 3e875beb..00000000 --- a/keystone/content/common/samples/userwithoutid.xml +++ /dev/null @@ -1,4 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<user xmlns="http://docs.openstack.org/identity/api/v2.0" - enabled="true" email="john.smith@example.org" - username="jqsmith"/> diff --git a/keystone/content/common/samples/validatetoken.json b/keystone/content/common/samples/validatetoken.json deleted file mode 100644 index 4a8db881..00000000 --- a/keystone/content/common/samples/validatetoken.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "access": { - "token": { - "id": "ab48a9efdfedb23ty3494", - "expires": "2010-11-01T03:32:15-05:00", - "tenant": { - "id": "345", - "name": "My Project" - } - }, - "user": { - "id": "123", - "name": "jqsmith", - "roles": [ - { - "id": "234", - "name": "compute:admin" - }, - { - "id": "234", - "name": "object-store:admin", - "tenantId": "1" - } - ], - "roles_links": [] - } - } -} diff --git a/keystone/content/common/samples/validatetoken.xml b/keystone/content/common/samples/validatetoken.xml deleted file mode 100644 index 12da9ebb..00000000 --- a/keystone/content/common/samples/validatetoken.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<access xmlns="http://docs.openstack.org/identity/api/v2.0"> - <token id="ab48a9efdfedb23ty3494" expires="2010-11-01T03:32:15-05:00"> - <tenant id="456" name="My Project" /> - </token> - <user id="123" username="jqsmith"> - <roles xmlns="http://docs.openstack.org/identity/api/v2.0"> - <role id="123" name="Admin" tenantId="one"/> - <role id="234" name="object-store:admin" tenantId="1"/> - </roles> - </user> -</access> diff --git a/keystone/content/common/samples/version-atom.xml b/keystone/content/common/samples/version-atom.xml deleted file mode 100644 index 3d78a4a3..00000000 --- a/keystone/content/common/samples/version-atom.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<feed xmlns="http://www.w3.org/2005/Atom"> - <title type="text">About This Version</title> - <updated>2011-01-21T11:33:21-06:00</updated> - <id>http://identity.api.openstack.org/v2.0/</id> - <author><name>OpenStack</name><uri>http://www.openstack.org/</uri></author> - <link rel="self" href="http://identity.api.openstack.org/v2.0/"/> - <entry> - <id>http://identity.api.openstack.org/v2.0/</id> - <title type="text">Version v2.0</title> - <updated>2011-01-21T11:33:21-06:00</updated> - <link rel="self" href="http://identity.api.openstack.org/v2.0/"/> - <link rel="describedby" type="application/pdf" - href="http://docs.openstack.org/identity/api/v2.0/identity-latest.pdf"/> - <link rel="describedby" type="application/vnd.sun.wadl+xml" - href="http://docs.openstack.org/identity/api/v2.0/application.wadl"/> - <content type="text">Version v2.0 CURRENT (2011-01-21T11:33:21-06:00)</content> - </entry> -</feed> diff --git a/keystone/content/common/samples/version.json b/keystone/content/common/samples/version.json deleted file mode 100644 index 4410f93f..00000000 --- a/keystone/content/common/samples/version.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "version": { - "id": "v2.0", - "status": "CURRENT", - "updated": "2011-01-21T11:33:21-06:00", - "links": [ - { - "rel": "self", - "href": "http://identity.api.openstack.org/v2.0/" - }, - { - "rel": "describedby", - "type": "application/pdf", - "href": "http://docs.openstack.org/identity/api/v2.0/identity-latest.pdf" - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "http://docs.openstack.org/identity/api/v2.0/identity.wadl" - } - ], - "media-types": [ - { - "base": "application/xml", - "type": "application/vnd.openstack.identity+xml;version=2.0" - }, - { - "base": "application/json", - "type": "application/vnd.openstack.identity+json;version=2.0" - } - ] - } -} diff --git a/keystone/content/common/samples/version.xml b/keystone/content/common/samples/version.xml deleted file mode 100644 index d0f77e42..00000000 --- a/keystone/content/common/samples/version.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<version xmlns="http://docs.openstack.org/common/api/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom" - id="v2.0" status="CURRENT" updated="2011-01-21T11:33:21-06:00"> - - <media-types> - <media-type base="application/xml" - type="application/vnd.openstack.identity+xml;version=2.0"/> - <media-type base="application/json" - type="application/vnd.openstack.identity+json;version=2.0"/> - </media-types> - - <atom:link rel="self" - href="http://identity.api.openstack.org/v2.0/"/> - - <atom:link rel="describedby" - type="application/pdf" - href="http://docs.openstack.org/identity/api/v2.0/identity-latest.pdf" /> - - <atom:link rel="describedby" - type="application/vnd.sun.wadl+xml" - href="http://docs.openstack.org/identity/api/v2.0/identity.wadl" /> -</version> diff --git a/keystone/content/common/samples/versions-atom.xml b/keystone/content/common/samples/versions-atom.xml deleted file mode 100644 index 5c864fce..00000000 --- a/keystone/content/common/samples/versions-atom.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<feed xmlns="http://www.w3.org/2005/Atom"> - <title type="text">Available API Versions</title> - <updated>2010-12-12T18:30:02.25Z</updated> - <id>http://identity.api.openstack.org/</id> - <author><name>OpenStack</name><uri>http://www.openstack.org/</uri></author> - <link rel="self" href="http://identity.api.openstack.org/"/> - <entry> - <id>http://identity.api.openstack.org/v2.0/</id> - <title type="text">Version v2.0</title> - <updated>2011-05-27T20:22:02.25Z</updated> - <link rel="self" href="http://identity.api.openstack.org/v2.0/"/> - <content type="text">Version v2.1 CURRENT (2011-05-27T20:22:02.25Z)</content> - </entry> - <entry> - <id>http://identity.api.openstack.org/v1.1/</id> - <title type="text">Version v1.1</title> - <updated>2010-12-12T18:30:02.25Z</updated> - <link rel="self" href="http://identity.api.openstack.org/v1.1/"/> - <content type="text">Version v1.1 CURRENT (2010-12-12T18:30:02.25Z)</content> - </entry> - <entry> - <id>http://identity.api.openstack.org/v1.0/</id> - <title type="text">Version v1.0</title> - <updated>2009-10-09T11:30:00Z</updated> - <link rel="self" href="http://identity.api.openstack.org/v1.0/"/> - <content type="text">Version v1.0 DEPRECATED (2009-10-09T11:30:00Z)</content> - </entry> -</feed> diff --git a/keystone/content/common/samples/versions.json b/keystone/content/common/samples/versions.json deleted file mode 100644 index e1e590f4..00000000 --- a/keystone/content/common/samples/versions.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "versions": [ - { - "id": "v1.0", - "status": "DEPRECATED", - "updated": "2009-10-09T11:30:00Z", - "links": [ - { - "rel": "self", - "href": "http://identity.api.openstack.org/v1.0/" - } - ] - }, - { - "id": "v1.1", - "status": "CURRENT", - "updated": "2010-12-12T18:30:02.25Z", - "links": [ - { - "rel": "self", - "href": "http://identity.api.openstack.org/v1.1/" - } - ] - }, - { - "id": "v2.0", - "status": "BETA", - "updated": "2011-05-27T20:22:02.25Z", - "links": [ - { - "rel": "self", - "href": "http://identity.api.openstack.org/v2.0/" - } - ] - } - ], - "versions_links": [] -} diff --git a/keystone/content/common/samples/versions.xml b/keystone/content/common/samples/versions.xml deleted file mode 100644 index caa9801b..00000000 --- a/keystone/content/common/samples/versions.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<versions xmlns="http://docs.openstack.org/common/api/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom"> - - <version id="v1.0" status="DEPRECATED" - updated="2009-10-09T11:30:00Z"> - <atom:link rel="self" - href="http://identity.api.openstack.org/v1.0/"/> - </version> - - <version id="v1.1" status="CURRENT" - updated="2010-12-12T18:30:02.25Z"> - <atom:link rel="self" - href="http://identity.api.openstack.org/v1.1/"/> - </version> - - <version id="v2.0" status="BETA" - updated="2011-05-27T20:22:02.25Z"> - <atom:link rel="self" - href="http://identity.api.openstack.org/v2.0/"/> - </version> - -</versions> diff --git a/keystone/content/common/style/schema.css b/keystone/content/common/style/schema.css deleted file mode 100644 index f174ca52..00000000 --- a/keystone/content/common/style/schema.css +++ /dev/null @@ -1,82 +0,0 @@ -/* - * (C) 2009 Rackspace Hosting, All Rights Reserved. - */ -body, div, dl, dt, dd, ul, ol, li, h2, h3, -h4, h5, h6, pre, code, form, fieldset, legend, -input, button, textarea, p, blockquote, th, td { - text-align: left; -} - -h1 { - font-size: 350%; - margin-bottom: 10px; -} - -#Content { - border: 1px solid; - padding: 0px 40px 40px; - margin-left: 155px; -} - -#SrcContent { - padding: 0px 40px 40px; - display: none; - margin-left: 155px; -} - -#Controller { - position: fixed; - width: 145px; - left: 10px; - top: 10px; -} - -.Sample { - display: none; -} - -.EnumValue{ - padding: 10px 0px; -} - -.EnumDoc{ - padding: 10px 10px 10px 0px; -} - -.ExternHref{ - padding-top: 5px; -} - -.ExternDoc{ - padding-right: 10px; -} - -pre { - overflow: auto; -} - -td { - padding: 0px 0px 0px 10px; - width: 50%; - font-size: 90%; -} - -table { - width: 100%; -} - -a { - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - -a:link { - color: #000090; -} - -a:visited { - color: #000090; -} diff --git a/keystone/content/common/style/shjs/sh_acid.css b/keystone/content/common/style/shjs/sh_acid.css deleted file mode 100644 index a34b786f..00000000 --- a/keystone/content/common/style/shjs/sh_acid.css +++ /dev/null @@ -1,151 +0,0 @@ -pre.sh_sourceCode {
- background-color: #eeeeee;
- color: #000000;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_keyword {
- color: #bb7977;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_type {
- color: #8080c0;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_string {
- color: #a68500;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_regexp {
- color: #a68500;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_specialchar {
- color: #ff00ff;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_comment {
- color: #ff8000;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_number {
- color: #800080;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_preproc {
- color: #0080c0;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_symbol {
- color: #ff0080;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_function {
- color: #004466;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_cbracket {
- color: #ff0080;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_url {
- color: #a68500;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_date {
- color: #bb7977;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_time {
- color: #bb7977;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_file {
- color: #bb7977;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_ip {
- color: #a68500;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_name {
- color: #a68500;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_variable {
- color: #0080c0;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_oldfile {
- color: #ff00ff;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_newfile {
- color: #a68500;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_difflines {
- color: #bb7977;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_selector {
- color: #0080c0;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_property {
- color: #bb7977;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_value {
- color: #a68500;
- font-weight: normal;
- font-style: normal;
-}
-
diff --git a/keystone/content/common/style/shjs/sh_darkblue.css b/keystone/content/common/style/shjs/sh_darkblue.css deleted file mode 100644 index 23fd6dab..00000000 --- a/keystone/content/common/style/shjs/sh_darkblue.css +++ /dev/null @@ -1,151 +0,0 @@ -pre.sh_sourceCode {
- background-color: #000040;
- color: #C7C7C7;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_keyword {
- color: #ffff60;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_type {
- color: #60ff60;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_string {
- color: #ffa0a0;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_regexp {
- color: #ffa0a0;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_specialchar {
- color: #ffa500;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_comment {
- color: #80a0ff;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_number {
- color: #42cad9;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_preproc {
- color: #ff80ff;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_symbol {
- color: #d8e91b;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_function {
- color: #ffffff;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_cbracket {
- color: #d8e91b;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_url {
- color: #ffa0a0;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_date {
- color: #ffff60;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_time {
- color: #ffff60;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_file {
- color: #ffff60;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_ip {
- color: #ffa0a0;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_name {
- color: #ffa0a0;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_variable {
- color: #26e0e7;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_oldfile {
- color: #ffa500;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_newfile {
- color: #ffa0a0;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_difflines {
- color: #ffff60;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_selector {
- color: #26e0e7;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_property {
- color: #ffff60;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_value {
- color: #ffa0a0;
- font-weight: normal;
- font-style: normal;
-}
-
diff --git a/keystone/content/common/style/shjs/sh_emacs.css b/keystone/content/common/style/shjs/sh_emacs.css deleted file mode 100644 index 6e019cbe..00000000 --- a/keystone/content/common/style/shjs/sh_emacs.css +++ /dev/null @@ -1,139 +0,0 @@ -pre.sh_sourceCode {
- background-color: #ffffff;
- color: #000000;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_keyword {
- color: #9c20ee;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_type {
- color: #208920;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_string {
- color: #bd8d8b;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_regexp {
- color: #bd8d8b;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_specialchar {
- color: #bd8d8b;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_comment {
- color: #ac2020;
- font-weight: normal;
- font-style: italic;
-}
-
-pre.sh_sourceCode .sh_number {
- color: #000000;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_preproc {
- color: #000000;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_function {
- color: #000000;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_url {
- color: #bd8d8b;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_date {
- color: #9c20ee;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_time {
- color: #9c20ee;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_file {
- color: #9c20ee;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_ip {
- color: #bd8d8b;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_name {
- color: #bd8d8b;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_variable {
- color: #0000ff;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_oldfile {
- color: #bd8d8b;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_newfile {
- color: #bd8d8b;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_difflines {
- color: #9c20ee;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_selector {
- color: #0000ff;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_property {
- color: #9c20ee;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_value {
- color: #bd8d8b;
- font-weight: normal;
- font-style: normal;
-}
-
diff --git a/keystone/content/common/style/shjs/sh_night.css b/keystone/content/common/style/shjs/sh_night.css deleted file mode 100644 index d8d371b4..00000000 --- a/keystone/content/common/style/shjs/sh_night.css +++ /dev/null @@ -1,151 +0,0 @@ -pre.sh_sourceCode {
- background-color: #000044;
- color: #dd00ff;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_keyword {
- color: #ffffff;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_type {
- color: #f1157c;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_string {
- color: #ffffff;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_regexp {
- color: #ffffff;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_specialchar {
- color: #82d66d;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_comment {
- color: #bfbfbf;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_number {
- color: #8ee119;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_preproc {
- color: #00bb00;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_symbol {
- color: #e7ee5c;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_function {
- color: #ff06cd;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_cbracket {
- color: #e7ee5c;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_url {
- color: #ffffff;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_date {
- color: #ffffff;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_time {
- color: #ffffff;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_file {
- color: #ffffff;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_ip {
- color: #ffffff;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_name {
- color: #ffffff;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_variable {
- color: #7aec27;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_oldfile {
- color: #82d66d;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_newfile {
- color: #ffffff;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_difflines {
- color: #ffffff;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_selector {
- color: #7aec27;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_property {
- color: #ffffff;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_value {
- color: #ffffff;
- font-weight: normal;
- font-style: normal;
-}
-
diff --git a/keystone/content/common/style/shjs/sh_pablo.css b/keystone/content/common/style/shjs/sh_pablo.css deleted file mode 100644 index 173cd7bf..00000000 --- a/keystone/content/common/style/shjs/sh_pablo.css +++ /dev/null @@ -1,151 +0,0 @@ -pre.sh_sourceCode {
- background-color: #000000;
- color: #ffffff;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_keyword {
- color: #c0c000;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_type {
- color: #00c000;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_string {
- color: #00ffff;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_regexp {
- color: #00ffff;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_specialchar {
- color: #0000ff;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_comment {
- color: #808080;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_number {
- color: #00ffff;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_preproc {
- color: #00ff00;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_symbol {
- color: #ff0000;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_function {
- color: #ff22b9;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_cbracket {
- color: #ff0000;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_url {
- color: #00ffff;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_date {
- color: #c0c000;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_time {
- color: #c0c000;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_file {
- color: #c0c000;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_ip {
- color: #00ffff;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_name {
- color: #00ffff;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_variable {
- color: #0000c0;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_oldfile {
- color: #0000ff;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_newfile {
- color: #00ffff;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_difflines {
- color: #c0c000;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_selector {
- color: #0000c0;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_property {
- color: #c0c000;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_value {
- color: #00ffff;
- font-weight: normal;
- font-style: normal;
-}
-
diff --git a/keystone/content/common/style/shjs/sh_print.css b/keystone/content/common/style/shjs/sh_print.css deleted file mode 100644 index 1e8c1168..00000000 --- a/keystone/content/common/style/shjs/sh_print.css +++ /dev/null @@ -1,145 +0,0 @@ -pre.sh_sourceCode {
- background-color: #ffffff;
- color: #000000;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_keyword {
- color: #000000;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_type {
- color: #000000;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_string {
- color: #000000;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_regexp {
- color: #000000;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_specialchar {
- color: #000000;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_comment {
- color: #666666;
- font-weight: normal;
- font-style: italic;
-}
-
-pre.sh_sourceCode .sh_number {
- color: #000000;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_preproc {
- color: #000000;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_symbol {
- color: #000000;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_cbracket {
- color: #000000;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_url {
- color: #000000;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_date {
- color: #000000;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_time {
- color: #000000;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_file {
- color: #000000;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_ip {
- color: #000000;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_name {
- color: #000000;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_variable {
- color: #000000;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_oldfile {
- color: #000000;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_newfile {
- color: #000000;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_difflines {
- color: #000000;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_selector {
- color: #000000;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_property {
- color: #000000;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_value {
- color: #000000;
- font-weight: normal;
- font-style: normal;
-}
-
diff --git a/keystone/content/common/style/shjs/sh_style.css b/keystone/content/common/style/shjs/sh_style.css deleted file mode 100644 index 6cd20b47..00000000 --- a/keystone/content/common/style/shjs/sh_style.css +++ /dev/null @@ -1,66 +0,0 @@ -pre.sh_sourceCode {
- background-color: white;
- color: black;
- font-style: normal;
- font-weight: normal;
-}
-
-pre.sh_sourceCode .sh_keyword { color: blue; font-weight: bold; } /* language keywords */
-pre.sh_sourceCode .sh_type { color: darkgreen; } /* basic types */
-pre.sh_sourceCode .sh_usertype { color: teal; } /* user defined types */
-pre.sh_sourceCode .sh_string { color: red; font-family: monospace; } /* strings and chars */
-pre.sh_sourceCode .sh_regexp { color: orange; font-family: monospace; } /* regular expressions */
-pre.sh_sourceCode .sh_specialchar { color: pink; font-family: monospace; } /* e.g., \n, \t, \\ */
-pre.sh_sourceCode .sh_comment { color: brown; font-style: italic; } /* comments */
-pre.sh_sourceCode .sh_number { color: purple; } /* literal numbers */
-pre.sh_sourceCode .sh_preproc { color: darkblue; font-weight: bold; } /* e.g., #include, import */
-pre.sh_sourceCode .sh_symbol { color: darkred; } /* e.g., <, >, + */
-pre.sh_sourceCode .sh_function { color: black; font-weight: bold; } /* function calls and declarations */
-pre.sh_sourceCode .sh_cbracket { color: red; } /* block brackets (e.g., {, }) */
-pre.sh_sourceCode .sh_todo { font-weight: bold; background-color: cyan; } /* TODO and FIXME */
-
-/* Predefined variables and functions (for instance glsl) */
-pre.sh_sourceCode .sh_predef_var { color: darkblue; }
-pre.sh_sourceCode .sh_predef_func { color: darkblue; font-weight: bold; }
-
-/* for OOP */
-pre.sh_sourceCode .sh_classname { color: teal; }
-
-/* line numbers (not yet implemented) */
-pre.sh_sourceCode .sh_linenum { color: black; font-family: monospace; }
-
-/* Internet related */
-pre.sh_sourceCode .sh_url { color: blue; text-decoration: underline; font-family: monospace; }
-
-/* for ChangeLog and Log files */
-pre.sh_sourceCode .sh_date { color: blue; font-weight: bold; }
-pre.sh_sourceCode .sh_time, pre.sh_sourceCode .sh_file { color: darkblue; font-weight: bold; }
-pre.sh_sourceCode .sh_ip, pre.sh_sourceCode .sh_name { color: darkgreen; }
-
-/* for Prolog, Perl... */
-pre.sh_sourceCode .sh_variable { color: darkgreen; }
-
-/* for LaTeX */
-pre.sh_sourceCode .sh_italics { color: darkgreen; font-style: italic; }
-pre.sh_sourceCode .sh_bold { color: darkgreen; font-weight: bold; }
-pre.sh_sourceCode .sh_underline { color: darkgreen; text-decoration: underline; }
-pre.sh_sourceCode .sh_fixed { color: green; font-family: monospace; }
-pre.sh_sourceCode .sh_argument { color: darkgreen; }
-pre.sh_sourceCode .sh_optionalargument { color: purple; }
-pre.sh_sourceCode .sh_math { color: orange; }
-pre.sh_sourceCode .sh_bibtex { color: blue; }
-
-/* for diffs */
-pre.sh_sourceCode .sh_oldfile { color: orange; }
-pre.sh_sourceCode .sh_newfile { color: darkgreen; }
-pre.sh_sourceCode .sh_difflines { color: blue; }
-
-/* for css */
-pre.sh_sourceCode .sh_selector { color: purple; }
-pre.sh_sourceCode .sh_property { color: blue; }
-pre.sh_sourceCode .sh_value { color: darkgreen; font-style: italic; }
-
-/* other */
-pre.sh_sourceCode .sh_section { color: black; font-weight: bold; }
-pre.sh_sourceCode .sh_paren { color: red; }
-pre.sh_sourceCode .sh_attribute { color: darkgreen; }
diff --git a/keystone/content/common/style/shjs/sh_whitengrey.css b/keystone/content/common/style/shjs/sh_whitengrey.css deleted file mode 100644 index 41df0e2c..00000000 --- a/keystone/content/common/style/shjs/sh_whitengrey.css +++ /dev/null @@ -1,139 +0,0 @@ -pre.sh_sourceCode {
- background-color: #ffffff;
- color: #696969;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_keyword {
- color: #696969;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_type {
- color: #696969;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_string {
- color: #008800;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_regexp {
- color: #008800;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_specialchar {
- color: #008800;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_comment {
- color: #1326a2;
- font-weight: normal;
- font-style: italic;
-}
-
-pre.sh_sourceCode .sh_number {
- color: #bb00ff;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_preproc {
- color: #470000;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_function {
- color: #000000;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_url {
- color: #008800;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_date {
- color: #696969;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_time {
- color: #696969;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_file {
- color: #696969;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_ip {
- color: #008800;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_name {
- color: #008800;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_variable {
- color: #696969;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_oldfile {
- color: #008800;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_newfile {
- color: #008800;
- font-weight: normal;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_difflines {
- color: #696969;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_selector {
- color: #696969;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_property {
- color: #696969;
- font-weight: bold;
- font-style: normal;
-}
-
-pre.sh_sourceCode .sh_value {
- color: #008800;
- font-weight: normal;
- font-style: normal;
-}
-
diff --git a/keystone/content/common/xsd/OS-KSADM.xsd b/keystone/content/common/xsd/OS-KSADM.xsd deleted file mode 100644 index 5dfa08dd..00000000 --- a/keystone/content/common/xsd/OS-KSADM.xsd +++ /dev/null @@ -1,108 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?xml-stylesheet type="text/xsl" href="../xslt/schema.xslt"?> - -<!-- (C) 2011 OpenStack LLC., All Rights Reserved --> - -<schema - elementFormDefault="qualified" - attributeFormDefault="unqualified" - xmlns="http://www.w3.org/2001/XMLSchema" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:OS-KSADM="http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom" - targetNamespace="http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0" - > - - <!-- Import ATOM specific schema definitions --> - <import vc:minVersion="1.1" namespace="http://www.w3.org/2005/Atom" - schemaLocation="atom/atom.xsd" /> - - <import namespace="http://docs.openstack.org/identity/api/v2.0" - schemaLocation="api.xsd" /> - - <!-- Elements --> - <element name="services" type="OS-KSADM:ServiceList"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - A list of services. - </p> - </xsd:documentation> - </annotation> - </element> - - <element name="service" type="OS-KSADM:Service" > - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - A service. - </p> - </xsd:documentation> - </annotation> - </element> - - <element name="extensibleCredentialsType" type="OS-KSADM:ExtensibleCredentialsType"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - An extensible credentials type. - </p> - </xsd:documentation> - </annotation> - </element> - - <!-- Complex Types --> - <complexType name="Service"> - <attribute name="id" type="xsd:string" use="required"/> - <attribute name="name" type="xsd:string" use="required"/> - <attribute name="type" type="identity:ExtensibleServiceType" use="required"/> - <attribute name="description" type="xsd:string" use="optional"/> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - - <complexType name="ServiceList"> - <sequence> - <element name="service" type="OS-KSADM:Service" minOccurs="0" maxOccurs="unbounded"/> - <element vc:minVersion="1.1" ref="atom:link" minOccurs="0" maxOccurs="unbounded" /> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </sequence> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - - <simpleType name="PasswordCredentialsType"> - <restriction base="xsd:string"> - <enumeration value="password"/> - </restriction> - </simpleType> - - <simpleType name="ExtensionCredentialsType"> - <restriction base="xsd:string"> - <pattern value="(\w|-)+-\w+"/> - </restriction> - </simpleType> - - <simpleType name="ExtensibleCredentialsType"> - <union memberTypes="OS-KSADM:PasswordCredentialsType OS-KSADM:ExtensionCredentialsType"/> - </simpleType> - - <!-- Complex Types --> - <complexType name="UserWithOnlyEnabled"> - <complexContent> - <restriction base="identity:User"> - <attribute name="id" type="xsd:string" use="prohibited"/> - <attribute name="email" type="xsd:string" use="prohibited"/> - <attribute name="username" type="xsd:string" use="prohibited"/> - <attribute name="enabled" type="xsd:boolean" use="required"/> - </restriction> - </complexContent> - </complexType> -</schema>
\ No newline at end of file diff --git a/keystone/content/common/xsd/OS-KSCATALOG.xsd b/keystone/content/common/xsd/OS-KSCATALOG.xsd deleted file mode 100644 index 86de52d1..00000000 --- a/keystone/content/common/xsd/OS-KSCATALOG.xsd +++ /dev/null @@ -1,193 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?xml-stylesheet type="text/xsl" href="../xslt/schema.xslt"?> - -<!-- (C) 2011 OpenStack LLC., All Rights Reserved --> - -<schema - elementFormDefault="qualified" - attributeFormDefault="unqualified" - xmlns="http://www.w3.org/2001/XMLSchema" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:OS-KSCATALOG="http://docs.openstack.org/identity/api/ext/OS-KSCATALOG/v1.0" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom" - targetNamespace="http://docs.openstack.org/identity/api/ext/OS-KSCATALOG/v1.0" - > - <!-- Import ATOM specific schema definitions --> - <import vc:minVersion="1.1" namespace="http://www.w3.org/2005/Atom" - schemaLocation="atom/atom.xsd" /> - - <import namespace="http://docs.openstack.org/identity/api/v2.0" - schemaLocation="api.xsd" /> - - <element name="endpointTemplates" type="OS-KSCATALOG:EndpointTemplateList"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - A list of Endpoint Templates. - </p> - </xsd:documentation> - </annotation> - </element> - - <element name="endpointTemplate" type="OS-KSCATALOG:EndpointTemplate"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - An Endpoint Template. - </p> - </xsd:documentation> - </annotation> - </element> - - <!-- Complex Types --> - <complexType name="EndpointTemplate"> - <sequence> - <element name="version" type="identity:VersionForService" maxOccurs="1" minOccurs="0"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - Version details. - </p> - </xsd:documentation> - </annotation> - </element> - <element vc:minVersion="1.1" ref="atom:link" minOccurs="0" maxOccurs="unbounded" /> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </sequence> - <attribute name="id" type="xsd:string" use="required"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - An ID uniquely identifying the Endpoint Template. - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="type" type="identity:ExtensibleServiceType" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - The OpenStack-registered type (e.g. 'compute', 'object-store', etc). - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="name" type="xsd:string" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - The commercial service name (e.g. 'My Nova Cloud Servers'). - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="region" type="xsd:string" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - The region of Endpoint Template. - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="publicURL" type="xsd:anyURI" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - The public URL to access represented service. - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="internalURL" type="xsd:anyURI" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - The internal version of the public URL. - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="adminURL" type="xsd:anyURI" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - The admin URL. - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="global" type="xsd:boolean" default="false" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - If true the Endpoint Template is automatically part of every account. - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="enabled" type="xsd:boolean" default="true" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - True if the Endpoint Template is enabled (active). - A Endpoint Template cannot be added if it's disabled or inactive (false). - </p> - </xsd:documentation> - </annotation> - </attribute> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - - <complexType name="EndpointTemplateList"> - <sequence> - <element name="endpointTemplate" type="OS-KSCATALOG:EndpointTemplate" minOccurs="0" maxOccurs="unbounded"/> - <element vc:minVersion="1.1" ref="atom:link" minOccurs="0" maxOccurs="unbounded" /> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </sequence> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - - <complexType name="EndpointTemplateWithOnlyId"> - <complexContent> - <restriction base="OS-KSCATALOG:EndpointTemplate"> - <attribute name="id" type="xsd:string" use="required"/> - <attribute name="type" type="identity:ExtensibleServiceType" use="prohibited"/> - <attribute name="name" type="xsd:string" use="prohibited"/> - <attribute name="region" type="xsd:string" use="prohibited"/> - <attribute name="publicURL" type="xsd:anyURI" use="prohibited"/> - <attribute name="internalURL" type="xsd:anyURI" use="prohibited"/> - <attribute name="adminURL" type="xsd:anyURI" use="prohibited"/> - <attribute name="global" type="xsd:boolean" use="prohibited"/> - <attribute name="enabled" type="xsd:boolean" use="prohibited"/> - </restriction> - </complexContent> - </complexType> -</schema> diff --git a/keystone/content/common/xsd/OS-KSEC2-credentials.xsd b/keystone/content/common/xsd/OS-KSEC2-credentials.xsd deleted file mode 100644 index 4b7da238..00000000 --- a/keystone/content/common/xsd/OS-KSEC2-credentials.xsd +++ /dev/null @@ -1,35 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?xml-stylesheet type="text/xsl" href="../xslt/schema.xslt"?> - -<schema - elementFormDefault="qualified" - attributeFormDefault="unqualified" - xmlns="http://www.w3.org/2001/XMLSchema" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:OS-KSEC2="http://docs.openstack.org/identity/api/ext/OS-KSEC2/v1.0" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom" - targetNamespace="http://docs.openstack.org/identity/api/ext/OS-KSEC2/v1.0" -> - <!--Import schema we are extending --> - <import namespace="http://docs.openstack.org/identity/api/v2.0" - schemaLocation="credentials.xsd"/> - - <!-- Elements --> - <element name="ec2Credentials" type="OS-KSEC2:ec2CredentialsType" substitutionGroup="identity:credential"/> - - <!-- Complex Types --> - <complexType name="ec2CredentialsType"> - <complexContent> - <extension base="identity:CredentialType"> - <attribute name="username" type="xsd:string" use="required" ></attribute> - <attribute name="key" type="xsd:string" use="required" ></attribute> - <attribute name="signature" type="xsd:string" use="required" ></attribute> - </extension> - </complexContent> - </complexType> -</schema> - - diff --git a/keystone/content/common/xsd/OS-KSS3-credentials.xsd b/keystone/content/common/xsd/OS-KSS3-credentials.xsd deleted file mode 100644 index 8de65bf9..00000000 --- a/keystone/content/common/xsd/OS-KSS3-credentials.xsd +++ /dev/null @@ -1,35 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?xml-stylesheet type="text/xsl" href="../xslt/schema.xslt"?> - -<schema - elementFormDefault="qualified" - attributeFormDefault="unqualified" - xmlns="http://www.w3.org/2001/XMLSchema" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:OS-KSEC2="http://docs.openstack.org/identity/api/ext/OS-KSEC2/v1.0" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom" - targetNamespace="http://docs.openstack.org/identity/api/ext/OS-KSEC2/v1.0" -> - <!--Import schema we are extending --> - <import namespace="http://docs.openstack.org/identity/api/v2.0" - schemaLocation="credentials.xsd"/> - - <!-- Elements --> - <element name="s3Credentials" type="OS-KSEC2:s3CredentialsType" substitutionGroup="identity:credential"/> - - <!-- Complex Types --> - <complexType name="s3CredentialsType"> - <complexContent> - <extension base="identity:CredentialType"> - <attribute name="username" type="xsd:string" use="required" ></attribute> - <attribute name="key" type="xsd:string" use="required" ></attribute> - <attribute name="signature" type="xsd:string" use="required" ></attribute> - </extension> - </complexContent> - </complexType> -</schema> - - diff --git a/keystone/content/common/xsd/RAX-KSADM-credentials.xsd b/keystone/content/common/xsd/RAX-KSADM-credentials.xsd deleted file mode 100644 index c1430ac3..00000000 --- a/keystone/content/common/xsd/RAX-KSADM-credentials.xsd +++ /dev/null @@ -1,33 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?xml-stylesheet type="text/xsl" href="../xslt/schema.xslt"?> - -<schema - elementFormDefault="qualified" - attributeFormDefault="unqualified" - xmlns="http://www.w3.org/2001/XMLSchema" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:RAX-KSADM="http://docs.rackspace.com/identity/api/ext/RAX-KSADM/v1.0" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom" - targetNamespace="http://docs.rackspace.com/identity/api/ext/RAX-KSADM/v1.0" - > - <!--Import schema we are extending --> - <import namespace="http://docs.openstack.org/identity/api/v2.0" - schemaLocation="credentials.xsd"/> - - <!-- Elements --> - <element name="apikeyCredentials" type="RAX-KSADM:apiKeyCredentials" substitutionGroup="identity:credential"/> - - <!-- Complex Types --> - <complexType name="apiKeyCredentials"> - <complexContent> - <extension base="identity:CredentialType"> - <attribute name="APIKey" type="xsd:string" use="required" ></attribute> - </extension> - </complexContent> - </complexType> - -</schema> - diff --git a/keystone/content/common/xsd/RAX-KSADM-users.xsd b/keystone/content/common/xsd/RAX-KSADM-users.xsd deleted file mode 100644 index d6e8026b..00000000 --- a/keystone/content/common/xsd/RAX-KSADM-users.xsd +++ /dev/null @@ -1,53 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?xml-stylesheet type="text/xsl" href="../xslt/schema.xslt"?> - -<schema - elementFormDefault="qualified" - attributeFormDefault="unqualified" - xmlns="http://www.w3.org/2001/XMLSchema" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:RAX-KSADM="http://docs.rackspace.com/identity/api/ext/RAX-KSADM/v1.0" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom" - targetNamespace="http://docs.rackspace.com/identity/api/ext/RAX-KSADM/v1.0" - > - <!--Import schema we are extending --> - <import namespace="http://docs.openstack.org/identity/api/v2.0" - schemaLocation="user.xsd"/> - - <!-- Elements --> - <complexType name="UserWithOnlyEnabled"> - <complexContent> - <restriction base="identity:User"> - <attribute name="id" type="xsd:string" use="prohibited"/> - <attribute name="username" type="xsd:string" use="prohibited"/> - <attribute name="email" type="xsd:string" use="prohibited"/> - <attribute name="enabled" type="xsd:boolean" use="required"/> - </restriction> - </complexContent> - </complexType> - - <complexType name="UserWithPassword"> - <complexContent> - <annotation> - <xsd:appinfo> - <xsdxt:samples> - <xsdxt:sample> - <xsdxt:code type="application/xml" href="../samples/RAX-KSADM-userWithPassword.xml" /> - </xsdxt:sample> - <xsdxt:sample> - <xsdxt:code type="application/json" href="../samples/RAX-KSADM-userWithPassword.json" /> - </xsdxt:sample> - </xsdxt:samples> - </xsd:appinfo> - </annotation> - <extension base="identity:User"> - <attribute name="password" type="xsd:string" use="optional"/> - <anyAttribute namespace="##other" processContents="lax" /> - </extension> - </complexContent> - </complexType> - -</schema> diff --git a/keystone/content/common/xsd/RAX-KSGRP-groups.xsd b/keystone/content/common/xsd/RAX-KSGRP-groups.xsd deleted file mode 100644 index 267bacc3..00000000 --- a/keystone/content/common/xsd/RAX-KSGRP-groups.xsd +++ /dev/null @@ -1,53 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?xml-stylesheet type="text/xsl" href="../xslt/schema.xslt"?> - - -<schema - elementFormDefault="qualified" - attributeFormDefault="unqualified" - xmlns="http://www.w3.org/2001/XMLSchema" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:RAX-KSGRP="http://docs.rackspace.com/identity/api/ext/RAX-KSGRP/v1.0" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom" - targetNamespace="http://docs.rackspace.com/identity/api/ext/RAX-KSGRP/v1.0" -> - <!--Import schema we are extending --> - <import namespace="http://docs.openstack.org/identity/api/v2.0" - schemaLocation="token.xsd"/> - - <import namespace="http://docs.openstack.org/identity/api/v2.0" - schemaLocation="api.xsd" /> - - - <element name="groups" type="RAX-KSGRP:Groups"/> - - <complexType name="Groups"> - <sequence> - <element name="group" type="RAX-KSGRP:Group" maxOccurs="100"/> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </sequence> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - - <complexType name="Group"> - <sequence> - <element name="description" type="xsd:string" maxOccurs="1"/> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </sequence> - <attribute name="id" type="xsd:string" use="required"/> - <attribute name="name" type="xsd:string" use="optional"/> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - - <complexType name="TenantGroup"> - <complexContent> - <extension base="RAX-KSGRP:Group"> - <attribute name="tenantId" type="xsd:string" use="required"/> - </extension> - </complexContent> - </complexType> -</schema> - diff --git a/keystone/content/common/xsd/RAX-KSKEY-credentials.xsd b/keystone/content/common/xsd/RAX-KSKEY-credentials.xsd deleted file mode 100644 index 69f5ce10..00000000 --- a/keystone/content/common/xsd/RAX-KSKEY-credentials.xsd +++ /dev/null @@ -1,52 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?xml-stylesheet type="text/xsl" href="../xslt/schema.xslt"?> - -<schema - elementFormDefault="qualified" - attributeFormDefault="unqualified" - xmlns="http://www.w3.org/2001/XMLSchema" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:RAX-KSKEY="http://docs.rackspace.com/identity/api/ext/RAX-KSKEY/v1.0" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom" - targetNamespace="http://docs.rackspace.com/identity/api/ext/RAX-KSKEY/v1.0" -> - <!--Import schema we are extending --> - <import namespace="http://docs.openstack.org/identity/api/v2.0" - schemaLocation="credentials.xsd"/> - - <!-- Elements --> - <element name="apiKeyCredentials" type="RAX-KSKEY:apiKeyCredentials" substitutionGroup="identity:credential"/> - - <!-- Complex Types --> - <complexType name="apiKeyCredentials"> - <complexContent> - <extension base="identity:CredentialType"> - <attribute name="username" type="xsd:string" use="optional" ></attribute> - <attribute name="apiKey" type="xsd:string" use="required" ></attribute> - </extension> - </complexContent> - </complexType> - - <complexType name="apiKeyCredentialsWithOnlyApiKey"> - <complexContent> - <restriction base="RAX-KSKEY:apiKeyCredentials"> - <attribute name="username" type="xsd:string" use="prohibited"/> - <attribute name="apiKey" type="xsd:string" use="required" > - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - The user's API Key. - </p> - </xsd:documentation> - </annotation> - </attribute> - </restriction> - </complexContent> - </complexType> - -</schema> diff --git a/keystone/content/common/xsd/RAX-KSQA-secretQA.xsd b/keystone/content/common/xsd/RAX-KSQA-secretQA.xsd deleted file mode 100644 index cc13a492..00000000 --- a/keystone/content/common/xsd/RAX-KSQA-secretQA.xsd +++ /dev/null @@ -1,51 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?xml-stylesheet type="text/xsl" href="../xslt/schema.xslt"?> - -<schema elementFormDefault="qualified" attributeFormDefault="unqualified" - xmlns="http://www.w3.org/2001/XMLSchema" xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:RAX-KSQA="http://docs.rackspace.com/identity/api/ext/RAX-KSQA/v1.0" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" xmlns:atom="http://www.w3.org/2005/Atom" - targetNamespace="http://docs.rackspace.com/identity/api/ext/RAX-KSQA/v1.0"> - - <!--Import schema we are extending --> - <import namespace="http://docs.openstack.org/identity/api/v2.0" - schemaLocation="credentials.xsd" /> - - <!-- Elements --> - <element name="secretQA" type="RAX-KSQA:SecretQA" - substitutionGroup="identity:credential" /> - - <!-- Complex Types --> - <complexType name="SecretQA"> - <complexContent> - <annotation> - <xsd:documentation xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - A Secret Question and Answer. The answer shall serve to prove - the user's identity as it should only be able to be answered - by the user who proposed the question. - </p> - </xsd:documentation> - <xsd:appinfo> - <xsdxt:samples> - <xsdxt:sample> - <xsdxt:code type="application/xml" href="../samples/RAX-KSQA-secretQA.xml" /> - </xsdxt:sample> - <xsdxt:sample> - <xsdxt:code type="application/json" href="../samples/RAX-KSQA-secretQA.json" /> - </xsdxt:sample> - </xsdxt:samples> - </xsd:appinfo> - </annotation> - <extension base="identity:CredentialType"> - <attribute name="username" type="xsd:string" use="optional" /> - <attribute name="question" type="xsd:string" use="optional" /> - <attribute name="answer" type="xsd:string" use="optional" /> - <anyAttribute namespace="##other" processContents="lax" /> - </extension> - </complexContent> - </complexType> - -</schema> diff --git a/keystone/content/common/xsd/api-common.xsd b/keystone/content/common/xsd/api-common.xsd deleted file mode 100644 index 0625e5be..00000000 --- a/keystone/content/common/xsd/api-common.xsd +++ /dev/null @@ -1,55 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?xml-stylesheet type="text/xsl" href="../xslt/schema.xslt"?> - -<!-- (C) 2011 OpenStack LLC., All Rights Reserved --> - -<schema - elementFormDefault="qualified" - attributeFormDefault="unqualified" - xmlns="http://www.w3.org/2001/XMLSchema" - xmlns:capi="http://docs.openstack.org/common/api/v1.0" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - targetNamespace="http://docs.openstack.org/common/api/v1.0" -> - <annotation> - <xsd:appinfo - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <xsdxt:title>Open Stack Common API Schema Types 1.0</xsdxt:title> - <xsdxt:link rev="index" href="extensions.xsd" /> - <xsdxt:link rev="index" href="limits.xsd" /> - <xsdxt:link rev="index" href="version.xsd" /> - </xsd:appinfo> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - This is the main index XML Schema document - for Common API Schema Types Version 1.0. - </p> - </xsd:documentation> - </annotation> - <include schemaLocation="extensions.xsd"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - Types related to extensions. - </p> - </xsd:documentation> - </annotation> - </include> - <include schemaLocation="version.xsd"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - Types related to API version details. - </p> - </xsd:documentation> - </annotation> - </include> -</schema> diff --git a/keystone/content/common/xsd/api.xsd b/keystone/content/common/xsd/api.xsd deleted file mode 100644 index 5b6140cc..00000000 --- a/keystone/content/common/xsd/api.xsd +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?xml-stylesheet type="text/xsl" href="../xslt/schema.xslt"?> - -<!-- (C) 2011 OpenStack LLC., All Rights Reserved --> - -<schema - elementFormDefault="qualified" - attributeFormDefault="unqualified" - xmlns="http://www.w3.org/2001/XMLSchema" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - targetNamespace="http://docs.openstack.org/identity/api/v2.0" -> - <include schemaLocation="token.xsd"/> - <include schemaLocation="tenant.xsd"/> - <include schemaLocation="fault.xsd"/> - <include schemaLocation="endpoints.xsd"/> - <include schemaLocation="roles.xsd"/> - <include schemaLocation="user.xsd"/> - <include schemaLocation="credentials.xsd"/> - <include schemaLocation="services.xsd"/> -</schema> diff --git a/keystone/content/common/xsd/atom/atom.xsd b/keystone/content/common/xsd/atom/atom.xsd deleted file mode 100644 index c515c497..00000000 --- a/keystone/content/common/xsd/atom/atom.xsd +++ /dev/null @@ -1,115 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<xs:schema elementFormDefault="qualified" attributeFormDefault="unqualified" - targetNamespace="http://www.w3.org/2005/Atom" - xmlns:html="http://www.w3.org/1999/xhtml" - xmlns:atom="http://www.w3.org/2005/Atom" - xmlns:xml="http://www.w3.org/XML/1998/namespace" - xmlns:xs="http://www.w3.org/2001/XMLSchema"> - - <xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="xml.xsd"/> - - <xs:simpleType name="relation"> - <xs:restriction base="xs:string"> - <xs:enumeration value="alternate" /> - <xs:enumeration value="appendix" /> - <xs:enumeration value="archives" /> - <xs:enumeration value="author" /> - <xs:enumeration value="bookmark" /> - <xs:enumeration value="chapter" /> - <xs:enumeration value="contents" /> - <xs:enumeration value="copyright" /> - <xs:enumeration value="current" /> - <xs:enumeration value="describedby" /> - <xs:enumeration value="edit" /> - <xs:enumeration value="edit-media" /> - <xs:enumeration value="first" /> - <xs:enumeration value="glossary" /> - <xs:enumeration value="help" /> - <xs:enumeration value="hub" /> - <xs:enumeration value="icon" /> - <xs:enumeration value="index" /> - <xs:enumeration value="last" /> - <xs:enumeration value="latest-version" /> - <xs:enumeration value="license" /> - <xs:enumeration value="monitor" /> - <xs:enumeration value="monitor-group" /> - <xs:enumeration value="next" /> - <xs:enumeration value="next-arvhice" /> - <xs:enumeration value="nofollow" /> - <xs:enumeration value="payment" /> - <xs:enumeration value="predecessor-version" /> - <xs:enumeration value="prefetch" /> - <xs:enumeration value="prev" /> - <xs:enumeration value="previous" /> - <xs:enumeration value="prev-archive" /> - <xs:enumeration value="replies" /> - <xs:enumeration value="search" /> - <xs:enumeration value="section" /> - <xs:enumeration value="self" /> - <xs:enumeration value="service" /> - <xs:enumeration value="start" /> - <xs:enumeration value="stylesheet" /> - <xs:enumeration value="subsection" /> - <xs:enumeration value="successor-version" /> - <xs:enumeration value="up" /> - <xs:enumeration value="version-history" /> - <xs:enumeration value="via" /> - <xs:enumeration value="working-copy" /> - <xs:enumeration value="working-copy-of" /> - </xs:restriction> - </xs:simpleType> - - <xs:element name="link" type="atom:link" /> - - <xs:complexType name="link"> - <xs:annotation> - <xs:documentation> - <html:p>See section 3.4 of the ATOM RFC <html:a href="http://tools.ietf.org/html/rfc4287">RFC4287</html:a></html:p> - </xs:documentation> - </xs:annotation> - - <xs:attribute name="rel" use="required" type="atom:relation"> - <xs:annotation> - <xs:documentation> - <html:p>TODO(Jorge)</html:p> - </xs:documentation> - </xs:annotation> - </xs:attribute> - - <xs:attribute name="type" use="optional" type="xs:string"> - <xs:annotation> - <xs:documentation> - <html:p>TODO(Jorge)</html:p> - </xs:documentation> - </xs:annotation> - </xs:attribute> - - <xs:attribute name="href" use="required" type="xs:anyURI"> - <xs:annotation> - <xs:documentation> - <html:p>TODO(Jorge)</html:p> - </xs:documentation> - </xs:annotation> - </xs:attribute> - - <xs:attribute name="hreflang" use="optional" type="xs:NMTOKEN"> - <xs:annotation> - <xs:documentation> - <html:p>TODO(Jorge)</html:p> - </xs:documentation> - </xs:annotation> - </xs:attribute> - - <xs:attribute name="title" use="optional" type="xs:string"> - <xs:annotation> - <xs:documentation> - <html:p>TODO(Jorge)</html:p> - </xs:documentation> - </xs:annotation> - </xs:attribute> - - <xs:attribute ref="xml:base" /> - <xs:attribute ref="xml:lang" /> - </xs:complexType> -</xs:schema> diff --git a/keystone/content/common/xsd/atom/xml.xsd b/keystone/content/common/xsd/atom/xml.xsd deleted file mode 100644 index aea7d0db..00000000 --- a/keystone/content/common/xsd/atom/xml.xsd +++ /dev/null @@ -1,287 +0,0 @@ -<?xml version='1.0'?> -<?xml-stylesheet href="../2008/09/xsd.xsl" type="text/xsl"?> -<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace" - xmlns:xs="http://www.w3.org/2001/XMLSchema" - xmlns ="http://www.w3.org/1999/xhtml" - xml:lang="en"> - - <xs:annotation> - <xs:documentation> - <div> - <h1>About the XML namespace</h1> - - <div class="bodytext"> - <p> - This schema document describes the XML namespace, in a form - suitable for import by other schema documents. - </p> - <p> - See <a href="http://www.w3.org/XML/1998/namespace.html"> - http://www.w3.org/XML/1998/namespace.html</a> and - <a href="http://www.w3.org/TR/REC-xml"> - http://www.w3.org/TR/REC-xml</a> for information - about this namespace. - </p> - <p> - Note that local names in this namespace are intended to be - defined only by the World Wide Web Consortium or its subgroups. - The names currently defined in this namespace are listed below. - They should not be used with conflicting semantics by any Working - Group, specification, or document instance. - </p> - <p> - See further below in this document for more information about <a - href="#usage">how to refer to this schema document from your own - XSD schema documents</a> and about <a href="#nsversioning">the - namespace-versioning policy governing this schema document</a>. - </p> - </div> - </div> - </xs:documentation> - </xs:annotation> - - <xs:attribute name="lang"> - <xs:annotation> - <xs:documentation> - <div> - - <h3>lang (as an attribute name)</h3> - <p> - denotes an attribute whose value - is a language code for the natural language of the content of - any element; its value is inherited. This name is reserved - by virtue of its definition in the XML specification.</p> - - </div> - <div> - <h4>Notes</h4> - <p> - Attempting to install the relevant ISO 2- and 3-letter - codes as the enumerated possible values is probably never - going to be a realistic possibility. - </p> - <p> - See BCP 47 at <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt"> - http://www.rfc-editor.org/rfc/bcp/bcp47.txt</a> - and the IANA language subtag registry at - <a href="http://www.iana.org/assignments/language-subtag-registry"> - http://www.iana.org/assignments/language-subtag-registry</a> - for further information. - </p> - <p> - The union allows for the 'un-declaration' of xml:lang with - the empty string. - </p> - </div> - </xs:documentation> - </xs:annotation> - <xs:simpleType> - <xs:union memberTypes="xs:language"> - <xs:simpleType> - <xs:restriction base="xs:string"> - <xs:enumeration value=""/> - </xs:restriction> - </xs:simpleType> - </xs:union> - </xs:simpleType> - </xs:attribute> - - <xs:attribute name="space"> - <xs:annotation> - <xs:documentation> - <div> - - <h3>space (as an attribute name)</h3> - <p> - denotes an attribute whose - value is a keyword indicating what whitespace processing - discipline is intended for the content of the element; its - value is inherited. This name is reserved by virtue of its - definition in the XML specification.</p> - - </div> - </xs:documentation> - </xs:annotation> - <xs:simpleType> - <xs:restriction base="xs:NCName"> - <xs:enumeration value="default"/> - <xs:enumeration value="preserve"/> - </xs:restriction> - </xs:simpleType> - </xs:attribute> - - <xs:attribute name="base" type="xs:anyURI"> <xs:annotation> - <xs:documentation> - <div> - - <h3>base (as an attribute name)</h3> - <p> - denotes an attribute whose value - provides a URI to be used as the base for interpreting any - relative URIs in the scope of the element on which it - appears; its value is inherited. This name is reserved - by virtue of its definition in the XML Base specification.</p> - - <p> - See <a - href="http://www.w3.org/TR/xmlbase/">http://www.w3.org/TR/xmlbase/</a> - for information about this attribute. - </p> - </div> - </xs:documentation> - </xs:annotation> - </xs:attribute> - - <xs:attribute name="id" type="xs:ID"> - <xs:annotation> - <xs:documentation> - <div> - - <h3>id (as an attribute name)</h3> - <p> - denotes an attribute whose value - should be interpreted as if declared to be of type ID. - This name is reserved by virtue of its definition in the - xml:id specification.</p> - - <p> - See <a - href="http://www.w3.org/TR/xml-id/">http://www.w3.org/TR/xml-id/</a> - for information about this attribute. - </p> - </div> - </xs:documentation> - </xs:annotation> - </xs:attribute> - - <xs:attributeGroup name="specialAttrs"> - <xs:attribute ref="xml:base"/> - <xs:attribute ref="xml:lang"/> - <xs:attribute ref="xml:space"/> - <xs:attribute ref="xml:id"/> - </xs:attributeGroup> - - <xs:annotation> - <xs:documentation> - <div> - - <h3>Father (in any context at all)</h3> - - <div class="bodytext"> - <p> - denotes Jon Bosak, the chair of - the original XML Working Group. This name is reserved by - the following decision of the W3C XML Plenary and - XML Coordination groups: - </p> - <blockquote> - <p> - In appreciation for his vision, leadership and - dedication the W3C XML Plenary on this 10th day of - February, 2000, reserves for Jon Bosak in perpetuity - the XML name "xml:Father". - </p> - </blockquote> - </div> - </div> - </xs:documentation> - </xs:annotation> - - <xs:annotation> - <xs:documentation> - <div xml:id="usage" id="usage"> - <h2><a name="usage">About this schema document</a></h2> - - <div class="bodytext"> - <p> - This schema defines attributes and an attribute group suitable - for use by schemas wishing to allow <code>xml:base</code>, - <code>xml:lang</code>, <code>xml:space</code> or - <code>xml:id</code> attributes on elements they define. - </p> - <p> - To enable this, such a schema must import this schema for - the XML namespace, e.g. as follows: - </p> - <pre> - <schema . . .> - . . . - <import namespace="http://www.w3.org/XML/1998/namespace" - schemaLocation="http://www.w3.org/2001/xml.xsd"/> - </pre> - <p> - or - </p> - <pre> - <import namespace="http://www.w3.org/XML/1998/namespace" - schemaLocation="http://www.w3.org/2009/01/xml.xsd"/> - </pre> - <p> - Subsequently, qualified reference to any of the attributes or the - group defined below will have the desired effect, e.g. - </p> - <pre> - <type . . .> - . . . - <attributeGroup ref="xml:specialAttrs"/> - </pre> - <p> - will define a type which will schema-validate an instance element - with any of those attributes. - </p> - </div> - </div> - </xs:documentation> - </xs:annotation> - - <xs:annotation> - <xs:documentation> - <div id="nsversioning" xml:id="nsversioning"> - <h2><a name="nsversioning">Versioning policy for this schema document</a></h2> - <div class="bodytext"> - <p> - In keeping with the XML Schema WG's standard versioning - policy, this schema document will persist at - <a href="http://www.w3.org/2009/01/xml.xsd"> - http://www.w3.org/2009/01/xml.xsd</a>. - </p> - <p> - At the date of issue it can also be found at - <a href="http://www.w3.org/2001/xml.xsd"> - http://www.w3.org/2001/xml.xsd</a>. - </p> - <p> - The schema document at that URI may however change in the future, - in order to remain compatible with the latest version of XML - Schema itself, or with the XML namespace itself. In other words, - if the XML Schema or XML namespaces change, the version of this - document at <a href="http://www.w3.org/2001/xml.xsd"> - http://www.w3.org/2001/xml.xsd - </a> - will change accordingly; the version at - <a href="http://www.w3.org/2009/01/xml.xsd"> - http://www.w3.org/2009/01/xml.xsd - </a> - will not change. - </p> - <p> - Previous dated (and unchanging) versions of this schema - document are at: - </p> - <ul> - <li><a href="http://www.w3.org/2009/01/xml.xsd"> - http://www.w3.org/2009/01/xml.xsd</a></li> - <li><a href="http://www.w3.org/2007/08/xml.xsd"> - http://www.w3.org/2007/08/xml.xsd</a></li> - <li><a href="http://www.w3.org/2004/10/xml.xsd"> - http://www.w3.org/2004/10/xml.xsd</a></li> - <li><a href="http://www.w3.org/2001/03/xml.xsd"> - http://www.w3.org/2001/03/xml.xsd</a></li> - </ul> - </div> - </div> - </xs:documentation> - </xs:annotation> - -</xs:schema> - diff --git a/keystone/content/common/xsd/credentials.xsd b/keystone/content/common/xsd/credentials.xsd deleted file mode 100644 index 36e59942..00000000 --- a/keystone/content/common/xsd/credentials.xsd +++ /dev/null @@ -1,105 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?xml-stylesheet type="text/xsl" href="../xslt/schema.xslt"?> - -<!-- (C) 2011 OpenStack LLC., All Rights Reserved --> - -<schema - elementFormDefault="qualified" - attributeFormDefault="unqualified" - xmlns="http://www.w3.org/2001/XMLSchema" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom" - targetNamespace="http://docs.openstack.org/identity/api/v2.0" -> - - <!-- Import ATOM specific schema definitions --> - <import vc:minVersion="1.1" namespace="http://www.w3.org/2005/Atom" - schemaLocation="atom/atom.xsd" /> - - <!-- Elements --> - <element name="auth" type="identity:AuthenticationRequest"/> - <element name="credential" type="identity:CredentialType"/> - <element name="credentials" type="identity:CredentialListType"/> - <element name="passwordCredentials" type="identity:PasswordCredentialsRequiredUsername" substitutionGroup="identity:credential"/> - - <!-- Complex Types --> - <complexType name="CredentialType" abstract="true"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - Base type for credential in Keystone. - </p> - </xsd:documentation> - </annotation> - <sequence> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </sequence> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - - - <complexType name="AuthenticationRequest"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - Both the tenantId and tenantName are optional, but should not be specified together. If both attributes are specified, the server SHOULD respond with a 400 Bad Request. - </p> - </xsd:documentation> - </annotation> - <sequence> - <choice> - <element ref="identity:credential" minOccurs="1"/> - <element name="token" type="identity:TokenForAuthenticationRequest"/> - </choice> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> - </sequence> - <attribute name="tenantId" type="xsd:string" use="optional"/> - <attribute name="tenantName" type="xsd:string" use="optional"/> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - - <complexType name="TokenForAuthenticationRequest"> - <attribute name="id" type="xsd:string" use="required"/> - </complexType> - - <complexType name="PasswordCredentialsBase"> - <complexContent> - <extension base="identity:CredentialType"> - <attribute name="username" type="xsd:string" use="optional" /> - <attribute name="password" type="xsd:string" use="required" /> - </extension> - </complexContent> - </complexType> - - <complexType name="PasswordCredentialsWithoutUsername"> - <complexContent> - <restriction base="identity:PasswordCredentialsBase"> - <attribute name="username" type="xsd:string" use="prohibited" /> - <attribute name="password" type="xsd:string" use="required" /> - </restriction> - </complexContent> - </complexType> - - <complexType name="PasswordCredentialsRequiredUsername"> - <complexContent> - <restriction base="identity:PasswordCredentialsBase"> - <attribute name="username" type="xsd:string" use="required" /> - <attribute name="password" type="xsd:string" use="required" /> - </restriction> - </complexContent> - </complexType> - - <complexType name="CredentialListType"> - <sequence> - <element ref="identity:credential" minOccurs="1" maxOccurs="unbounded"/> - </sequence> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> -</schema> diff --git a/keystone/content/common/xsd/endpoints.xsd b/keystone/content/common/xsd/endpoints.xsd deleted file mode 100644 index dbb07516..00000000 --- a/keystone/content/common/xsd/endpoints.xsd +++ /dev/null @@ -1,161 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?xml-stylesheet type="text/xsl" href="../xslt/schema.xslt"?> - -<!-- (C) 2011 OpenStack LLC., All Rights Reserved --> - -<schema - elementFormDefault="qualified" - attributeFormDefault="unqualified" - xmlns="http://www.w3.org/2001/XMLSchema" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom" - targetNamespace="http://docs.openstack.org/identity/api/v2.0" - > - <include schemaLocation="services.xsd"/> - <include schemaLocation="token.xsd"/> - <!-- Import ATOM specific schema definitions --> - <import vc:minVersion="1.1" namespace="http://www.w3.org/2005/Atom" - schemaLocation="atom/atom.xsd" /> - - <element name="endpoint" type="identity:Endpoint"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - An Endpoint. - </p> - </xsd:documentation> - </annotation> - </element> - <element name="endpoints" type="identity:EndpointList"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - A list of Endpoints. - </p> - </xsd:documentation> - </annotation> - </element> - - <!-- Complex Types --> - <complexType name="Endpoint"> - <sequence> - <element name="version" type="identity:VersionForService" maxOccurs="1" minOccurs="0"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - Version details. - </p> - </xsd:documentation> - </annotation> - </element> - <element vc:minVersion="1.1" ref="atom:link" minOccurs="0" maxOccurs="unbounded" /> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </sequence> - <attribute name="id" type="xsd:string" use="required"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - An ID uniquely identifying the Endpoint. - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="type" type="identity:ExtensibleServiceType" use="required"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - The OpenStack-registered type (e.g. 'compute', 'object-store', etc). - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="name" type="xsd:string" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - The commercial service name (e.g. 'My Nova Cloud Servers'). - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="region" type="xsd:string" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - The region of Endpoint Template. - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="publicURL" type="xsd:anyURI" use="required"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - The public URL to access represented service. - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="internalURL" type="xsd:anyURI" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - The internal version of the public URL. - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="adminURL" type="xsd:anyURI" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - The admin URL. - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="tenantId" type="xsd:string" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - Tenant id to which the endpoints belong. - </p> - </xsd:documentation> - </annotation> - </attribute> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - <complexType name="EndpointList"> - <sequence> - <element name="endpoint" type="identity:Endpoint" minOccurs="0" maxOccurs="unbounded"/> - <element vc:minVersion="1.1" ref="atom:link" minOccurs="0" maxOccurs="unbounded" /> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </sequence> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> -</schema> diff --git a/keystone/content/common/xsd/extensions.xsd b/keystone/content/common/xsd/extensions.xsd deleted file mode 100644 index 26694c07..00000000 --- a/keystone/content/common/xsd/extensions.xsd +++ /dev/null @@ -1,60 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?xml-stylesheet type="text/xsl" href="../xslt/schema.xslt"?> - -<!-- (C) 2011 OpenStack LLC., All Rights Reserved --> - -<xsd:schema elementFormDefault="qualified" attributeFormDefault="unqualified" - targetNamespace="http://docs.openstack.org/common/api/v1.0" - xmlns:ext="http://docs.openstack.org/common/api/v1.0" - xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" - xmlns:html="http://www.w3.org/1999/xhtml" - xmlns:atom="http://www.w3.org/2005/Atom" - xmlns:xsd="http://www.w3.org/2001/XMLSchema"> - - <!-- Import ATOM specific schema definitions --> - <xsd:import namespace="http://www.w3.org/2005/Atom" schemaLocation="atom/atom.xsd" /> - - <xsd:element name="extensions" type="ext:Extensions"/> - <xsd:element name="extension" type="ext:Extension"/> - - <xsd:complexType name="Extensions"> - <xsd:sequence> - <xsd:element name="extension" type="ext:Extension" minOccurs="0" maxOccurs="unbounded" /> - <xsd:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </xsd:sequence> - <xsd:anyAttribute namespace="##other" processContents="lax"/> - </xsd:complexType> - - <xsd:complexType name="Extension"> - <xsd:sequence> - <xsd:element name="description" type="xsd:string" minOccurs="1" /> - <!--<xsd:element vc:minVersion="1.1" ref="atom:link" minOccurs="0" maxOccurs="unbounded" />--> - <xsd:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </xsd:sequence> - <xsd:attribute name="name" type="xsd:string" use="required"/> - <xsd:attribute name="namespace" type="xsd:anyURI" use="required"/> - <xsd:attribute name="alias" type="ext:Alias" use="required"/> - <xsd:attribute name="updated" type="xsd:dateTime" use="optional"/> - <xsd:anyAttribute namespace="##other" processContents="lax"/> - <!--TODO(Ziad)resolve asser issue - <xsd:assert vc:minVersion="1.1" test="atom:link[@rel='describedby']"> - <xsd:annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - There should be at least one atom link - with a describedby relation. - </p> - </xsd:documentation> - </xsd:annotation> - </xsd:assert>--> - </xsd:complexType> - - <xsd:simpleType name="Alias"> - <xsd:restriction base="xsd:string"> - <xsd:pattern value="\w+\-\w+" /> - </xsd:restriction> - </xsd:simpleType> - -</xsd:schema>
\ No newline at end of file diff --git a/keystone/content/common/xsd/fault.xsd b/keystone/content/common/xsd/fault.xsd deleted file mode 100644 index 4776ddaf..00000000 --- a/keystone/content/common/xsd/fault.xsd +++ /dev/null @@ -1,138 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?xml-stylesheet type="text/xsl" href="../xslt/schema.xslt"?> - -<!-- (C) 2011 OpenStack LLC., All Rights Reserved --> - -<schema - elementFormDefault="qualified" - attributeFormDefault="unqualified" - xmlns="http://www.w3.org/2001/XMLSchema" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - targetNamespace="http://docs.openstack.org/identity/api/v2.0" -> - <!-- Fault Elements --> - <element name="identityFault" type="identity:IdentityFault"/> - <element name="serviceUnavailable" type="identity:ServiceUnavailableFault"/> - <element name="badRequest" type="identity:BadRequestFault"/> - <element name="unauthorized" type="identity:UnauthorizedFault"/> - <element name="overLimit" type="identity:OverLimitFault"/> - <element name="userDisabled" type="identity:UserDisabledFault"/> - <element name="forbidden" type="identity:ForbiddenFault"/> - <element name="itemNotFound" type="identity:ItemNotFoundFault"/> - <element name="tenantConflict" type="identity:TenantConflictFault"/> - - <!-- Fault Types --> - <complexType name="IdentityFault"> - <sequence> - <element name="message" type="xsd:string"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - A human readable message that is appropriate for display - to the end user. - </p> - </xsd:documentation> - </annotation> - </element> - <element name="details" type="xsd:string" minOccurs="0"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - The optional <details> element may contain useful - information for tracking down errors (e.g a stack - trace). This information may or may not be appropriate - for display to an end user. - </p> - </xsd:documentation> - </annotation> - </element> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </sequence> - <attribute name="code" type="xsd:int" use="required"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - The HTTP status code associated with the current fault. - </p> - </xsd:documentation> - </annotation> - </attribute> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - - <complexType name="ServiceUnavailableFault"> - <complexContent> - <extension base="identity:IdentityFault"> - </extension> - </complexContent> - </complexType> - - <complexType name="BadRequestFault"> - <complexContent> - <extension base="identity:IdentityFault"> - </extension> - </complexContent> - </complexType> - - <complexType name="UnauthorizedFault"> - <complexContent> - <extension base="identity:IdentityFault"> - </extension> - </complexContent> - </complexType> - - <complexType name="UserDisabledFault"> - <complexContent> - <extension base="identity:IdentityFault"> - </extension> - </complexContent> - </complexType> - - <complexType name="ForbiddenFault"> - <complexContent> - <extension base="identity:IdentityFault"> - </extension> - </complexContent> - </complexType> - - <complexType name="ItemNotFoundFault"> - <complexContent> - <extension base="identity:IdentityFault"> - </extension> - </complexContent> - </complexType> - - <complexType name="TenantConflictFault"> - <complexContent> - <extension base="identity:IdentityFault"> - </extension> - </complexContent> - </complexType> - - <complexType name="OverLimitFault"> - <complexContent> - <extension base="identity:IdentityFault"> - <attribute name="retryAt" type="xsd:dateTime" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - An optional dateTime denoting when an operation should - be retried. - </p> - </xsd:documentation> - </annotation> - </attribute> - </extension> - </complexContent> - </complexType> - -</schema> diff --git a/keystone/content/common/xsd/roles.xsd b/keystone/content/common/xsd/roles.xsd deleted file mode 100644 index 0ed30143..00000000 --- a/keystone/content/common/xsd/roles.xsd +++ /dev/null @@ -1,66 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?xml-stylesheet type="text/xsl" href="../xslt/schema.xslt"?> - -<!-- (C) 2011 OpenStack LLC., All Rights Reserved --> - -<schema - elementFormDefault="qualified" - attributeFormDefault="unqualified" - xmlns="http://www.w3.org/2001/XMLSchema" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom" - targetNamespace="http://docs.openstack.org/identity/api/v2.0" -> - - <!-- Import ATOM specific schema definitions --> - <import vc:minVersion="1.1" namespace="http://www.w3.org/2005/Atom" - schemaLocation="atom/atom.xsd" /> - - <!-- Elements --> - <element name="roles" type="identity:RoleList" > - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - A list of roles. - </p> - </xsd:documentation> - </annotation> - </element> - - <element name="role" type="identity:Role" > - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - A role. - </p> - </xsd:documentation> - </annotation> - </element> - - - <!-- Complex Types --> - <complexType name="Role"> - <attribute name="id" type="xsd:string" use="optional"/> - <attribute name="name" type="xsd:string" use="required"/> - <attribute name="description" type="xsd:string" use="optional"/> - <attribute name="serviceId" type="xsd:string" use="optional"/> - <attribute name="tenantId" type="xsd:string" use="optional"/> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - - <complexType name="RoleList"> - <sequence> - <element name="role" type="identity:Role" minOccurs="0" maxOccurs="unbounded"/> - <element vc:minVersion="1.1" ref="atom:link" minOccurs="0" maxOccurs="unbounded" /> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </sequence> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> -</schema>
\ No newline at end of file diff --git a/keystone/content/common/xsd/services.xsd b/keystone/content/common/xsd/services.xsd deleted file mode 100644 index 248e1e93..00000000 --- a/keystone/content/common/xsd/services.xsd +++ /dev/null @@ -1,103 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?xml-stylesheet type="text/xsl" href="../xslt/schema.xslt"?> - -<!-- (C) 2011 OpenStack LLC., All Rights Reserved --> - -<schema - elementFormDefault="qualified" - attributeFormDefault="unqualified" - xmlns="http://www.w3.org/2001/XMLSchema" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom" - targetNamespace="http://docs.openstack.org/identity/api/v2.0" -> - - <!-- Import ATOM specific schema definitions --> - <import vc:minVersion="1.1" namespace="http://www.w3.org/2005/Atom" - schemaLocation="atom/atom.xsd" /> - - <!-- Simple Types --> - <simpleType name="ExtensibleServiceType"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - An extensible service type allows all of the - strings defined in <a href="#type_ServiceType" - title="See definition of - ServiceType">ServiceType</a> or an - alias prefixed status. - </p> - </xsd:documentation> - </annotation> - <union memberTypes="identity:ServiceType identity:ExtendedService"/> - </simpleType> - - <simpleType name="ServiceType"> - <restriction base="xsd:string"> - <enumeration value="compute"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - The type for an OpenStack Compute API 1.1 compatible service. - </p> - </xsd:documentation> - </annotation> - </enumeration> - <enumeration value="object-store"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - The type for a Swift-compatible service. - </p> - </xsd:documentation> - </annotation> - </enumeration> - <enumeration value="image-service"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - The type for a Glance-compatible service - </p> - </xsd:documentation> - </annotation> - </enumeration> - <enumeration value="identity"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - The type for a Keystone-compatible service. - </p> - </xsd:documentation> - </annotation> - </enumeration> - </restriction> - </simpleType> - - <simpleType name="ExtendedService"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - A non-core service type which must contain an extension prefix. - </p> - </xsd:documentation> - </annotation> - <restriction base="xsd:string"> - <pattern value="(\w|-)+:\w+"/> - </restriction> - </simpleType> -</schema>
\ No newline at end of file diff --git a/keystone/content/common/xsd/tenant.xsd b/keystone/content/common/xsd/tenant.xsd deleted file mode 100644 index 8e64bd14..00000000 --- a/keystone/content/common/xsd/tenant.xsd +++ /dev/null @@ -1,151 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?xml-stylesheet type="text/xsl" href="../xslt/schema.xslt"?> - -<!-- (C) 2011 OpenStack LLC., All Rights Reserved --> - -<schema - elementFormDefault="qualified" - attributeFormDefault="unqualified" - xmlns="http://www.w3.org/2001/XMLSchema" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom" - targetNamespace="http://docs.openstack.org/identity/api/v2.0" -> - <!-- Import ATOM specific schema definitions --> - <import vc:minVersion="1.1" namespace="http://www.w3.org/2005/Atom" - schemaLocation="atom/atom.xsd" /> - - <!-- Elements --> - <element name="tenant" type="identity:Tenant"> - <annotation> - <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> - <p> - A container used to group or isolate resources and/or identity - objects. Depending on the service operator, a tenant may map to a customer, - account, organization, or project. - </p> - </xsd:documentation> - <xsd:appinfo> - <xsdxt:samples> - Â Â Â Â Â <xsdxt:sample> - Â Â Â Â Â Â <xsdxt:code type="application/xml" href="../samples/tenant.xml" /> - Â Â Â Â Â </xsdxt:sample> - Â Â Â Â Â <xsdxt:sample> - Â Â Â Â Â Â <xsdxt:code type="application/json" href="../samples/tenant.json" /> - Â Â Â Â Â </xsdxt:sample> - Â Â Â Â </xsdxt:samples> - </xsd:appinfo> - </annotation> - </element> - <element name="tenants" type="identity:Tenants"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - A list of tenants. - </p> - </xsd:documentation> - </annotation> - </element> - - <!-- Complex Types --> - <complexType name="Tenants"> - <sequence> - <element name="tenant" type="identity:Tenant" maxOccurs="100"/> - <element vc:minVersion="1.1" ref="atom:link" minOccurs="0" maxOccurs="unbounded" /> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </sequence> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - - <complexType name="Tenant"> - <sequence> - <element name="description" type="xsd:string" minOccurs="0"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - An free text description of the tenant. - </p> - </xsd:documentation> - </annotation> - </element> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </sequence> - <attribute name="id" type="xsd:string" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - An ID uniquely identifying the tenant. This usually comes from the back-end store. - This value is guaranteed to be unique and immutable (it will never change). - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="name" type="xsd:string" use="required"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - The name of the tenant. This is guaranteed to be unique, but may change. - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="enabled" type="xsd:boolean" use="optional" default="true"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - An boolean signifying if a tenant is enabled or not. A disabled tenant - cannot be authenticated against. - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="display-name" type="xsd:string" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - A human-readable, friendly name for use in user interfaces. - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute type="xsd:dateTime" name="updated" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - A time-stamp identifying the modification time of the - tenant. - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute type="xsd:dateTime" name="created" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - A creation time-stamp for the tenant. - </p> - </xsd:documentation> - </annotation> - </attribute> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> -</schema> diff --git a/keystone/content/common/xsd/token.xsd b/keystone/content/common/xsd/token.xsd deleted file mode 100644 index 5e410716..00000000 --- a/keystone/content/common/xsd/token.xsd +++ /dev/null @@ -1,298 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?xml-stylesheet type="text/xsl" href="../xslt/schema.xslt"?> - -<!-- (C) 2011 OpenStack LLC., All Rights Reserved --> - -<schema - elementFormDefault="qualified" - attributeFormDefault="unqualified" - xmlns="http://www.w3.org/2001/XMLSchema" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom" - targetNamespace="http://docs.openstack.org/identity/api/v2.0" -> - - <include schemaLocation="roles.xsd"/> - <include schemaLocation="services.xsd"/> - - <!-- Import ATOM specific schema definitions --> - <import vc:minVersion="1.1" namespace="http://www.w3.org/2005/Atom" - schemaLocation="atom/atom.xsd" /> - - <!-- Elements --> - <element name="access" type="identity:AuthenticateResponse"/> - - <!-- Complex Types --> - <complexType name="Token"> - <annotation> - <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> - <p> - A token is an arbitrary bit of text that is used to access - resources. Each token has a scope which describes which - resources are accessible with it. A token may be - revoked at anytime and is valid for a finite duration. - </p> - <p> - While Keystone supports token-based authentication in this release, - the intention is for it to support additional protocols in the - future. The desire is for it to be an integration service, and not - a full-fledged identity store and management solution. - </p> - </xsd:documentation> - <xsd:appinfo> - <xsdxt:samples> - <xsdxt:sample> - <xsdxt:code type="application/xml" href="../samples/token.xml" /> - </xsdxt:sample> - <xsdxt:sample> - <xsdxt:code type="application/json" href="../samples/token.json" /> - </xsdxt:sample> - </xsdxt:samples> - </xsd:appinfo> - </annotation> - <sequence> - <element name="tenant" type="identity:TenantForAuthenticateResponse"/> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </sequence> - <attribute name="id" type="xsd:string" use="required"/> - <attribute name="expires" type="xsd:dateTime" use="required"/> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - - <complexType name="AuthenticateResponse"> - <sequence> - <element name="token" type="identity:Token"/> - <element name="user" type="identity:UserForAuthenticateResponse"/> - <element name="serviceCatalog" type="identity:ServiceCatalog" minOccurs="0"/> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </sequence> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - - <complexType name="UserForAuthenticateResponse"> - <sequence> - <element name="roles" type="identity:RoleList" /> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </sequence> - <attribute name="id" type="xsd:string"/> - <attribute name="name" type="xsd:string"/> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - - <complexType name="TenantForAuthenticateResponse"> - <attribute name="id" type="xsd:string"/> - <attribute name="name" type="xsd:string"/> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - - <complexType name="ServiceCatalog"> - <annotation> - <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> - <p> - The service catalog lists the services you have access to - </p> - <div class="design"> - <p> - We optimized for future flexibility around the hierarchy. So we - left the design as a flat list of endpoints with attributes and the - consumer can categorize as they need. - This results in potential duplication (such as with the version/@list) - but we acceopt that normalization cost in order to not force an - artificial hierarchy (suchas on region, which can be optional). - </p> - </div> - </xsd:documentation> - <xsd:appinfo> - <xsdxt:samples> - Â Â Â Â Â <xsdxt:sample> - Â Â Â Â Â Â <xsdxt:code type="application/xml" href="../samples/services.xml" /> - Â Â Â Â Â </xsdxt:sample> - Â Â Â Â Â <xsdxt:sample> - Â Â Â Â Â Â <xsdxt:code type="application/json" href="../samples/services.json" /> - Â Â Â Â Â </xsdxt:sample> - Â Â Â Â </xsdxt:samples> - </xsd:appinfo> - </annotation> - <sequence> - <element name="service" type="identity:ServiceForCatalog" minOccurs="1" maxOccurs="unbounded"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - A list of services. - </p> - </xsd:documentation> - </annotation> - </element> - <element vc:minVersion="1.1" ref="atom:link" minOccurs="0" maxOccurs="unbounded" /> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </sequence> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - - <complexType name="ServiceForCatalog"> - <sequence> - <element name="endpoint" type="identity:EndpointForService" minOccurs="1" maxOccurs="unbounded"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - A list of endpoints. - </p> - </xsd:documentation> - </annotation> - </element> - <element vc:minVersion="1.1" ref="atom:link" minOccurs="0" maxOccurs="unbounded" /> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </sequence> - <attribute name="type" type="identity:ExtensibleServiceType" use="required"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - The OpenStack-registered type (e.g. 'compute', 'object-store', etc). - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="name" type="xsd:string" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - The commercial service name (e.g. 'My Nova Cloud Servers'). - </p> - </xsd:documentation> - </annotation> - </attribute> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - - <!--EndpointForService--> - <complexType name="EndpointForService"> - <sequence> - <element name="version" type="identity:VersionForService" maxOccurs="1" minOccurs="0"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - Version details. - </p> - </xsd:documentation> - </annotation> - </element> - <element vc:minVersion="1.1" ref="atom:link" minOccurs="0" maxOccurs="unbounded" /> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </sequence> - <attribute name="region" type="xsd:string" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - The name of the region where the endpoint - lives. Example: airport codes; LHR (UK), - STL (Saint Louis) - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="tenantId" type="xsd:string" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - Tenant id to which the endpoints belong. - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="publicURL" type="xsd:anyURI" use="required"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - Public accessible service URL. - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="internalURL" type="xsd:anyURI" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - A service URL, accessible only locally within that - cloud (generally over a high bandwidth, low latency, - free of charge link). - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="adminURL" type="xsd:anyURI" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - An Admin URL (used for administration using privileged - calls). This may expose - additional functionality not found in the public and - internal URL. - </p> - </xsd:documentation> - </annotation> - </attribute> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - - <!-- VersionForService --> - <complexType name="VersionForService"> - <attribute name="id" type="xsd:string" use="required"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - Id of the version. - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="info" type="xsd:anyURI" use="required"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - URI to get the information specific to this version. - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="list" type="xsd:anyURI" use="required"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - URI to get the information about all versions. - </p> - </xsd:documentation> - </annotation> - </attribute> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> -</schema> - diff --git a/keystone/content/common/xsd/user.xsd b/keystone/content/common/xsd/user.xsd deleted file mode 100644 index 526b0f84..00000000 --- a/keystone/content/common/xsd/user.xsd +++ /dev/null @@ -1,131 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?xml-stylesheet type="text/xsl" href="../xslt/schema.xslt"?> - -<!-- (C) 2011 OpenStack LLC., All Rights Reserved --> - -<schema - elementFormDefault="qualified" - attributeFormDefault="unqualified" - xmlns="http://www.w3.org/2001/XMLSchema" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" - xmlns:atom="http://www.w3.org/2005/Atom" - targetNamespace="http://docs.openstack.org/identity/api/v2.0" -> - - <!-- Import ATOM specific schema definitions --> - <import vc:minVersion="1.1" namespace="http://www.w3.org/2005/Atom" - schemaLocation="atom/atom.xsd" /> - - <!-- Elements --> - <element name="users" type="identity:UserList" > - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/2001/XMLSchema"> - <p> - A list of Users. - </p> - </xsd:documentation> - </annotation> - </element> - - <element name="user" type="identity:User"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/2001/XMLSchema"> - <p> - A Keystone User. - </p> - </xsd:documentation> - </annotation> - </element> - - <!-- Complex Types --> - <complexType name="User"> - <attribute name="id" type="xsd:string" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - An automatically generated, unique, immutable (it will never change) identifier - for the user. This is generated by the backend this user is stored in. - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="username" type="xsd:string" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - A unique, mutable (it can change) user name that may be used by the user - an identifier when presenting credentials. - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="email" type="xsd:string" use="optional"/> - <attribute name="enabled" type="xsd:boolean" default="true" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - A true/false value that determines if the user may authenticate or not. - If enabled is false, the user will not be able to authenticate. - How this value is stored or generated is dependent on the backend in use. - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="display-name" type="xsd:string" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - A human-readable, friendly name for use in user interfaces. - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="updated" type="xsd:dateTime" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - A time-stamp identifying the modification time of the - user. - </p> - </xsd:documentation> - </annotation> - </attribute> - <attribute name="created" type="xsd:dateTime" use="optional"> - <annotation> - <xsd:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - A creation time-stamp for the user. - </p> - </xsd:documentation> - </annotation> - </attribute> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - - <complexType name="UserList"> - <sequence> - <element name="user" type="identity:User" minOccurs="0" maxOccurs="unbounded"/> - <element vc:minVersion="1.1" ref="atom:link" minOccurs="0" maxOccurs="unbounded" /> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </sequence> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> -</schema> diff --git a/keystone/content/common/xsd/version.xsd b/keystone/content/common/xsd/version.xsd deleted file mode 100644 index a65d1ff2..00000000 --- a/keystone/content/common/xsd/version.xsd +++ /dev/null @@ -1,357 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<?xml-stylesheet type="text/xsl" href="../xslt/schema.xslt"?> - -<!-- (C) 2011 OpenStack LLC., All Rights Reserved --> - -<xs:schema elementFormDefault="qualified" attributeFormDefault="unqualified" - targetNamespace="http://docs.openstack.org/common/api/v1.0" - xmlns:vers="http://docs.openstack.org/common/api/v1.0" - xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xmlns:html="http://www.w3.org/1999/xhtml" - xmlns:atom="http://www.w3.org/2005/Atom" - xmlns:xs="http://www.w3.org/2001/XMLSchema"> - - <xs:annotation> - <xs:appinfo - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <xsdxt:title>Version Types</xsdxt:title> - <xsdxt:link rel="index" href="api-common.xsd" /> - </xs:appinfo> - <xs:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - This schema file defines all types related to versioning. - </p> - </xs:documentation> - </xs:annotation> - - <!-- Import ATOM specific schema definitions --> - <xs:import namespace="http://www.w3.org/2005/Atom" schemaLocation="atom/atom.xsd" /> - - <!-- Multiple choices --> - <xs:element name="choices" type="vers:VersionChoiceList"> - <xs:annotation> - <xs:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - This element is returned when the version of the - resource cannot be determined. The element - provides a list of choices for the resource. - </p> - </xs:documentation> - <xs:appinfo> - <xsdxt:samples> - <xsdxt:sample> - <xsdxt:code type="application/xml" href="../samples/choices.xml" /> - </xsdxt:sample> - <xsdxt:sample> - <xsdxt:code type="application/json" href="../samples/choices.json" /> - </xsdxt:sample> - </xsdxt:samples> - </xs:appinfo> - </xs:annotation> - </xs:element> - - <!-- Versioning --> - <xs:element name="versions" type="vers:VersionChoiceList"> - <xs:annotation> - <xs:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - Provides a list of supported versions. - </p> - </xs:documentation> - <xs:appinfo> - <xsdxt:samples> - <xsdxt:sample> - <xsdxt:code type="application/xml" href="../samples/versions.xml" /> - </xsdxt:sample> - <xsdxt:sample> - <xsdxt:code type="application/json" href="../samples/versions.json" /> - </xsdxt:sample> - <xsdxt:sample> - <xsdxt:code type="application/atom+xml" href="../samples/versions-atom.xml" /> - </xsdxt:sample> - </xsdxt:samples> - </xs:appinfo> - </xs:annotation> - </xs:element> - <xs:element name="version" type="vers:VersionChoice" vc:minVersion="1.0" vc:maxVersion="1.1"> - <xs:annotation> - <xs:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - This element provides detailed meta information - regarding the status of the current API version. - This is the XSD 1.0 compatible element definition. - </p> - </xs:documentation> - </xs:annotation> - </xs:element> - - <xs:element name="version2" type="vers:VersionChoiceRoot" vc:minVersion="1.1"> - <xs:annotation> - <xs:documentation - xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - This element provides detailed meta information - regarding the status of the current API - version. The description should include a pointer - to both a human readable and a machine processable - description of the API service. - </p> - </xs:documentation> - <xs:appinfo> - <xsdxt:samples> - <xsdxt:sample> - <xsdxt:code type="application/xml" href="../samples/version.xml" /> - </xsdxt:sample> - <xsdxt:sample> - <xsdxt:code type="application/json" href="../samples/version.json" /> - </xsdxt:sample> - <xsdxt:sample> - <xsdxt:code type="application/atom+xml" href="../samples/version-atom.xml" /> - </xsdxt:sample> - </xsdxt:samples> - </xs:appinfo> - </xs:annotation> - </xs:element> - - <!-- Types --> - <xs:simpleType name="VersionStatus"> - <xs:annotation> - <xs:documentation> - <html:p> - The VersionStatus type describes a service's operational status. - </html:p> - </xs:documentation> - </xs:annotation> - - <xs:restriction base="xs:string"> - <xs:enumeration value="ALPHA"> - <xs:annotation> - <xs:documentation> - <html:p> - This is a new service the API. Thi API - contract may be set, but the implementaiton - may not be 100% complient with it. Developers - are encouraged to begin testing aganst an - ALPHA version to provide feedback. - </html:p> - </xs:documentation> - </xs:annotation> - </xs:enumeration> - <xs:enumeration value="BETA"> - <xs:annotation> - <xs:documentation> - <html:p> - A status of BETA indicates that this - version is a candidate for the next major - release and may feature functionality not - available in the current - version. Developers are encouraged to test - and begin the migration processes to a - BETA version. Note that a BETA version is - undergoing testing, it has not been - officially released, and my not be stable. - </html:p> - </xs:documentation> - </xs:annotation> - </xs:enumeration> - <xs:enumeration value="CURRENT"> - <xs:annotation> - <xs:documentation> - <html:p> - The API version is stable and has been - tested. Developers are encouraged to - develop against this API version. The - current released version of the API will - always be marked as CURRENT. - </html:p> - </xs:documentation> - </xs:annotation> - </xs:enumeration> - <xs:enumeration value="DEPRECATED"> - <xs:annotation> - <xs:documentation> - <html:p> - A status of DEPRECATED indicates that a - newer version of the API is - available. Application developers are - discouraged from using this version and - should instead develop against the latest - current version of the API. - </html:p> - </xs:documentation> - </xs:annotation> - </xs:enumeration> - </xs:restriction> - </xs:simpleType> - - <xs:complexType name="VersionChoiceList"> - <xs:annotation> - <xs:documentation> - <html:p> - A version choice list outlines a collection of - resources at various versions. - </html:p> - </xs:documentation> - </xs:annotation> - <xs:sequence> - <xs:element name="version" type="vers:VersionChoice" minOccurs="1" maxOccurs="unbounded" /> - <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </xs:sequence> - <xs:anyAttribute namespace="##other" processContents="lax"/> - <!--TODO(Ziad)resolve assert issue - <xs:assert vc:minVersion="1.1" test="every $v in vers:version satisfies $v/atom:link[@rel='self']"> - <xs:annotation> - <xs:documentation> - <html:p> - In version lists, every single version must - contain at least one self link. - </html:p> - </xs:documentation> - </xs:annotation> - </xs:assert>--> - </xs:complexType> - - <xs:complexType name="VersionChoiceRoot" vc:minVersion="1.1"> - <xs:complexContent> - <xs:extension base="vers:VersionChoice"> - <!--TODO(Ziad)resolve asser issue - <xs:assert test="atom:link[@rel='describedby']"> - <xs:annotation> - <xs:documentation> - <html:p> - When used as a root element, a version choice - must contain at least one describedby link. - </html:p> - </xs:documentation> - </xs:annotation> - </xs:assert>--> - </xs:extension> - </xs:complexContent> - </xs:complexType> - - <xs:complexType name="VersionChoice"> - <xs:annotation> - <xs:documentation> - <html:p> - A version choice contains relevant information - about an available service that a user can then - use to target a specific version of the service. - </html:p> - </xs:documentation> - </xs:annotation> - - <xs:sequence> - <xs:element name="media-types" type="vers:MediaTypeList" minOccurs="0" maxOccurs="1" /> - <!--<xs:element vc:minVersion="1.1" ref="atom:link" minOccurs="0" maxOccurs="unbounded" />--> - <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </xs:sequence> - - <xs:attribute name="id" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> - <html:p> - The ID of a version choice represents the service version's unique - identifier. This ID is guaranteed to be unique only among the - service version choices outlined in the VersionChoiceList. - </html:p> - </xs:documentation> - </xs:annotation> - </xs:attribute> - - <xs:attribute name="status" type="vers:VersionStatus" use="required"> - <xs:annotation> - <xs:documentation> - <html:p> - A version choice's status describes the current operational state of - the given service version. The operational status is captured in a - simple type enumeration called VersionStatus. - </html:p> - </xs:documentation> - </xs:annotation> - </xs:attribute> - - <xs:attribute name="updated" type="xs:dateTime" use="optional"> - <xs:annotation> - <xs:documentation> - <html:p> - A version choice's updated attribute describes - the time when the version was updated. The - time should be updated anytime - <html:strong>anything</html:strong> in the - version has changed: documentation, - extensions, bug fixes. - </html:p> - </xs:documentation> - </xs:annotation> - </xs:attribute> - <xs:anyAttribute namespace="##other" processContents="lax"/> - </xs:complexType> - - <xs:complexType name="MediaTypeList"> - <xs:annotation> - <xs:documentation> - <html:p> - A MediaTypeList outlines a collection of valid media types for a given - service version. - </html:p> - </xs:documentation> - </xs:annotation> - - <xs:sequence> - <xs:element name="media-type" type="vers:MediaType" minOccurs="1" maxOccurs="unbounded" /> - <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </xs:sequence> - <xs:anyAttribute namespace="##other" processContents="lax"/> - </xs:complexType> - - <xs:complexType name="MediaType"> - <xs:annotation> - <xs:documentation> - <html:p> - A MediaType describes what content types the service version understands. - </html:p> - </xs:documentation> - </xs:annotation> - <xs:sequence> - <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded" /> - </xs:sequence> - <xs:attribute name="base" type="xs:string" use="optional" default=""> - <xs:annotation> - <xs:documentation> - <html:p> - The base of a given media type describes the - simple MIME type that then a more complicated - media type can be derived from. These types - are basic and provide no namespace or version - specific data are are only provided as a - convenience. - </html:p> - </xs:documentation> - </xs:annotation> - </xs:attribute> - - <xs:attribute name="type" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> - <html:p> - The type attribute of a MediaType describes - the MIME specific identifier of the media type - in question. - </html:p> - </xs:documentation> - </xs:annotation> - </xs:attribute> - <xs:anyAttribute namespace="##other" processContents="lax"/> - </xs:complexType> -</xs:schema> diff --git a/keystone/content/common/xslt/schema.xslt b/keystone/content/common/xslt/schema.xslt deleted file mode 100644 index 6d602cc7..00000000 --- a/keystone/content/common/xslt/schema.xslt +++ /dev/null @@ -1,1342 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<!-- (C) 2009 Rackspace Hosting, All Rights Reserved --> - - -<xslt:stylesheet version="1.0" - xmlns:xslt="http://www.w3.org/1999/XSL/Transform" - xmlns:html="http://www.w3.org/1999/xhtml" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" - xmlns="http://www.w3.org/1999/xhtml" - > - - <xslt:output method="html" - encoding="UTF-8" - media-type="text/html" - doctype-public = "-//W3C//DTD HTML 4.01//EN" - doctype-system = "http://www.w3.org/TR/html4/strict.dtd" /> - - <!-- Params --> - <xslt:param name="base"> - <xslt:choose> - <xslt:when test="/xsd:schema/@xsdxt:base"> - <xslt:value-of select="/xsd:schema/@xsdxt:base"/> - </xslt:when> - <xslt:otherwise> - <xslt:text>..</xslt:text> - </xslt:otherwise> - </xslt:choose> - </xslt:param> - - <!-- Global Variables --> - <xslt:variable name="defaultTitle">XML Schema Documentation</xslt:variable> - <xslt:variable name="templateType">application/xhtml+xml</xslt:variable> - <xslt:variable name="schemaNamespace">http://www.w3.org/2001/XMLSchema</xslt:variable> - <xslt:variable name="schemaDatatypeURI">http://web4.w3.org/TR/2001/REC-xmlschema-2-20010502/#</xslt:variable> - - <xslt:variable name="dQuote">"</xslt:variable> - <xslt:variable name="sQuote">'</xslt:variable> - - <!-- The namespace prefixes --> - <xslt:variable name="targetPrefix"> - <xslt:for-each select="/xsd:schema/namespace::node()"> - <xslt:if test=".=/xsd:schema/@targetNamespace"> - <xslt:value-of select="name(.)"/> - </xslt:if> - </xslt:for-each> - </xslt:variable> - - <xslt:variable name="schemaPrefix"> - <xslt:for-each select="/xsd:schema/namespace::node()"> - <xslt:if test="(.=$schemaNamespace) and (string-length(.) > 0)"> - <xslt:value-of select="name(.)"/> - </xslt:if> - </xslt:for-each> - </xslt:variable> - - <!-- Anchor prefixes --> - <xslt:variable name="elementPrefix">element_</xslt:variable> - <xslt:variable name="attributePrefix">attrib_</xslt:variable> - <xslt:variable name="attributeGroupPrefix">attgrp_</xslt:variable> - <xslt:variable name="groupPrefix">grp_</xslt:variable> - <xslt:variable name="typePrefix">type_</xslt:variable> - - <!-- YUI BASE: --> - <!-- - We only load YUI style sheets here. We bring js stuff - dynamically. Stylesheet's can't really be brought dynamically. - They need to be loaded before anything else. - --> - <xslt:variable name="YUI_BASE">http://yui.yahooapis.com/2.7.0/build/</xslt:variable> - <xslt:variable name="YUI_RESET_STYLESHEET"> - <xslt:value-of select="concat($YUI_BASE,'reset/reset-min.css')" /> - </xslt:variable> - <xslt:variable name="YUI_BASE_STYLESHEET"> - <xslt:value-of select="concat($YUI_BASE,'base/base-min.css')" /> - </xslt:variable> - <xslt:variable name="YUI_FONTS_STYLESHEET"> - <xslt:value-of select="concat($YUI_BASE,'fonts/fonts-min.css')" /> - </xslt:variable> - <xslt:variable name="YUI_GRIDS_STYLESHEET"> - <xslt:value-of select="concat($YUI_BASE,'grids/grids-min.css')" /> - </xslt:variable> - - <xslt:template name="addStylesheet"> - <xslt:param name="sheet" /> - <xslt:element name="link"> - <xslt:attribute name="rel">stylesheet</xslt:attribute> - <xslt:attribute name="type">text/css</xslt:attribute> - <xslt:attribute name="href"> - <xslt:value-of select="$sheet"/> - </xslt:attribute> - </xslt:element> - </xslt:template> - - <!-- Templates --> - <xslt:template name="SchemaHandler" match="xsd:schema"> - <html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> - <xslt:call-template name="addStylesheet"> - <xslt:with-param name="sheet" select="$YUI_RESET_STYLESHEET"/> - </xslt:call-template> - <xslt:call-template name="addStylesheet"> - <xslt:with-param name="sheet" select="$YUI_BASE_STYLESHEET"/> - </xslt:call-template> - <xslt:call-template name="addStylesheet"> - <xslt:with-param name="sheet" select="$YUI_FONTS_STYLESHEET"/> - </xslt:call-template> - <xslt:call-template name="addStylesheet"> - <xslt:with-param name="sheet" select="$YUI_GRIDS_STYLESHEET"/> - </xslt:call-template> - <xslt:call-template name="addStylesheet"> - <xslt:with-param name="sheet" select="concat($base,'/style/schema.css')"/> - </xslt:call-template> - - <!-- - Add custom links... - --> - <xslt:for-each select="//xsdxt:link"> - <xslt:if test="not(@qname)"> - <xslt:element name="link"> - <xslt:if test="@rev"> - <xslt:attribute name="rev"><xslt:value-of select="@rev"/></xslt:attribute> - </xslt:if> - <xslt:if test="@rel"> - <xslt:attribute name="rel"><xslt:value-of select="@rel"/></xslt:attribute> - </xslt:if> - <xslt:if test="@href"> - <xslt:attribute name="href"><xslt:value-of select="@href"/></xslt:attribute> - </xslt:if> - <xslt:if test="@type"> - <xslt:attribute name="type"><xslt:value-of select="@type"/></xslt:attribute> - </xslt:if> - </xslt:element> - </xslt:if> - </xslt:for-each> - - <!-- - Set the title if it's available, default title if not. - --> - <xslt:choose> - <xslt:when test="xsd:annotation/xsd:appinfo/xsdxt:title"> - <title><xslt:value-of select="xsd:annotation/xsd:appinfo/xsdxt:title"/></title> - </xslt:when> - <xslt:otherwise> - <title><xslt:value-of select="$defaultTitle"/></title> - </xslt:otherwise> - </xslt:choose> - - <!-- Schema scripts --> - <script type="text/javascript" src="{$base}/js/trc/util.js"> </script> - <script type="text/javascript" src="{$base}/js/trc/schema/layoutManager.js"> </script> - <script type="text/javascript" src="{$base}/js/trc/schema/sampleManager.js"> </script> - <script type="text/javascript" src="{$base}/js/trc/schema/controller.js"> </script> - - <xslt:if test="//xsdxt:samples | //xsdxt:code"> - <script type="text/javascript"> - <xslt:for-each select="//xsdxt:samples"> - <xslt:variable name="elmId"><xslt:value-of select="generate-id(.)"/></xslt:variable> - <xslt:if test="xsdxt:sample"> - <xslt:text>trc.schema.sampleManager.samples["</xslt:text> - <xslt:value-of select="$elmId"/> - <xslt:text>"]=[</xslt:text> - <xslt:for-each select="xsdxt:sample"> - <xslt:call-template name="StringToJavascript"> - <xslt:with-param name="inString" select="generate-id(.)"/> - </xslt:call-template> - <xslt:if test="generate-id(../xsdxt:sample[count(../xsdxt:sample)]) != - generate-id(.) - "> - <xslt:text>,</xslt:text> - </xslt:if> - </xslt:for-each> - <xslt:text>];</xslt:text> - </xslt:if> - </xslt:for-each> - <xslt:if test="//xsdxt:code"> - <xslt:text>trc.schema.sampleManager.codes.push(</xslt:text> - <xslt:for-each select="//xsdxt:code"> - <xslt:text>{ id : </xslt:text> - <xslt:value-of select="concat($dQuote,generate-id(.),$dQuote)" /> - <xslt:text>, type : "</xslt:text> - <xslt:choose> - <xslt:when test="@type"> - <xslt:value-of select="@type"/> - </xslt:when> - <xslt:otherwise> - <xslt:text>application/xml</xslt:text> - </xslt:otherwise> - </xslt:choose> - <xslt:text>", href : </xslt:text> - <xslt:choose> - <xslt:when test="@href"> - <xslt:value-of select="concat($dQuote,@href,$dQuote)"/> - </xslt:when> - <xslt:otherwise> - <xslt:text>null</xslt:text> - </xslt:otherwise> - </xslt:choose> - <xslt:text>}</xslt:text> - <xslt:text>,</xslt:text> - </xslt:for-each> - <xslt:text>null);</xslt:text> - </xslt:if> - </script> - </xslt:if> - - <xslt:call-template name="ControllerJSHandler" /> - - <!-- - Copy any HTML header tags here - --> - <xslt:for-each select="//xsdxt:head"> - <xslt:choose> - <xslt:when test="not(@type)"> - <xslt:copy-of select="./*" /> - </xslt:when> - <xslt:when test="@type = $templateType"> - <xslt:copy-of select="./*" /> - </xslt:when> - </xslt:choose> - </xslt:for-each> - </head> - <body> - <div id="Controller"> - </div> - <div id="doc"> - <div id="Main"> - <div id="SrcContent"> - <div class="SampleCode"> - <pre id="SrcContentCode">Loading...</pre> - </div> - </div> - <div id="Content"> - <!-- - If there is a title use it as a first heading, otherwise, - use default title. - --> - <xslt:choose> - <xslt:when test="xsd:annotation/xsd:appinfo/xsdxt:title"> - <h1><xslt:value-of select="xsd:annotation/xsd:appinfo/xsdxt:title"/></h1> - </xslt:when> - <xslt:otherwise> - <h1><xslt:value-of select="$defaultTitle"/></h1> - </xslt:otherwise> - </xslt:choose> - - <!-- - Schema attributes - --> - <table summary="Schema-level attributes"> - <tbody> - <xslt:for-each select="@*"> - <tr> - <td><xslt:value-of select="local-name(.)"/></td> - <td><xslt:value-of select="."/></td> - </tr> - </xslt:for-each> - </tbody> - </table> - - <!-- - Copy schema-level documentation if there's anything to - copy. This also processes any internal documentation - annotations: currently just xsdxt:code. - --> - <xslt:apply-templates select="xsd:annotation/xsd:documentation/*" mode="Docs"/> - - <!-- - Next comes custom header... - --> - <div id="Header"> - <xslt:for-each select="//xsdxt:header"> - <xslt:choose> - <xslt:when test="not(@type)"> - <xslt:copy-of select="./*" /> - </xslt:when> - <xslt:when test="@type = $templateType"> - <xslt:copy-of select="./*" /> - </xslt:when> - </xslt:choose> - </xslt:for-each> - </div> - - - <!-- - Namespace info, not all borowsers have namespace node - support. Specifically Firefox currently lacks it. - - See: - https://bugzilla.mozilla.org/show_bug.cgi?id=94270 - - In this case we ask the user to try a different - browser: Opera, Safari, or even IE. - --> - <h2>Namespaces</h2> - <xslt:choose> - <xslt:when test="count(namespace::*) = 0"> - <!--Namespaces are not available...--> - <div class="Warning"> - <p> - Your browser does not seem to have support for - namespace nodes in XPath. If you're a Firefox - user, please consider voting to get this issue - resolved: - <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=94270" - title="FireFox Bug 94270"> - https://bugzilla.mozilla.org/show_bug.cgi?id=94270 - </a> - </p> - </div> - </xslt:when> - <xslt:otherwise> - <table summary="Namespace details"> - <tbody> - <xslt:for-each select="namespace::*"> - <xslt:sort /> - <tr> - <td><xslt:value-of select="name(.)"/></td> - <td><xslt:value-of select="."/></td> - </tr> - </xslt:for-each> - </tbody> - </table> - </xslt:otherwise> - </xslt:choose> - - <!-- - Next, call the handlers for the top schema elements. - --> - <xslt:if test="xsd:import"> - <xslt:call-template name="ImportHandler" /> - </xslt:if> - <xslt:if test="xsd:include"> - <xslt:call-template name="IncludeHandler" /> - </xslt:if> - - <xslt:if test="xsd:element"> - <xslt:call-template name="ElementHandler" /> - </xslt:if> - - <xslt:if test="xsd:complexType"> - <xslt:call-template name="ComplexTypeHandler" /> - </xslt:if> - - <xslt:if test="xsd:simpleType"> - <xslt:call-template name="SimpleTypeHandler" /> - </xslt:if> - - <!-- Finally, custom footers --> - <div id="Footer"> - <xslt:for-each select="//xsdxt:footer"> - <xslt:choose> - <xslt:when test="not(@type)"> - <xslt:copy-of select="./*" /> - </xslt:when> - <xslt:when test="@type = $templateType"> - <xslt:copy-of select="./*" /> - </xslt:when> - </xslt:choose> - </xslt:for-each> - </div> - </div> - </div> - </div> - </body> - </html> - </xslt:template> - - <xslt:template name="ControllerExternJSLinks"> - <xslt:param name="nodes" /> - - <xslt:text>trc.schema.controller.links['</xslt:text> - <xslt:value-of select="local-name($nodes[1])"/> - <xslt:text>']=[</xslt:text> - <xslt:for-each select="$nodes"> - <xslt:call-template name="ControllerJSLink"> - <xslt:with-param name="href" select="@schemaLocation"/> - <xslt:with-param name="name"> - <xslt:choose> - <xslt:when test="@namespace"> - <xslt:value-of select="@namespace" /> - </xslt:when> - <xslt:otherwise> - <xslt:value-of select="@schemaLocation" /> - </xslt:otherwise> - </xslt:choose> - </xslt:with-param> - <xslt:with-param name="title"> - <xslt:choose> - <xslt:when test="@namespace"> - <xslt:value-of select="concat('View schema for namespace ',@namespace)"/> - </xslt:when> - <xslt:otherwise> - <xslt:value-of select="concat('Visit schema ',@schemaLocation)"/> - </xslt:otherwise> - </xslt:choose> - </xslt:with-param> - </xslt:call-template> - <xslt:if test="$nodes[count($nodes)]/@schemaLocation != - @schemaLocation"> - <xslt:text>,</xslt:text> - </xslt:if> - </xslt:for-each> - <xslt:text>];</xslt:text> - </xslt:template> - - <xslt:template name="ControllerIndexJSLink"> - <xslt:param name="node" select="//xsdxt:link[@rel = 'index']" /> - - <xslt:text>trc.schema.controller.index = </xslt:text> - <xslt:call-template name="ControllerJSLink"> - <xslt:with-param name="href"> - <xslt:value-of select="$node/@href"/> - </xslt:with-param> - <xslt:with-param name="name"> - <xslt:text>index</xslt:text> - </xslt:with-param> - <xslt:with-param name="title"> - <xslt:text>Index Schema Document</xslt:text> - </xslt:with-param> - </xslt:call-template> - <xslt:text>;</xslt:text> - </xslt:template> - - <xslt:template name="ControllerNamedElementJSLink"> - <xslt:param name="nodes" /> - <xslt:param name="anchorPrefix" /> - - <xslt:text>trc.schema.controller.links['</xslt:text> - <xslt:value-of select="local-name($nodes[1])"/> - <xslt:text>']=[</xslt:text> - <xslt:for-each select="$nodes"> - <xslt:call-template name="ControllerJSLink"> - <xslt:with-param name="href"> - <xslt:text>#</xslt:text> - <xslt:value-of select="$anchorPrefix" /> - <xslt:value-of select="@name" /> - </xslt:with-param> - <xslt:with-param name="name"> - <xslt:call-template name="StringToName"/> - </xslt:with-param> - <xslt:with-param name="title"> - <xslt:text>See definition of </xslt:text> - <xslt:call-template name="StringToName"/> - </xslt:with-param> - </xslt:call-template> - <xslt:if test="generate-id($nodes[count($nodes)]) != - generate-id(.)"> - <xslt:text>,</xslt:text> - </xslt:if> - </xslt:for-each> - <xslt:text>];</xslt:text> - </xslt:template> - - <xslt:template name="ControllerJSLink"> - <xslt:param name="name" /> - <xslt:param name="href" /> - <xslt:param name="title" /> - - <xslt:text>{ href : </xslt:text> - <xslt:call-template name="StringToJavascript"> - <xslt:with-param name="inString"> - <xslt:value-of select="$href"/> - </xslt:with-param> - </xslt:call-template> - <xslt:text>, name : </xslt:text> - <xslt:call-template name="StringToJavascript"> - <xslt:with-param name="inString"> - <xslt:value-of select="$name"/> - </xslt:with-param> - </xslt:call-template> - <xslt:text>, title : </xslt:text> - <xslt:call-template name="StringToJavascript"> - <xslt:with-param name="inString"> - <xslt:value-of select="$title"/> - </xslt:with-param> - </xslt:call-template> - <xslt:text>}</xslt:text> - </xslt:template> - - <!-- - Adds javascript for controller data.. - --> - <xslt:template name="ControllerJSHandler"> - <script type="text/javascript"> - <xslt:if test="xsd:import"> - <xslt:call-template name="ControllerExternJSLinks"> - <xslt:with-param name="nodes" select="xsd:import" /> - </xslt:call-template> - </xslt:if> - <xslt:if test="xsd:include"> - <xslt:call-template name="ControllerExternJSLinks"> - <xslt:with-param name="nodes" select="xsd:include" /> - </xslt:call-template> - </xslt:if> - <xslt:if test="xsd:element"> - <xslt:call-template name="ControllerNamedElementJSLink"> - <xslt:with-param name="nodes" select="xsd:element"/> - <xslt:with-param name="anchorPrefix" select="$elementPrefix"/> - </xslt:call-template> - </xslt:if> - <xslt:if test="xsd:attribute"> - <xslt:call-template name="ControllerNamedElementJSLink"> - <xslt:with-param name="nodes" select="xsd:attribute"/> - <xslt:with-param name="anchorPrefix" select="$attributePrefix"/> - </xslt:call-template> - </xslt:if> - <xslt:if test="xsd:complexType"> - <xslt:call-template name="ControllerNamedElementJSLink"> - <xslt:with-param name="nodes" select="xsd:complexType"/> - <xslt:with-param name="anchorPrefix" select="$typePrefix"/> - </xslt:call-template> - </xslt:if> - <xslt:if test="xsd:simpleType"> - <xslt:call-template name="ControllerNamedElementJSLink"> - <xslt:with-param name="nodes" select="xsd:simpleType"/> - <xslt:with-param name="anchorPrefix" select="$typePrefix"/> - </xslt:call-template> - </xslt:if> - <xslt:if test="//xsdxt:link[@rel = 'index']"> - <xslt:call-template name="ControllerIndexJSLink" /> - </xslt:if> - </script> - </xslt:template> - - <xslt:template name="ImportHandler"> - <h2>Imports</h2> - <table summary="A list of imported XML Schema" class="ImportTable"> - <tbody> - <xslt:for-each select="xsd:import"> - <tr> - <td> - <xslt:value-of select="@namespace"/> - </td> - <td> - <div class="Extern"> - <div class="ExternHref"> - <xslt:element name="a"> - <xslt:attribute name="href"><xslt:value-of select="@schemaLocation"/></xslt:attribute> - <xslt:attribute name="title">Visit <xslt:value-of select="@schemaLocation"/></xslt:attribute> - <xslt:value-of select="@schemaLocation"/> - </xslt:element> - </div> - <div class="ExternDoc"> - <xslt:apply-templates select="xsd:annotation/xsd:documentation/*" mode="Docs"/> - </div> - </div> - </td> - </tr> - </xslt:for-each> - </tbody> - </table> - </xslt:template> - - <xslt:template name="IncludeHandler"> - <h2>Includes</h2> - <table summary="A list of included XML Schema"> - <tbody> - <xslt:for-each select="xsd:include"> - <tr> - <td> - <div class="Extern"> - <div class="ExternHref"> - <xslt:element name="a"> - <xslt:attribute name="href"><xslt:value-of select="@schemaLocation"/></xslt:attribute> - <xslt:attribute name="title">Visit <xslt:value-of select="@schemaLocation"/></xslt:attribute> - <xslt:value-of select="@schemaLocation"/> - </xslt:element> - </div> - <div class="ExternDoc"> - <xslt:apply-templates select="xsd:annotation/xsd:documentation/*" mode="Docs"/> - </div> - </div> - </td> - </tr> - </xslt:for-each> - </tbody> - </table> - </xslt:template> - - <xslt:template name="ElementHandler"> - <h2>Elements</h2> - <xslt:for-each select="xsd:element"> - <xslt:call-template name="NamedElement"> - <xslt:with-param name="anchorPrefix" select="$elementPrefix" /> - </xslt:call-template> - <xslt:if test="xsd:annotation/xsd:appinfo/xsdxt:samples"> - <xslt:apply-templates select="xsd:annotation/xsd:appinfo/xsdxt:samples" mode="Docs" /> - </xslt:if> - </xslt:for-each> - </xslt:template> - - <xslt:template name="SampleHandler" match="xsdxt:samples" mode="Docs"> - <xslt:variable name="sampleID" select="generate-id(.)"/> - <xslt:if test="xsdxt:description"> - <xslt:apply-templates select="xsdxt:description/*" mode="Docs" /> - </xslt:if> - <form action=""> - <div class="SampleControl"> - <xslt:element name="select"> - <xslt:attribute name="onchange"> - <xslt:text>trc.schema.sampleManager.showSample(</xslt:text> - <xslt:call-template name="StringToJavascript"> - <xslt:with-param name="inString" select="$sampleID"/> - </xslt:call-template> - <xslt:text>);</xslt:text> - </xslt:attribute> - <xslt:attribute name="id"> - <xslt:value-of select="$sampleID"/> - </xslt:attribute> - <xslt:for-each select="xsdxt:sample"> - <xslt:element name="option"> - <xslt:attribute name="value"> - <xslt:value-of select="generate-id(.)"/> - </xslt:attribute> - <xslt:choose> - <xslt:when test="@title"> - <xslt:value-of select="@title"/> - </xslt:when> - <xslt:otherwise> - <xslt:value-of select="./xsdxt:code/@type"/> - </xslt:otherwise> - </xslt:choose> - </xslt:element> - </xslt:for-each> - </xslt:element> - </div> - </form> - <xslt:for-each select="xsdxt:sample"> - <xslt:element name="div"> - <xslt:attribute name="id"><xslt:value-of select="generate-id(.)"/></xslt:attribute> - <xslt:attribute name="class">Sample</xslt:attribute> - <div class="SampleDesc"> - <xslt:apply-templates select="xsdxt:description/*" mode="Docs"/> - </div> - <xslt:apply-templates select="xsdxt:code" mode="Docs"/> - </xslt:element> - </xslt:for-each> - </xslt:template> - - <!-- - Documentation templates, copy everything but process the - xsdxt:code tag. - --> - <xslt:template match="xsdxt:code" mode="Docs"> - <div class="SampleCode"> - <xslt:element name="pre"> - <xslt:attribute name="id"> - <xslt:value-of select="generate-id(.)"/> - </xslt:attribute> - <xslt:choose> - <xslt:when test="@href"> - <xslt:text>Loading...</xslt:text> - </xslt:when> - <xslt:otherwise> - <xslt:value-of select="."/> - </xslt:otherwise> - </xslt:choose> - </xslt:element> - </div> - </xslt:template> - - <xslt:template match="*" mode="Docs"> - <xslt:copy-of select="." /> - </xslt:template> - - <xslt:template name="ComplexTypeHandler"> - <h2>Complex Types</h2> - <xslt:for-each select="xsd:complexType"> - <xslt:call-template name="NamedElement"> - <xslt:with-param name="anchorPrefix" select="$typePrefix" /> - </xslt:call-template> - <xslt:apply-templates /> - </xslt:for-each> - </xslt:template> - - <xslt:template name="SimpleTypeHandler"> - <h2>Simple Types</h2> - <xslt:for-each select="xsd:simpleType"> - <xslt:call-template name="NamedElement"> - <xslt:with-param name="anchorPrefix" select="$typePrefix" /> - </xslt:call-template> - <xslt:apply-templates /> - </xslt:for-each> - </xslt:template> - - <xslt:template name="NamedElementLink"> - <xslt:param name="anchorPrefix" /> - <xslt:call-template name="Anchor"> - <xslt:with-param name="href"> - <xslt:text>#</xslt:text> - <xslt:value-of select="$anchorPrefix"/> - <xslt:value-of select="@name"/> - </xslt:with-param> - <xslt:with-param name="content"> - <xslt:call-template name="StringToName" /> - </xslt:with-param> - </xslt:call-template> - </xslt:template> - - <xslt:template name="NamedElement"> - <xslt:param name="anchorPrefix" /> - <xslt:element name="a"> - <xslt:attribute name="id"><xslt:value-of select="$anchorPrefix"/><xslt:value-of select="@name"/></xslt:attribute> - <!-- - Placing a comment here causes the anchor tag to be closed - correctly in IE 8. - --> - <xslt:comment> - <xslt:value-of select="@name"/> - </xslt:comment> - </xslt:element> - <h3> - <xslt:call-template name="StringToName" /> - </h3> - - <xslt:choose> - <!-- look for extensions and restrictions in type names --> - <xslt:when test="$anchorPrefix = $typePrefix"> - <xslt:if test=".//xsd:extension"> - <div class="NameAddl"> - <xslt:text> extends: </xslt:text> - <xslt:for-each select=".//xsd:extension"> - <xslt:apply-templates select="@base" mode="QNameToLink" /> - <xslt:if test=".//xsd:extension[count(.//xsd:extension)]/@base != @base"> - <xslt:text>,</xslt:text> - </xslt:if> - </xslt:for-each> - </div> - </xslt:if> - <xslt:if test=".//xsd:restriction"> - <div class="NameAddl"> - <xslt:text> restricts: </xslt:text> - <xslt:for-each select=".//xsd:restriction"> - <xslt:apply-templates select="@base" mode="QNameToLink" /> - <xslt:if test=".//xsd:restriction[count(.//xsd:restriction)]/@base != @base"> - <xslt:text>,</xslt:text> - </xslt:if> - </xslt:for-each> - </div> - </xslt:if> - </xslt:when> - </xslt:choose> - - <xslt:call-template name="AttribsAndDocs" /> - - </xslt:template> - - <!-- Display all attributes besides @name --> - <xslt:template name="Attribs"> - <xslt:param name="isSubItem" select="false()"/> - <xslt:if test="(count(@*) > 1) or ((count(@*) = 1) and not(@name))"> - <xslt:element name="div"> - <xslt:attribute name="class"> - <xslt:choose> - <xslt:when test="$isSubItem = true()"> - <xslt:text>SubAttributes</xslt:text> - </xslt:when> - <xslt:otherwise> - <xslt:text>Attributes</xslt:text> - </xslt:otherwise> - </xslt:choose> - </xslt:attribute> - <table summary="Attributes"> - <tbody> - <xslt:for-each select="@*"> - <xslt:sort select="local-name(.)"/> - <xslt:if test="local-name(.) != 'name'"> - <tr> - <td><xslt:value-of select="local-name(.)"/></td> - <td><xslt:call-template name="QNameToLink"/></td> - </tr> - </xslt:if> - </xslt:for-each> - </tbody> - </table> - </xslt:element> - </xslt:if> - </xslt:template> - - <xslt:template name="Docs"> - <xslt:param name="isSubItem" select="false()"/> - <!-- - Copy element-level documentation - --> - <xslt:if test="xsd:annotation/xsd:documentation"> - <xslt:element name="div"> - <xslt:attribute name="class"> - <xslt:choose> - <xslt:when test="$isSubItem = true()"> - <xslt:text>SubDocumentation</xslt:text> - </xslt:when> - <xslt:otherwise> - <xslt:text>Documentation</xslt:text> - </xslt:otherwise> - </xslt:choose> - </xslt:attribute> - <xslt:apply-templates select="xsd:annotation/xsd:documentation/*" mode="Docs"/> - </xslt:element> - </xslt:if> - </xslt:template> - - <xslt:template name="AttribsAndDocs"> - <xslt:param name="isSubItem" select="false()"/> - <xslt:call-template name="Attribs"> - <xslt:with-param name="isSubItem" select="$isSubItem"/> - </xslt:call-template> - <xslt:call-template name="Docs"> - <xslt:with-param name="isSubItem" select="$isSubItem"/> - </xslt:call-template> - </xslt:template> - - <!-- - Convert a qname to a link. - --> - <xslt:template name="QNameToLink" match="@*" mode="QNameToLink"> - <xslt:param name="qname" select="normalize-space(.)"/> - <xslt:choose> - <xslt:when test="contains($qname,':')"> - <xslt:variable name="prefix" select="substring-before($qname,':')"/> - <xslt:variable name="localName" select="substring-after($qname,':')"/> - <xslt:choose> - <xslt:when test="//xsdxt:link[(@qname = $qname) and (@rel = 'schema')]"> - <xslt:call-template name="Anchor"> - <xslt:with-param name="href" select="//xsdxt:link[(@qname = $qname) and (@rel = 'schema')]/@href"/> - <xslt:with-param name="title" select="concat('See ',$localName)"/> - </xslt:call-template> - </xslt:when> - <xslt:when test="$prefix = $targetPrefix"> - <xslt:call-template name="Anchor"> - <xslt:with-param name="href"><xslt:call-template name="QNameToLocalAnchor"/></xslt:with-param> - <xslt:with-param name="title" select="concat('See ',$localName)"/> - </xslt:call-template> - </xslt:when> - <xslt:when test="$prefix = $schemaPrefix"> - <xslt:call-template name="Anchor"> - <xslt:with-param name="href"><xslt:call-template name="QNameToXSDAnchor"/></xslt:with-param> - <xslt:with-param name="title" select="concat('See ',$localName)"/> - </xslt:call-template> - </xslt:when> - <xslt:otherwise> - <xslt:call-template name="Anchor"> - <xslt:with-param name="href"><xslt:call-template name="QNameToForeignAnchor"/></xslt:with-param> - <xslt:with-param name="title" select="concat('See ',$localName)"/> - </xslt:call-template> - </xslt:otherwise> - </xslt:choose> - </xslt:when> - <xslt:otherwise> - <xslt:value-of select="."/> - </xslt:otherwise> - </xslt:choose> - </xslt:template> - - <!-- Write an anchor if it's defined for the current node --> - <xslt:template name="Anchor"> - <xslt:param name="href" /> <!-- if empty don't make an anchor --> - <xslt:param name="title"> - <xslt:if test="@name"> - <xslt:value-of select="concat('See ',@name)"/> - </xslt:if> - </xslt:param> - <xslt:param name="content" select="."/> - <xslt:choose> - <xslt:when test="string-length($href) != 0"> - <xslt:element name="a"> - <xslt:attribute name="href"><xslt:value-of select="$href"/></xslt:attribute> - <xslt:attribute name="title"><xslt:value-of select="$title"/></xslt:attribute> - <xslt:value-of select="$content"/> - </xslt:element> - </xslt:when> - <xslt:otherwise><xslt:value-of select="."/></xslt:otherwise> - </xslt:choose> - </xslt:template> - - <!-- - Given a quname attribute pointing to a forign XSD type return a - link if a single import statement exists with a schemaLocation - attribute - --> - <xslt:template name="QNameToForeignAnchor"> - <xslt:param name="qname" select="normalize-space(.)"/> - <xslt:param name="localName" select="substring-after($qname, ':')"/> - <xslt:param name="prefix" select="substring-before($qname, ':')"/> - <xslt:if test="namespace-uri(..) = $schemaNamespace"> - <xslt:variable name="namespace"> - <xslt:for-each select="/xsd:schema/namespace::node()"> - <xslt:if test="name(.)=$prefix"> - <xslt:value-of select="."/> - </xslt:if> - </xslt:for-each> - </xslt:variable> - <xslt:if test="(string-length($namespace) > 0) and - (count(/xsd:schema/xsd:import[@namespace = $namespace]) = 1) and - /xsd:schema/xsd:import[@namespace = $namespace]/@schemaLocation - "> - <xslt:value-of select="/xsd:schema/xsd:import[@namespace = $namespace]/@schemaLocation"/> - </xslt:if> - </xslt:if> - </xslt:template> - - <!-- - Given a qname attribute pointing to an XSD type, returns an anchor - to the XSD definition. This only works for type references. - --> - <xslt:template name="QNameToXSDAnchor"> - <xslt:param name="qname" select="normalize-space(.)"/> - <xslt:param name="localName" select="substring-after($qname, ':')"/> - <xslt:if test="namespace-uri(..) = $schemaNamespace"> - <xslt:if test="(local-name(.) = 'type') or (local-name(.) = 'base')"> - <xslt:value-of select="concat($schemaDatatypeURI,$localName)" /> - </xslt:if> - </xslt:if> - </xslt:template> - - <!-- - Given a qname attribute, returns an anchor target for that qname, - or an empty string if an anchor cannot be generated for whatever - reason. - --> - <xslt:template name="QNameToLocalAnchor"> - <xslt:param name="qname" select="normalize-space(.)"/> - <xslt:param name="localName" select="substring-after($qname, ':')"/> - <xslt:if test="namespace-uri(..) = $schemaNamespace"> - <xslt:choose> - <xslt:when test="(local-name(.) = 'type') or - (local-name(.) = 'base') or - (local-name(.) = 'itemType')"><xslt:call-template name="LocalTypeAnchor"><xslt:with-param name="localName" select="$localName"/> - </xslt:call-template></xslt:when> - <xslt:when test="local-name(.) = 'ref'"><xslt:call-template name="LocalRefAnchor"><xslt:with-param name="localName" select="$localName"/> - </xslt:call-template></xslt:when> - </xslt:choose> - </xslt:if> - </xslt:template> - - <xslt:template name="LocalRefAnchor"> - <xslt:param name="localName" /> - <xslt:choose> - <!-- Element Reference --> - <xslt:when test="local-name(..) = 'element'"> - <xslt:call-template name="LocalRefAnchorBuilder"> - <xslt:with-param name="localName" select="$localName"/> - <xslt:with-param name="search" select="/xsd:schema/xsd:element[@name= $localName]"/> - <xslt:with-param name="refPrefix" select="$elementPrefix"/> - </xslt:call-template> - </xslt:when> - <!-- Attribute Reference --> - <xslt:when test="local-name(..) = 'attribute'"> - <xslt:call-template name="LocalRefAnchorBuilder"> - <xslt:with-param name="localName" select="$localName"/> - <xslt:with-param name="search" select="/xsd:schema/xsd:attribute[@name= $localName]"/> - <xslt:with-param name="refPrefix" select="$attributePrefix"/> - </xslt:call-template> - </xslt:when> - <!-- Attribute Group Reference --> - <xslt:when test="local-name(..) = 'attributeGroup'"> - <xslt:call-template name="LocalRefAnchorBuilder"> - <xslt:with-param name="localName" select="$localName"/> - <xslt:with-param name="search" select="/xsd:schema/xsd:attributeGroup[@name= $localName]"/> - <xslt:with-param name="refPrefix" select="$attributeGroupPrefix"/> - </xslt:call-template> - </xslt:when> - <!-- Group Reference --> - <xslt:when test="local-name(..) = 'group'"> - <xslt:call-template name="LocalRefAnchorBuilder"> - <xslt:with-param name="localName" select="$localName"/> - <xslt:with-param name="search" select="/xsd:schema/xsd:group[@name= $localName]"/> - <xslt:with-param name="refPrefix" select="$groupPrefix"/> - </xslt:call-template> - </xslt:when> - </xslt:choose> - </xslt:template> - - <xslt:template name="LocalRefAnchorBuilder"> - <xslt:param name="localName" /> - <xslt:param name="search" /> - <xslt:param name="refPrefix" /> - <xslt:choose> - <xslt:when test="$search"> - <xslt:value-of select="concat('#',$refPrefix,$localName)"/> - </xslt:when> - <!-- - If we have a single incude then we assume it's - included... - --> - <xslt:when test="count(/xsd:schema/xsd:include) = 1"><xslt:value-of - select="concat(/xsd:schema/xsd:include/@schemaLocation,'#',$refPrefix,$localName)"/></xslt:when> - </xslt:choose> - </xslt:template> - - <!-- - Given a local name as a pram, returns a local "type" anchor or an - empty string if one cannot be generated. - --> - <xslt:template name="LocalTypeAnchor"> - <xslt:param name="localName" /> - <xslt:choose> - <!-- Search the types --> - <xslt:when - test="/xsd:schema/xsd:complexType[@name = $localName] or - /xsd:schema/xsd:simpleType[@name = $localName]" - ><xslt:value-of select="concat('#',$typePrefix,$localName)"/></xslt:when> - <!-- - If we haven't hit yet see if we have an include. - Currently this only works with a single include. - --> - <xslt:when - test="count(/xsd:schema/xsd:include) = 1"><xslt:value-of - select="concat(/xsd:schema/xsd:include/@schemaLocation,'#',$typePrefix,$localName)"/></xslt:when> - <!-- Can't tell so send an empty string... --> - <xslt:otherwise /> - </xslt:choose> - </xslt:template> - - <!-- Internal sequences --> - <xslt:template match="xsd:sequence"> - <div class="Sequence"> - <span class="h4">Sequence</span> - <xslt:call-template name="AttribsAndDocs" /> - <xslt:apply-templates /> - </div> - </xslt:template> - - <xslt:template name="SubItem"> - <xslt:param name="name" /> - <div class="SubItem"> - <div class="SubItemProps"> - <div class="SubName"> - <xslt:value-of select="$name"/> - </div> - <xslt:call-template name="Attribs"> - <xslt:with-param name="isSubItem" select="true()"/> - </xslt:call-template> - </div> - <xslt:call-template name="Docs"> - <xslt:with-param name="isSubItem" select="true()"/> - </xslt:call-template> - </div> - </xslt:template> - - <xslt:template match="xsd:element"> - <xslt:call-template name="SubItem"> - <xslt:with-param name="name"> - <xslt:choose> - <xslt:when test="@name"> - <xslt:call-template name="StringToElementName"> - <xslt:with-param name="inString" select="@name"/> - </xslt:call-template> - </xslt:when> - <xslt:when test="@ref"> - <xslt:variable name="elementName" select="substring-after(@ref,':')"/> - <xslt:call-template name="StringToElementName"> - <xslt:with-param name="inString" select="$elementName"/> - </xslt:call-template> - </xslt:when> - </xslt:choose> - </xslt:with-param> - </xslt:call-template> - </xslt:template> - - <xslt:template match="xsd:any"> - <xslt:call-template name="SubItem"> - <xslt:with-param name="name"> - <xslt:text><?> (Any Element)</xslt:text> - </xslt:with-param> - </xslt:call-template> - </xslt:template> - - <xslt:template match="xsd:anyAttribute"> - <xslt:call-template name="SubItem"> - <xslt:with-param name="name"> - <xslt:text>@? (Any Attribute)</xslt:text> - </xslt:with-param> - </xslt:call-template> - </xslt:template> - - <xslt:template match="xsd:restriction"> - <div class="SubName"> - <xslt:text>restriction</xslt:text> - </div> - <table summary="Restriction Props and Attributes"> - <tbody> - <xslt:for-each select="@*"> - <xslt:sort select="local-name(.)"/> - <xslt:if test="local-name(.) != 'name'"> - <tr> - <td><xslt:value-of select="local-name(.)"/></td> - <td><xslt:call-template name="QNameToLink"/></td> - </tr> - </xslt:if> - </xslt:for-each> - - <!-- simple restrictions --> - <xslt:for-each select="xsd:minExclusive | xsd:minInclusive | - xsd:maxExclusive | xsd:maxInclusive | - xsd:totalDigits | xsd:fractionDigits | - xsd:length | xsd:minLength | - xsd:maxLength | xsd:minLength | - xsd:whitespace | xsd:pattern - "> - <tr> - <td><xslt:value-of select="local-name(.)"/></td> - <xslt:call-template name="DisplaySimpleRestriction"/> - </tr> - </xslt:for-each> - - <xslt:if test="xsd:enumeration"> - <tr> - <td>enum values</td> - <xslt:call-template name="DisplayEnumeration"> - <xslt:with-param name="enum" select="xsd:enumeration[1]"/> - </xslt:call-template> - </tr> - <xslt:for-each select="xsd:enumeration"> - <xslt:if test="@value != ../xsd:enumeration[1]/@value"> - <tr> - <td></td> - <xslt:call-template name="DisplayEnumeration"/> - </tr> - </xslt:if> - </xslt:for-each> - </xslt:if> - </tbody> - </table> - - <!-- - Copy restriction docs documentation... - --> - <xslt:if test="xsd:annotation/xsd:documentation"> - <xslt:apply-templates select="xsd:annotation/xsd:documentation/*" mode="Docs"/> - </xslt:if> - - <!-- - Apply templates for unhandled children - --> - <xslt:apply-templates select="xsd:simpleType | xsd:group | - xsd:all | xsd:choice | - xsd:sequence | xsd:attribute | - xsd:attributeGroup | xsd:anyAttribute" /> - </xslt:template> - - <!-- - Displays an enumeration in a table... - --> - <xslt:template name="DisplayEnumeration"> - <xslt:param name="enum" select="." /> - <td> - <div class="Enum"> - <div class="EnumValue"> - <xslt:value-of select="$enum/@value"/> - <xslt:if test="$enum/@id"> - <xslt:text> (id = </xslt:text> - <xslt:value-of select="$enum/@id"/> - <xslt:text>)</xslt:text> - </xslt:if> - </div> - <xslt:if test="$enum/xsd:annotation/xsd:documentation"> - <div class="EnumDoc"> - <xslt:apply-templates select="$enum/xsd:annotation/xsd:documentation/*" mode="Docs"/> - </div> - </xslt:if> - </div> - </td> - </xslt:template> - - <!-- - A Simple restriction in a table fragment. - --> - <xslt:template name="DisplaySimpleRestriction"> - <xslt:param name="restriction" select="." /> - <td> - <xslt:value-of select="$restriction/@value"/> - <xslt:if test="$restriction/@id"> - <xslt:text> (id = </xslt:text> - <xslt:value-of select="$restriction/@id"/> - <xslt:text>)</xslt:text> - </xslt:if> - <xslt:if test="$restriction/@fixed = 'true'"> - <xslt:text> (fixed)</xslt:text> - </xslt:if> - </td> - <xslt:if test="$restriction/xsd:annotation/xsd:documentation"> - <td> - <xslt:apply-templates select="$restriction/xsd:annotation/xsd:documentation/*" mode="Docs"/> - </td> - </xslt:if> - </xslt:template> - - <!-- Catch all for the missed elements --> - <xslt:template match="xsd:*"> - <xslt:if test="local-name(.) != 'annotation'"> - <div class="SubElementName"> - <xslt:value-of select="local-name(.)"/> - </div> - <xslt:call-template name="AttribsAndDocs" /> - <div class="SubElementContent"> - <xslt:apply-templates /> - </div> - </xslt:if> - </xslt:template> - - <xslt:template match="xsd:attribute"> - <xslt:call-template name="SubItem"> - <xslt:with-param name="name"> - <xslt:choose> - <xslt:when test="@name"> - <xslt:call-template name="StringToAttributeName"> - <xslt:with-param name="inString" select="@name"/> - </xslt:call-template> - </xslt:when> - <xslt:when test="@ref"> - <xslt:variable name="attribName" select="substring-after(@ref,':')"/> - <xslt:call-template name="StringToAttributeName"> - <xslt:with-param name="inString" select="$attribName"/> - </xslt:call-template> - </xslt:when> - </xslt:choose> - </xslt:with-param> - </xslt:call-template> - </xslt:template> - - <!-- ignore other text --> - <xslt:template match="text()" /> - - <!-- Convert a string to a name --> - <xslt:template name="StringToName"> - <xslt:param name="inString" select="@name" /> - <xslt:param name="inNode" select="." /> - - <xslt:choose> - <!-- element names handled with StringToElementName --> - <xslt:when test="(local-name($inNode) = 'element') and - (namespace-uri($inNode) = $schemaNamespace) - "> - <xslt:call-template name="StringToElementName"> - <xslt:with-param name="inString" select="$inString"/> - </xslt:call-template> - </xslt:when> - - <!-- attribute names handled with StringToAttributeName --> - <xslt:when test="(local-name($inNode) = 'attribute') and - (namespace-uri($inNode) = $schemaNamespace) - "> - <xslt:call-template name="StringToAttributeName"> - <xslt:with-param name="inString" select="$inString"/> - </xslt:call-template> - </xslt:when> - - <xslt:otherwise> - <xslt:value-of select="@name"/> - </xslt:otherwise> - </xslt:choose> - </xslt:template> - - <!-- Convert a string to an element name --> - <xslt:template name="StringToElementName"> - <xslt:param name="inString" /> - <xslt:text><</xslt:text> - <xslt:value-of select="$inString" /> - <xslt:text>></xslt:text> - </xslt:template> - - <!-- Convert a string to an attribute name --> - <xslt:template name="StringToAttributeName"> - <xslt:param name="inString" /> - <xslt:text>@</xslt:text> - <xslt:value-of select="$inString" /> - </xslt:template> - - <!-- - Convert a string parameter to an escapted Javascript string in - quotes. - --> - <xslt:template name="StringToJavascript"> - <xslt:param name="inString" /> - <!-- quote the string --> - <xslt:variable name="quotedString" - select="concat($dQuote,translate($inString,$dQuote,$sQuote),$dQuote)"/> - <!-- replace linefeeds with \n --> - <xslt:variable name="lfString"> - <xslt:call-template name="ReplaceText"> - <xslt:with-param name="inString" select="$quotedString"/> - <xslt:with-param name="searchString" select="'
'"/> - <xslt:with-param name="replaceString" select="'\n'"/> - </xslt:call-template> - </xslt:variable> - <!-- replace tabs with 5 spaces --> - <xslt:variable name="tabString"> - <xslt:call-template name="ReplaceText"> - <xslt:with-param name="inString" select="$lfString"/> - <xslt:with-param name="searchString" select="'	'"/> - <xslt:with-param name="replaceString" select="' '"/> - </xslt:call-template> - </xslt:variable> - <!-- remove carrige returns --> - <xslt:variable name="crString" select="translate($tabString,'
','')"/> - <!-- replace < with unicode sequence --> - <xslt:variable name="ltString"> - <xslt:call-template name="ReplaceText"> - <xslt:with-param name="inString" select="$crString"/> - <xslt:with-param name="searchString" select="'<'"/> - <xslt:with-param name="replaceString" select="'\u003c'"/> - </xslt:call-template> - </xslt:variable> - <!-- replace > with unicode sequence --> - <xslt:variable name="gtString"> - <xslt:call-template name="ReplaceText"> - <xslt:with-param name="inString" select="$ltString"/> - <xslt:with-param name="searchString" select="'>'"/> - <xslt:with-param name="replaceString" select="'\u003e'"/> - </xslt:call-template> - </xslt:variable> - <xslt:value-of select="$gtString"/> - </xslt:template> - - <!-- - Simple search and replace - --> - <xslt:template name="ReplaceText"> - <xslt:param name="inString" /> - <xslt:param name="searchString"/> - <xslt:param name="replaceString"/> - - <xslt:choose> - <xslt:when test="$searchString and - contains($inString, $searchString)"> - <xslt:value-of select="substring-before($inString, $searchString)"/> - <xslt:value-of select="$replaceString"/> - <xslt:call-template name="ReplaceText"> - <xslt:with-param name="inString" select="substring-after($inString, $searchString)"/> - <xslt:with-param name="searchString" select="$searchString"/> - <xslt:with-param name="replaceString" select="$replaceString"/> - </xslt:call-template> - </xslt:when> - <xslt:otherwise> - <xslt:value-of select="$inString"/> - </xslt:otherwise> - </xslt:choose> - </xslt:template> -</xslt:stylesheet> diff --git a/keystone/content/multiple_choice.json.tpl b/keystone/content/multiple_choice.json.tpl deleted file mode 100644 index 578502ac..00000000 --- a/keystone/content/multiple_choice.json.tpl +++ /dev/null @@ -1,26 +0,0 @@ -{ - "choices": [ - { - "id": "v{{API_VERSION}}", - "status": "{{API_VERSION_STATUS}}", - "links": [ - { - "rel": "self", - "href": "{{PROTOCOL}}://{{HOST}}:{{PORT}}/v{{API_VERSION}}{{RESOURCE_PATH}}" - } - ], - "media-types": { - "values": [ - { - "base": "application/xml", - "type": "application/vnd.openstack.identity+xml;version={{API_VERSION}}" - }, - { - "base": "application/json", - "type": "application/vnd.openstack.identity+json;version={{API_VERSION}}" - } - ] - } - } - ] -}
\ No newline at end of file diff --git a/keystone/content/multiple_choice.xml.tpl b/keystone/content/multiple_choice.xml.tpl deleted file mode 100644 index 9f3b88a4..00000000 --- a/keystone/content/multiple_choice.xml.tpl +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<choices - xmlns="http://docs.openstack.org/common/api/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom"> - <version id="v{{API_VERSION}}" status="{{API_VERSION_STATUS}}"> - <media-types> - <media-type - base="application/xml" - type="application/vnd.openstack.identity+xml;version={{API_VERSION}}" /> - <media-type - base="application/json" - type="application/vnd.openstack.identity+json;version={{API_VERSION}}" /> - </media-types> - <atom:link rel="self" href="{{PROTOCOL}}://{{HOST}}:{{PORT}}/v{{API_VERSION}}{{RESOURCE_PATH}}" /> - </version> -</choices>
\ No newline at end of file diff --git a/keystone/content/service/OS-KSEC2-service-devguide.pdf b/keystone/content/service/OS-KSEC2-service-devguide.pdf Binary files differdeleted file mode 100644 index 7ebf1078..00000000 --- a/keystone/content/service/OS-KSEC2-service-devguide.pdf +++ /dev/null diff --git a/keystone/content/service/RAX-KSGRP-service-devguide.pdf b/keystone/content/service/RAX-KSGRP-service-devguide.pdf Binary files differdeleted file mode 100644 index add0cb28..00000000 --- a/keystone/content/service/RAX-KSGRP-service-devguide.pdf +++ /dev/null diff --git a/keystone/content/service/RAX-KSKEY-service-devguide.pdf b/keystone/content/service/RAX-KSKEY-service-devguide.pdf Binary files differdeleted file mode 100644 index 289ceb48..00000000 --- a/keystone/content/service/RAX-KSKEY-service-devguide.pdf +++ /dev/null diff --git a/keystone/content/service/extensions.json b/keystone/content/service/extensions.json deleted file mode 100644 index 7a514fd7..00000000 --- a/keystone/content/service/extensions.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extensions": { - "values": [] - } -} diff --git a/keystone/content/service/extensions.xml b/keystone/content/service/extensions.xml deleted file mode 100644 index d63c1b96..00000000 --- a/keystone/content/service/extensions.xml +++ /dev/null @@ -1,5 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<extensions xmlns="http://docs.openstack.org/common/api/v2.0" - xmlns:atom="http://www.w3.org/2005/Atom"> -</extensions> diff --git a/keystone/content/service/identity.wadl b/keystone/content/service/identity.wadl deleted file mode 100644 index d91a7030..00000000 --- a/keystone/content/service/identity.wadl +++ /dev/null @@ -1,182 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- (C) 2011 OpenStack LLC., All Rights Reserved --> -<!--*******************************************************--> -<!-- Import Common XML Entities --> -<!-- --> -<!-- You can resolve the entites with xmllint --> -<!-- --> -<!-- xmllint -noent identity.wadl --> -<!--*******************************************************--> -<!DOCTYPE application [ - <!ENTITY % common SYSTEM "../common/common.ent"> - %common; -]> - -<application xmlns="http://wadl.dev.java.net/2009/02" - xmlns:identity="http://docs.openstack.org/identity/api/v2.0" - xmlns:capi="http://docs.openstack.org/common/api/v1.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:html="http://www.w3.org/1999/xhtml" - xmlns:wadl="http://wadl.dev.java.net/2009/02" - xsi:schemaLocation="http://docs.openstack.org/identity/api/v2.0 ../common/xsd/api.xsd - http://docs.openstack.org/common/api/v1.0 ../common/xsd/api-common.xsd - http://wadl.dev.java.net/2009/02 http://www.w3.org/Submission/wadl/wadl.xsd - "> - - <grammars> - <include href="../common/xsd/api.xsd"/> - <include href="../common/xsd/api-common.xsd"/> - </grammars> - - <!--*******************************************************--> - <!-- All Resources --> - <!--*******************************************************--> - - <!-- We should use SSL in production --> - <resources base="http://localhost:5000"> - <resource id="version" type="#VersionDetails" path="v2.0/"> - <resource id="extensions" type="#ExtensionList" path="extensions"> - <resource id="extension" path="{alias}"> - <param name="alias" style="template" type="xsd:string"/> - <method href="#getExtension"/> - </resource> - </resource> - <resource id="tokens" path="tokens"> - <method href="#authenticate"/> - </resource> - <resource id="tenants" path="tenants"> - <method href="#listTenants"/> - </resource> - </resource> - </resources> - - <!--***************************************************--> - <!-- Resource Types --> - <!--*******************************************************--> - - <resource_type id="VersionDetails"> - <method href="#getVersionInfo"/> - </resource_type> - <resource_type id="ExtensionList"> - <doc xml:lang="EN" title="Extension List"> - <p xmlns="http://www.w3.org/1999/xhtml"> - A list of supported extensions. - </p> - </doc> - <method href="#listExtensions"/> - </resource_type> - - <!--*******************************************************--> - <!-- All Methods --> - <!--*******************************************************--> - - <!-- Version --> - - <method name="GET" id="getVersionInfo"> - <doc xml:lang="EN" title="Version Details"> - <p xmlns="http://www.w3.org/1999/xhtml"> - Returns detailed information about this specific version of the API. - </p> - </doc> - <response status="200 203"> - <representation mediaType="application/xml" element="capi:version"> - <param name="location" style="plain" type="xsd:anyURI" required="true" path="/capi:version/atom:link[@rel='self']/@href"> - <link resource_type="#VersionDetails" rel="self"/> - </param> - </representation> - <representation mediaType="application/json"/> - </response> - &commonFaults; - &getFaults; - </method> - - <!-- Extensions --> - - <method name="GET" id="listExtensions"> - <doc xml:lang="EN" title="List Extensions"> - <p xmlns="http://www.w3.org/1999/xhtml">List all available extensions.</p> - </doc> - <response status="200 203"> - <representation mediaType="application/xml" element="capi:extensions"> - <param name="next" style="plain" type="xsd:anyURI" path="/capi:extensions/atom:link[@rel='next']/@href"> - <link resource_type="#ExtensionList" rel="next"/> - </param> - <param name="previous" style="plain" type="xsd:anyURI" path="/capi:extensions/atom:link[@rel='previous']/@href"> - <link resource_type="#ExtensionList" rel="previous"/> - </param> - </representation> - <representation mediaType="application/json"/> - </response> - &commonFaults; - &getFaults; - </method> - <method name="GET" id="getExtension"> - <doc xml:lang="EN" title="Get Server Details"> - <p xmlns="http://www.w3.org/1999/xhtml">Get details about a specific extension.</p> - </doc> - <response status="200 203"> - <representation mediaType="application/xml" element="capi:extension"/> - <representation mediaType="application/json"/> - </response> - &commonFaults; - &getFaults; - </method> - - <!-- Token Operations --> - <method name="POST" id="authenticate"> - <wadl:doc xml:lang="EN" title="Authenticate" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - Client authentication is provided via a ReST interface using the POST method, - with v2.0/tokens supplied as the path. A payload of credentials must be included - in the body. See <a href="xsd/credentials.xsd">supported credentials</a> - </p> - <p> - Each ReST request against the Keystone system requires the inclusion of a - specific authorization token HTTP x-header, defined as X-Auth-Token. Clients obtain - this token, along with the URL to other service APIs, by first authenticating against the - Keystone Service and supplying valid credentials. - </p> - <p> - The Keystone Service is a ReSTful web service. It is the entry point to all service APIs. - To access the Keystone Service, you must know URL of the Keystone service. - </p> - </wadl:doc> - <request> - <representation mediaType="application/xml" element="identity:auth"/> - <representation mediaType="application/json"/> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:access"/> - <representation mediaType="application/json"/> - </response> - <response status="403"> - <representation mediaType="application/xml" element="identity:userDisabled"/> - <representation mediaType="application/json"/> - </response> - &commonFaults; - &getFaults; - </method> - - <!-- Tenant Operations --> - - <method name="GET" id="listTenants"> - <wadl:doc title="List Tenants" xml:lang="EN" - xmlns="http://www.w3.org/1999/xhtml"> - <p> - Returns a list of tenants. - </p> - </wadl:doc> - <request> - <param name="marker" style="query" required="false" type="xsd:string"/> - <param name="limit" style="query" required="false" type="xsd:int"/> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:tenants"/> - <representation mediaType="application/json"/> - </response> - &commonFaults; - &getFaults; - </method> -</application> diff --git a/keystone/content/service/identitydevguide.pdf b/keystone/content/service/identitydevguide.pdf Binary files differdeleted file mode 100644 index 5229e5d4..00000000 --- a/keystone/content/service/identitydevguide.pdf +++ /dev/null diff --git a/keystone/content/service/version.atom.tpl b/keystone/content/service/version.atom.tpl deleted file mode 100644 index e39250d5..00000000 --- a/keystone/content/service/version.atom.tpl +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<feed xmlns="http://www.w3.org/2005/Atom"> - <title type="text">Available API Versions</title> - <updated>2010-12-22T00:00:00.00Z</updated> - <id>http://identity.api.openstack.org/</id> - <author><name>OpenStack</name><uri>http://www.openstack.org/</uri></author> - <link rel="self" href="http://identity.api.openstack.org/"/> - <entry> - <id>http://identity.api.openstack.org/v2.0/</id> - <title type="text">Version v2.0</title> - <updated>2011-09-30T00:00:00.00Z</updated> - <link rel="self" href="http://identity.api.openstack.org/v2.0/"/> - <content type="text">Version v2.0 BETA (2011-09-30T00:00:00.00Z)</content> - </entry> - <entry> - <id>http://identity.api.openstack.org/v1.1/</id> - <title type="text">Version v1.1</title> - <updated>2011-01-21T11:33:21-06:00</updated> - <link rel="self" href="http://identity.api.openstack.org/v1.1/"/> - <content type="text">Version v1.1 CURRENT (2011-01-21T11:33:21-06:00)</content> - </entry> - <entry> - <id>http://identity.api.openstack.org/v1.0/</id> - <title type="text">Version v1.0</title> - <updated>2009-10-09T11:30:00Z</updated> - <link rel="self" href="http://identity.api.openstack.org/v1.0/"/> - <content type="text">Version v1.0 DEPRECATED (2009-10-09T11:30:00Z)</content> - </entry> -</feed> diff --git a/keystone/content/service/version.json.tpl b/keystone/content/service/version.json.tpl deleted file mode 100644 index 0eb824bf..00000000 --- a/keystone/content/service/version.json.tpl +++ /dev/null @@ -1,32 +0,0 @@ -{ - "versions": { - "values": [{ - "id": "v{{API_VERSION}}", - "status": "{{API_VERSION_STATUS}}", - "updated": "{{API_VERSION_DATE}}", - "links": [{ - "rel": "self", - "href": "http://{{HOST}}:{{PORT}}/v2.0/" - }, { - "rel": "describedby", - "type": "text/html", - "href": "http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/content/" - }, { - "rel": "describedby", - "type": "application/pdf", - "href": "http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/identity-dev-guide-{{API_VERSION}}.pdf" - }, { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "http://{{HOST}}:{{PORT}}/v2.0/identity.wadl" - }], - "media-types": [{ - "base": "application/xml", - "type": "application/vnd.openstack.identity-v2.0+xml" - }, { - "base": "application/json", - "type": "application/vnd.openstack.identity-v2.0+json" - }] - }] - } -}
\ No newline at end of file diff --git a/keystone/content/service/version.xml.tpl b/keystone/content/service/version.xml.tpl deleted file mode 100644 index 30a46489..00000000 --- a/keystone/content/service/version.xml.tpl +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<versions xmlns="http://docs.openstack.org/common/api/v2.0" - xmlns:atom="http://www.w3.org/2005/Atom"> - - <version id="v{{API_VERSION}}" status="{{API_VERSION_STATUS}}" updated="{{API_VERSION_DATE}}"> - - <media-types> - <media-type base="application/xml" - type="application/vnd.openstack.identity-v{{API_VERSION}}+xml"/> - <media-type base="application/json" - type="application/vnd.openstack.identity-v{{API_VERSION}}+json"/> - </media-types> - - <atom:link rel="self" - href="http://{{HOST}}:{{PORT}}/v{{API_VERSION}}/"/> - - <atom:link rel="describedby" - type="text/html" - href="http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/content/" /> - - <atom:link rel="describedby" - type="application/pdf" - href="http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/identity-dev-guide-{{API_VERSION}}.pdf" /> - - <atom:link rel="describedby" - type="application/vnd.sun.wadl+xml" - href="http://{{HOST}}:{{PORT}}/v2.0/identity.wadl" /> - </version> -</versions>
\ No newline at end of file diff --git a/keystone/contrib/admin_crud/__init__.py b/keystone/contrib/admin_crud/__init__.py new file mode 100644 index 00000000..eb8c4f17 --- /dev/null +++ b/keystone/contrib/admin_crud/__init__.py @@ -0,0 +1 @@ +from keystone.contrib.admin_crud.core import * diff --git a/keystone/contrib/admin_crud/core.py b/keystone/contrib/admin_crud/core.py new file mode 100644 index 00000000..15a597ed --- /dev/null +++ b/keystone/contrib/admin_crud/core.py @@ -0,0 +1,150 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +from keystone import catalog +from keystone import identity +from keystone.common import wsgi + + +class CrudExtension(wsgi.ExtensionRouter): + """Previously known as the OS-KSADM extension. + + Provides a bunch of CRUD operations for internal data types. + + """ + + def add_routes(self, mapper): + tenant_controller = identity.TenantController() + user_controller = identity.UserController() + role_controller = identity.RoleController() + service_controller = catalog.ServiceController() + + # Tenant Operations + mapper.connect('/tenants', controller=tenant_controller, + action='create_tenant', + conditions=dict(method=['POST'])) + mapper.connect('/tenants/{tenant_id}', + controller=tenant_controller, + action='update_tenant', + conditions=dict(method=['PUT', 'POST'])) + mapper.connect('/tenants/{tenant_id}', + controller=tenant_controller, + action='delete_tenant', + conditions=dict(method=['DELETE'])) + mapper.connect('/tenants/{tenant_id}/users', + controller=user_controller, + action='get_tenant_users', + conditions=dict(method=['GET'])) + + # User Operations + mapper.connect('/users', + controller=user_controller, + action='get_users', + conditions=dict(method=['GET'])) + mapper.connect('/users', + controller=user_controller, + action='create_user', + conditions=dict(method=['POST'])) + # NOTE(termie): not in diablo + mapper.connect('/users/{user_id}', + controller=user_controller, + action='update_user', + conditions=dict(method=['PUT'])) + mapper.connect('/users/{user_id}', + controller=user_controller, + action='delete_user', + conditions=dict(method=['DELETE'])) + + # COMPAT(diablo): the copy with no OS-KSADM is from diablo + mapper.connect('/users/{user_id}/password', + controller=user_controller, + action='set_user_password', + conditions=dict(method=['PUT'])) + mapper.connect('/users/{user_id}/OS-KSADM/password', + controller=user_controller, + action='set_user_password', + conditions=dict(method=['PUT'])) + + # COMPAT(diablo): the copy with no OS-KSADM is from diablo + mapper.connect('/users/{user_id}/tenant', + controller=user_controller, + action='update_user_tenant', + conditions=dict(method=['PUT'])) + mapper.connect('/users/{user_id}/OS-KSADM/tenant', + controller=user_controller, + action='update_user_tenant', + conditions=dict(method=['PUT'])) + + # COMPAT(diablo): the copy with no OS-KSADM is from diablo + mapper.connect('/users/{user_id}/enabled', + controller=user_controller, + action='set_user_enabled', + conditions=dict(method=['PUT'])) + mapper.connect('/users/{user_id}/OS-KSADM/enabled', + controller=user_controller, + action='set_user_enabled', + conditions=dict(method=['PUT'])) + + # User Roles + mapper.connect('/users/{user_id}/roles/OS-KSADM/{role_id}', + controller=role_controller, action='add_role_to_user', + conditions=dict(method=['PUT'])) + mapper.connect('/users/{user_id}/roles/OS-KSADM/{role_id}', + controller=role_controller, action='delete_role_from_user', + conditions=dict(method=['DELETE'])) + + # COMPAT(diablo): User Roles + mapper.connect('/users/{user_id}/roleRefs', + controller=role_controller, action='get_role_refs', + conditions=dict(method=['GET'])) + mapper.connect('/users/{user_id}/roleRefs', + controller=role_controller, action='create_role_ref', + conditions=dict(method=['POST'])) + mapper.connect('/users/{user_id}/roleRefs/{role_ref_id}', + controller=role_controller, action='delete_role_ref', + conditions=dict(method=['DELETE'])) + + # User-Tenant Roles + mapper.connect( + '/tenants/{tenant_id}/users/{user_id}/roles/OS-KSADM/{role_id}', + controller=role_controller, action='add_role_to_user', + conditions=dict(method=['PUT'])) + mapper.connect( + '/tenants/{tenant_id}/users/{user_id}/roles/OS-KSADM/{role_id}', + controller=role_controller, action='remove_role_from_user', + conditions=dict(method=['DELETE'])) + + # Service Operations + mapper.connect('/OS-KSADM/services', + controller=service_controller, + action='get_services', + conditions=dict(method=['GET'])) + mapper.connect('/OS-KSADM/services', + controller=service_controller, + action='create_service', + conditions=dict(method=['POST'])) + mapper.connect('/OS-KSADM/services/{service_id}', + controller=service_controller, + action='delete_service', + conditions=dict(method=['DELETE'])) + mapper.connect('/OS-KSADM/services/{service_id}', + controller=service_controller, + action='get_service', + conditions=dict(method=['GET'])) + + # Role Operations + mapper.connect('/OS-KSADM/roles', + controller=role_controller, + action='create_role', + conditions=dict(method=['POST'])) + mapper.connect('/OS-KSADM/roles', + controller=role_controller, + action='get_roles', + conditions=dict(method=['GET'])) + mapper.connect('/OS-KSADM/roles/{role_id}', + controller=role_controller, + action='get_role', + conditions=dict(method=['GET'])) + mapper.connect('/OS-KSADM/roles/{role_id}', + controller=role_controller, + action='delete_role', + conditions=dict(method=['DELETE'])) diff --git a/keystone/contrib/ec2/__init__.py b/keystone/contrib/ec2/__init__.py new file mode 100644 index 00000000..6c85c66d --- /dev/null +++ b/keystone/contrib/ec2/__init__.py @@ -0,0 +1 @@ +from keystone.contrib.ec2.core import * diff --git a/keystone/contrib/extensions/admin/osec2/__init__.py b/keystone/contrib/ec2/backends/__init__.py index e69de29b..e69de29b 100644 --- a/keystone/contrib/extensions/admin/osec2/__init__.py +++ b/keystone/contrib/ec2/backends/__init__.py diff --git a/keystone/contrib/ec2/backends/kvs.py b/keystone/contrib/ec2/backends/kvs.py new file mode 100644 index 00000000..6aa2fe81 --- /dev/null +++ b/keystone/contrib/ec2/backends/kvs.py @@ -0,0 +1,31 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +from keystone.common import kvs + + +class Ec2(kvs.Base): + # Public interface + def get_credential(self, credential_id): + credential_ref = self.db.get('credential-%s' % credential_id) + return credential_ref + + def list_credentials(self, user_id): + credential_ids = self.db.get('credential_list', []) + rv = [self.get_credential(x) for x in credential_ids] + return [x for x in rv if x['user_id'] == user_id] + + # CRUD + def create_credential(self, credential_id, credential): + self.db.set('credential-%s' % credential_id, credential) + credential_list = set(self.db.get('credential_list', [])) + credential_list.add(credential_id) + self.db.set('credential_list', list(credential_list)) + return credential + + def delete_credential(self, credential_id): + old_credential = self.db.get('credential-%s' % credential_id) + self.db.delete('credential-%s' % credential_id) + credential_list = set(self.db.get('credential_list', [])) + credential_list.remove(credential_id) + self.db.set('credential_list', list(credential_list)) + return None diff --git a/keystone/contrib/ec2/backends/sql.py b/keystone/contrib/ec2/backends/sql.py new file mode 100644 index 00000000..3105660e --- /dev/null +++ b/keystone/contrib/ec2/backends/sql.py @@ -0,0 +1,52 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +from keystone.common import sql +from keystone.common.sql import migration + + +class Ec2Credential(sql.ModelBase, sql.DictBase): + __tablename__ = 'ec2_credential' + access = sql.Column(sql.String(64), primary_key=True) + secret = sql.Column(sql.String(64)) + user_id = sql.Column(sql.String(64)) + tenant_id = sql.Column(sql.String(64)) + + @classmethod + def from_dict(cls, user_dict): + return cls(**user_dict) + + def to_dict(self): + return dict(self.iteritems()) + + +class Ec2(sql.Base): + def get_credential(self, credential_id): + session = self.get_session() + credential_ref = session.query(Ec2Credential)\ + .filter_by(access=credential_id).first() + if not credential_ref: + return + return credential_ref.to_dict() + + def list_credentials(self, user_id): + session = self.get_session() + credential_refs = session.query(Ec2Credential)\ + .filter_by(user_id=user_id) + return [x.to_dict() for x in credential_refs] + + # CRUD + def create_credential(self, credential_id, credential): + session = self.get_session() + with session.begin(): + credential_ref = Ec2Credential.from_dict(credential) + session.add(credential_ref) + session.flush() + return credential_ref.to_dict() + + def delete_credential(self, credential_id): + session = self.get_session() + credential_ref = session.query(Ec2Credential)\ + .filter_by(access=credential_id).first() + with session.begin(): + session.delete(credential_ref) + session.flush() diff --git a/keystone/contrib/ec2/core.py b/keystone/contrib/ec2/core.py new file mode 100644 index 00000000..08a286cc --- /dev/null +++ b/keystone/contrib/ec2/core.py @@ -0,0 +1,288 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +"""Main entry point into the EC2 Credentials service. + +This service allows the creation of access/secret credentials used for +the ec2 interop layer of OpenStack. + +A user can create as many access/secret pairs, each of which map to a +specific tenant. This is required because OpenStack supports a user +belonging to multiple tenants, whereas the signatures created on ec2-style +requests don't allow specification of which tenant the user wishs to act +upon. + +To complete the cycle, we provide a method that OpenStack services can +use to validate a signature and get a corresponding openstack token. This +token allows method calls to other services within the context the +access/secret was created. As an example, nova requests keystone to validate +the signature of a request, receives a token, and then makes a request to +glance to list images needed to perform the requested task. + +""" + +import uuid + +import webob.exc + +from keystone import catalog +from keystone import config +from keystone import exception +from keystone import identity +from keystone import policy +from keystone import service +from keystone import token +from keystone.common import manager +from keystone.common import utils +from keystone.common import wsgi + + +CONF = config.CONF + + +class Manager(manager.Manager): + """Default pivot point for the EC2 Credentials 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.ec2.driver) + + +class Ec2Extension(wsgi.ExtensionRouter): + def add_routes(self, mapper): + ec2_controller = Ec2Controller() + # validation + mapper.connect('/ec2tokens', + controller=ec2_controller, + action='authenticate', + conditions=dict(method=['POST'])) + + # crud + mapper.connect('/users/{user_id}/credentials/OS-EC2', + controller=ec2_controller, + action='create_credential', + conditions=dict(method=['POST'])) + mapper.connect('/users/{user_id}/credentials/OS-EC2', + controller=ec2_controller, + action='get_credentials', + conditions=dict(method=['GET'])) + mapper.connect('/users/{user_id}/credentials/OS-EC2/{credential_id}', + controller=ec2_controller, + action='get_credential', + conditions=dict(method=['GET'])) + mapper.connect('/users/{user_id}/credentials/OS-EC2/{credential_id}', + controller=ec2_controller, + action='delete_credential', + conditions=dict(method=['DELETE'])) + + +class Ec2Controller(wsgi.Application): + def __init__(self): + self.catalog_api = catalog.Manager() + self.identity_api = identity.Manager() + self.token_api = token.Manager() + self.policy_api = policy.Manager() + self.ec2_api = Manager() + super(Ec2Controller, self).__init__() + + def check_signature(self, creds_ref, credentials): + signer = utils.Ec2Signer(creds_ref['secret']) + signature = signer.generate(credentials) + if signature == credentials['signature']: + return + # NOTE(vish): Some libraries don't use the port when signing + # requests, so try again without port. + elif ':' in credentials['signature']: + hostname, _port = credentials['host'].split(':') + credentials['host'] = hostname + signature = signer.generate(credentials) + if signature != credentials.signature: + # TODO(termie): proper exception + msg = 'Invalid signature' + raise webob.exc.HTTPUnauthorized(explanation=msg) + else: + msg = 'Signature not supplied' + raise webob.exc.HTTPUnauthorized(explanation=msg) + + def authenticate(self, context, credentials=None, + ec2Credentials=None): + """Validate a signed EC2 request and provide a token. + + Other services (such as Nova) use this **admin** call to determine + if a request they signed received is from a valid user. + + If it is a valid signature, an openstack token that maps + to the user/tenant is returned to the caller, along with + all the other details returned from a normal token validation + call. + + The returned token is useful for making calls to other + OpenStack services within the context of the request. + + :param context: standard context + :param credentials: dict of ec2 signature + :param ec2Credentials: DEPRECATED dict of ec2 signature + :returns: token: openstack token equivalent to access key along + with the corresponding service catalog and roles + """ + + # FIXME(ja): validate that a service token was used! + + # NOTE(termie): backwards compat hack + if not credentials and ec2Credentials: + credentials = ec2Credentials + + creds_ref = self.ec2_api.get_credential(context, + credentials['access']) + if not creds_ref: + msg = 'Access key not found' + raise webob.exc.HTTPUnauthorized(explanation=msg) + + self.check_signature(creds_ref, credentials) + + # TODO(termie): don't create new tokens every time + # TODO(termie): this is copied from TokenController.authenticate + token_id = uuid.uuid4().hex + tenant_ref = self.identity_api.get_tenant( + context=context, + tenant_id=creds_ref['tenant_id']) + user_ref = self.identity_api.get_user( + context=context, + user_id=creds_ref['user_id']) + metadata_ref = self.identity_api.get_metadata( + context=context, + user_id=user_ref['id'], + tenant_id=tenant_ref['id']) + catalog_ref = self.catalog_api.get_catalog( + context=context, + user_id=user_ref['id'], + tenant_id=tenant_ref['id'], + metadata=metadata_ref) + + token_ref = self.token_api.create_token( + context, token_id, dict(id=token_id, + user=user_ref, + tenant=tenant_ref, + metadata=metadata_ref)) + + # TODO(termie): optimize this call at some point and put it into the + # the return for metadata + # fill out the roles in the metadata + roles_ref = [] + for role_id in metadata_ref.get('roles', []): + roles_ref.append(self.identity_api.get_role(context, role_id)) + + # TODO(termie): make this a util function or something + # TODO(termie): i don't think the ec2 middleware currently expects a + # full return, but it contains a note saying that it + # would be better to expect a full return + token_controller = service.TokenController() + return token_controller._format_authenticate( + token_ref, roles_ref, catalog_ref) + + def create_credential(self, context, user_id, tenant_id): + """Create a secret/access pair for use with ec2 style auth. + + Generates a new set of credentials that map the the user/tenant + pair. + + :param context: standard context + :param user_id: id of user + :param tenant_id: id of tenant + :returns: credential: dict of ec2 credential + """ + if not self._is_admin(context): + self._assert_identity(context, user_id) + cred_ref = {'user_id': user_id, + 'tenant_id': tenant_id, + 'access': uuid.uuid4().hex, + 'secret': uuid.uuid4().hex} + self.ec2_api.create_credential(context, cred_ref['access'], cred_ref) + return {'credential': cred_ref} + + def get_credentials(self, context, user_id): + """List all credentials for a user. + + :param context: standard context + :param user_id: id of user + :returns: credentials: list of ec2 credential dicts + """ + if not self._is_admin(context): + self._assert_identity(context, user_id) + return {'credentials': self.ec2_api.list_credentials(context, user_id)} + + def get_credential(self, context, user_id, credential_id): + """Retreive a user's access/secret pair by the access key. + + Grab the full access/secret pair for a given access key. + + :param context: standard context + :param user_id: id of user + :param credential_id: access key for credentials + :returns: credential: dict of ec2 credential + """ + if not self._is_admin(context): + self._assert_identity(context, user_id) + return {'credential': self.ec2_api.get_credential(context, + credential_id)} + + def delete_credential(self, context, user_id, credential_id): + """Delete a user's access/secret pair. + + Used to revoke a user's access/secret pair + + :param context: standard context + :param user_id: id of user + :param credential_id: access key for credentials + :returns: bool: success + """ + if not self._is_admin(context): + self._assert_identity(context, user_id) + self._assert_owner(context, user_id, credential_id) + return self.ec2_api.delete_credential(context, credential_id) + + def _assert_identity(self, context, user_id): + """Check that the provided token belongs to the user. + + :param context: standard context + :param user_id: id of user + :raises webob.exc.HTTPForbidden: when token is invalid + + """ + try: + token_ref = self.token_api.get_token(context=context, + token_id=context['token_id']) + except exception.TokenNotFound: + raise exception.Unauthorized() + token_user_id = token_ref['user'].get('id') + if not token_user_id == user_id: + raise webob.exc.HTTPForbidden() + + def _is_admin(self, context): + """Wrap admin assertion error return statement. + + :param context: standard context + :returns: bool: success + + """ + try: + self.assert_admin(context) + return True + except AssertionError: + return False + + def _assert_owner(self, context, user_id, credential_id): + """Ensure the provided user owns the credential. + + :param context: standard context + :param user_id: expected credential owner + :param credential_id: id of credential object + :raises webob.exc.HTTPForbidden: on failure + + """ + cred_ref = self.ec2_api.get_credential(context, credential_id) + if not user_id == cred_ref['user_id']: + raise webob.exc.HTTPForbidden() diff --git a/keystone/contrib/extensions/__init__.py b/keystone/contrib/extensions/__init__.py deleted file mode 100644 index 6d4247ba..00000000 --- a/keystone/contrib/extensions/__init__.py +++ /dev/null @@ -1,52 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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 logging - -from keystone import config -from keystone import utils - -CONF = config.CONF -EXTENSION_PREFIX = 'keystone.contrib.extensions.' -DEFAULT_EXTENSIONS = ['osksadm', 'oskscatalog'] -CONFIG_EXTENSION_PROPERTY = 'extensions' - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class BaseExtensionConfigurer(object): - def configure_extensions(self, extension_type, mapper): - extensions = CONF[CONFIG_EXTENSION_PROPERTY] or \ - DEFAULT_EXTENSIONS - extensions = [extension.strip() for extension in extensions] - for supported_extension in extensions: - self.extension_handlers = [] - supported_extension = "%s%s.%s" % ( - EXTENSION_PREFIX, extension_type, supported_extension.strip()) - try: - extension_module = utils.import_module(supported_extension) - if hasattr(extension_module, 'ExtensionHandler'): - extension_class = extension_module.ExtensionHandler() - extension_class.map_extension_methods(mapper) - self.extension_handlers.append(extension_class) - except Exception as err: - logger.exception("Could not load extension for %s:%s %s" % - (extension_type, supported_extension, err)) - - def get_extension_handlers(self): - return self.extension_handlers diff --git a/keystone/contrib/extensions/admin/__init__.py b/keystone/contrib/extensions/admin/__init__.py deleted file mode 100644 index 7db667ef..00000000 --- a/keystone/contrib/extensions/admin/__init__.py +++ /dev/null @@ -1,36 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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 ast -import logging -from keystone import utils -from keystone.contrib.extensions import BaseExtensionConfigurer -ADMIN_CONFIGURER = None -EXTENSION_ADMIN_PREFIX = 'admin' - - -class AdminExtensionConfigurer(BaseExtensionConfigurer): - def configure(self, mapper): - self.configure_extensions(EXTENSION_ADMIN_PREFIX, mapper) - - -def get_extension_configurer(): - global ADMIN_CONFIGURER - if not ADMIN_CONFIGURER: - ADMIN_CONFIGURER = AdminExtensionConfigurer() - return ADMIN_CONFIGURER diff --git a/keystone/contrib/extensions/admin/extension.py b/keystone/contrib/extensions/admin/extension.py deleted file mode 100644 index d2ae1525..00000000 --- a/keystone/contrib/extensions/admin/extension.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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 - - -class BaseExtensionHandler(object): - def map_extension_methods(self, mapper): - raise NotImplementedError diff --git a/keystone/contrib/extensions/admin/hpidm/__init__.py b/keystone/contrib/extensions/admin/hpidm/__init__.py deleted file mode 100644 index 451a662a..00000000 --- a/keystone/contrib/extensions/admin/hpidm/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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.contrib.extensions.admin.extension import BaseExtensionHandler -from keystone.controllers.token import TokenController - - -class ExtensionHandler(BaseExtensionHandler): - def map_extension_methods(self, mapper): - pass diff --git a/keystone/contrib/extensions/admin/hpidm/extension.json b/keystone/contrib/extensions/admin/hpidm/extension.json deleted file mode 100644 index 5804de07..00000000 --- a/keystone/contrib/extensions/admin/hpidm/extension.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "extension": { - "name": "HP Token Validation Extension", - "namespace": "http://docs.openstack.org/identity/api/ext/HP-IDM/v1.0", - "alias": "HP-IDM", - "updated": "2011-12-06T19:00:00-00:00", - "description": "Validate token with the optional serviceId parameter so that only the roles associated with the given service IDs are returned. See https://bugs.launchpad.net/keystone/+bug/890411 for more details.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/HP-IDM-admin-devguide.pdf" - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "https://raw.github.com/openstack/keystone/master/keystone/content/admin/HP-IDM-admin.wadl" - } - ] - } -} diff --git a/keystone/contrib/extensions/admin/hpidm/extension.xml b/keystone/contrib/extensions/admin/hpidm/extension.xml deleted file mode 100644 index 0482faac..00000000 --- a/keystone/contrib/extensions/admin/hpidm/extension.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - <extension - xmlns="http://docs.openstack.org/common/api/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom" - name="HP Token Validation Extension" - namespace="http://docs.openstack.org/identity/api/ext/HP-IDM/v1.0" - alias="HP-IDM" - updated="2011-12-25T17:00:00-00:00"> - - <description> - Validate token with the optional serviceId parameter so that only the roles associated with the given service IDs are returned. See https://bugs.launchpad.net/keystone/+bug/890411 for more details. - </description> - - <atom:link rel="describedby" - type="application/pdf" - href="https://github.com/openstack/keystone/raw/master/keystone/content/admin/HP-IDM-admin-devguide.pdf"/> - <atom:link rel="describedby" - type="application/vnd.sun.wadl+xml" - href="https://github.com/openstack/keystone/raw/master/keystone/content/admin/HP-IDM-admin.wadl"/> - </extension> - diff --git a/keystone/contrib/extensions/admin/osec2/extension.json b/keystone/contrib/extensions/admin/osec2/extension.json deleted file mode 100644 index 195091b5..00000000 --- a/keystone/contrib/extensions/admin/osec2/extension.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "extension": { - "name": "OpenStack EC2 authentication Extension", - "namespace": "http://docs.openstack.org/identity/api/ext/OS-KSEC2/v1.0", - "alias": "OS-KSEC2", - "updated": "2011-08-25T09:50:00-00:00", - "description": "Adds the capability to support EC2 style authentication.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/OS-KSEC2-admin-devguide.pdf" - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "https://raw.github.com/openstack/keystone/master/keystone/content/admin/OS-KSEC2-admin.wadl" - } - ] - } -} diff --git a/keystone/contrib/extensions/admin/osec2/extension.xml b/keystone/contrib/extensions/admin/osec2/extension.xml deleted file mode 100644 index ea243792..00000000 --- a/keystone/contrib/extensions/admin/osec2/extension.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - <extension - name="OpenStack EC2 authentication Extension" - namespace="http://docs.openstack.org/identity/api/ext/OS-KSEC2/v1.0" - alias="OS-KSEC2" - updated="2011-08-25T09:50:00-00:00"> - - <description> - Adds the capability to support EC2 style authentication. - </description> - - <atom:link rel="describedby" - type="application/pdf" - href="https://github.com/openstack/keystone/raw/master/keystone/content/admin/OS-KSEC2-admin-devguide.pdf"/> - <atom:link rel="describedby" - type="application/vnd.sun.wadl+xml" - href="https://raw.github.com/openstack/keystone/master/keystone/content/admin/OS-KSEC2-admin.wadl"/> - </extension> - diff --git a/keystone/contrib/extensions/admin/osksadm/__init__.py b/keystone/contrib/extensions/admin/osksadm/__init__.py deleted file mode 100644 index c660a7aa..00000000 --- a/keystone/contrib/extensions/admin/osksadm/__init__.py +++ /dev/null @@ -1,149 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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.contrib.extensions.admin.extension import BaseExtensionHandler -from keystone.controllers.services import ServicesController -from keystone.controllers.roles import RolesController -from keystone.controllers.user import UserController -from keystone.controllers.tenant import TenantController -from keystone.controllers.credentials import CredentialsController - - -class ExtensionHandler(BaseExtensionHandler): - def map_extension_methods(self, mapper): - tenant_controller = TenantController() - roles_controller = RolesController() - user_controller = UserController() - credentials_controller = CredentialsController() - - # Tenant Operations - mapper.connect("/tenants", controller=tenant_controller, - action="create_tenant", - conditions=dict(method=["POST"])) - mapper.connect("/tenants/{tenant_id}", - controller=tenant_controller, - action="update_tenant", conditions=dict(method=["POST"])) - mapper.connect("/tenants/{tenant_id}", - controller=tenant_controller, - action="delete_tenant", conditions=dict(method=["DELETE"])) - mapper.connect("/tenants/{tenant_id}/users", - controller=user_controller, - action="get_tenant_users", - conditions=dict(method=["GET"])) - - #Add/Delete Tenant specific role. - mapper.connect( - "/tenants/{tenant_id}/users/{user_id}/roles/OS-KSADM/{role_id}", - controller=roles_controller, action="add_role_to_user", - conditions=dict(method=["PUT"])) - mapper.connect( - "/tenants/{tenant_id}/users/{user_id}/roles/OS-KSADM/{role_id}", - controller=roles_controller, action="delete_role_from_user", - conditions=dict(method=["DELETE"])) - # User Operations - mapper.connect("/users", - controller=user_controller, - action="get_users", - conditions=dict(method=["GET"])) - mapper.connect("/users", - controller=user_controller, - action="create_user", - conditions=dict(method=["POST"])) - mapper.connect("/users/{user_id}", - controller=user_controller, - action="update_user", - conditions=dict(method=["POST"])) - mapper.connect("/users/{user_id}", - controller=user_controller, - action="delete_user", - conditions=dict(method=["DELETE"])) - #API doesn't have any of the shorthand updates as of now. - mapper.connect("/users/{user_id}/OS-KSADM/password", - controller=user_controller, - action="set_user_password", - conditions=dict(method=["PUT"])) - mapper.connect("/users/{user_id}/OS-KSADM/tenant", - controller=user_controller, - action="update_user_tenant", - conditions=dict(method=["PUT"])) - # Test this, test failed - mapper.connect("/users/{user_id}/OS-KSADM/enabled", - controller=user_controller, - action="set_user_enabled", - conditions=dict(method=["PUT"])) - #User Roles - #Add/Delete Global role. - mapper.connect("/users/{user_id}/roles/OS-KSADM/{role_id}", - controller=roles_controller, action="add_role_to_user", - conditions=dict(method=["PUT"])) - mapper.connect("/users/{user_id}/roles/OS-KSADM/{role_id}", - controller=roles_controller, action="delete_role_from_user", - conditions=dict(method=["DELETE"])) - - # Services Operations - services_controller = ServicesController() - mapper.connect("/OS-KSADM/services", - controller=services_controller, - action="get_services", - conditions=dict(method=["GET"])) - mapper.connect("/OS-KSADM/services", - controller=services_controller, - action="create_service", - conditions=dict(method=["POST"])) - mapper.connect("/OS-KSADM/services/{service_id}", - controller=services_controller, - action="delete_service", - conditions=dict(method=["DELETE"])) - mapper.connect("/OS-KSADM/services/{service_id}", - controller=services_controller, - action="get_service", - conditions=dict(method=["GET"])) - #Roles Operations - mapper.connect("/OS-KSADM/roles", controller=roles_controller, - action="create_role", conditions=dict(method=["POST"])) - mapper.connect("/OS-KSADM/roles", controller=roles_controller, - action="get_roles", conditions=dict(method=["GET"])) - mapper.connect("/OS-KSADM/roles/{role_id}", - controller=roles_controller, action="get_role", - conditions=dict(method=["GET"])) - mapper.connect("/OS-KSADM/roles/{role_id}", - controller=roles_controller, action="delete_role", - conditions=dict(method=["DELETE"])) - - #Credentials Operations - mapper.connect("/users/{user_id}/OS-KSADM/credentials", - controller=credentials_controller, action="get_credentials", - conditions=dict(method=["GET"])) - mapper.connect("/users/{user_id}/OS-KSADM/credentials", - controller=credentials_controller, action="add_credential", - conditions=dict(method=["POST"])) - mapper.connect("/users/{user_id}/OS-KSADM/"\ - "credentials/passwordCredentials", - controller=credentials_controller, - action="get_password_credential", - conditions=dict(method=["GET"])) - mapper.connect("/users/{user_id}/OS-KSADM/credentials"\ - "/passwordCredentials", - controller=credentials_controller, - action="update_password_credential", - conditions=dict(method=["POST"])) - mapper.connect("/users/{user_id}/"\ - "OS-KSADM/credentials/passwordCredentials", - controller=credentials_controller, - action="delete_password_credential", - conditions=dict(method=["DELETE"])) diff --git a/keystone/contrib/extensions/admin/osksadm/extension.json b/keystone/contrib/extensions/admin/osksadm/extension.json deleted file mode 100644 index 2c644f97..00000000 --- a/keystone/contrib/extensions/admin/osksadm/extension.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "extension": { - "name": "Openstack Keystone Admin", - "namespace": "http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0", - "alias": "OS-KSADM", - "updated": "2011-08-19T13:25:27-06:00", - "description": "Openstack extensions to Keystone v2.0 API enabling Admin Operations.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/OS-KSADM-admin-devguide.pdf" - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/OS-KSADM-admin.wadl" - } - ] - } -} diff --git a/keystone/contrib/extensions/admin/osksadm/extension.xml b/keystone/contrib/extensions/admin/osksadm/extension.xml deleted file mode 100644 index b3c0a7a6..00000000 --- a/keystone/contrib/extensions/admin/osksadm/extension.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<extension xmlns="http://docs.openstack.org/common/api/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom" - name="Openstack Keystone Admin" namespace="http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0" - alias="OS-KSADM" - updated="2011-08-14T13:25:27-06:00"> - <description> - Openstack extensions to Keystone v2.0 - API enabling Admin Operations. - </description> - <atom:link rel="describedby" type="application/pdf" - href="https://github.com/openstack/keystone/raw/master/keystone/content/admin/OS-KSADM-admin-devguide.pdf"/> - <atom:link rel="describedby" type="application/pdf" - href="https://github.com/openstack/keystone/raw/master/keystone/content/admin/OS-KSADM-admin.wadl"/> -</extension> diff --git a/keystone/contrib/extensions/admin/oskscatalog/__init__.py b/keystone/contrib/extensions/admin/oskscatalog/__init__.py deleted file mode 100644 index 38f5cf65..00000000 --- a/keystone/contrib/extensions/admin/oskscatalog/__init__.py +++ /dev/null @@ -1,63 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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.contrib.extensions.admin.extension import BaseExtensionHandler -from keystone.controllers.endpointtemplates import EndpointTemplatesController - - -class ExtensionHandler(BaseExtensionHandler): - def map_extension_methods(self, mapper): - #EndpointTemplates Calls - endpoint_templates_controller = EndpointTemplatesController() - mapper.connect("/OS-KSCATALOG/endpointTemplates", - controller=endpoint_templates_controller, - action="get_endpoint_templates", - conditions=dict(method=["GET"])) - mapper.connect("/OS-KSCATALOG/endpointTemplates", - controller=endpoint_templates_controller, - action="add_endpoint_template", - conditions=dict(method=["POST"])) - mapper.connect( - "/OS-KSCATALOG/endpointTemplates/{endpoint_template_id}", - controller=endpoint_templates_controller, - action="get_endpoint_template", - conditions=dict(method=["GET"])) - mapper.connect( - "/OS-KSCATALOG/endpointTemplates/{endpoint_template_id}", - controller=endpoint_templates_controller, - action="modify_endpoint_template", - conditions=dict(method=["PUT"])) - mapper.connect( - "/OS-KSCATALOG/endpointTemplates/{endpoint_template_id}", - controller=endpoint_templates_controller, - action="delete_endpoint_template", - conditions=dict(method=["DELETE"])) - #Endpoint Calls - mapper.connect("/tenants/{tenant_id}/OS-KSCATALOG/endpoints", - controller=endpoint_templates_controller, - action="get_endpoints_for_tenant", - conditions=dict(method=["GET"])) - mapper.connect("/tenants/{tenant_id}/OS-KSCATALOG/endpoints", - controller=endpoint_templates_controller, - action="add_endpoint_to_tenant", - conditions=dict(method=["POST"])) - mapper.connect( - "/tenants/{tenant_id}/OS-KSCATALOG/endpoints/{endpoint_id}", - controller=endpoint_templates_controller, - action="remove_endpoint_from_tenant", - conditions=dict(method=["DELETE"])) diff --git a/keystone/contrib/extensions/admin/oskscatalog/extension.json b/keystone/contrib/extensions/admin/oskscatalog/extension.json deleted file mode 100644 index c84c0e59..00000000 --- a/keystone/contrib/extensions/admin/oskscatalog/extension.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "extension": { - "name": "Openstack Keystone Catalog", - "namespace": "http://docs.openstack.org/identity/api/ext/OS-KSCATALOG/v1.0", - "alias": "OS-KSCATALOG", - "updated": "2011-07-13T13:25:27-06:00", - "description": "Openstack extensions to Keystone v2.0 API enabling Admin Operations to support Catalog.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/OS-KSCATALOG-admin-devguide.pdf" - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/OS-KSCATALOG-admin.wadl" - } - ] - } -} diff --git a/keystone/contrib/extensions/admin/oskscatalog/extension.xml b/keystone/contrib/extensions/admin/oskscatalog/extension.xml deleted file mode 100644 index 8664c77f..00000000 --- a/keystone/contrib/extensions/admin/oskscatalog/extension.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<extension xmlns="http://docs.openstack.org/common/api/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom" - name="Openstack Keystone Catalog" namespace="http://docs.openstack.org/identity/api/ext/OS-KSCATALOG/v1.0" - alias="OS-KSCATALOG" - updated="2011-08-14T13:25:27-06:00"> - <description> - Openstack extensions to Keystone v2.0 API enabling Admin Operations - to support Catalog. - </description> - <atom:link rel="describedby" type="application/pdf" - href="https://github.com/openstack/keystone/raw/master/keystone/content/admin/OS-KSCATALOG-admin-devguide.pdf"/> - <atom:link rel="describedby" type="application/vnd.sun.wadl+xml" - href="https://github.com/openstack/keystone/raw/master/keystone/content/admin/OS-KSCATALOG-admin.wadl"/> -</extension> diff --git a/keystone/contrib/extensions/admin/osksvalidate/__init__.py b/keystone/contrib/extensions/admin/osksvalidate/__init__.py deleted file mode 100644 index 36a50bd7..00000000 --- a/keystone/contrib/extensions/admin/osksvalidate/__init__.py +++ /dev/null @@ -1,37 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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.contrib.extensions.admin.extension import BaseExtensionHandler -from keystone.contrib.extensions.admin.osksvalidate import handler - - -class ExtensionHandler(BaseExtensionHandler): - def map_extension_methods(self, mapper): - extension_controller = handler.SecureValidationController() - - # Token Operations - mapper.connect("/OS-KSVALIDATE/token/validate", - controller=extension_controller, - action="handle_validate_request", - conditions=dict(method=["GET"])) - - mapper.connect("/OS-KSVALIDATE/token/endpoints", - controller=extension_controller, - action="handle_endpoints_request", - conditions=dict(method=["GET"])) - # TODO(zns): make this handle all routes by using the mapper diff --git a/keystone/contrib/extensions/admin/osksvalidate/extension.json b/keystone/contrib/extensions/admin/osksvalidate/extension.json deleted file mode 100644 index b1f0c65b..00000000 --- a/keystone/contrib/extensions/admin/osksvalidate/extension.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "extension": { - "name": "Openstack Keystone Admin", - "namespace": "http://docs.openstack.org/identity/api/ext/OS-KSVALIDATE/v1.0", - "alias": "OS-KSVALIDATE", - "updated": "2012-01-12T12:17:00-06:00", - "description": "Openstack extensions to Keystone v2.0 API for Secure Token Validation.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/OS-KSVALIDATE-admin-devguide.pdf" - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/OS-KSVALIDATE-admin.wadl" - } - ] - } -} diff --git a/keystone/contrib/extensions/admin/osksvalidate/extension.xml b/keystone/contrib/extensions/admin/osksvalidate/extension.xml deleted file mode 100644 index 6643484a..00000000 --- a/keystone/contrib/extensions/admin/osksvalidate/extension.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<extension xmlns="http://docs.openstack.org/common/api/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom" - name="Openstack Keystone Admin" namespace="http://docs.openstack.org/identity/api/ext/OS-KSVALIDATE/v1.0" - alias="OS-KSVALIDATE" - updated="2012-01-12T12:17:00-06:00"> - <description> - Openstack extensions to Keystone v2.0 - API for Secure Token Validation. - </description> - <atom:link rel="describedby" type="application/pdf" - href="https://github.com/openstack/keystone/raw/master/keystone/content/admin/OS-KSVALIDATE-admin-devguide.pdf"/> - <atom:link rel="describedby" type="application/pdf" - href="https://github.com/openstack/keystone/raw/master/keystone/content/admin/OS-KSVALIDATE-admin.wadl"/> -</extension> diff --git a/keystone/contrib/extensions/admin/osksvalidate/handler.py b/keystone/contrib/extensions/admin/osksvalidate/handler.py deleted file mode 100644 index 4ce7e7c5..00000000 --- a/keystone/contrib/extensions/admin/osksvalidate/handler.py +++ /dev/null @@ -1,46 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-2011 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. - - -""" -Router & Controller for handling Secure Token Validation - -""" -import logging - -from keystone.common import wsgi -from keystone.controllers.token import TokenController - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class SecureValidationController(wsgi.Controller): - """Controller for Tenant related operations""" - - # pylint: disable=W0231 - def __init__(self): - self.token_controller = TokenController() - - logger.info("Initializing Secure Token Validation extension") - - def handle_validate_request(self, req): - token_id = req.headers.get("X-Subject-Token") - return self.token_controller.validate_token(req=req, token_id=token_id) - - def handle_endpoints_request(self, req): - token_id = req.headers.get("X-Subject-Token") - return self.token_controller.endpoints(req=req, token_id=token_id) diff --git a/keystone/contrib/extensions/admin/raxgrp/extension.json b/keystone/contrib/extensions/admin/raxgrp/extension.json deleted file mode 100644 index 31744b3d..00000000 --- a/keystone/contrib/extensions/admin/raxgrp/extension.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "extension": { - "name": "Rackspace Keystone Group Extensions", - "namespace": "http://docs.rackspace.com/identity/api/ext/RAX-KSGROUP/v1.0", - "alias": "RAX-KSGRP", - "updated": "2011-08-14T13:25:27-06:00", - "description": "Rackspace extensions to Keystone v2.0 API enabling groups.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/RAX-KSGRP-admin-devguide.pdf" - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/RAX-KSGRP-admin.wadl" - } - ] - } -} diff --git a/keystone/contrib/extensions/admin/raxgrp/extension.xml b/keystone/contrib/extensions/admin/raxgrp/extension.xml deleted file mode 100644 index 22f77faa..00000000 --- a/keystone/contrib/extensions/admin/raxgrp/extension.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<extension xmlns="http://docs.openstack.org/common/api/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom" - name="Rackspace Keystone Group Extensions" namespace="http://docs.rackspace.com/identity/api/ext/RAX-KSGROUP/v1.0" - alias="RAX-KSGRP" - updated="2011-08-14T13:25:27-06:00"> - <description> - Rackspace extensions to Keystone v2.0 API - enabling groups. - </description> - <atom:link rel="describedby" type="application/pdf" - href="https://github.com/openstack/keystone/raw/master/keystone/content/admin/RAX-KSGRP-admin-devguide.pdf"/> - <atom:link rel="describedby" type="application/vnd.sun.wadl+xml" - href="https://github.com/openstack/keystone/raw/master/keystone/content/admin/RAX-KSGRP-admin.wadl"/> -</extension> diff --git a/keystone/contrib/extensions/admin/raxkey/extension.json b/keystone/contrib/extensions/admin/raxkey/extension.json deleted file mode 100644 index 301ad6a2..00000000 --- a/keystone/contrib/extensions/admin/raxkey/extension.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "extension": { - "name": "Rackspace API Key Authentication", - "namespace": "http://docs.rackspace.com/identity/api/ext/RAX-KSKEY/v1.0", - "alias": "RAX-KSKEY", - "updated": "2011-07-13T13:25:27-06:00", - "description": "Rackspace extensions to Keystone v2.0 API enabling API Key authentication.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/RAX-KSKEY-admin-devguide.pdf" - }, - { - "rel": "describedby", - "type": "application/vnd.sun.wadl+xml", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/admin/RAX-KSKEY-admin.wadl" - } - ] - } -} diff --git a/keystone/contrib/extensions/admin/raxkey/extension.xml b/keystone/contrib/extensions/admin/raxkey/extension.xml deleted file mode 100644 index 175d7302..00000000 --- a/keystone/contrib/extensions/admin/raxkey/extension.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<extension xmlns="http://docs.openstack.org/common/api/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom" - name="Rackspace API Key authentication" namespace="http://docs.rackspace.com/identity/api/ext/RAX-KSKEY/v1.0" - alias="RAX-KSKEY-admin" - updated="2011-08-14T13:25:27-06:00"> - <description> - Rackspace extensions to Keystone v2.0 API - enabling API Key authentication. - </description> - <atom:link rel="describedby" type="application/pdf" - href="https://github.com/openstack/keystone/raw/master/keystone/content/admin/RAX-KSKEY-admin-devguide.pdf"/> - <atom:link rel="describedby" type="application/vnd.sun.wadl+xml" - href="https://github.com/openstack/keystone/raw/master/keystone/content/admin/RAX-KSKEY-admin.wadl"/> -</extension> diff --git a/keystone/contrib/extensions/admin/raxkey/frontend.py b/keystone/contrib/extensions/admin/raxkey/frontend.py deleted file mode 100644 index 4f3f0796..00000000 --- a/keystone/contrib/extensions/admin/raxkey/frontend.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-2011 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. - -""" -RACKSPACE API KEY EXTENSION - -This WSGI component -- detects calls with extensions in them. -- processes the necessary components -""" - -import json -import logging -from lxml import etree -import os -from webob.exc import Request, Response - -from keystone import utils - -EXTENSION_ALIAS = "RAX-KSKEY-admin" - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class FrontEndFilter(object): - """API Key Middleware that handles authentication with API Key""" - - def __init__(self, app, conf): - """ Common initialization code """ - logger.info(_("Starting the %s extension" % - EXTENSION_ALIAS)) - self.conf = conf - self.app = app - - def __call__(self, env, start_response): - """ Handle incoming request. Transform. And send downstream. """ - request = Request(env) - if request.path == "/extensions": - if env['KEYSTONE_API_VERSION'] == '2.0': - request = Request(env) - response = request.get_response(self.app) - if response.status_int == 200: - if response.content_type == 'application/json': - #load json for this extension from file - thisextension = open(os.path.join( - os.path.dirname(__file__), - "extension.json")).read() - thisextensionjson = json.loads(thisextension) - - #load json in response - body = json.loads(response.body) - extensionsarray = body["extensions"]["values"] - - #add this extension and return the response - extensionsarray.append(thisextensionjson) - newresp = Response( - content_type='application/json', - body=json.dumps(body)) - return newresp(env, start_response) - elif response.content_type == 'application/xml': - #load xml for this extension from file - thisextensionxml = etree.parse(os.path.join( - os.path.dirname(__file__), - "extension.xml")).getroot() - #load xml being returned in response - body = etree.fromstring(response.body) - - #add this extension and return the response - body.append(thisextensionxml) - newresp = Response( - content_type='application/xml', - body=etree.tostring(body)) - return newresp(env, start_response) - - # return the response - return response(env, start_response) - - #default action, bypass - return self.app(env, start_response) - - -def filter_factory(global_conf, **local_conf): - """Returns a WSGI filter app for use with paste.deploy.""" - conf = global_conf.copy() - conf.update(local_conf) - - def ext_filter(app): - """Closure to return""" - return FrontEndFilter(app, conf) - return ext_filter diff --git a/keystone/contrib/extensions/extensions.json b/keystone/contrib/extensions/extensions.json deleted file mode 100644 index 7a514fd7..00000000 --- a/keystone/contrib/extensions/extensions.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extensions": { - "values": [] - } -} diff --git a/keystone/contrib/extensions/extensions.xml b/keystone/contrib/extensions/extensions.xml deleted file mode 100644 index ed5ee9c6..00000000 --- a/keystone/contrib/extensions/extensions.xml +++ /dev/null @@ -1,5 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<extensions xmlns="http://docs.openstack.org/common/api/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom"> -</extensions> diff --git a/keystone/contrib/extensions/service/__init__.py b/keystone/contrib/extensions/service/__init__.py deleted file mode 100644 index e0eeb867..00000000 --- a/keystone/contrib/extensions/service/__init__.py +++ /dev/null @@ -1 +0,0 @@ -EXTENSION_SERVICE_PREFIX = 'service' diff --git a/keystone/contrib/extensions/service/osec2/extension.json b/keystone/contrib/extensions/service/osec2/extension.json deleted file mode 100644 index a2d28717..00000000 --- a/keystone/contrib/extensions/service/osec2/extension.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extension": { - "name": "OpenStack EC2 authentication Extension", - "namespace": "http://docs.openstack.org/identity/api/ext/OS-KSEC2/v1.0", - "alias": "OS-KSEC2", - "updated": "2011-08-25T09:50:00-00:00", - "description": "Adds the capability to support EC2 style authentication.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/service/OS-KSEC2-service-devguide.pdf" - } - ] - } -} diff --git a/keystone/contrib/extensions/service/osec2/extension.xml b/keystone/contrib/extensions/service/osec2/extension.xml deleted file mode 100644 index 0f8307f2..00000000 --- a/keystone/contrib/extensions/service/osec2/extension.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - <extension - name="OpenStack EC2 authentication Extension" - namespace="http://docs.openstack.org/identity/api/ext/OS-KSEC2/v1.0" - alias="OS-KSEC2-service" - updated="2011-08-25T09:50:00-00:00"> - - <description> - Adds the capability to support EC2 style authentication. - </description> - - <atom:link rel="describedby" - type="application/pdf" - href="https://github.com/openstack/keystone/raw/master/keystone/content/service/OS-KSEC2-service-devguide.pdf"/> - </extension> - diff --git a/keystone/contrib/extensions/service/osec2/frontend.py b/keystone/contrib/extensions/service/osec2/frontend.py deleted file mode 100644 index b2741b89..00000000 --- a/keystone/contrib/extensions/service/osec2/frontend.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-2011 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. - -""" -RACKSPACE API KEY EXTENSION - -This WSGI component -- detects calls with extensions in them. -- processes the necessary components -""" - -import json -import os -import logging -from lxml import etree -from webob.exc import Request, Response - -from keystone import utils - -EXTENSION_ALIAS = "OS-EC2" - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class FrontEndFilter(object): - """API Key Middleware that handles authentication with API Key""" - - def __init__(self, app, conf): - """ Common initialization code """ - logger.info(_("Starting the %s extension" % - EXTENSION_ALIAS)) - self.conf = conf - self.app = app - - def __call__(self, env, start_response): - """ Handle incoming request. Transform. And send downstream. """ - request = Request(env) - if request.path == "/extensions": - if env['KEYSTONE_API_VERSION'] == '2.0': - request = Request(env) - response = request.get_response(self.app) - if response.status_int == 200: - if response.content_type == 'application/json': - #load json for this extension from file - thisextension = open(os.path.join( - os.path.dirname(__file__), - "extension.json")).read() - thisextensionjson = json.loads(thisextension) - - #load json in response - body = json.loads(response.body) - extensionsarray = body["extensions"]["values"] - - #add this extension and return the response - extensionsarray.append(thisextensionjson) - newresp = Response( - content_type='application/json', - body=json.dumps(body)) - return newresp(env, start_response) - elif response.content_type == 'application/xml': - #load xml for this extension from file - thisextensionxml = etree.parse(os.path.join( - os.path.dirname(__file__), - "extension.xml")).getroot() - #load xml being returned in response - body = etree.fromstring(response.body) - - #add this extension and return the response - body.append(thisextensionxml) - newresp = Response( - content_type='application/xml', - body=etree.tostring(body)) - return newresp(env, start_response) - - # return the response - return response(env, start_response) - - #default action, bypass - return self.app(env, start_response) - - -def filter_factory(global_conf, **local_conf): - """Returns a WSGI filter app for use with paste.deploy.""" - conf = global_conf.copy() - conf.update(local_conf) - - def ext_filter(app): - """Closure to return""" - return FrontEndFilter(app, conf) - return ext_filter diff --git a/keystone/contrib/extensions/service/raxgrp/api.py b/keystone/contrib/extensions/service/raxgrp/api.py deleted file mode 100755 index 1882a13f..00000000 --- a/keystone/contrib/extensions/service/raxgrp/api.py +++ /dev/null @@ -1,141 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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.backends.api import BaseUserAPI - - -#Base APIs -class RAXEXTBaseUserAPI(BaseUserAPI): - - def get_by_group(self, user_id, group_id): - raise NotImplementedError - - def tenant_group(self, values): - raise NotImplementedError - - def tenant_group_delete(self, id, group_id): - raise NotImplementedError - - def get_groups(self, id): - raise NotImplementedError - - def users_tenant_group_get_page(self, group_id, marker, limit): - raise NotImplementedError - - def users_tenant_group_get_page_markers(self, group_id, marker, limit): - raise NotImplementedError - - def get_group_by_tenant(self, id): - raise NotImplementedError - - def delete_tenant_user(self, id, tenant_id): - raise NotImplementedError - - def users_get_by_tenant(self, user_id, tenant_id): - raise NotImplementedError - - def user_role_add(self, values): - raise NotImplementedError - - def user_get_update(self, id): - raise NotImplementedError - - def users_get_page(self, marker, limit): - raise NotImplementedError - - def users_get_page_markers(self, marker, limit): - raise NotImplementedError - - def users_get_by_tenant_get_page(self, tenant_id, role_id, marker, limit): - raise NotImplementedError - - def users_get_by_tenant_get_page_markers(self, tenant_id, - role_id, marker, limit): - raise NotImplementedError - - def check_password(self, user, password): - raise NotImplementedError - - -class RAXEXTBaseTenantGroupAPI(object): - def create(self, values): - raise NotImplementedError - - def is_empty(self, id): - raise NotImplementedError - - def get(self, id, tenant): - raise NotImplementedError - - def get_page(self, tenant_id, marker, limit): - raise NotImplementedError - - def get_page_markers(self, tenant_id, marker, limit): - raise NotImplementedError - - def update(self, id, tenant_id, values): - raise NotImplementedError - - def delete(self, id, tenant_id): - raise NotImplementedError - - -class RAXEXTBaseGroupAPI(object): - def get(self, id): - raise NotImplementedError - - def get_users(self, id): - raise NotImplementedError - - def get_all(self): - raise NotImplementedError - - def get_page(self, marker, limit): - raise NotImplementedError - - def get_page_markers(self, marker, limit): - raise NotImplementedError - - def delete(self, id): - raise NotImplementedError - - def get_by_user_get_page(self, user_id, marker, limit): - raise NotImplementedError - - def get_by_user_get_page_markers(self, user_id, marker, limit): - raise NotImplementedError - - -#API -#TODO(Yogi) Refactor all API to separate classes specific to models. -GROUP = RAXEXTBaseGroupAPI() -TENANT_GROUP = RAXEXTBaseTenantGroupAPI() -USER = RAXEXTBaseUserAPI() - - -# Function to dynamically set module references. -def set_value(variable_name, value): - if variable_name == 'group': - global GROUP - GROUP = value - elif variable_name == 'tenant_group': - global TENANT_GROUP - TENANT_GROUP = value - elif variable_name == 'user': - global USER - USER = value diff --git a/keystone/contrib/extensions/service/raxgrp/extension.json b/keystone/contrib/extensions/service/raxgrp/extension.json deleted file mode 100644 index e2c889dd..00000000 --- a/keystone/contrib/extensions/service/raxgrp/extension.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extension": { - "name": "Rackspace Keystone Group Extensions", - "namespace": "http://docs.rackspace.com/identity/api/ext/RAX-KSGROUP/v1.0", - "alias": "RAX-KSGRP", - "updated": "2011-08-14T13:25:27-06:00", - "description": "Rackspace extensions to Keystone v2.0 API enabling groups.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/service/RAX-KSGRP-service-devguide.pdf" - } - ] - } -} diff --git a/keystone/contrib/extensions/service/raxgrp/extension.xml b/keystone/contrib/extensions/service/raxgrp/extension.xml deleted file mode 100644 index f05855ac..00000000 --- a/keystone/contrib/extensions/service/raxgrp/extension.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<extension xmlns="http://docs.openstack.org/common/api/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom" - name="Rackspace Keystone Group Extensions" namespace="http://docs.rackspace.com/identity/api/ext/RAX-KSGROUP/v1.0" - alias="RAX-KSGRP" - updated="2011-08-14T13:25:27-06:00"> - <description> - Rackspace extensions to Keystone v2.0 API - enabling groups. - </description> - <atom:link rel="describedby" type="application/pdf" - href="http://docs.rackspacecloud.com/identity/api/ext/RAX-KSGRP-service-devguide.pdf"/> -</extension> diff --git a/keystone/contrib/extensions/service/raxkey/__init__.py b/keystone/contrib/extensions/service/raxkey/__init__.py deleted file mode 100644 index e69de29b..00000000 --- a/keystone/contrib/extensions/service/raxkey/__init__.py +++ /dev/null diff --git a/keystone/contrib/extensions/service/raxkey/extension.json b/keystone/contrib/extensions/service/raxkey/extension.json deleted file mode 100644 index ad72420e..00000000 --- a/keystone/contrib/extensions/service/raxkey/extension.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extension": { - "name": "Rackspace API Key Authentication", - "namespace": "http://docs.rackspace.com/identity/api/ext/RAX-KSKEY/v1.0", - "alias": "RAX-KSKEY", - "updated": "2011-08-14T13:25:27-06:00", - "description": "Rackspace extensions to Keystone v2.0 API enabling API Key authentication.", - "links": [ - { - "rel": "describedby", - "type": "application/pdf", - "href": "https://github.com/openstack/keystone/raw/master/keystone/content/service/RAX-KSKEY-service-devguide.pdf" - } - ] - } -} diff --git a/keystone/contrib/extensions/service/raxkey/extension.xml b/keystone/contrib/extensions/service/raxkey/extension.xml deleted file mode 100644 index 981cb2bc..00000000 --- a/keystone/contrib/extensions/service/raxkey/extension.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<extension xmlns="http://docs.openstack.org/common/api/v1.0" - xmlns:atom="http://www.w3.org/2005/Atom" - name="Rackspace API Key authentication" namespace="http://docs.rackspace.com/identity/api/ext/RAX-KSKEY/v1.0" - alias="RAX-KSKEY" - updated="2011-08-14T13:25:27-06:00"> - <description> - Rackspace extensions to Keystone v2.0 API - enabling API Key authentication. - </description> - <atom:link rel="describedby" type="application/pdf" - href="https://github.com/openstack/keystone/raw/master/keystone/content/service/RAX-KSKEY-service-devguide.pdf"/> -</extension> diff --git a/keystone/contrib/extensions/service/raxkey/frontend.py b/keystone/contrib/extensions/service/raxkey/frontend.py deleted file mode 100644 index 1a050013..00000000 --- a/keystone/contrib/extensions/service/raxkey/frontend.py +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-2011 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. - -""" -RACKSPACE API KEY EXTENSION - -Deprecated middleware. We still have it here to not break compatiblity with -configuration files that add it to the pipeline. -""" -import logging - -from keystone import utils - -EXTENSION_ALIAS = "RAX-KEY" - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class FrontEndFilter(object): - """API Key Middleware that handles authentication with API Key""" - - def __init__(self, app, conf): - """ Common initialization code """ - logger.warn(_("WARNING: Starting the %s extension which " - "is deprecated" % - EXTENSION_ALIAS)) - self.conf = conf - self.app = app - - def __call__(self, env, start_response): - logger.warn("%s middleware is deprecated and will be removed in " - "Essex+1 (Fall fo 2012). Remove it from your " - "configuration files." % EXTENSION_ALIAS) - #Kept for backward compatibility with existing configuration files. - #Does nothing now. - return self.app(env, start_response) - - -def filter_factory(global_conf, **local_conf): - """Returns a WSGI filter app for use with paste.deploy.""" - conf = global_conf.copy() - conf.update(local_conf) - - def ext_filter(app): - """Closure to return""" - return FrontEndFilter(app, conf) - return ext_filter diff --git a/keystone/contrib/s3/__init__.py b/keystone/contrib/s3/__init__.py new file mode 100644 index 00000000..22149596 --- /dev/null +++ b/keystone/contrib/s3/__init__.py @@ -0,0 +1 @@ +from keystone.contrib.s3.core import * diff --git a/keystone/contrib/s3/core.py b/keystone/contrib/s3/core.py new file mode 100644 index 00000000..d69fade4 --- /dev/null +++ b/keystone/contrib/s3/core.py @@ -0,0 +1,37 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +"""Main entry point into the S3 Credentials service. + +TODO-DOCS +""" + +import base64 +import hmac + +from hashlib import sha1 + +from keystone import config +from keystone.common import wsgi +from keystone.contrib import ec2 + +CONF = config.CONF + + +class S3Extension(wsgi.ExtensionRouter): + def add_routes(self, mapper): + controller = S3Controller() + # validation + mapper.connect('/s3tokens', + controller=controller, + action='authenticate', + conditions=dict(method=['POST'])) + + +class S3Controller(ec2.Ec2Controller): + def check_signature(self, creds_ref, credentials): + msg = base64.urlsafe_b64decode(str(credentials['token'])) + key = str(creds_ref['secret']) + signed = base64.encodestring(hmac.new(key, msg, sha1).digest()).strip() + + if credentials['signature'] != signed: + raise Exception('Not Authorized') diff --git a/keystone/controllers/__init__.py b/keystone/controllers/__init__.py deleted file mode 100644 index e69de29b..00000000 --- a/keystone/controllers/__init__.py +++ /dev/null diff --git a/keystone/controllers/base_controller.py b/keystone/controllers/base_controller.py deleted file mode 100644 index 5b87e860..00000000 --- a/keystone/controllers/base_controller.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 2011 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. - -""" -Base Class Controller - -""" -import functools -import logging - -from keystone import utils -from keystone.common import wsgi - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class BaseController(wsgi.Controller): - """Base Controller class for Keystone""" - - @staticmethod - def get_url(req): - return '%s://%s:%s%s' % ( - req.environ['wsgi.url_scheme'], - req.environ.get("SERVER_NAME"), - req.environ.get("SERVER_PORT"), - req.environ['PATH_INFO']) - - def get_marker_limit_and_url(self, req): - marker = req.GET["marker"] if "marker" in req.GET else None - limit = req.GET["limit"] if "limit" in req.GET else 10 - url = self.get_url(req) - return (marker, limit, url) diff --git a/keystone/controllers/credentials.py b/keystone/controllers/credentials.py deleted file mode 100644 index b7d4022b..00000000 --- a/keystone/controllers/credentials.py +++ /dev/null @@ -1,71 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-2011 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. - - -""" -Credentials Controller - -""" -import logging - -from keystone import utils -from keystone.controllers.base_controller import BaseController -from keystone.logic import service -from keystone.logic.types.credential import PasswordCredentials - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class CredentialsController(BaseController): - """Controller for Credentials related operations""" - def __init__(self): - self.identity_service = service.IdentityService() - - @utils.wrap_error - def get_credentials(self, req, user_id): - marker, limit, url = self.get_marker_limit_and_url(req) - credentials = self.identity_service.get_credentials( - utils.get_auth_token(req), user_id, marker, limit, url) - return utils.send_result(200, req, credentials) - - @utils.wrap_error - def get_password_credential(self, req, user_id): - credentials = self.identity_service.get_password_credentials( - utils.get_auth_token(req), user_id) - return utils.send_result(200, req, credentials) - - @utils.wrap_error - def delete_password_credential(self, req, user_id): - self.identity_service.delete_password_credentials( - utils.get_auth_token(req), user_id) - return utils.send_result(204, None) - - @utils.wrap_error - def add_credential(self, req, user_id): - credential = utils.get_normalized_request_content( - PasswordCredentials, req) - credential = self.identity_service.create_password_credentials( - utils.get_auth_token(req), user_id, credential) - return utils.send_result(201, req, credential) - - @utils.wrap_error - def update_password_credential(self, req, user_id): - credential = utils.get_normalized_request_content( - PasswordCredentials, req) - credential = self.identity_service.update_password_credentials( - utils.get_auth_token(req), user_id, credential) - return utils.send_result(200, req, credential) diff --git a/keystone/controllers/endpointtemplates.py b/keystone/controllers/endpointtemplates.py deleted file mode 100644 index 4113d4d8..00000000 --- a/keystone/controllers/endpointtemplates.py +++ /dev/null @@ -1,99 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-2011 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. - - -""" -EndpointTemplates Controller - -""" -import logging - -from keystone import utils -from keystone.controllers.base_controller import BaseController -from keystone.logic import service -from keystone.logic.types.endpoint import EndpointTemplate - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class EndpointTemplatesController(BaseController): - """Controller for EndpointTemplates related operations""" - - def __init__(self): - self.identity_service = service.IdentityService() - - @utils.wrap_error - def get_endpoint_templates(self, req): - marker, limit, url = self.get_marker_limit_and_url(req) - service_id = req.GET["serviceId"] if "serviceId" in req.GET else None - if service_id: - endpoint_templates = self.identity_service.\ - get_endpoint_templates_by_service( - utils.get_auth_token(req), service_id, marker, limit, url) - else: - endpoint_templates = self.identity_service.get_endpoint_templates( - utils.get_auth_token(req), marker, limit, url) - return utils.send_result(200, req, endpoint_templates) - - @utils.wrap_error - def add_endpoint_template(self, req): - endpoint_template = utils.get_normalized_request_content( - EndpointTemplate, req) - return utils.send_result(201, req, - self.identity_service.add_endpoint_template( - utils.get_auth_token(req), endpoint_template)) - - @utils.wrap_error - def modify_endpoint_template(self, req, endpoint_template_id): - endpoint_template = utils.\ - get_normalized_request_content(EndpointTemplate, req) - return utils.send_result(201, req, - self.identity_service.modify_endpoint_template(\ - utils.get_auth_token(req), - endpoint_template_id, endpoint_template)) - - @utils.wrap_error - def delete_endpoint_template(self, req, endpoint_template_id): - rval = self.identity_service.delete_endpoint_template( - utils.get_auth_token(req), endpoint_template_id) - return utils.send_result(204, req, rval) - - @utils.wrap_error - def get_endpoint_template(self, req, endpoint_template_id): - endpoint_template = self.identity_service.get_endpoint_template( - utils.get_auth_token(req), endpoint_template_id) - return utils.send_result(200, req, endpoint_template) - - @utils.wrap_error - def get_endpoints_for_tenant(self, req, tenant_id): - marker, limit, url = self.get_marker_limit_and_url(req) - endpoints = self.identity_service.get_tenant_endpoints( - utils.get_auth_token(req), marker, limit, url, tenant_id) - return utils.send_result(200, req, endpoints) - - @utils.wrap_error - def add_endpoint_to_tenant(self, req, tenant_id): - endpoint = utils.get_normalized_request_content(EndpointTemplate, req) - return utils.send_result(201, req, - self.identity_service.create_endpoint_for_tenant( - utils.get_auth_token(req), tenant_id, endpoint)) - - @utils.wrap_error - def remove_endpoint_from_tenant(self, req, tenant_id, endpoint_id): - rval = self.identity_service.delete_endpoint(utils.get_auth_token(req), - endpoint_id) - return utils.send_result(204, req, rval) diff --git a/keystone/controllers/extensions.py b/keystone/controllers/extensions.py deleted file mode 100644 index 171710f6..00000000 --- a/keystone/controllers/extensions.py +++ /dev/null @@ -1,48 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-2011 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. - - -""" -Extensions Controller - -""" -import logging - -from keystone import utils -from keystone.controllers.base_controller import BaseController -from keystone.logic.extension_reader import ExtensionsReader -from keystone.contrib.extensions.admin import EXTENSION_ADMIN_PREFIX -from keystone.contrib.extensions.service import EXTENSION_SERVICE_PREFIX - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class ExtensionsController(BaseController): - """Controller for extensions related methods""" - - def __init__(self, is_service_operation=None): - super(ExtensionsController, self).__init__() - if is_service_operation: - self.extension_prefix = EXTENSION_SERVICE_PREFIX - else: - self.extension_prefix = EXTENSION_ADMIN_PREFIX - self.extension_reader = ExtensionsReader(self.extension_prefix) - - @utils.wrap_error - def get_extensions_info(self, req): - return utils.send_result(200, req, - self.extension_reader.get_extensions()) diff --git a/keystone/controllers/roles.py b/keystone/controllers/roles.py deleted file mode 100644 index 0cc516f7..00000000 --- a/keystone/controllers/roles.py +++ /dev/null @@ -1,101 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-2011 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. - - -""" -Roles Controller - -""" -import logging - -from keystone import utils -from keystone.controllers.base_controller import BaseController -from keystone.models import Role -from keystone.logic import service - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class RolesController(BaseController): - """Controller for Role related operations""" - - def __init__(self): - self.identity_service = service.IdentityService() - - # Not exposed yet. - @utils.wrap_error - def create_role(self, req): - role = utils.get_normalized_request_content(Role, req) - return utils.send_result(201, req, - self.identity_service.create_role(utils.get_auth_token(req), role)) - - @utils.wrap_error - def delete_role(self, req, role_id): - rval = self.identity_service.delete_role( - utils.get_auth_token(req), role_id) - return utils.send_result(204, req, rval) - - @utils.wrap_error - def get_roles(self, req): - role_name = req.GET["name"] if "name" in req.GET else None - if role_name: - return self.__get_roles_by_name(req, role_name) - else: - return self.__get_all_roles(req) - - def __get_roles_by_name(self, req, role_name): - tenant = self.identity_service.get_role_by_name( - utils.get_auth_token(req), role_name) - return utils.send_result(200, req, tenant) - - def __get_all_roles(self, req): - service_id = req.GET["serviceId"] if "serviceId" in req.GET else None - marker, limit, url = self.get_marker_limit_and_url(req) - if service_id: - roles = self.identity_service.get_roles_by_service( - utils.get_auth_token(req), marker, limit, url, - service_id) - return utils.send_result(200, req, roles) - else: - roles = self.identity_service.get_roles( - utils.get_auth_token(req), marker, limit, url) - return utils.send_result(200, req, roles) - - @utils.wrap_error - def get_role(self, req, role_id): - role = self.identity_service.get_role(utils.get_auth_token(req), - role_id) - return utils.send_result(200, req, role) - - @utils.wrap_error - def add_role_to_user(self, req, user_id, role_id, tenant_id=None): - self.identity_service.add_role_to_user(utils.get_auth_token(req), - user_id, role_id, tenant_id) - return utils.send_result(201, None) - - @utils.wrap_error - def delete_role_from_user(self, req, user_id, role_id, tenant_id=None): - self.identity_service.remove_role_from_user(utils.get_auth_token(req), - user_id, role_id, tenant_id) - return utils.send_result(204, req, None) - - @utils.wrap_error - def get_user_roles(self, req, user_id, tenant_id=None): - marker, limit, url = self.get_marker_limit_and_url(req) - roles = self.identity_service.get_user_roles( - utils.get_auth_token(req), marker, limit, url, user_id, tenant_id) - return utils.send_result(200, req, roles) diff --git a/keystone/controllers/services.py b/keystone/controllers/services.py deleted file mode 100755 index 9d1c0f4f..00000000 --- a/keystone/controllers/services.py +++ /dev/null @@ -1,69 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-2011 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. - - -""" -Services Controller - -""" -import logging - -from keystone import utils -from keystone.controllers.base_controller import BaseController -from keystone.models import Service -from keystone.logic import service - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class ServicesController(BaseController): - """Controller for Service related operations""" - - def __init__(self): - self.identity_service = service.IdentityService() - - @utils.wrap_error - def create_service(self, req): - service = utils.get_normalized_request_content(Service, req) - return utils.send_result(201, req, - self.identity_service.create_service(utils.get_auth_token(req), - service)) - - @utils.wrap_error - def get_services(self, req): - service_name = req.GET["name"] if "name" in req.GET else None - if service_name: - tenant = self.identity_service.get_service_by_name( - utils.get_auth_token(req), service_name) - return utils.send_result(200, req, tenant) - else: - marker, limit, url = self.get_marker_limit_and_url(req) - services = self.identity_service.get_services( - utils.get_auth_token(req), marker, limit, url) - return utils.send_result(200, req, services) - - @utils.wrap_error - def get_service(self, req, service_id): - service = self.identity_service.get_service( - utils.get_auth_token(req), service_id) - return utils.send_result(200, req, service) - - @utils.wrap_error - def delete_service(self, req, service_id): - rval = self.identity_service.delete_service(utils.get_auth_token(req), - service_id) - return utils.send_result(204, req, rval) diff --git a/keystone/controllers/staticfiles.py b/keystone/controllers/staticfiles.py deleted file mode 100644 index f6c79186..00000000 --- a/keystone/controllers/staticfiles.py +++ /dev/null @@ -1,89 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-2011 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. - - -""" -Static Files Controller - -Serves static files like PDF, WADL, etc... -""" -import logging -import os -from webob import Response - -from keystone import utils -from keystone.common import template -from keystone.controllers.base_controller import BaseController - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class StaticFilesController(BaseController): - """Controller for contract documents""" - @staticmethod - @utils.wrap_error - def get_pdf_contract(req, pdf, root="content/"): - resp = Response() - filepath = root + pdf - return template.static_file(resp, req, filepath, - root=utils.get_app_root(), mimetype="application/pdf") - - @staticmethod - @utils.wrap_error - def get_wadl_contract(req, wadl, root): - resp = Response() - return template.static_file(resp, req, root + wadl, - root=utils.get_app_root(), mimetype="application/vnd.sun.wadl+xml") - - @staticmethod - @utils.wrap_error - def get_xsd_contract(req, xsd, root="content/"): - resp = Response() - return template.static_file(resp, req, root + "xsd/" + xsd, - root=utils.get_app_root(), mimetype="application/xml") - - @staticmethod - @utils.wrap_error - def get_xsd_atom_contract(req, xsd, root="content/"): - resp = Response() - return template.static_file(resp, req, root + "xsd/atom/" + xsd, - root=utils.get_app_root(), mimetype="application/xml") - - @staticmethod - @utils.wrap_error - def get_static_file(req, path, file, mimetype=None, root="content/"): - resp = Response() - - if mimetype is None: - if utils.is_xml_response(req): - mimetype = "application/xml" - elif utils.is_json_response(req): - mimetype = "application/json" - else: - logger.debug("Unhandled mime type: %s" % req.content_type) - - basename, extension = os.path.splitext(file) - resp_file = "%s%s%s" % (root, path, file) - if extension is None or extension == '': - if mimetype == "application/xml": - resp_file = "%s.xml" % resp_file - elif mimetype == "application/json": - resp_file = "%s.json" % resp_file - - logger.debug("Returning contents from file '%s'" % resp_file) - return template.static_file(resp, req, resp_file, - root=utils.get_app_root(), mimetype=mimetype) diff --git a/keystone/controllers/tenant.py b/keystone/controllers/tenant.py deleted file mode 100644 index 562998c7..00000000 --- a/keystone/controllers/tenant.py +++ /dev/null @@ -1,81 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-2011 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. - - -""" -Tenant Controller - -""" -import logging - -from keystone import utils -from keystone.controllers.base_controller import BaseController -from keystone.logic import service -from keystone.models import Tenant - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class TenantController(BaseController): - """Controller for Tenant related operations""" - - def __init__(self, is_service_operation=None): - self.identity_service = service.IdentityService() - self.is_service_operation = is_service_operation - logger.debug("Initializing: 'Service API' mode=%s" % - self.is_service_operation) - - @utils.wrap_error - def create_tenant(self, req): - tenant = utils.get_normalized_request_content(Tenant, req) - return utils.send_result(201, req, - self.identity_service.create_tenant(utils.get_auth_token(req), - tenant)) - - @utils.wrap_error - def get_tenants(self, req): - tenant_name = req.GET["name"] if "name" in req.GET else None - if tenant_name: - tenant = self.identity_service.get_tenant_by_name( - utils.get_auth_token(req), - tenant_name) - return utils.send_result(200, req, tenant) - else: - marker, limit, url = self.get_marker_limit_and_url(req) - tenants = self.identity_service.get_tenants( - utils.get_auth_token(req), marker, limit, url, - self.is_service_operation) - return utils.send_result(200, req, tenants) - - @utils.wrap_error - def get_tenant(self, req, tenant_id): - tenant = self.identity_service.get_tenant(utils.get_auth_token(req), - tenant_id) - return utils.send_result(200, req, tenant) - - @utils.wrap_error - def update_tenant(self, req, tenant_id): - tenant = utils.get_normalized_request_content(Tenant, req) - rval = self.identity_service.update_tenant(utils.get_auth_token(req), - tenant_id, tenant) - return utils.send_result(200, req, rval) - - @utils.wrap_error - def delete_tenant(self, req, tenant_id): - rval = self.identity_service.delete_tenant(utils.get_auth_token(req), - tenant_id) - return utils.send_result(204, req, rval) diff --git a/keystone/controllers/token.py b/keystone/controllers/token.py deleted file mode 100644 index 1f2e36c8..00000000 --- a/keystone/controllers/token.py +++ /dev/null @@ -1,141 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-2011 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. - - -""" -Token Controller - -This module contains the TokenController class which receives token-related -calls from the request routers. - -""" -import logging - -from keystone import config -from keystone import utils -from keystone.controllers.base_controller import BaseController -from keystone.logic import extension_reader -from keystone.logic.types import auth -from keystone.logic.types import fault -from keystone.logic import service - -CONF = config.CONF -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class TokenController(BaseController): - """Controller for token related operations""" - - def __init__(self): - self.identity_service = service.IdentityService() - logger.debug("Token controller init with HP-IDM extension: %s" % \ - extension_reader.is_extension_supported('hpidm')) - - @utils.wrap_error - def authenticate(self, req): - credential_type = utils.detect_credential_type(req) - if credential_type == "passwordCredentials": - auth_with_credentials = utils.get_normalized_request_content( - auth.AuthWithPasswordCredentials, req) - result = self.identity_service.authenticate( - auth_with_credentials) - return utils.send_result(200, req, result) - elif credential_type == "token": - unscoped = utils.get_normalized_request_content( - auth.AuthWithUnscopedToken, req) - result = self.identity_service.\ - authenticate_with_unscoped_token(unscoped) - return utils.send_result(200, req, result) - elif credential_type == "OS-KSEC2:ec2Credentials": - return self._authenticate_ec2(req) - elif credential_type == "OS-KSS3:s3Credentials": - return self._authenticate_s3(req) - elif credential_type in ["ec2Credentials", "OS-KSEC2-ec2Credentials"]: - logger.warning('Received EC2 credentials in %s format. Processing ' - 'may fail. Update the client code sending this ' - 'format' % credential_type) - return self._authenticate_ec2(req) - else: - raise fault.BadRequestFault("Invalid credentials %s" % - credential_type) - - @utils.wrap_error - def authenticate_ec2(self, req): - return self._authenticate_ec2(req) - - def _authenticate_ec2(self, req): - """Undecorated EC2 handler""" - creds = utils.get_normalized_request_content(auth.Ec2Credentials, req) - return utils.send_result(200, req, - self.identity_service.authenticate_ec2(creds)) - - @utils.wrap_error - def authenticate_s3(self, req): - return self._authenticate_s3(req) - - def _authenticate_s3(self, req): - """Undecorated S3 handler""" - creds = utils.get_normalized_request_content(auth.S3Credentials, req) - return utils.send_result(200, req, - self.identity_service.authenticate_s3(creds)) - - def _validate_token(self, req, token_id): - """Validates the token, and that it belongs to the specified tenant""" - belongs_to = req.GET.get('belongsTo') - service_ids = None - if extension_reader.is_extension_supported('hpidm'): - # service IDs are only relevant if hpidm extension is enabled - service_ids = req.GET.get('HP-IDM-serviceId') - return self.identity_service.validate_token( - utils.get_auth_token(req), token_id, belongs_to, service_ids) - - @utils.wrap_error - def validate_token(self, req, token_id): - if CONF.disable_tokens_in_url: - fault.ServiceUnavailableFault() - else: - result = self._validate_token(req, token_id) - return utils.send_result(200, req, result) - - @utils.wrap_error - def check_token(self, req, token_id): - """Validates the token, but only returns a status code (HEAD)""" - if CONF.disable_tokens_in_url: - fault.ServiceUnavailableFault() - else: - self._validate_token(req, token_id) - return utils.send_result(200, req) - - @utils.wrap_error - def delete_token(self, req, token_id): - if CONF.disable_tokens_in_url: - fault.ServiceUnavailableFault() - else: - return utils.send_result(204, req, - self.identity_service.revoke_token( - utils.get_auth_token(req), token_id)) - - @utils.wrap_error - def endpoints(self, req, token_id): - if CONF.disable_tokens_in_url: - fault.ServiceUnavailableFault() - else: - marker, limit, url = self.get_marker_limit_and_url(req) - return utils.send_result(200, req, - self.identity_service.get_endpoints_for_token( - utils.get_auth_token(req), - token_id, marker, limit, url)) diff --git a/keystone/controllers/user.py b/keystone/controllers/user.py deleted file mode 100644 index c7f37f64..00000000 --- a/keystone/controllers/user.py +++ /dev/null @@ -1,105 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-2011 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. - - -""" -User Controller - -""" -import logging - -from keystone import utils -from keystone.controllers.base_controller import BaseController -from keystone.logic import service -from keystone.logic.types.user import User, User_Update - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class UserController(BaseController): - """Controller for User related operations""" - - def __init__(self): - self.identity_service = service.IdentityService() - - @utils.wrap_error - def create_user(self, req): - u = utils.get_normalized_request_content(User, req) - return utils.send_result(201, req, self.identity_service.create_user( - utils.get_auth_token(req), u)) - - @utils.wrap_error - def get_users(self, req): - user_name = req.GET["name"] if "name" in req.GET else None - if user_name: - tenant = self.identity_service.get_user_by_name( - utils.get_auth_token(req), - user_name) - return utils.send_result(200, req, tenant) - else: - marker, limit, url = self.get_marker_limit_and_url(req) - users = self.identity_service.get_users(utils.get_auth_token(req), - marker, limit, url) - return utils.send_result(200, req, users) - - @utils.wrap_error - def get_user(self, req, user_id): - user = self.identity_service.get_user(utils.get_auth_token(req), - user_id) - return utils.send_result(200, req, user) - - @utils.wrap_error - def update_user(self, req, user_id): - user = utils.get_normalized_request_content(User_Update, req) - rval = self.identity_service.update_user(utils.get_auth_token(req), - user_id, user) - return utils.send_result(200, req, rval) - - @utils.wrap_error - def delete_user(self, req, user_id): - rval = self.identity_service.delete_user(utils.get_auth_token(req), - user_id) - return utils.send_result(204, req, rval) - - @utils.wrap_error - def set_user_password(self, req, user_id): - user = utils.get_normalized_request_content(User_Update, req) - rval = self.identity_service.set_user_password( - utils.get_auth_token(req), user_id, user) - return utils.send_result(200, req, rval) - - @utils.wrap_error - def set_user_enabled(self, req, user_id): - user = utils.get_normalized_request_content(User_Update, req) - rval = self.identity_service.enable_disable_user( - utils.get_auth_token(req), user_id, user) - return utils.send_result(200, req, rval) - - @utils.wrap_error - def update_user_tenant(self, req, user_id): - user = utils.get_normalized_request_content(User_Update, req) - rval = self.identity_service.set_user_tenant(utils.get_auth_token(req), - user_id, user) - return utils.send_result(200, req, rval) - - @utils.wrap_error - def get_tenant_users(self, req, tenant_id): - marker, limit, url = self.get_marker_limit_and_url(req) - role_id = req.GET["roleId"] if "roleId" in req.GET else None - users = self.identity_service.get_tenant_users( - utils.get_auth_token(req), tenant_id, role_id, marker, limit, url) - return utils.send_result(200, req, users) diff --git a/keystone/controllers/version.py b/keystone/controllers/version.py deleted file mode 100644 index b494c5f5..00000000 --- a/keystone/controllers/version.py +++ /dev/null @@ -1,116 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-2011 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. - - -""" -Version Controller - -""" -import logging -import os -from webob import Response - -from keystone import utils -from keystone import version -from keystone.common import template -from keystone.controllers.base_controller import BaseController - -# Calculate root path (to get to static files) -POSSIBLE_TOPDIR = os.path.normpath(os.path.join(os.path.dirname(__file__), - os.pardir, - os.pardir)) - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class VersionController(BaseController): - """Controller for version related methods""" - @utils.wrap_error - def get_version_info(self, req, file="version"): - resp = Response() - resp.charset = 'UTF-8' - if utils.is_xml_response(req): - resp_file = os.path.join(POSSIBLE_TOPDIR, - "keystone/content/%s.xml.tpl" % file) - resp.content_type = "application/xml" - elif utils.is_atom_response(req): - resp_file = os.path.join(POSSIBLE_TOPDIR, - "keystone/content/%s.atom.tpl" % file) - resp.content_type = "application/atom+xml" - else: - resp_file = os.path.join(POSSIBLE_TOPDIR, - "keystone/content/%s.json.tpl" % file) - resp.content_type = "application/json" - - hostname = req.environ.get("SERVER_NAME") - port = req.environ.get("SERVER_PORT") - if 'HTTPS' in req.environ: - protocol = 'https' - else: - protocol = 'http' - - resp.unicode_body = template.template(resp_file, - PROTOCOL=protocol, - HOST=hostname, - PORT=port, - API_VERSION=version.API_VERSION, - API_VERSION_STATUS=version.API_VERSION_STATUS, - API_VERSION_DATE=version.API_VERSION_DATE) - - return resp - - @utils.wrap_error - def get_multiple_choice(self, req, file="multiple_choice", path=None): - """ Returns a multiple-choices response based on API spec - - Response will include in it only one choice, which is for the - current API version. The response is a 300 Multiple Choice - response with either an XML or JSON body. - - """ - if path is None: - path = '' - logger.debug("300 Multiple Choices response: %s" % path) - resp = Response(status="300 Multiple Choices") - resp.charset = 'UTF-8' - if utils.is_xml_response(req): - resp_file = os.path.join(POSSIBLE_TOPDIR, - "keystone/content/%s.xml.tpl" % file) - resp.content_type = "application/xml" - else: - resp_file = os.path.join(POSSIBLE_TOPDIR, - "keystone/content/%s.json.tpl" % file) - resp.content_type = "application/json" - - hostname = req.environ.get("SERVER_NAME") - port = req.environ.get("SERVER_PORT") - if 'HTTPS' in req.environ: - protocol = 'https' - else: - protocol = 'http' - - resp.unicode_body = template.template(resp_file, - PROTOCOL=protocol, - HOST=hostname, - PORT=port, - API_VERSION=version.API_VERSION, - API_VERSION_STATUS=version.API_VERSION_STATUS, - API_VERSION_DATE=version.API_VERSION_DATE, - RESOURCE_PATH=path - ) - - return resp diff --git a/keystone/exception.py b/keystone/exception.py new file mode 100644 index 00000000..c2f32afa --- /dev/null +++ b/keystone/exception.py @@ -0,0 +1,58 @@ +import re + + +class Error(StandardError): + """Base error class. + + Child classes should define an HTTP status code, title, and a doc string. + + """ + code = None + title = None + + def __init__(self, message=None, **kwargs): + """Use the doc string as the error message by default.""" + message = message or self.__doc__ % kwargs + super(Error, self).__init__(message) + + def __str__(self): + """Cleans up line breaks and indentation from doc strings.""" + string = super(Error, self).__str__() + string = re.sub('[ \n]+', ' ', string) + string = string.strip() + return string + + +class ValidationError(Error): + """Expecting to find %(attribute)s in %(target)s. + + The server could not comply with the request since it is either malformed + or otherwise incorrect. + + The client is assumed to be in error. + + """ + code = 400 + title = 'Bad Request' + + +class Unauthorized(Error): + """The request you have made requires authentication.""" + code = 401 + title = 'Not Authorized' + + +class Forbidden(Error): + """You are not authorized to perform the requested action: %(action)s""" + code = 403 + title = 'Not Authorized' + + +class NotFound(Error): + """Could not find: %(target)s""" + code = 404 + title = 'Not Found' + + +class TokenNotFound(NotFound): + """Could not find token: %(token_id)s""" diff --git a/keystone/frontends/__init__.py b/keystone/frontends/__init__.py deleted file mode 100644 index e69de29b..00000000 --- a/keystone/frontends/__init__.py +++ /dev/null diff --git a/keystone/frontends/d5_compat.py b/keystone/frontends/d5_compat.py deleted file mode 100644 index 46395db7..00000000 --- a/keystone/frontends/d5_compat.py +++ /dev/null @@ -1,455 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-2011 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. - -""" -D5 API Compatibility Module - -This WSGI component adds support for the D5 API contract. That contract was -an unofficial contract that made it into live deployments in the wild, so -this middleware is an attempt to support production deployemnts of that -code and allow them to interoperate with Keystone trunk while gradually moving -to updated Keystone code. - -The middleware transforms responses in this way: -- POST /tokens that come in D5 format (not wrapped in "auth":{}) will receive - a D5 formatted response (wrapped in "auth":{} instead of "access":{}) -- GET /tokens/{id} will respond with both an "auth" and an "access" wrapper - (since we can't tell if the caller is expecting a D5 or Diablo final - response) - -Notes: -- GET /tokens will not repond in D5 syntax in XML (because only one root - can exist in XML and I chose not to break Diablo) -- This relies on the URL normalizer (middlewre/url.py) to set - KEYSTONE_API_VERSION. Without that set to '2.0', this middleware does - nothing -""" - -import copy -import json -import logging -logger = logging.getLogger(__name__) # pylint: disable=C0103 -try: - from lxml import etree -except ImportError as exc: - logging.exception(exc) - raise exc -from webob.exc import Request - -from keystone.logic.types import fault -import keystone.utils as utils - -PROTOCOL_NAME = "D5 API Compatibility" - - -class D5AuthBase(object): - """ Handles validating json and XML syntax of auth requests """ - - def __init__(self, tenant_id=None, tenant_name=None): - self.tenant_id = tenant_id - self.tenant_name = tenant_name - - @staticmethod - def _validate_auth(obj, *valid_keys): - root = obj.keys()[0] - - for key in root: - if not key in valid_keys: - logger.debug("Invalid attribute: " % key) - raise fault.BadRequestFault('Invalid attribute(s): %s' % key) - - if root.get('tenantId') and root.get('tenantName'): - msg = _('Expecting either Tenant ID or Tenant Name, but not both') - logger.debug(msg) - raise fault.BadRequestFault(msg) - - return root - - @staticmethod - def _validate_key(obj, key, required_keys, optional_keys): - if not key in obj: - raise fault.BadRequestFault('Expecting %s' % key) - - ret = obj[key] - - for skey in ret: - if not skey in required_keys and not skey in optional_keys: - msg = _('Invalid attribute(s): %s' % skey) - logger.debug(msg) - raise fault.BadRequestFault(msg) - - for required_key in required_keys: - if not ret.get(required_key): - msg = _('Expecting %s:%s' % (key, required_key)) - logger.debug(msg) - raise fault.BadRequestFault(msg) - return ret - - -class D5AuthWithPasswordCredentials(D5AuthBase): - def __init__(self, username, password, tenant_id=None, tenant_name=None): - super(D5AuthWithPasswordCredentials, self).__init__(tenant_id, - tenant_name) - self.username = username - self.password = password - - @staticmethod - def from_xml(xml_str): - try: - dom = etree.Element("root") - dom.append(etree.fromstring(xml_str)) - password_credentials = \ - dom.find("{http://docs.openstack.org/identity/api/v2.0}" - "passwordCredentials") - if password_credentials is None: - raise fault.BadRequestFault("Expecting passwordCredentials") - tenant_id = password_credentials.get("tenantId") - tenant_name = password_credentials.get("tenantName") - username = password_credentials.get("username") - utils.check_empty_string(username, "Expecting a username") - password = password_credentials.get("password") - utils.check_empty_string(password, "Expecting a password") - - if tenant_id and tenant_name: - msg = _("Expecting either Tenant ID or Tenant Name, but not " - "both") - logger.debug(msg) - raise fault.BadRequestFault(msg) - - return D5AuthWithPasswordCredentials(username, password, - tenant_id, tenant_name) - except etree.LxmlError as e: - msg = _("Cannot parse passwordCredentials") - logger.debug(msg) - raise fault.BadRequestFault(msg, str(e)) - - @staticmethod - def from_json(json_str): - try: - obj = json.loads(json_str) - - cred = D5AuthBase._validate_key(obj, 'passwordCredentials', - required_keys=['username', 'password'], - optional_keys=['tenantId', 'tenantName']) - - return D5AuthWithPasswordCredentials(cred['username'], - cred['password'], - cred.get('tenantId'), - cred.get('tenantName')) - except (ValueError, TypeError) as e: - msg = _("Cannot parse passwordCredentials") - logger.debug(msg) - raise fault.BadRequestFault(msg, str(e)) - - def to_json(self): - """ Format the response in Diablo/Stable contract format """ - data = {"auth": {"passwordCredentials": { - "username": self.username, - "password": self.password}}} - if self.tenant_id: - data["auth"]["tenantId"] = self.tenant_id - else: - if self.tenant_name: - data["auth"]["tenant_name"] = self.tenant_name - return json.dumps(data) - - def to_xml(self): - """ Format the response in Diablo/Stable contract format """ - dom = etree.Element("auth", - xmlns="http://docs.openstack.org/identity/api/v2.0") - - password_credentials = etree.Element("passwordCredentials", - username=self.username, - password=self.password) - - if self.tenant_id: - dom.set("tenantId", self.tenant_id) - else: - if self.tenant_name: - dom.set("tenant_name", self.tenant_name) - - dom.append(password_credentials) - - return etree.tostring(dom) - - -class D5toDiabloAuthData(object): - """Authentation Information returned upon successful login. - - This class handles rendering to JSON and XML. It renders - the token, the user data, the roles, and the service catalog. - """ - xml = None - json = None - - def __init__(self, init_json=None, init_xml=None): - if init_json: - logger.debug("init D5toDiabloAuthData with json") - self.json = init_json - if init_xml is not None: - logger.debug("init D5toDiabloAuthData with xml") - self.xml = init_xml - - @staticmethod - def from_xml(xml_str): - """ Verify Diablo syntax and return class initialized with data""" - try: - dom = etree.Element("root") - dom.append(etree.fromstring(xml_str)) - root = \ - dom.find("{http://docs.openstack.org/identity/api/v2.0}" - "access") - if root is None: - logger.debug("Expecting access") - raise fault.BadRequestFault("Expecting access") - return D5toDiabloAuthData(init_xml=root) - except etree.LxmlError as e: - msg = _("Cannot parse Diablo response") - logger.debug(msg) - raise fault.BadRequestFault(msg, str(e)) - - @staticmethod - def from_json(json_str): - """ Verify Diablo syntax and return class initialized with data""" - try: - obj = json.loads(json_str) - auth = obj["access"] - return D5toDiabloAuthData(init_json=auth) - except (ValueError, TypeError) as e: - msg = _("Cannot parse auth response") - logger.debug(msg) - raise fault.BadRequestFault(msg, str(e)) - - def to_xml(self): - """ Convert to D5 syntax from Diablo""" - if self.xml is None: - if self.json is None: - logger.debug("Cannot deserialize response since no json or xml" - "data seems to have been passed in") - raise NotImplementedError - else: - msg = _("%s initialized with json, but xml requested" % - self.__class__.__str__) - logger.debug(msg) - raise fault.IdentityFault(msg) - dom = etree.Element("auth", - xmlns="http://docs.openstack.org/identity/api/v2.0") - for element in self.xml: - dom.append(element) - return etree.tostring(dom) - - def to_json(self): - """ Convert to D5 syntax from Diablo""" - if self.json is None: - if self.xml is None: - logger.debug("Cannot deserialize response since no json or xml" - "data seems to have been passed in") - raise NotImplementedError - else: - msg = _("%s initialized with xml, but json requested" % - self.__class__.__str__) - logger.debug(msg) - raise fault.IdentityFault(msg) - d5_data = {"auth": self.json.copy()} - if 'auth' in d5_data and 'serviceCatalog' in d5_data['auth']: - d5_data['auth']['serviceCatalog'] = \ - dict([(s['type'], s['endpoints']) - for s in d5_data['auth']['serviceCatalog']]) - - return json.dumps(d5_data) - - -class D5ValidateData(object): - """Authentation Information returned upon successful token validation.""" - xml = None - json = None - - def __init__(self, init_json=None, init_xml=None): - if init_json: - self.json = init_json - if init_xml is not None: - self.xml = init_xml - - @staticmethod - def from_xml(xml_str): - """ Verify Diablo syntax and return class initialized with data""" - try: - dom = etree.Element("root") - dom.append(etree.fromstring(xml_str)) - root = \ - dom.find("{http://docs.openstack.org/identity/api/v2.0}" - "access") - if root is None: - logger.debug("Expecting 'access' element") - raise fault.BadRequestFault("Expecting access") - return D5ValidateData(init_xml=root) - except etree.LxmlError as e: - msg = _("Cannot parse Diablo response") - logger.debug(msg) - raise fault.BadRequestFault(msg, str(e)) - - @staticmethod - def from_json(json_str): - """ Verify Diablo syntax and return class initialized with data""" - try: - obj = json.loads(json_str) - return D5ValidateData(init_json=obj) - except (ValueError, TypeError) as e: - msg = _("Cannot parse Diablo response") - logger.debug(msg) - raise fault.BadRequestFault(msg, str(e)) - - def to_xml(self): - """ Returns only Diablo syntax (can only have one root in XML) - - This middleware is designed to provide D5 compatibility but NOT - at the expense of breaking the Diablo contract.""" - if self.xml is None: - if self.json is None: - logger.debug("Cannot deserialize response since no json or xml" - "data seems to have been passed in") - raise NotImplementedError - else: - msg = _("%s initialized with json, but xml requested" % - self.__class__.__str__) - logger.debug(msg) - raise fault.IdentityFault(msg) - return etree.tostring(self.xml) - - def to_json(self): - """ Returns both Diablo and D5 syntax ("access" and "auth")""" - if self.json is None: - if self.xml is None: - logger.debug("Cannot deserialize response since no json or xml" - "data seems to have been passed in") - raise NotImplementedError - else: - msg = _("%s initialized with xml, but json requested" % - self.__class__.__str__) - logger.debug(msg) - raise fault.IdentityFault(msg) - d5_data = self.json.copy() - auth = {} - for key, value in self.json["access"].iteritems(): - auth[key] = copy.copy(value) - if "user" in auth: - # D5 returns 'username' only - user = auth["user"] - user["username"] = user["name"] - del user["name"] - del user["id"] - - # D5 has 'tenantId' under token - token = auth["token"] - if 'tenant' in token: - tenant = token["tenant"] - token["tenantId"] = tenant["id"] - - if "roles" in auth["user"]: - auth["user"]["roleRefs"] = [] - rolerefs = auth["user"]["roleRefs"] - for role in auth["user"]["roles"]: - ref = {} - ref["id"] = role["id"] - ref["roleId"] = role["name"] - if "tenantId" in role: - ref["tenantId"] = role["tenantId"] - rolerefs.append(ref) - del auth["user"]["roles"] - d5_data["auth"] = auth - - return json.dumps(d5_data) - - -class D5AuthProtocol(object): - """D5 Cmpatibility Middleware that transforms client calls and responses""" - - def __init__(self, app, conf): - """ Common initialization code """ - msg = _("Starting the %s component" % PROTOCOL_NAME) - logger.info(msg) - self.conf = conf - self.app = app - - def __call__(self, env, start_response): - """ Handle incoming request. Transform. And send downstream. """ - logger.debug("Entering D5AuthProtocol.__call__") - request = Request(env) - if 'KEYSTONE_API_VERSION' in env and \ - env['KEYSTONE_API_VERSION'] == '2.0': - if request.path.startswith("/tokens"): - is_d5_request = False - if request.method == "POST": - try: - auth_with_credentials = \ - utils.get_normalized_request_content( - D5AuthWithPasswordCredentials, request) - # Convert request body to Diablo syntax - if request.content_type == "application/xml": - request.body = auth_with_credentials.to_xml() - else: - request.body = auth_with_credentials.to_json() - is_d5_request = True - logger.warn("Detected a D5-formatted call") - except: - pass - - if is_d5_request: - response = request.get_response(self.app) - #Handle failures. - if not str(response.status).startswith('20'): - return response(env, start_response) - logger.warn("Responding in D5-format") - auth_data = utils.get_normalized_request_content( - D5toDiabloAuthData, response) - resp = utils.send_result(response.status_int, request, - auth_data) - return resp(env, start_response) - else: - # Pass through - return self.app(env, start_response) - - elif request.method == "GET": - if request.path.endswith("/endpoints"): - # Pass through - return self.app(env, start_response) - else: - response = request.get_response(self.app) - #Handle failures. - if not str(response.status).startswith('20'): - return response(env, start_response) - logger.warn("Adding D5-format to call validate call") - validate_data = utils.get_normalized_request_content( - D5ValidateData, response) - resp = utils.send_result(response.status_int, request, - validate_data) - return resp(env, start_response) - - # All other calls pass to downstream WSGI component - return self.app(env, start_response) - - -def filter_factory(global_conf, **local_conf): - """Returns a WSGI filter app for use with paste.deploy.""" - conf = global_conf.copy() - conf.update(local_conf) - - def auth_filter(wsgiapp): - """Closure to return""" - return D5AuthProtocol(wsgiapp, conf) - return auth_filter diff --git a/keystone/frontends/legacy_token_auth.py b/keystone/frontends/legacy_token_auth.py deleted file mode 100644 index a63395d5..00000000 --- a/keystone/frontends/legacy_token_auth.py +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-2011 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. - -""" -RACKSPACE LEGACY AUTH - STUB - -This WSGI component -- transforms rackspace auth header credentials to keystone credentials -and makes an authentication call on keystone.- transforms response it -receives into custom headers defined in properties and returns -the response. -""" - -import ast -import json -import logging -from webob.exc import Request - -import keystone.utils as utils - -PROTOCOL_NAME = "Legacy Authentication" - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class AuthProtocol(object): - """Legacy Auth Middleware that handles authenticating client calls""" - - def __init__(self, app, conf): - """ Common initialization code """ - msg = _("Starting the %s component" % PROTOCOL_NAME) - logger.info(msg) - self.conf = conf - self.app = app - - # Handle 1.0 and 1.1 calls via middleware. - # Right now I am treating every call of 1.0 and 1.1 as call - # to authenticate - def __call__(self, env, start_response): - """ Handle incoming request. Transform. And send downstream. """ - logger.debug("Entering AuthProtocol.__call__") - request = Request(env) - if env.get('KEYSTONE_API_VERSION') in ['1.0', '1.1']: - logger.debug("This is a v%s call, so taking over" % - env.get('KEYSTONE_API_VERSION')) - params = {"auth": {"passwordCredentials": - {"username": utils.get_auth_user(request), - "password": utils.get_auth_key(request)}}} - #Make request to keystone - new_request = Request.blank('/tokens') - new_request.method = 'POST' - new_request.headers['Content-type'] = 'application/json' - new_request.accept = 'application/json' - new_request.body = json.dumps(params) - logger.debug("Sending v2.0-formatted request downstream") - response = new_request.get_response(self.app) - logger.debug("Got back %s" % response.status) - #Handle failures. - if not str(response.status).startswith('20'): - return response(env, start_response) - headers = self.__transform_headers( - json.loads(response.body)) - logger.debug("Transformed the response. Responding to v1.x client") - resp = utils.send_legacy_result(204, headers) - return resp(env, start_response) - else: - logger.debug("Not a v1.0/v1.1 call, so passing downstream") - return self.app(env, start_response) - - def __transform_headers(self, content): - """Transform Keystone auth to legacy headers""" - headers = {} - if "access" in content: - auth = content["access"] - if "token" in auth: - headers["X-Auth-Token"] = auth["token"]["id"] - if "serviceCatalog" in auth: - services = auth["serviceCatalog"] - service_mappings = ast.literal_eval( - self.conf.get("service_header_mappings", - self.conf["service-header-mappings"])) - for service in services: - service_name = service["name"] - service_urls = '' - for endpoint in service["endpoints"]: - if len(service_urls) > 0: - service_urls += ',' - service_urls += endpoint["publicURL"] - if len(service_urls) > 0: - if service_mappings.get(service_name): - headers[service_mappings.get( - service_name)] = service_urls - else: - #For Services that are not mapped, - #use X- prefix followed by service name. - header = 'X-%s' % service_name.upper() - logger.debug("Adding header to response: %s=%s" % - (header, service_urls)) - headers[header] = service_urls - return headers - - -def filter_factory(global_conf, **local_conf): - """Returns a WSGI filter app for use with paste.deploy.""" - conf = global_conf.copy() - conf.update(local_conf) - - def auth_filter(app): - """Closure to return""" - return AuthProtocol(app, conf) - return auth_filter diff --git a/keystone/frontends/normalizer.py b/keystone/frontends/normalizer.py deleted file mode 100644 index 6c2246b5..00000000 --- a/keystone/frontends/normalizer.py +++ /dev/null @@ -1,212 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010 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. - - -""" -Auth Middleware that accepts URL query extension. - -This module can be installed as a filter in front of your service to -detect extension in the resource URI (e.g., foo/resource.xml) to -specify HTTP response body type. If an extension is specified, it -overwrites the Accept header in the request, if present. - -""" - -import logging -import webob.acceptparse -from webob import Request - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - -# Maps supported URL prefixes to API_VERSION -PATH_PREFIXES = { - '/v2.0': '2.0', - '/v1.1': '1.1', - '/v1.0': '1.0'} - -# Maps supported URL extensions to RESPONSE_ENCODING -PATH_SUFFIXES = { - '.json': 'json', - '.xml': 'xml', - '.atom': 'atom+xml'} - -# Maps supported Accept headers to RESPONSE_ENCODING and API_VERSION -ACCEPT_HEADERS = { - 'application/vnd.openstack.identity+json;version=2.0': ('json', '2.0'), - 'application/vnd.openstack.identity+xml;version=2.0': ('xml', '2.0'), - 'application/vnd.openstack.identity-v2.0+json': ('json', '2.0'), - 'application/vnd.openstack.identity-v2.0+xml': ('xml', '2.0'), - 'application/vnd.openstack.identity+json;version=1.1': ('json', '1.1'), - 'application/vnd.openstack.identity+xml;version=1.1': ('xml', '1.1'), - 'application/vnd.openstack.identity-v1.1+json': ('json', '1.1'), - 'application/vnd.openstack.identity-v1.1+xml': ('xml', '1.1'), - 'application/vnd.openstack.identity-v1.0+json': ('json', '1.0'), - 'application/vnd.openstack.identity-v1.0+xml': ('xml', '1.0'), - 'application/vnd.openstack.identity+json;version=1.0': ('json', '1.0'), - 'application/vnd.openstack.identity+xml;version=1.0': ('xml', '1.0'), - 'application/json': ('json', None), - 'application/xml': ('xml', None), - 'application/atom+xml': ('atom+xml', None)} - -DEFAULT_RESPONSE_ENCODING = 'json' -PROTOCOL_NAME = "URL Normalizer" - - -class NormalizingFilter(object): - """Middleware filter to handle URL and Accept header normalization""" - - def __init__(self, app, conf): - msg = "Starting the %s component" % PROTOCOL_NAME - logger.info(msg) - # app is the next app in WSGI chain - eventually the OpenStack service - self.app = app - self.conf = conf - - def __call__(self, env, start_response): - # Inspect the request for mime type and API version - env = normalize_accept_header(env) - env = normalize_path_prefix(env) - env = normalize_path_suffix(env) - env['PATH_INFO'] = normalize_starting_slash(env.get('PATH_INFO')) - env['PATH_INFO'] = normalize_trailing_slash(env['PATH_INFO']) - - # Fall back on defaults, if necessary - env['KEYSTONE_RESPONSE_ENCODING'] = env.get( - 'KEYSTONE_RESPONSE_ENCODING') or DEFAULT_RESPONSE_ENCODING - env['HTTP_ACCEPT'] = 'application/' + (env.get( - 'KEYSTONE_RESPONSE_ENCODING') or DEFAULT_RESPONSE_ENCODING) - - if 'KEYSTONE_API_VERSION' not in env: - # Version was not specified in path or headers - # return multiple choice unless the version controller can handle - # this request - if env['PATH_INFO'] not in ['/', '']: - logger.debug("Call without a version - returning 300. Path=%s" - % env.get('PATH_INFO', '')) - from keystone.controllers.version import VersionController - controller = VersionController() - response = controller.get_multiple_choice(req=Request(env), - file='multiple_choice') - return response(env, start_response) - - return self.app(env, start_response) - - -def normalize_accept_header(env): - """Matches the preferred Accept encoding to supported encodings. - - Sets KEYSTONE_RESPONSE_ENCODING and KEYSTONE_API_VERSION, if appropriate. - - Note:: webob.acceptparse ignores ';version=' values - """ - accept_value = env.get('HTTP_ACCEPT') - - if accept_value: - if accept_value in ACCEPT_HEADERS.keys(): - logger.debug("Found direct match for mimetype %s" % accept_value) - best_accept = accept_value - else: - try: - accept = webob.acceptparse.Accept(accept_value) - except TypeError: - logger.warn("Falling back to `webob` v1.1 and older support. " - "Check your version of webob") - accept = webob.acceptparse.Accept('Accept', accept_value) - - best_accept = accept.best_match(ACCEPT_HEADERS.keys()) - - if best_accept: - response_encoding, api_version = ACCEPT_HEADERS[best_accept] - logger.debug('%s header matched with %s (API=%s, TYPE=%s)', - accept_value, best_accept, api_version, - response_encoding) - - if response_encoding: - env['KEYSTONE_RESPONSE_ENCODING'] = response_encoding - - if api_version: - env['KEYSTONE_API_VERSION'] = api_version - else: - logger.debug('%s header could not be matched', accept_value) - - return env - - -def normalize_path_prefix(env): - """Handles recognized PATH_INFO prefixes. - - Looks for a version prefix on the PATH_INFO, sets KEYSTONE_API_VERSION - accordingly, and removes the prefix to normalize the request.""" - for prefix in PATH_PREFIXES.keys(): - if env['PATH_INFO'].startswith(prefix): - env['KEYSTONE_API_VERSION'] = PATH_PREFIXES[prefix] - env['PATH_INFO'] = env['PATH_INFO'][len(prefix):] - break - - return env - - -def normalize_path_suffix(env): - """Hnadles recognized PATH_INFO suffixes. - - Looks for a recognized suffix on the PATH_INFO, sets the - KEYSTONE_RESPONSE_ENCODING accordingly, and removes the suffix to normalize - the request.""" - for suffix in PATH_SUFFIXES.keys(): - if env['PATH_INFO'].endswith(suffix): - env['KEYSTONE_RESPONSE_ENCODING'] = PATH_SUFFIXES[suffix] - env['PATH_INFO'] = env['PATH_INFO'][:-len(suffix)] - break - - return env - - -def normalize_starting_slash(path_info): - """Removes a trailing slash from the given path, if any.""" - # Ensure the path at least contains a slash - if not path_info: - return '/' - - # Ensure the path starts with a slash - elif path_info[0] != '/': - return '/' + path_info - - # No need to change anything - else: - return path_info - - -def normalize_trailing_slash(path_info): - """Removes a trailing slash from the given path, if any.""" - # Remove trailing slash, unless it's the only char - if len(path_info) > 1 and path_info[-1] == '/': - return path_info[:-1] - - # No need to change anything - else: - return path_info - - -def filter_factory(global_conf, **local_conf): - """Returns a WSGI filter app for use with paste.deploy.""" - conf = global_conf.copy() - conf.update(local_conf) - - def ext_filter(app): - return NormalizingFilter(app, conf) - return ext_filter diff --git a/keystone/identity/__init__.py b/keystone/identity/__init__.py new file mode 100644 index 00000000..3a86d5a5 --- /dev/null +++ b/keystone/identity/__init__.py @@ -0,0 +1 @@ +from keystone.identity.core import * diff --git a/keystone/contrib/extensions/admin/raxkey/__init__.py b/keystone/identity/backends/__init__.py index e69de29b..e69de29b 100644 --- a/keystone/contrib/extensions/admin/raxkey/__init__.py +++ b/keystone/identity/backends/__init__.py diff --git a/keystone/identity/backends/kvs.py b/keystone/identity/backends/kvs.py new file mode 100644 index 00000000..7dfc8633 --- /dev/null +++ b/keystone/identity/backends/kvs.py @@ -0,0 +1,222 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +from keystone import identity +from keystone.common import kvs +from keystone.common import utils + + +def _filter_user(user_ref): + if user_ref: + user_ref = user_ref.copy() + user_ref.pop('password', None) + user_ref.pop('tenants', None) + return user_ref + + +def _ensure_hashed_password(user_ref): + pw = user_ref.get('password', None) + if pw is not None: + user_ref['password'] = utils.hash_password(pw) + return user_ref + + +class Identity(kvs.Base, identity.Driver): + # Public interface + def authenticate(self, user_id=None, tenant_id=None, password=None): + """Authenticate based on a user, tenant and password. + + Expects the user object to have a password field and the tenant to be + in the list of tenants on the user. + + """ + user_ref = self._get_user(user_id) + tenant_ref = None + metadata_ref = None + if (not user_ref + or not utils.check_password(password, user_ref.get('password'))): + raise AssertionError('Invalid user / password') + if tenant_id and tenant_id not in user_ref['tenants']: + raise AssertionError('Invalid tenant') + + tenant_ref = self.get_tenant(tenant_id) + if tenant_ref: + metadata_ref = self.get_metadata(user_id, tenant_id) + else: + metadata_ref = {} + return (_filter_user(user_ref), tenant_ref, metadata_ref) + + def get_tenant(self, tenant_id): + tenant_ref = self.db.get('tenant-%s' % tenant_id) + return tenant_ref + + def get_tenant_by_name(self, tenant_name): + tenant_ref = self.db.get('tenant_name-%s' % tenant_name) + return tenant_ref + + def _get_user(self, user_id): + user_ref = self.db.get('user-%s' % user_id) + return user_ref + + def _get_user_by_name(self, user_name): + user_ref = self.db.get('user_name-%s' % user_name) + return user_ref + + def get_user(self, user_id): + return _filter_user(self._get_user(user_id)) + + def get_user_by_name(self, user_name): + return _filter_user(self._get_user_by_name(user_name)) + + def get_metadata(self, user_id, tenant_id): + return self.db.get('metadata-%s-%s' % (tenant_id, user_id)) + + def get_role(self, role_id): + role_ref = self.db.get('role-%s' % role_id) + return role_ref + + def list_users(self): + user_ids = self.db.get('user_list', []) + return [self.get_user(x) for x in user_ids] + + def list_roles(self): + role_ids = self.db.get('role_list', []) + return [self.get_role(x) for x in role_ids] + + # These should probably be part of the high-level API + def add_user_to_tenant(self, tenant_id, user_id): + user_ref = self._get_user(user_id) + tenants = set(user_ref.get('tenants', [])) + tenants.add(tenant_id) + self.update_user(user_id, {'tenants': list(tenants)}) + + def remove_user_from_tenant(self, tenant_id, user_id): + user_ref = self._get_user(user_id) + tenants = set(user_ref.get('tenants', [])) + tenants.remove(tenant_id) + self.update_user(user_id, {'tenants': list(tenants)}) + + def get_tenants_for_user(self, user_id): + user_ref = self._get_user(user_id) + return user_ref.get('tenants', []) + + def get_roles_for_user_and_tenant(self, user_id, tenant_id): + metadata_ref = self.get_metadata(user_id, tenant_id) + if not metadata_ref: + metadata_ref = {} + return metadata_ref.get('roles', []) + + def add_role_to_user_and_tenant(self, user_id, tenant_id, role_id): + metadata_ref = self.get_metadata(user_id, tenant_id) + if not metadata_ref: + metadata_ref = {} + roles = set(metadata_ref.get('roles', [])) + roles.add(role_id) + metadata_ref['roles'] = list(roles) + self.update_metadata(user_id, tenant_id, metadata_ref) + + def remove_role_from_user_and_tenant(self, user_id, tenant_id, role_id): + metadata_ref = self.get_metadata(user_id, tenant_id) + if not metadata_ref: + metadata_ref = {} + roles = set(metadata_ref.get('roles', [])) + roles.remove(role_id) + metadata_ref['roles'] = list(roles) + self.update_metadata(user_id, tenant_id, metadata_ref) + + # CRUD + def create_user(self, user_id, user): + if self.get_user(user_id): + raise Exception('Duplicate id') + if self.get_user_by_name(user['name']): + raise Exception('Duplicate name') + user = _ensure_hashed_password(user) + self.db.set('user-%s' % user_id, user) + self.db.set('user_name-%s' % user['name'], user) + user_list = set(self.db.get('user_list', [])) + user_list.add(user_id) + self.db.set('user_list', list(user_list)) + return user + + def update_user(self, user_id, user): + if 'name' in user: + existing = self.db.get('user_name-%s' % user['name']) + if existing and user_id != existing['id']: + raise Exception('Duplicate name') + # get the old name and delete it too + old_user = self.db.get('user-%s' % user_id) + new_user = old_user.copy() + user = _ensure_hashed_password(user) + new_user.update(user) + new_user['id'] = user_id + self.db.delete('user_name-%s' % old_user['name']) + self.db.set('user-%s' % user_id, new_user) + self.db.set('user_name-%s' % new_user['name'], new_user) + return new_user + + def delete_user(self, user_id): + old_user = self.db.get('user-%s' % user_id) + self.db.delete('user_name-%s' % old_user['name']) + self.db.delete('user-%s' % user_id) + user_list = set(self.db.get('user_list', [])) + user_list.remove(user_id) + self.db.set('user_list', list(user_list)) + return None + + def create_tenant(self, tenant_id, tenant): + if self.get_tenant(tenant_id): + raise Exception('Duplicate id') + if self.get_tenant_by_name(tenant['name']): + raise Exception('Duplicate name') + self.db.set('tenant-%s' % tenant_id, tenant) + self.db.set('tenant_name-%s' % tenant['name'], tenant) + return tenant + + def update_tenant(self, tenant_id, tenant): + if 'name' in tenant: + existing = self.db.get('tenant_name-%s' % tenant['name']) + if existing and tenant_id != existing['id']: + raise Exception('Duplicate name') + # get the old name and delete it too + old_tenant = self.db.get('tenant-%s' % tenant_id) + new_tenant = old_tenant.copy() + new_tenant['id'] = tenant_id + self.db.delete('tenant_name-%s' % old_tenant['name']) + self.db.set('tenant-%s' % tenant_id, new_tenant) + self.db.set('tenant_name-%s' % new_tenant['name'], new_tenant) + return tenant + + def delete_tenant(self, tenant_id): + old_tenant = self.db.get('tenant-%s' % tenant_id) + self.db.delete('tenant_name-%s' % old_tenant['name']) + self.db.delete('tenant-%s' % tenant_id) + return None + + def create_metadata(self, user_id, tenant_id, metadata): + self.db.set('metadata-%s-%s' % (tenant_id, user_id), metadata) + return metadata + + def update_metadata(self, user_id, tenant_id, metadata): + self.db.set('metadata-%s-%s' % (tenant_id, user_id), metadata) + return metadata + + def delete_metadata(self, user_id, tenant_id): + self.db.delete('metadata-%s-%s' % (tenant_id, user_id)) + return None + + def create_role(self, role_id, role): + self.db.set('role-%s' % role_id, role) + role_list = set(self.db.get('role_list', [])) + role_list.add(role_id) + self.db.set('role_list', list(role_list)) + return role + + def update_role(self, role_id, role): + self.db.set('role-%s' % role_id, role) + return role + + def delete_role(self, role_id): + self.db.delete('role-%s' % role_id) + role_list = set(self.db.get('role_list', [])) + role_list.remove(role_id) + self.db.set('role_list', list(role_list)) + return None diff --git a/keystone/identity/backends/pam.py b/keystone/identity/backends/pam.py new file mode 100644 index 00000000..d9606601 --- /dev/null +++ b/keystone/identity/backends/pam.py @@ -0,0 +1,29 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +from __future__ import absolute_import + +import pam + + +class PamIdentity(object): + """Very basic identity based on PAM. + + Tenant is always the same as User, root user has admin role. + """ + + def authenticate(self, username, password, **kwargs): + if pam.authenticate(username, password): + metadata = {} + if username == 'root': + metadata['is_admin'] == True + + tenant = {'id': username, + 'name': username} + user = {'id': username, + 'name': username} + + return (tenant, user, metadata) + + def get_tenants(self, username): + return [{'id': username, + 'name': username}] diff --git a/keystone/identity/backends/sql.py b/keystone/identity/backends/sql.py new file mode 100644 index 00000000..4918a942 --- /dev/null +++ b/keystone/identity/backends/sql.py @@ -0,0 +1,366 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import copy + +from keystone import identity +from keystone.common import sql +from keystone.common import utils +from keystone.common.sql import migration + + +def _filter_user(user_ref): + if user_ref: + user_ref.pop('password', None) + return user_ref + + +def _ensure_hashed_password(user_ref): + pw = user_ref.get('password', None) + if pw is not None: + user_ref['password'] = utils.hash_password(pw) + return user_ref + + +class User(sql.ModelBase, sql.DictBase): + __tablename__ = 'user' + id = sql.Column(sql.String(64), primary_key=True) + name = sql.Column(sql.String(64), unique=True) + #password = sql.Column(sql.String(64)) + extra = sql.Column(sql.JsonBlob()) + + @classmethod + def from_dict(cls, user_dict): + # shove any non-indexed properties into extra + extra = {} + for k, v in user_dict.copy().iteritems(): + # TODO(termie): infer this somehow + if k not in ['id', 'name']: + extra[k] = user_dict.pop(k) + + user_dict['extra'] = extra + return cls(**user_dict) + + def to_dict(self): + extra_copy = self.extra.copy() + extra_copy['id'] = self.id + extra_copy['name'] = self.name + return extra_copy + + +class Tenant(sql.ModelBase, sql.DictBase): + __tablename__ = 'tenant' + id = sql.Column(sql.String(64), primary_key=True) + name = sql.Column(sql.String(64), unique=True) + extra = sql.Column(sql.JsonBlob()) + + @classmethod + def from_dict(cls, tenant_dict): + # shove any non-indexed properties into extra + extra = {} + for k, v in tenant_dict.copy().iteritems(): + # TODO(termie): infer this somehow + if k not in ['id', 'name']: + extra[k] = tenant_dict.pop(k) + + tenant_dict['extra'] = extra + return cls(**tenant_dict) + + def to_dict(self): + extra_copy = copy.deepcopy(self.extra) + extra_copy['id'] = self.id + extra_copy['name'] = self.name + return extra_copy + + +class Role(sql.ModelBase, sql.DictBase): + __tablename__ = 'role' + id = sql.Column(sql.String(64), primary_key=True) + name = sql.Column(sql.String(64)) + + +class Metadata(sql.ModelBase, sql.DictBase): + __tablename__ = 'metadata' + #__table_args__ = ( + # sql.Index('idx_metadata_usertenant', 'user', 'tenant'), + # ) + + user_id = sql.Column(sql.String(64), primary_key=True) + tenant_id = sql.Column(sql.String(64), primary_key=True) + data = sql.Column(sql.JsonBlob()) + + +class UserTenantMembership(sql.ModelBase, sql.DictBase): + """Tenant membership join table.""" + __tablename__ = 'user_tenant_membership' + user_id = sql.Column(sql.String(64), + sql.ForeignKey('user.id'), + primary_key=True) + tenant_id = sql.Column(sql.String(64), + sql.ForeignKey('tenant.id'), + primary_key=True) + + +class Identity(sql.Base, identity.Driver): + # Internal interface to manage the database + def db_sync(self): + migration.db_sync() + + # Identity interface + def authenticate(self, user_id=None, tenant_id=None, password=None): + """Authenticate based on a user, tenant and password. + + Expects the user object to have a password field and the tenant to be + in the list of tenants on the user. + + """ + user_ref = self._get_user(user_id) + tenant_ref = None + metadata_ref = None + if (not user_ref + or not utils.check_password(password, user_ref.get('password'))): + raise AssertionError('Invalid user / password') + + tenants = self.get_tenants_for_user(user_id) + if tenant_id and tenant_id not in tenants: + raise AssertionError('Invalid tenant') + + tenant_ref = self.get_tenant(tenant_id) + if tenant_ref: + metadata_ref = self.get_metadata(user_id, tenant_id) + else: + metadata_ref = {} + return (_filter_user(user_ref), tenant_ref, metadata_ref) + + def get_tenant(self, tenant_id): + session = self.get_session() + tenant_ref = session.query(Tenant).filter_by(id=tenant_id).first() + if not tenant_ref: + return + return tenant_ref.to_dict() + + def get_tenant_by_name(self, tenant_name): + session = self.get_session() + tenant_ref = session.query(Tenant).filter_by(name=tenant_name).first() + if not tenant_ref: + return + return tenant_ref.to_dict() + + def _get_user(self, user_id): + session = self.get_session() + user_ref = session.query(User).filter_by(id=user_id).first() + if not user_ref: + return + return user_ref.to_dict() + + def _get_user_by_name(self, user_name): + session = self.get_session() + user_ref = session.query(User).filter_by(name=user_name).first() + if not user_ref: + return + return user_ref.to_dict() + + def get_user(self, user_id): + return _filter_user(self._get_user(user_id)) + + def get_user_by_name(self, user_name): + return _filter_user(self._get_user_by_name(user_name)) + + def get_metadata(self, user_id, tenant_id): + session = self.get_session() + metadata_ref = session.query(Metadata)\ + .filter_by(user_id=user_id)\ + .filter_by(tenant_id=tenant_id)\ + .first() + return getattr(metadata_ref, 'data', None) + + def get_role(self, role_id): + session = self.get_session() + role_ref = session.query(Role).filter_by(id=role_id).first() + return role_ref + + def list_users(self): + session = self.get_session() + user_refs = session.query(User) + return [_filter_user(x.to_dict()) for x in user_refs] + + def list_roles(self): + session = self.get_session() + role_refs = session.query(Role) + return list(role_refs) + + # These should probably be part of the high-level API + def add_user_to_tenant(self, tenant_id, user_id): + session = self.get_session() + q = session.query(UserTenantMembership)\ + .filter_by(user_id=user_id)\ + .filter_by(tenant_id=tenant_id) + rv = q.first() + if rv: + return + + with session.begin(): + session.add(UserTenantMembership(user_id=user_id, + tenant_id=tenant_id)) + session.flush() + + def remove_user_from_tenant(self, tenant_id, user_id): + session = self.get_session() + membership_ref = session.query(UserTenantMembership)\ + .filter_by(user_id=user_id)\ + .filter_by(tenant_id=tenant_id)\ + .first() + with session.begin(): + session.delete(membership_ref) + session.flush() + + def get_tenants_for_user(self, user_id): + session = self.get_session() + membership_refs = session.query(UserTenantMembership)\ + .filter_by(user_id=user_id)\ + .all() + + return [x.tenant_id for x in membership_refs] + + def get_roles_for_user_and_tenant(self, user_id, tenant_id): + metadata_ref = self.get_metadata(user_id, tenant_id) + if not metadata_ref: + metadata_ref = {} + return metadata_ref.get('roles', []) + + def add_role_to_user_and_tenant(self, user_id, tenant_id, role_id): + metadata_ref = self.get_metadata(user_id, tenant_id) + is_new = False + if not metadata_ref: + is_new = True + metadata_ref = {} + roles = set(metadata_ref.get('roles', [])) + roles.add(role_id) + metadata_ref['roles'] = list(roles) + if not is_new: + self.update_metadata(user_id, tenant_id, metadata_ref) + else: + self.create_metadata(user_id, tenant_id, metadata_ref) + + def remove_role_from_user_and_tenant(self, user_id, tenant_id, role_id): + metadata_ref = self.get_metadata(user_id, tenant_id) + is_new = False + if not metadata_ref: + is_new = True + metadata_ref = {} + roles = set(metadata_ref.get('roles', [])) + roles.remove(role_id) + metadata_ref['roles'] = list(roles) + if not is_new: + self.update_metadata(user_id, tenant_id, metadata_ref) + else: + self.create_metadata(user_id, tenant_id, metadata_ref) + + # CRUD + def create_user(self, user_id, user): + user = _ensure_hashed_password(user) + session = self.get_session() + with session.begin(): + user_ref = User.from_dict(user) + session.add(user_ref) + session.flush() + return user_ref.to_dict() + + def update_user(self, user_id, user): + session = self.get_session() + with session.begin(): + user_ref = session.query(User).filter_by(id=user_id).first() + old_user_dict = user_ref.to_dict() + user = _ensure_hashed_password(user) + for k in user: + old_user_dict[k] = user[k] + new_user = User.from_dict(old_user_dict) + + user_ref.name = new_user.name + user_ref.extra = new_user.extra + session.flush() + return user_ref + + def delete_user(self, user_id): + session = self.get_session() + user_ref = session.query(User).filter_by(id=user_id).first() + with session.begin(): + session.delete(user_ref) + session.flush() + + def create_tenant(self, tenant_id, tenant): + session = self.get_session() + with session.begin(): + tenant_ref = Tenant.from_dict(tenant) + session.add(tenant_ref) + session.flush() + return tenant_ref.to_dict() + + def update_tenant(self, tenant_id, tenant): + session = self.get_session() + with session.begin(): + tenant_ref = session.query(Tenant).filter_by(id=tenant_id).first() + old_tenant_dict = tenant_ref.to_dict() + for k in tenant: + old_tenant_dict[k] = tenant[k] + new_tenant = Tenant.from_dict(old_tenant_dict) + + tenant_ref.name = new_tenant.name + tenant_ref.extra = new_tenant.extra + session.flush() + return tenant_ref + + def delete_tenant(self, tenant_id): + session = self.get_session() + tenant_ref = session.query(Tenant).filter_by(id=tenant_id).first() + with session.begin(): + session.delete(tenant_ref) + session.flush() + + def create_metadata(self, user_id, tenant_id, metadata): + session = self.get_session() + with session.begin(): + session.add(Metadata(user_id=user_id, + tenant_id=tenant_id, + data=metadata)) + session.flush() + return metadata + + def update_metadata(self, user_id, tenant_id, metadata): + session = self.get_session() + with session.begin(): + metadata_ref = session.query(Metadata)\ + .filter_by(user_id=user_id)\ + .filter_by(tenant_id=tenant_id)\ + .first() + data = metadata_ref.data.copy() + for k in metadata: + data[k] = metadata[k] + metadata_ref.data = data + session.flush() + return metadata_ref + + def delete_metadata(self, user_id, tenant_id): + self.db.delete('metadata-%s-%s' % (tenant_id, user_id)) + return None + + def create_role(self, role_id, role): + session = self.get_session() + with session.begin(): + session.add(Role(**role)) + session.flush() + return role + + def update_role(self, role_id, role): + session = self.get_session() + with session.begin(): + role_ref = session.query(Role).filter_by(id=role_id).first() + for k in role: + role_ref[k] = role[k] + session.flush() + return role_ref + + def delete_role(self, role_id): + session = self.get_session() + role_ref = session.query(Role).filter_by(id=role_id).first() + with session.begin(): + session.delete(role_ref) diff --git a/keystone/identity/core.py b/keystone/identity/core.py new file mode 100644 index 00000000..66c2cdce --- /dev/null +++ b/keystone/identity/core.py @@ -0,0 +1,537 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +"""Main entry point into the Identity service.""" + +import uuid +import urllib +import urlparse + +import webob.exc + +from keystone import catalog +from keystone import config +from keystone import exception +from keystone import policy +from keystone import token +from keystone.common import manager +from keystone.common import wsgi + + +CONF = config.CONF + + +class Manager(manager.Manager): + """Default pivot point for the Identity 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.identity.driver) + + +class Driver(object): + """Interface description for an Identity driver.""" + + def authenticate(self, user_id=None, tenant_id=None, password=None): + """Authenticate a given user, tenant and password. + + Returns: (user, tenant, metadata). + + """ + raise NotImplementedError() + + def get_tenant(self, tenant_id): + """Get a tenant by id. + + Returns: tenant_ref or None. + + """ + raise NotImplementedError() + + def get_tenant_by_name(self, tenant_name): + """Get a tenant by name. + + Returns: tenant_ref or None. + + """ + raise NotImplementedError() + + def get_user(self, user_id): + """Get a user by id. + + Returns: user_ref or None. + + """ + raise NotImplementedError() + + def get_user_by_name(self, user_name): + """Get a user by name. + + Returns: user_ref or None. + + """ + raise NotImplementedError() + + def get_role(self, role_id): + """Get a role by id. + + Returns: role_ref or None. + + """ + raise NotImplementedError() + + def list_users(self): + """List all users in the system. + + NOTE(termie): I'd prefer if this listed only the users for a given + tenant. + + Returns: a list of user_refs or an empty list. + + """ + raise NotImplementedError() + + def list_roles(self): + """List all roles in the system. + + Returns: a list of role_refs or an empty list. + + """ + raise NotImplementedError() + + # NOTE(termie): six calls below should probably be exposed by the api + # more clearly when the api redesign happens + def add_user_to_tenant(self, tenant_id, user_id): + raise NotImplementedError() + + def remove_user_from_tenant(self, tenant_id, user_id): + raise NotImplementedError() + + def get_tenants_for_user(self, user_id): + """Get the tenants associated with a given user. + + Returns: a list of tenant ids. + + """ + raise NotImplementedError() + + def get_roles_for_user_and_tenant(self, user_id, tenant_id): + """Get the roles associated with a user within given tenant. + + Returns: a list of role ids. + + """ + raise NotImplementedError() + + def add_role_for_user_and_tenant(self, user_id, tenant_id, role_id): + """Add a role to a user within given tenant.""" + raise NotImplementedError() + + def remove_role_from_user_and_tenant(self, user_id, tenant_id, role_id): + """Remove a role from a user within given tenant.""" + raise NotImplementedError() + + # user crud + def create_user(self, user_id, user): + raise NotImplementedError() + + def update_user(self, user_id, user): + raise NotImplementedError() + + def delete_user(self, user_id): + raise NotImplementedError() + + # tenant crud + def create_tenant(self, tenant_id, tenant): + raise NotImplementedError() + + def update_tenant(self, tenant_id, tenant): + raise NotImplementedError() + + def delete_tenant(self, tenant_id, tenant): + raise NotImplementedError() + + # metadata crud + def create_metadata(self, user_id, tenant_id, metadata): + raise NotImplementedError() + + def update_metadata(self, user_id, tenant_id, metadata): + raise NotImplementedError() + + def delete_metadata(self, user_id, tenant_id, metadata): + raise NotImplementedError() + + # role crud + def create_role(self, role_id, role): + raise NotImplementedError() + + def update_role(self, role_id, role): + raise NotImplementedError() + + def delete_role(self, role_id): + raise NotImplementedError() + + +class PublicRouter(wsgi.ComposableRouter): + def add_routes(self, mapper): + tenant_controller = TenantController() + mapper.connect('/tenants', + controller=tenant_controller, + action='get_tenants_for_token', + conditions=dict(methods=['GET'])) + + +class AdminRouter(wsgi.ComposableRouter): + def add_routes(self, mapper): + # Tenant Operations + tenant_controller = TenantController() + mapper.connect('/tenants', + controller=tenant_controller, + action='get_tenants_for_token', + conditions=dict(method=['GET'])) + mapper.connect('/tenants/{tenant_id}', + controller=tenant_controller, + action='get_tenant', + conditions=dict(method=['GET'])) + + # User Operations + user_controller = UserController() + mapper.connect('/users/{user_id}', + controller=user_controller, + action='get_user', + conditions=dict(method=['GET'])) + + # Role Operations + roles_controller = RoleController() + mapper.connect('/tenants/{tenant_id}/users/{user_id}/roles', + controller=roles_controller, + action='get_user_roles', + conditions=dict(method=['GET'])) + mapper.connect('/users/{user_id}/roles', + controller=user_controller, + action='get_user_roles', + conditions=dict(method=['GET'])) + + +class TenantController(wsgi.Application): + def __init__(self): + self.identity_api = Manager() + self.policy_api = policy.Manager() + self.token_api = token.Manager() + super(TenantController, self).__init__() + + def get_tenants_for_token(self, context, **kw): + """Get valid tenants for token based on token used to authenticate. + + Pulls the token from the context, validates it and gets the valid + tenants for the user in the token. + + Doesn't care about token scopedness. + + """ + try: + token_ref = self.token_api.get_token(context=context, + token_id=context['token_id']) + except exception.NotFound: + raise exception.Unauthorized() + + user_ref = token_ref['user'] + tenant_ids = self.identity_api.get_tenants_for_user( + context, user_ref['id']) + tenant_refs = [] + for tenant_id in tenant_ids: + tenant_refs.append(self.identity_api.get_tenant( + context=context, + tenant_id=tenant_id)) + params = { + 'limit': context['query_string'].get('limit'), + 'marker': context['query_string'].get('marker'), + } + return self._format_tenant_list(tenant_refs, **params) + + def get_tenant(self, context, tenant_id): + # TODO(termie): this stuff should probably be moved to middleware + self.assert_admin(context) + tenant = self.identity_api.get_tenant(context, tenant_id) + if not tenant: + return webob.exc.HTTPNotFound() + return {'tenant': tenant} + + # CRUD Extension + def create_tenant(self, context, tenant): + tenant_ref = self._normalize_dict(tenant) + self.assert_admin(context) + tenant_id = (tenant_ref.get('id') + and tenant_ref.get('id') + or uuid.uuid4().hex) + tenant_ref['id'] = tenant_id + + tenant = self.identity_api.create_tenant( + context, tenant_id, tenant_ref) + return {'tenant': tenant} + + def update_tenant(self, context, tenant_id, tenant): + self.assert_admin(context) + tenant_ref = self.identity_api.update_tenant( + context, tenant_id, tenant) + return {'tenant': tenant_ref} + + def delete_tenant(self, context, tenant_id, **kw): + self.assert_admin(context) + self.identity_api.delete_tenant(context, tenant_id) + + def get_tenant_users(self, context, **kw): + self.assert_admin(context) + raise NotImplementedError() + + def _format_tenant_list(self, tenant_refs, **kwargs): + marker = kwargs.get('marker') + page_idx = 0 + if marker is not None: + for (marker_idx, tenant) in enumerate(tenant_refs): + if tenant['id'] == marker: + # we start pagination after the marker + page_idx = marker_idx + 1 + break + else: + msg = 'Marker could not be found' + raise webob.exc.HTTPBadRequest(explanation=msg) + + limit = kwargs.get('limit') + if limit is not None: + try: + limit = int(limit) + assert limit >= 0 + except (ValueError, AssertionError): + msg = 'Invalid limit value' + raise webob.exc.HTTPBadRequest(explanation=msg) + + tenant_refs = tenant_refs[page_idx:limit] + + for x in tenant_refs: + x['enabled'] = True + o = {'tenants': tenant_refs, + 'tenants_links': []} + return o + + +class UserController(wsgi.Application): + def __init__(self): + self.catalog_api = catalog.Manager() + self.identity_api = Manager() + self.policy_api = policy.Manager() + self.token_api = token.Manager() + super(UserController, self).__init__() + + def get_user(self, context, user_id): + self.assert_admin(context) + user_ref = self.identity_api.get_user(context, user_id) + if not user_ref: + raise webob.exc.HTTPNotFound() + return {'user': user_ref} + + def get_users(self, context): + # NOTE(termie): i can't imagine that this really wants all the data + # about every single user in the system... + self.assert_admin(context) + user_refs = self.identity_api.list_users(context) + return {'users': user_refs} + + # CRUD extension + def create_user(self, context, user): + user = self._normalize_dict(user) + self.assert_admin(context) + tenant_id = user.get('tenantId', None) + user_id = uuid.uuid4().hex + user_ref = user.copy() + user_ref['id'] = user_id + new_user_ref = self.identity_api.create_user( + context, user_id, user_ref) + if tenant_id: + self.identity_api.add_user_to_tenant(tenant_id, user_id) + return {'user': new_user_ref} + + def update_user(self, context, user_id, user): + # NOTE(termie): this is really more of a patch than a put + self.assert_admin(context) + user_ref = self.identity_api.update_user(context, user_id, user) + return {'user': user_ref} + + def delete_user(self, context, user_id): + self.assert_admin(context) + self.identity_api.delete_user(context, user_id) + + def set_user_enabled(self, context, user_id, user): + return self.update_user(context, user_id, user) + + def set_user_password(self, context, user_id, user): + return self.update_user(context, user_id, user) + + def update_user_tenant(self, context, user_id, user): + """Update the default tenant.""" + # ensure that we're a member of that tenant + tenant_id = user.get('tenantId') + self.identity_api.add_user_to_tenant(context, tenant_id, user_id) + return self.update_user(context, user_id, user) + + +class RoleController(wsgi.Application): + def __init__(self): + self.catalog_api = catalog.Manager() + self.identity_api = Manager() + self.token_api = token.Manager() + self.policy_api = policy.Manager() + super(RoleController, self).__init__() + + # COMPAT(essex-3) + def get_user_roles(self, context, user_id, tenant_id=None): + """Get the roles for a user and tenant pair. + + Since we're trying to ignore the idea of user-only roles we're + not implementing them in hopes that the idea will die off. + + """ + if tenant_id is None: + raise Exception('User roles not supported: tenant_id required') + roles = self.identity_api.get_roles_for_user_and_tenant( + context, user_id, tenant_id) + return {'roles': [self.identity_api.get_role(context, x) + for x in roles]} + + # CRUD extension + def get_role(self, context, role_id): + self.assert_admin(context) + role_ref = self.identity_api.get_role(context, role_id) + if not role_ref: + raise webob.exc.HTTPNotFound() + return {'role': role_ref} + + def create_role(self, context, role): + role = self._normalize_dict(role) + self.assert_admin(context) + role_id = uuid.uuid4().hex + role['id'] = role_id + role_ref = self.identity_api.create_role(context, role_id, role) + return {'role': role_ref} + + def delete_role(self, context, role_id): + self.assert_admin(context) + role_ref = self.identity_api.delete_role(context, role_id) + + def get_roles(self, context): + self.assert_admin(context) + roles = self.identity_api.list_roles(context) + # TODO(termie): probably inefficient at some point + return {'roles': roles} + + def add_role_to_user(self, context, user_id, role_id, tenant_id=None): + """Add a role to a user and tenant pair. + + Since we're trying to ignore the idea of user-only roles we're + not implementing them in hopes that the idea will die off. + + """ + self.assert_admin(context) + if tenant_id is None: + raise Exception('User roles not supported: tenant_id required') + + # This still has the weird legacy semantics that adding a role to + # a user also adds them to a tenant + self.identity_api.add_user_to_tenant(context, tenant_id, user_id) + self.identity_api.add_role_to_user_and_tenant( + context, user_id, tenant_id, role_id) + role_ref = self.identity_api.get_role(context, role_id) + return {'role': role_ref} + + def remove_role_from_user(self, context, user_id, role_id, tenant_id=None): + """Remove a role from a user and tenant pair. + + Since we're trying to ignore the idea of user-only roles we're + not implementing them in hopes that the idea will die off. + + """ + self.assert_admin(context) + if tenant_id is None: + raise Exception('User roles not supported: tenant_id required') + + # This still has the weird legacy semantics that adding a role to + # a user also adds them to a tenant + self.identity_api.remove_role_from_user_and_tenant( + context, user_id, tenant_id, role_id) + roles = self.identity_api.get_roles_for_user_and_tenant( + context, user_id, tenant_id) + if not roles: + self.identity_api.remove_user_from_tenant( + context, tenant_id, user_id) + return + + # COMPAT(diablo): CRUD extension + def get_role_refs(self, context, user_id): + """Ultimate hack to get around having to make role_refs first-class. + + This will basically iterate over the various roles the user has in + all tenants the user is a member of and create fake role_refs where + the id encodes the user-tenant-role information so we can look + up the appropriate data when we need to delete them. + + """ + self.assert_admin(context) + user_ref = self.identity_api.get_user(context, user_id) + tenant_ids = self.identity_api.get_tenants_for_user(context, user_id) + o = [] + for tenant_id in tenant_ids: + role_ids = self.identity_api.get_roles_for_user_and_tenant( + context, user_id, tenant_id) + for role_id in role_ids: + ref = {'roleId': role_id, + 'tenantId': tenant_id, + 'userId': user_id} + ref['id'] = urllib.urlencode(ref) + o.append(ref) + return {'roles': o} + + # COMPAT(diablo): CRUD extension + def create_role_ref(self, context, user_id, role): + """This is actually used for adding a user to a tenant. + + In the legacy data model adding a user to a tenant required setting + a role. + + """ + self.assert_admin(context) + # TODO(termie): for now we're ignoring the actual role + tenant_id = role.get('tenantId') + role_id = role.get('roleId') + self.identity_api.add_user_to_tenant(context, tenant_id, user_id) + self.identity_api.add_role_to_user_and_tenant( + context, user_id, tenant_id, role_id) + role_ref = self.identity_api.get_role(context, role_id) + return {'role': role_ref} + + # COMPAT(diablo): CRUD extension + def delete_role_ref(self, context, user_id, role_ref_id): + """This is actually used for deleting a user from a tenant. + + In the legacy data model removing a user from a tenant required + deleting a role. + + To emulate this, we encode the tenant and role in the role_ref_id, + and if this happens to be the last role for the user-tenant pair, + we remove the user from the tenant. + + """ + self.assert_admin(context) + # TODO(termie): for now we're ignoring the actual role + role_ref_ref = urlparse.parse_qs(role_ref_id) + tenant_id = role_ref_ref.get('tenantId')[0] + role_id = role_ref_ref.get('roleId')[0] + self.identity_api.remove_role_from_user_and_tenant( + context, user_id, tenant_id, role_id) + roles = self.identity_api.get_roles_for_user_and_tenant( + context, user_id, tenant_id) + if not roles: + self.identity_api.remove_user_from_tenant( + context, tenant_id, user_id) diff --git a/keystone/logic/__init__.py b/keystone/logic/__init__.py deleted file mode 100644 index e69de29b..00000000 --- a/keystone/logic/__init__.py +++ /dev/null diff --git a/keystone/logic/extension_reader.py b/keystone/logic/extension_reader.py deleted file mode 100644 index 79a7fd49..00000000 --- a/keystone/logic/extension_reader.py +++ /dev/null @@ -1,142 +0,0 @@ -# Copyright (c) 2011 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 os -import json -from lxml import etree - -from keystone import config -from keystone import utils -from keystone.contrib.extensions import CONFIG_EXTENSION_PROPERTY -from keystone.contrib.extensions import DEFAULT_EXTENSIONS -from keystone.logic.types.extension import Extensions - -EXTENSIONS_PATH = 'contrib/extensions' -CONF = config.CONF - - -def get_supported_extensions(): - """ - Returns list of supported extensions. - """ - extensions = CONF[CONFIG_EXTENSION_PROPERTY] or DEFAULT_EXTENSIONS - return [extension.strip() for extension in extensions] - - -def is_extension_supported(extension_name): - """ - Return True if the extension is enabled, False otherwise. - extension_name - extension name - extension_name is case-sensitive. - """ - if extension_name is not None: - return extension_name in get_supported_extensions() - return False - - -class ExtensionsReader(object): - """Reader to read static extensions content""" - def __init__(self, extension_prefix): - self.extensions = None - self.extension_prefix = extension_prefix - self.root = None - self.supported_extensions = None - self.__init_extensions() - - def __init_extensions(self): - self.extensions = Extensions(self.__get_json_extensions(), - self.__get_xml_extensions()) - - def __get_json_extensions(self): - """ Initializes and returns all json static extension content.""" - body = self.__get_all_json_extensions() - extensionsarray = body["extensions"]["values"] - for supported_extension in self.__get_supported_extensions(): - thisextensionjson = self.__get_extension_json( - supported_extension) - if thisextensionjson is not None: - extensionsarray.append(thisextensionjson) - return json.dumps(body) - - def __get_xml_extensions(self): - """ Initializes and returns all xml static extension content.""" - body = self.__get_all_xml_extensions() - for supported_extension in self.__get_supported_extensions(): - thisextensionxml = self.__get_extension_xml(supported_extension) - if thisextensionxml is not None: - body.append(thisextensionxml) - return etree.tostring(body) - - def __get_root(self): - """ Returns application root.Has a local reference for reuse.""" - if self.root is None: - self.root = utils.get_app_root() - self.root = os.path.abspath(self.root) + os.sep - return self.root - - def __get_file(self, resp_file): - """ Helper get file method.""" - root = self.__get_root() - filename = os.path.abspath(os.path.join(root, resp_file.strip('/\\'))) - return open(filename).read() - - def __get_all_json_extensions(self): - """ Gets empty json extensions content to which specific - extensions are added.""" - resp_file = "%s/%s.json" % (EXTENSIONS_PATH, 'extensions') - allextensions = self.__get_file(resp_file) - return json.loads(allextensions) - - def __get_all_xml_extensions(self): - """ Gets empty xml extensions content - to which specific extensions are added.""" - resp_file = "%s/%s.xml" % (EXTENSIONS_PATH, 'extensions') - allextensions = self.__get_file(resp_file) - return etree.fromstring(allextensions) - - def __get_supported_extensions(self): - """ Returns list of supported extensions.""" - if self.supported_extensions is None: - self.supported_extensions = get_supported_extensions() - return self.supported_extensions - - def __get_extension_json(self, extension_name): - """Returns specific extension's json content.""" - thisextension = self.__get_extension_file(extension_name, 'json') - return thisextension if not thisextension\ - else json.loads(thisextension.read()) - - def __get_extension_xml(self, extension_name): - """Returns specific extension's xml content.""" - thisextension = self.__get_extension_file(extension_name, 'xml') - return thisextension if not thisextension\ - else etree.parse(thisextension).getroot() - - def __get_extension_file(self, extension_name, request_type): - """Returns specific static extension file.""" - try: - extension_dir = "%s/%s/%s" % (EXTENSIONS_PATH, - self.extension_prefix, extension_name) - extension_dir = os.path.abspath(os.path.join(self.__get_root(), - extension_dir.strip('/\\'))) - extension_file = open(os.path.join(extension_dir, - "extension." + request_type)) - return extension_file - except IOError: - return None - - def get_extensions(self): - """Return Extensions result.""" - return self.extensions diff --git a/keystone/logic/service.py b/keystone/logic/service.py deleted file mode 100755 index 9387464d..00000000 --- a/keystone/logic/service.py +++ /dev/null @@ -1,1558 +0,0 @@ -# Copyright (c) 2010-2011 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. -# -# pylint: disable=C0302,W0603,W0602 - -from datetime import datetime, timedelta -import functools -import logging -import uuid - -from keystone import config -from keystone.logic.types import auth, atom -from keystone.logic.signer import Signer -import keystone.backends as backends -import keystone.backends.models as models -from keystone.logic.types import fault -from keystone.logic.types.tenant import Tenants -from keystone.logic.types.user import User, User_Update, Users -from keystone.logic.types.endpoint import Endpoint, Endpoints, \ - EndpointTemplate, EndpointTemplates -from keystone.logic.types.credential import Credentials, PasswordCredentials -from keystone import utils -# New imports as we refactor old backend design and models -from keystone.models import Tenant, Token -from keystone.models import Role, Roles -from keystone.models import Service, Services -from keystone.managers.token import Manager as TokenManager -from keystone.managers.tenant import Manager as TenantManager -from keystone.managers.user import Manager as UserManager -from keystone.managers.role import Manager as RoleManager -from keystone.managers.grant import Manager as GrantManager -from keystone.managers.service import Manager as ServiceManager -from keystone.managers.endpoint_template import Manager \ - as EndpointTemplateManager -from keystone.managers.endpoint import Manager as EndpointManager -from keystone.managers.credential import Manager as CredentialManager - -CONF = config.CONF - -#Reference to Admin Role. -ADMIN_ROLE_ID = None -ADMIN_ROLE_NAME = None -SERVICE_ADMIN_ROLE_ID = None -SERVICE_ADMIN_ROLE_NAME = None -GLOBAL_SERVICE_ID = None # to facilitate global roles for validate tokens - -LOG = logging.getLogger(__name__) - - -def admin_token_validator(fnc): - """Decorator that applies the validate_admin_token() method.""" - @functools.wraps(fnc) - def _wrapper(self, token_id, *args, **kwargs): - self.validate_admin_token(token_id) - return fnc(self, token_id, *args, **kwargs) - return _wrapper - - -def service_admin_token_validator(fnc): - """Decorator that applies the validate_service_admin_token() method.""" - @functools.wraps(fnc) - def _wrapper(self, token_id, *args, **kwargs): - self.validate_service_admin_token(token_id) - return fnc(self, token_id, *args, **kwargs) - return _wrapper - - -# pylint: disable=R0902 -class IdentityService(object): - """Implements the Identity service - - This class handles all logic of routing requests to the correct - backend as well as validating incoming/outgoing data - """ - - def __init__(self): - """ Initialize - - Loads all necessary backends to handle incoming requests. - """ - backends.configure_backends() - self.token_manager = TokenManager() - self.tenant_manager = TenantManager() - self.user_manager = UserManager() - self.role_manager = RoleManager() - self.grant_manager = GrantManager() - self.service_manager = ServiceManager() - self.endpoint_template_manager = EndpointTemplateManager() - self.endpoint_manager = EndpointManager() - self.credential_manager = CredentialManager() - - # pylint: disable=W0603 - global ADMIN_ROLE_NAME - ADMIN_ROLE_NAME = CONF.keystone_admin_role - - global SERVICE_ADMIN_ROLE_NAME - SERVICE_ADMIN_ROLE_NAME = CONF.keystone_service_admin_role - - global GLOBAL_SERVICE_ID - GLOBAL_SERVICE_ID = CONF.global_service_id or "global" - - LOG.debug("init with ADMIN_ROLE_NAME=%s, SERVICE_ADMIN_ROLE_NAME=%s, " - "GLOBAL_SERVICE_ID=%s" % (ADMIN_ROLE_NAME, - SERVICE_ADMIN_ROLE_NAME, - GLOBAL_SERVICE_ID)) - - # - # Token Operations - # - def authenticate(self, auth_request): - LOG.debug("Authenticating with passwordCredentials") - if not isinstance(auth_request, auth.AuthWithPasswordCredentials): - raise fault.BadRequestFault( - "Expecting auth_with_password_credentials!") - - def validate(duser): - return self.user_manager.check_password(duser.id, - auth_request.password) - - if auth_request.tenant_name: - dtenant = self.validate_tenant_by_name(auth_request.tenant_name) - auth_request.tenant_id = dtenant.id - elif auth_request.tenant_id: - dtenant = self.validate_tenant_by_id(auth_request.tenant_id) - - user = self.user_manager.get_by_name(auth_request.username) - if not user: - LOG.debug("Did not find user with name=%s" % auth_request.username) - raise fault.UnauthorizedFault("Unauthorized") - - return self._authenticate(validate, user.id, auth_request.tenant_id) - - def authenticate_with_unscoped_token(self, auth_request): - LOG.debug("Authenticating with token (unscoped)") - if not isinstance(auth_request, auth.AuthWithUnscopedToken): - raise fault.BadRequestFault("Expecting auth_with_unscoped_token!") - - # We *should* check for an unscoped token here, but as long as - # POST /tokens w/ credentials auto-scopes to User.tenantId, users can't - # reach this flow. - # _token, user = validate_unscoped_token(auth_request.token_id) - _token, user = self._validate_token(auth_request.token_id) - - if auth_request.tenant_name: - dtenant = self.validate_tenant_by_name(auth_request.tenant_name) - auth_request.tenant_id = dtenant.id - elif auth_request.tenant_id: - dtenant = self.validate_tenant_by_id(auth_request.tenant_id) - - # pylint: disable=W0613 - def validate(duser): - # The user is already authenticated - return True - return self._authenticate(validate, user.id, auth_request.tenant_id) - - def authenticate_ec2(self, credentials): - LOG.debug("Authenticating with EC2 credentials") - if not isinstance(credentials, auth.Ec2Credentials): - raise fault.BadRequestFault("Expecting Ec2 Credentials!") - - creds = self.credential_manager.get_by_access(credentials.access) - if not creds: - raise fault.UnauthorizedFault("No credentials found for %s" - % credentials.access) - - # pylint: disable=W0613 - def validate(duser): - signer = Signer(creds.secret) - signature = signer.generate(credentials) - if signature == credentials.signature: - return True - # NOTE(vish): Some libraries don't use the port when signing - # requests, so try again without port. - if ':' in credentials.host: - hostname, _port = credentials.host.split(":") - credentials.host = hostname - signature = signer.generate(credentials) - return signature == credentials.signature - return False - return self._authenticate(validate, creds.user_id, - creds.tenant_id) - - def authenticate_s3(self, credentials): - # Check credentials - if not isinstance(credentials, auth.S3Credentials): - raise fault.BadRequestFault("Expecting S3 Credentials!") - - creds = self.credential_manager.get_by_access(credentials.access) - if not creds: - raise fault.UnauthorizedFault("No credentials found for %s" - % credentials.access) - - def validate(duser): # pylint: disable=W0613 - signer = Signer(creds.secret) - signature = signer.generate(credentials, s3=True) - if signature == credentials.signature: - return True - return False - - return self._authenticate(validate, creds.user_id, creds.tenant_id) - - def _authenticate(self, validate, user_id, tenant_id=None): - LOG.debug("Authenticating user %s (tenant: %s)" % (user_id, tenant_id)) - if tenant_id: - duser = self.user_manager.get_by_tenant(user_id, tenant_id) - if duser is None: - LOG.debug("User %s is not authorized on tenant %s" % ( - user_id, tenant_id)) - raise fault.UnauthorizedFault("Unauthorized on this tenant") - else: - duser = self.user_manager.get(user_id) - if duser is None: - LOG.debug("User with id %s not found" % user_id) - raise fault.UnauthorizedFault("Unauthorized") - - if not duser.enabled: - LOG.debug("User %s is not enabled" % user_id) - raise fault.UserDisabledFault("Your account has been disabled") - - if not validate(duser): - LOG.debug("validate() returned false") - raise fault.UnauthorizedFault("Unauthorized") - - # use user's default tenant_id if one is not specified - tenant_id = tenant_id or duser.tenant_id - - # check for an existing token - dtoken = self.token_manager.find(duser.id, tenant_id) - - if not dtoken or dtoken.expires < datetime.now(): - LOG.debug("Token was not found or expired. Creating a new token " - "for the user") - # Create new token - dtoken = Token() - dtoken.id = str(uuid.uuid4()) - dtoken.user_id = duser.id - dtoken.tenant_id = tenant_id - dtoken.expires = datetime.now() + timedelta(days=1) - dtoken = self.token_manager.create(dtoken) - return self.get_auth_data(dtoken) - - # pylint: disable=W0613 - @service_admin_token_validator - def validate_token(self, admin_token, token_id, belongs_to=None, - service_ids=None): - (token, user) = self._validate_token(token_id, belongs_to, True) - if service_ids and (token.tenant_id or belongs_to): - # scope token, validate the service IDs if present - service_ids = self.parse_service_ids(service_ids) - self.validate_service_ids(service_ids) - auth_data = self.get_validate_data(token, user, service_ids) - if service_ids and (token.tenant_id or belongs_to): - # we have service Ids and scope token, make sure we have some roles - if not auth_data.user.rolegrants.values: - raise fault.UnauthorizedFault("No roles found for scope token") - return auth_data - - @admin_token_validator - def revoke_token(self, admin_token, token_id): - dtoken = self.token_manager.get(token_id) - if not dtoken: - raise fault.ItemNotFoundFault("Token not found") - - self.token_manager.delete(token_id) - - @staticmethod - def parse_service_ids(service_ids): - """ - Method to parse the service IDs string. - service_ids -- comma-separated service IDs - parse and return a list of service IDs. - """ - if service_ids: - return [x.strip() for x in service_ids.split(',')] - return [] - - def validate_service_ids(self, service_ids): - """ - Method to validate the service IDs. - service_ids -- list of service IDs - If not service IDs or encounter an invalid service ID, - fault.UnauthorizedFault will be raised. - """ - if not service_ids: - raise fault.UnauthorizedFault("Missing service IDs") - - services = [self.service_manager.get(service_id) for service_id in - service_ids if not service_id == GLOBAL_SERVICE_ID] - if not all(services): - raise fault.UnauthorizedFault( - "Invalid service ID: %s" % (service_ids)) - - def get_roles_names_by_service_ids(self, service_ids): - """ - Method to find all the roles for the given service IDs. - service_ids -- list of service IDs - """ - roles = [] - for service_id in service_ids: - if service_id != GLOBAL_SERVICE_ID: - sroles = self.role_manager.get_by_service( - service_id=service_id) - if sroles: - roles = roles + sroles - return [role.name for role in roles] - - def get_global_roles_for_user(self, user_id): - """ - Method to return all the global roles for the given user. - user_id -- user ID - """ - ts = [] - drolegrants = self.grant_manager.list_global_roles_for_user(user_id) - for drolegrant in drolegrants: - drole = self.role_manager.get(drolegrant.role_id) - ts.append(Role(drolegrant.role_id, drole.name, - None, drolegrant.tenant_id)) - return ts - - def get_tenant_roles_for_user_and_services(self, user_id, tenant_id, - service_ids): - """ - Method to return all the tenant roles for the given user, - filtered by service ID. - user_id -- user ID - tenant_id -- tenant ID - service_ids -- service IDs - If service_ids are specified, will return the roles filtered by - service IDs. - """ - ts = [] - if tenant_id and user_id: - drolegrants = self.grant_manager.list_tenant_roles_for_user( - user_id, tenant_id) - for drolegrant in drolegrants: - drole = self.role_manager.get(drolegrant.role_id) - ts.append(Role(drolegrant.role_id, drole.name, - None, drolegrant.tenant_id)) - - if service_ids: - # if service IDs are specified, filter roles by service IDs - sroles_names = self.get_roles_names_by_service_ids(service_ids) - return [role for role in ts - if role.name in sroles_names] - else: - return ts - - @service_admin_token_validator - def get_endpoints_for_token(self, admin_token, - token_id, marker, limit, url,): - dtoken = self.token_manager.get(token_id) - if not dtoken: - raise fault.ItemNotFoundFault("Token not found") - if not dtoken.tenant_id: - raise fault.ItemNotFoundFault("Token not mapped to any tenant.") - return self.fetch_tenant_endpoints( - marker, limit, url, dtoken.tenant_id) - - def get_token_info(self, token_id): - """returns token and user object for a token_id""" - - token = None - user = None - if token_id: - token = self.token_manager.get(token_id) - if token: - user = self.user_manager.get(token.user_id) - return (token, user) - - def _validate_token(self, token_id, belongs_to=None, is_check_token=None): - """ - Method to validate a token. - token_id -- id of the token that needs to be validated. - belongs_to -- optional tenant_id to check whether the token is - mapped to a specific tenant. - is_check_token -- optional argument that tells whether - we check the existence of a Token using another Token - to authenticate. This value decides the faults that are to be thrown. - """ - if not token_id: - raise fault.UnauthorizedFault("Missing token") - - (token, user) = self.get_token_info(token_id) - - if not token: - if is_check_token: - raise fault.ItemNotFoundFault("Token does not exist.") - else: - raise fault.UnauthorizedFault( - "Bad token, please reauthenticate") - - if token.expires < datetime.now(): - if is_check_token: - raise fault.ItemNotFoundFault("Token expired, please renew.") - else: - raise fault.ForbiddenFault("Token expired, please renew.") - - if not user.enabled: - raise fault.UserDisabledFault("User %s has been disabled!" - % user.id) - - if user.tenant_id: - self.validate_tenant_by_id(user.tenant_id) - - if token.tenant_id: - self.validate_tenant_by_id(token.tenant_id) - - if belongs_to and unicode(token.tenant_id) != unicode(belongs_to): - raise fault.UnauthorizedFault("Unauthorized on this tenant") - - return (token, user) - - def has_admin_role(self, token_id): - """ Checks if the token belongs to a user who has Keystone admin - rights. - - Returns (token, user) if true. False otherwise. - - This is currently assigned using a global role assignment - (i.e. role assigned without a tenant id). The actual name of the - role is defined in the config file using the keystone-admin-role - setting - """ - (token, user) = self._validate_token(token_id) - self.init_admin_role_identifiers() - if self.has_role(None, user, ADMIN_ROLE_ID): - return (token, user) - else: - return False - - def has_service_admin_role(self, token_id): - """ Checks if the token belongs to a user who has Keystone Service - Admin rights. (Note: Keystone Admin rights include Keystone Service - Admin). - - Returns (token, user) if true. False otherwise. - - This is currently assigned using a global role assignment - (i.e. role assigned without a tenant id). The actual name of the role - is defined in the config file using the keystone-admin-role setting - """ - (token, user) = self._validate_token(token_id) - self.init_admin_role_identifiers() - if self.has_role(None, user, SERVICE_ADMIN_ROLE_ID): - return (token, user) - else: - return self.has_admin_role(token_id) - - def validate_admin_token(self, token_id): - """ Validates that the token belongs to a user who has Keystone admin - rights. Raises an Unauthorized exception if not. - - This is currently assigned using a global role assignment - (i.e. role assigned without a tenant id). The actual name of the role - is defined in the config file using the keystone-admin-role setting - """ - result = self.has_admin_role(token_id) - if result: - return result - else: - raise fault.UnauthorizedFault( - "You are not authorized to make this call") - - def validate_service_admin_token(self, token_id): - """ Validates that the token belongs to a user who has Keystone admin - or Keystone Service Admin rights. Raises an Unaithorized exception if - not. - - These are currently assigned using a global role assignments - (i.e. roles assigned without a tenant id). The actual name of the roles - is defined in the config file using the keystone-admin-role and - keystone-service-admin-role settings - """ - # Does the user have the Service Admin role - result = self.has_service_admin_role(token_id) - if result: - LOG.debug("token is associated with service admin role") - return result - # Does the user have the Admin role (which includes Service Admin - # rights) - result = self.has_admin_role(token_id) - if result: - LOG.debug("token is associated with admin role, so responding" - "positively from validate_service_admin_token") - return result - - LOG.debug("token is not associated with admin or service admin role") - raise fault.UnauthorizedFault( - "You are not authorized to make this call") - - def init_admin_role_identifiers(self): - global ADMIN_ROLE_ID, SERVICE_ADMIN_ROLE_ID - if SERVICE_ADMIN_ROLE_ID is None: - role = self.role_manager.get_by_name(SERVICE_ADMIN_ROLE_NAME) - if role: - SERVICE_ADMIN_ROLE_ID = role.id - else: - LOG.warn('No service admin role found (searching for name=%s.' - % SERVICE_ADMIN_ROLE_NAME) - if ADMIN_ROLE_ID is None: - role = self.role_manager.get_by_name(ADMIN_ROLE_NAME) - if role: - ADMIN_ROLE_ID = role.id - else: - LOG.warn('No admin role found (searching for name=%s.' - % ADMIN_ROLE_NAME) - - def has_role(self, env, user, role): - """Checks if a user has a specific role. - - env: provides the context - user: the user to be checked - role: the role to check that the user has - """ - for rolegrant in\ - self.grant_manager.list_global_roles_for_user(user.id): - if ((rolegrant.role_id == role) - and rolegrant.tenant_id is None): - return True - LOG.debug("User %s failed check - did not have role %s" % - (user.id, role)) - return False - - # pylint: disable=W0613 - @staticmethod - def is_owner(env, user, object): - """Checks if a user is the owner of an object. - - This is done by checking if the user id matches the 'owner_id' - field of the object - - env: provides the context - user: the user to be checked - role: the role to check that the user has - """ - if hasattr(object, 'owner_id'): - if object.owner_id == user.id: - return True - return False - - def validate_unscoped_token(self, token_id, belongs_to=None): - (token, user) = self._validate_token(token_id, belongs_to) - - if token.tenant_id: - raise fault.ForbiddenFault("Expecting unscoped token") - return (token, user) - - def validate_tenant_by_id(self, tenant_id): - if not tenant_id: - raise fault.UnauthorizedFault("Missing tenant id") - - dtenant = self.tenant_manager.get(tenant_id) - return self.validate_tenant(dtenant) - - def validate_tenant_by_name(self, tenant_name): - if not tenant_name: - raise fault.UnauthorizedFault("Missing tenant name") - - dtenant = self.tenant_manager.get_by_name(name=tenant_name) - return self.validate_tenant(dtenant) - - def get_auth_data(self, dtoken): - """returns AuthData object for a token - - AuthData is used for rendering authentication responses - """ - tenant = None - endpoints = None - - if dtoken.tenant_id: - dtenant = self.tenant_manager.get(dtoken.tenant_id) - tenant = auth.Tenant(id=dtenant.id, name=dtenant.name) - endpoints = self.tenant_manager.get_all_endpoints(dtoken.tenant_id) - else: - endpoints = self.tenant_manager.get_all_endpoints(None) - - token = auth.Token(dtoken.expires, dtoken.id, tenant) - duser = self.user_manager.get(dtoken.user_id) - - ts = [] - if dtoken.tenant_id: - drolegrants = self.grant_manager.list_tenant_roles_for_user( - duser.id, dtoken.tenant_id) - for drolegrant in drolegrants: - drole = self.role_manager.get(drolegrant.role_id) - ts.append(Role(drolegrant.role_id, drole.name, - description=drole.desc, tenant_id=drolegrant.tenant_id)) - drolegrants = self.grant_manager.list_global_roles_for_user(duser.id) - for drolegrant in drolegrants: - drole = self.role_manager.get(drolegrant.role_id) - ts.append(Role(drolegrant.role_id, drole.name, - description=drole.desc, tenant_id=drolegrant.tenant_id)) - user = auth.User(duser.id, duser.name, None, None, Roles(ts, [])) - if self.has_service_admin_role(token.id): - # Privileged users see the adminURL as well - url_types = ['admin', 'internal', 'public'] - else: - url_types = ['internal', 'public'] - return auth.AuthData(token, user, endpoints, url_types=url_types) - - def get_validate_data(self, dtoken, duser, service_ids=None): - """return ValidateData object for a token/user pair""" - global GLOBAL_SERVICE_ID - tenant = None - if dtoken.tenant_id: - dtenant = self.tenant_manager.get(dtoken.tenant_id) - tenant = auth.Tenant(id=dtenant.id, name=dtenant.name) - - token = auth.Token(dtoken.expires, dtoken.id, tenant) - - ts = self.get_tenant_roles_for_user_and_services(duser.id, - dtoken.tenant_id, - service_ids) - if (not dtoken.tenant_id or not service_ids or - (GLOBAL_SERVICE_ID in service_ids)): - # return the global roles for unscoped tokens or - # its ID is in the service IDs - ts = ts + self.get_global_roles_for_user(duser.id) - - # Also get the user's tenant's name - tenant_name = None - if duser.tenant_id: - utenant = self.tenant_manager.get(duser.tenant_id) - tenant_name = utenant.name - - user = auth.User(duser.id, duser.name, duser.tenant_id, - tenant_name, Roles(ts, [])) - return auth.ValidateData(token, user) - - @staticmethod - def validate_tenant(dtenant): - if not dtenant: - raise fault.UnauthorizedFault("Tenant not found") - - if dtenant.enabled is None or \ - str(dtenant.enabled).lower() not in ['1', 'true']: - raise fault.TenantDisabledFault("Tenant %s has been disabled!" - % dtenant.id) - return dtenant - - # - # Tenant Operations - # - @admin_token_validator - def create_tenant(self, admin_token, tenant): - if not isinstance(tenant, Tenant): - raise fault.BadRequestFault("Expecting a Tenant") - - utils.check_empty_string(tenant.name, "Expecting a unique Tenant Name") - if self.tenant_manager.get_by_name(tenant.name) is not None: - raise fault.TenantConflictFault( - "A tenant with that name already exists") - dtenant = Tenant() - dtenant.name = tenant.name - dtenant.description = tenant.description - dtenant.enabled = tenant.enabled - return self.tenant_manager.create(dtenant) - - # pylint: disable=R0914 - def get_tenants(self, admin_token, marker, limit, url, - is_service_operation=False): - """Fetch tenants for either an admin or service operation.""" - ts = [] - - if is_service_operation: - # Check regular token validity. - (_token, user) = self._validate_token(admin_token, belongs_to=None, - is_check_token=False) - scope = _token.tenant_id - default_tenant = user.tenant_id - - if (scope is None or - ((scope and default_tenant) and (scope == default_tenant))): - # Return all tenants specific to user if token has no scope - # or if token is scoped to a default tenant - dtenants = self.tenant_manager.list_for_user_get_page( - user.id, marker, limit) - prev_page, next_page = self.tenant_manager.\ - list_for_user_get_page_markers(user.id, marker, limit) - else: - # Return scoped tenant only - dtenants = [self.tenant_manager.get(scope or default_tenant)] - prev_page = 2 - next_page = None - limit = 10 - else: - #Check Admin Token - (_token, user) = self.validate_admin_token(admin_token) - # Return all tenants - dtenants = self.tenant_manager.get_page(marker, limit) - prev_page, next_page = self.tenant_manager.get_page_markers(marker, - limit) - - for dtenant in dtenants: - t = Tenant(id=dtenant.id, name=dtenant.name, - description=dtenant.desc, enabled=dtenant.enabled) - ts.append(t) - - links = self.get_links(url, prev_page, next_page, limit) - return Tenants(ts, links) - - @admin_token_validator - def get_tenant(self, admin_token, tenant_id): - dtenant = self.tenant_manager.get(tenant_id) - if not dtenant: - raise fault.ItemNotFoundFault("The tenant could not be found") - return Tenant(dtenant.id, dtenant.name, dtenant.desc, dtenant.enabled) - - @admin_token_validator - def get_tenant_by_name(self, admin_token, tenant_name): - dtenant = self.tenant_manager.get_by_name(tenant_name) - if not dtenant: - raise fault.ItemNotFoundFault("The tenant could not be found") - return dtenant - - @admin_token_validator - def update_tenant(self, admin_token, tenant_id, tenant): - if not isinstance(tenant, Tenant): - raise fault.BadRequestFault("Expecting a Tenant") - - dtenant = self.tenant_manager.get(tenant_id) - if dtenant is None: - raise fault.ItemNotFoundFault("The tenant could not be found") - - utils.check_empty_string(tenant.name, "Expecting a unique Tenant Name") - - if tenant.name != dtenant.name and \ - self.tenant_manager.get_by_name(tenant.name): - raise fault.TenantConflictFault( - "A tenant with that name already exists") - values = {'id': tenant_id, 'desc': tenant.description, - 'enabled': tenant.enabled, 'name': tenant.name} - self.tenant_manager.update(values) - dtenant = self.tenant_manager.get(tenant_id) - return dtenant - - @admin_token_validator - def delete_tenant(self, admin_token, tenant_id): - dtenant = self.tenant_manager.get(tenant_id) - if dtenant is None: - raise fault.ItemNotFoundFault("The tenant could not be found") - - self.tenant_manager.delete(dtenant.id) - return None - - # - # User Operations - # - @admin_token_validator - def create_user(self, admin_token, user): - self.validate_and_fetch_user_tenant(user.tenant_id) - - if not isinstance(user, User): - raise fault.BadRequestFault("Expecting a User") - - utils.check_empty_string(user.name, - "Expecting a unique user Name") - - if self.user_manager.get_by_name(user.name): - raise fault.UserConflictFault( - "A user with that name already exists") - - if self.user_manager.get_by_email(user.email): - raise fault.EmailConflictFault( - "A user with that email already exists") - - duser = models.User() - duser.name = user.name - duser.password = user.password - duser.email = user.email - duser.enabled = user.enabled - duser.tenant_id = user.tenant_id - duser = self.user_manager.create(duser) - user.id = duser.id - return user - - def validate_and_fetch_user_tenant(self, tenant_id): - if tenant_id: - dtenant = self.tenant_manager.get(tenant_id) - if dtenant is None: - raise fault.ItemNotFoundFault("The tenant is not found") - elif not dtenant.enabled: - raise fault.TenantDisabledFault( - "Your account has been disabled") - return dtenant - - # pylint: disable=R0913 - @admin_token_validator - def get_tenant_users(self, admin_token, tenant_id, - role_id, marker, limit, url): - if tenant_id is None: - raise fault.BadRequestFault("Expecting a Tenant Id") - dtenant = self.tenant_manager.get(tenant_id) - if dtenant is None: - raise fault.ItemNotFoundFault("The tenant not found") - if not dtenant.enabled: - raise fault.TenantDisabledFault("Your account has been disabled") - if role_id: - if not self.role_manager.get(role_id): - raise fault.ItemNotFoundFault("The role not found") - ts = [] - dtenantusers = self.user_manager.users_get_by_tenant_get_page( - tenant_id, role_id, marker, limit) - for dtenantuser in dtenantusers: - try: - troles = dtenantuser.tenant_roles - except AttributeError: - troles = None - ts.append(User(None, dtenantuser.id, dtenantuser.name, tenant_id, - dtenantuser.email, dtenantuser.enabled, troles)) - links = [] - if ts.__len__(): - prev, next = self.\ - user_manager.users_get_by_tenant_get_page_markers( - tenant_id, role_id, marker, limit) - links = self.get_links(url, prev, next, limit) - return Users(ts, links) - - @admin_token_validator - def get_users(self, admin_token, marker, limit, url): - ts = [] - dusers = self.user_manager.users_get_page(marker, limit) - for duser in dusers: - ts.append(User(None, duser.id, duser.name, duser.tenant_id, - duser.email, duser.enabled)) - links = [] - if ts.__len__(): - prev, next = self.user_manager.users_get_page_markers(marker, - limit) - links = self.get_links(url, prev, next, limit) - return Users(ts, links) - - @admin_token_validator - def get_user(self, admin_token, user_id): - duser = self.user_manager.get(user_id) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - return User_Update(id=duser.id, tenant_id=duser.tenant_id, - email=duser.email, enabled=duser.enabled, name=duser.name) - - @admin_token_validator - def get_user_by_name(self, admin_token, user_name): - duser = self.user_manager.get_by_name(user_name) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - return User_Update(id=duser.id, tenant_id=duser.tenant_id, - email=duser.email, enabled=duser.enabled, name=duser.name) - - @admin_token_validator - def update_user(self, admin_token, user_id, user): - duser = self.user_manager.get(user_id) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - - if not isinstance(user, User): - raise fault.BadRequestFault("Expecting a User") - - utils.check_empty_string(user.name, - "Expecting a unique username") - - if user.name != duser.name and \ - self.user_manager.get_by_name(user.name): - raise fault.UserConflictFault( - "A user with that name already exists") - - if user.email != duser.email and \ - self.user_manager.get_by_email(user.email) is not None: - raise fault.EmailConflictFault("Email already exists") - - values = {'id': user_id, 'email': user.email, 'name': user.name} - self.user_manager.update(values) - duser = self.user_manager.get(user_id) - return User(duser.password, duser.id, duser.name, duser.tenant_id, - duser.email, duser.enabled) - - @admin_token_validator - def set_user_password(self, admin_token, user_id, user): - duser = self.user_manager.get(user_id) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - - if not isinstance(user, User): - raise fault.BadRequestFault("Expecting a User") - - duser = self.user_manager.get(user_id) - if duser is None: - raise fault.ItemNotFoundFault("The user could not be found") - - values = {'id': user_id, 'password': user.password} - - self.user_manager.update(values) - - return User_Update(password=user.password) - - @admin_token_validator - def enable_disable_user(self, admin_token, user_id, user): - duser = self.user_manager.get(user_id) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - if not isinstance(user, User): - raise fault.BadRequestFault("Expecting a User") - - values = {'id': user_id, 'enabled': user.enabled} - - self.user_manager.update(values) - - duser = self.user_manager.get(user_id) - - return User_Update(enabled=user.enabled) - - @admin_token_validator - def set_user_tenant(self, admin_token, user_id, user): - duser = self.user_manager.get(user_id) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - if not isinstance(user, User): - raise fault.BadRequestFault("Expecting a User") - - self.validate_and_fetch_user_tenant(user.tenant_id) - values = {'id': user_id, 'tenant_id': user.tenant_id} - self.user_manager.update(values) - return User_Update(tenant_id=user.tenant_id) - - @admin_token_validator - def delete_user(self, admin_token, user_id): - duser = self.user_manager.get(user_id) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - - self.user_manager.delete(user_id) - return None - - def create_role(self, admin_token, role): - user = self.validate_service_admin_token(admin_token)[1] - - if not isinstance(role, Role): - raise fault.BadRequestFault("Expecting a Role") - - utils.check_empty_string(role.name, "Expecting a Role name") - - if self.role_manager.get_by_name(role.name) is not None: - raise fault.RoleConflictFault( - "A role with that name '%s' already exists" % role.name) - - #Check if the role name includes an embedded service: in it - #if so, verify the service exists - if role.service_id is None: - split = role.name.split(":") - if isinstance(split, list) and len(split) > 1: - service_name = split[0] - service = self.service_manager.get_by_name(service_name) - if service is None: - raise fault.BadRequestFault( - "A service with the name %s doesn't exist." - % service_name) - role.service_id = service.id - - # Check ownership of the service (or overriding admin rights) - if role.service_id: - service = self.service_manager.get(role.service_id) - if service is None: - raise fault.BadRequestFault( - "A service with that id doesn't exist.") - if not role.name.startswith(service.name + ":"): - raise fault.BadRequestFault( - "Role should begin with service name '%s:'" % service.name) - if not self.is_owner(None, user, service): - if not self.has_admin_role(admin_token): - raise fault.UnauthorizedFault( - "You do not have ownership of the '%s' service" \ - % service.name) - - drole = models.Role() - drole.name = role.name - drole.desc = role.description - drole.service_id = role.service_id - drole = self.role_manager.create(drole) - role.id = drole.id - return role - - @service_admin_token_validator - def get_roles(self, admin_token, marker, limit, url): - droles = self.role_manager.get_page(marker, limit) - prev, next = self.role_manager.get_page_markers(marker, limit) - links = self.get_links(url, prev, next, limit) - ts = self.transform_roles(droles) - return Roles(ts, links) - - @service_admin_token_validator - def get_roles_by_service(self, admin_token, marker, limit, url, - service_id): - droles = self.role_manager.get_by_service_get_page(service_id, marker, - limit) - prev, next = self.role_manager.get_by_service_get_page_markers( - service_id, marker, limit) - links = self.get_links(url, prev, next, limit) - ts = self.transform_roles(droles) - return Roles(ts, links) - - @staticmethod - def transform_roles(droles): - return [Role(drole.id, drole.name, drole.desc, drole.service_id) - for drole in droles] - - @service_admin_token_validator - def get_role(self, admin_token, role_id): - drole = self.role_manager.get(role_id) - if not drole: - raise fault.ItemNotFoundFault("The role could not be found") - return Role(drole.id, drole.name, drole.desc, drole.service_id) - - @service_admin_token_validator - def get_role_by_name(self, admin_token, role_name): - drole = self.role_manager.get_by_name(role_name) - if not drole: - raise fault.ItemNotFoundFault("The role could not be found") - return Role(drole.id, drole.name, - drole.desc, drole.service_id) - - def delete_role(self, admin_token, role_id): - user = self.validate_service_admin_token(admin_token)[1] - - drole = self.role_manager.get(role_id) - if not drole: - raise fault.ItemNotFoundFault("The role could not be found") - - # Check ownership of the service (or overriding admin rights) - if drole.service_id: - service = self.service_manager.get(drole.service_id) - if service: - if not self.is_owner(None, user, service): - if not self.has_admin_role(admin_token): - raise fault.UnauthorizedFault( - "You do not have ownership of the '%s' service" - % service.name) - - rolegrants = self.grant_manager.rolegrant_list_by_role(role_id) - if rolegrants is not None: - for rolegrant in rolegrants: - self.grant_manager.rolegrant_delete(rolegrant.id) - self.role_manager.delete(role_id) - - @service_admin_token_validator - def add_role_to_user(self, admin_token, user_id, role_id, tenant_id=None): - duser = self.user_manager.get(user_id) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - - drole = self.role_manager.get(role_id) - if drole is None: - raise fault.ItemNotFoundFault("The role not found") - if tenant_id is not None: - dtenant = self.tenant_manager.get(tenant_id) - if dtenant is None: - raise fault.ItemNotFoundFault("The tenant not found") - - drolegrant = self.grant_manager.rolegrant_get_by_ids(user_id, role_id, - tenant_id) - if drolegrant is not None: - raise fault.RoleConflictFault( - "This role is already mapped to the user.") - - drolegrant = models.UserRoleAssociation() - drolegrant.user_id = duser.id - drolegrant.role_id = drole.id - if tenant_id is not None: - drolegrant.tenant_id = dtenant.id - self.user_manager.user_role_add(drolegrant) - - @service_admin_token_validator - def remove_role_from_user(self, admin_token, user_id, role_id, - tenant_id=None): - drolegrant = self.grant_manager.rolegrant_get_by_ids(user_id, role_id, - tenant_id) - if drolegrant is None: - raise fault.ItemNotFoundFault( - "This role is not mapped to the user.") - self.grant_manager.rolegrant_delete(drolegrant.id) - - # pylint: disable=R0913, R0914 - @service_admin_token_validator - def get_user_roles(self, admin_token, marker, - limit, url, user_id, tenant_id): - duser = self.user_manager.get(user_id) - - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - - if tenant_id is not None: - dtenant = self.tenant_manager.get(tenant_id) - if not dtenant: - raise fault.ItemNotFoundFault("The tenant could not be found.") - ts = [] - drolegrants = self.grant_manager.rolegrant_get_page(marker, limit, - user_id, tenant_id) - for drolegrant in drolegrants: - drole = self.role_manager.get(drolegrant.role_id) - ts.append(Role(drole.id, drole.name, - drole.desc, drole.service_id)) - prev, next = self.grant_manager.rolegrant_get_page_markers( - user_id, tenant_id, marker, limit) - links = self.get_links(url, prev, next, limit) - return Roles(ts, links) - - def add_endpoint_template(self, admin_token, endpoint_template): - user = self.validate_service_admin_token(admin_token)[1] - - if not isinstance(endpoint_template, EndpointTemplate): - raise fault.BadRequestFault("Expecting a EndpointTemplate") - - utils.check_empty_string(endpoint_template.name, - "Expecting Endpoint Template name.") - utils.check_empty_string(endpoint_template.type, - "Expecting Endpoint Template type.") - - dservice = self.service_manager.get_by_name_and_type( - endpoint_template.name, - endpoint_template.type) - if dservice is None: - raise fault.BadRequestFault( - "A service with that name and type doesn't exist.") - - # Check ownership of the service (or overriding admin rights) - if not self.is_owner(None, user, dservice): - if not self.has_admin_role(admin_token): - raise fault.UnauthorizedFault( - "You do not have ownership of the '%s' service" \ - % dservice.name) - - dendpoint_template = models.EndpointTemplates() - dendpoint_template.region = endpoint_template.region - dendpoint_template.service_id = dservice.id - dendpoint_template.public_url = endpoint_template.public_url - dendpoint_template.admin_url = endpoint_template.admin_url - dendpoint_template.internal_url = endpoint_template.internal_url - dendpoint_template.enabled = endpoint_template.enabled - dendpoint_template.is_global = endpoint_template.is_global - dendpoint_template.version_id = endpoint_template.version_id - dendpoint_template.version_list = endpoint_template.version_list - dendpoint_template.version_info = endpoint_template.version_info - dendpoint_template = self.endpoint_template_manager.create( - dendpoint_template) - endpoint_template.id = dendpoint_template.id - return endpoint_template - - def modify_endpoint_template(self, admin_token, endpoint_template_id, - endpoint_template): - user = self.validate_service_admin_token(admin_token)[1] - - if not isinstance(endpoint_template, EndpointTemplate): - raise fault.BadRequestFault("Expecting a EndpointTemplate") - dendpoint_template = self.endpoint_template_manager.get( - endpoint_template_id) - if not dendpoint_template: - raise fault.ItemNotFoundFault( - "The endpoint template could not be found") - - #Check if the passed service exist. - utils.check_empty_string(endpoint_template.name, - "Expecting Endpoint Template name.") - utils.check_empty_string(endpoint_template.type, - "Expecting Endpoint Template type.") - - dservice = self.service_manager.get(dendpoint_template.service_id) - if not dservice: - raise fault.BadRequestFault( - "A service with that name and type doesn't exist.") - - # Check ownership of the service (or overriding admin rights) - if not self.is_owner(None, user, dservice): - if not self.has_admin_role(admin_token): - raise fault.UnauthorizedFault( - "You do not have ownership of the '%s' service" \ - % dservice.name) - - dendpoint_template.region = endpoint_template.region - dendpoint_template.service_id = dservice.id - dendpoint_template.public_url = endpoint_template.public_url - dendpoint_template.admin_url = endpoint_template.admin_url - dendpoint_template.internal_url = endpoint_template.internal_url - dendpoint_template.enabled = endpoint_template.enabled - dendpoint_template.is_global = endpoint_template.is_global - dendpoint_template.version_id = endpoint_template.version_id - dendpoint_template.version_list = endpoint_template.version_list - dendpoint_template.version_info = endpoint_template.version_info - dendpoint_template = self.endpoint_template_manager.update( - dendpoint_template) - return EndpointTemplate( - dendpoint_template.id, - dendpoint_template.region, - dservice.name, - dservice.type, - dendpoint_template.public_url, - dendpoint_template.admin_url, - dendpoint_template.internal_url, - dendpoint_template.enabled, - dendpoint_template.is_global, - dendpoint_template.version_id, - dendpoint_template.version_list, - dendpoint_template.version_info - ) - - def delete_endpoint_template(self, admin_token, endpoint_template_id): - user = self.validate_service_admin_token(admin_token)[1] - dendpoint_template = self.endpoint_template_manager.get( - endpoint_template_id) - if not dendpoint_template: - raise fault.ItemNotFoundFault( - "The endpoint template could not be found") - - dservice = self.service_manager.get(dendpoint_template.service_id) - if dservice: - # Check ownership of the service (or overriding admin rights) - if not self.is_owner(None, user, dservice): - if not self.has_admin_role(admin_token): - raise fault.UnauthorizedFault( - "You do not have ownership of the '%s' service" \ - % dservice.name) - else: - # Cannot verify service ownership, so verify full admin rights - if not self.has_admin_role(admin_token): - raise fault.UnauthorizedFault( - "You do not have ownership of the '%s' service" \ - % dservice.name) - - #Delete Related endpoints - endpoints = self.endpoint_manager.\ - endpoint_get_by_endpoint_template(endpoint_template_id) - if endpoints is not None: - for endpoint in endpoints: - self.endpoint_manager.delete(endpoint.id) - self.endpoint_template_manager.delete(endpoint_template_id) - - @service_admin_token_validator - def get_endpoint_templates(self, admin_token, marker, limit, url): - dendpoint_templates = self.endpoint_template_manager.get_page(marker, - limit) - ts = self.transform_endpoint_templates(dendpoint_templates) - prev, next = self.endpoint_template_manager.get_page_markers(marker, - limit) - links = self.get_links(url, prev, next, limit) - return EndpointTemplates(ts, links) - - @service_admin_token_validator - def get_endpoint_templates_by_service(self, admin_token, - service_id, marker, limit, url): - dservice = self.service_manager.get(service_id) - if dservice is None: - raise fault.ItemNotFoundFault( - "No service with the id %s found." % service_id) - dendpoint_templates = self.endpoint_template_manager.\ - get_by_service_get_page(service_id, marker, limit) - ts = self.transform_endpoint_templates(dendpoint_templates) - prev, next = self.endpoint_template_manager.\ - get_by_service_get_page_markers(service_id, marker, limit) - links = self.get_links(url, prev, next, limit) - return EndpointTemplates(ts, links) - - def transform_endpoint_templates(self, dendpoint_templates): - ts = [] - for dendpoint_template in dendpoint_templates: - dservice = self.service_manager.get(dendpoint_template.service_id) - ts.append(EndpointTemplate( - dendpoint_template.id, - dendpoint_template.region, - dservice.name, - dservice.type, - dendpoint_template.public_url, - dendpoint_template.admin_url, - dendpoint_template.internal_url, - dendpoint_template.enabled, - dendpoint_template.is_global, - dendpoint_template.version_id, - dendpoint_template.version_list, - dendpoint_template.version_info - )) - return ts - - @service_admin_token_validator - def get_endpoint_template(self, admin_token, endpoint_template_id): - dendpoint_template = self.endpoint_template_manager.get( - endpoint_template_id) - if not dendpoint_template: - raise fault.ItemNotFoundFault( - "The endpoint template could not be found") - dservice = self.service_manager.get(dendpoint_template.service_id) - return EndpointTemplate( - dendpoint_template.id, - dendpoint_template.region, - dservice.name, - dservice.type, - dendpoint_template.public_url, - dendpoint_template.admin_url, - dendpoint_template.internal_url, - dendpoint_template.enabled, - dendpoint_template.is_global, - dendpoint_template.version_id, - dendpoint_template.version_list, - dendpoint_template.version_info - ) - - @service_admin_token_validator - def get_tenant_endpoints(self, admin_token, marker, limit, url, tenant_id): - return self.fetch_tenant_endpoints(marker, limit, - url, tenant_id) - - def fetch_tenant_endpoints(self, marker, limit, url, tenant_id): - if tenant_id is None: - raise fault.BadRequestFault("Expecting a Tenant Id") - - if self.tenant_manager.get(tenant_id) is None: - raise fault.ItemNotFoundFault("The tenant not found") - - ts = [] - - dtenant_endpoints = \ - self.endpoint_manager.endpoint_get_by_tenant_get_page(tenant_id, - marker, limit) - for dtenant_endpoint in dtenant_endpoints: - dendpoint_template = self.endpoint_template_manager.get( - dtenant_endpoint.endpoint_template_id) - dservice = self.service_manager.get(dendpoint_template.service_id) - ts.append(Endpoint( - dtenant_endpoint.id, - dtenant_endpoint.tenant_id, - dendpoint_template.region, - dservice.name, - dservice.type, - dendpoint_template.public_url, - dendpoint_template.admin_url, - dendpoint_template.internal_url, - dendpoint_template.version_id, - dendpoint_template.version_list, - dendpoint_template.version_info - )) - links = [] - if ts.__len__(): - prev, next = \ - self.endpoint_manager.endpoint_get_by_tenant_get_page_markers( - tenant_id, marker, limit) - links = self.get_links(url, prev, next, limit) - return Endpoints(ts, links) - - @service_admin_token_validator - def create_endpoint_for_tenant(self, admin_token, tenant_id, - endpoint_template): - utils.check_empty_string(tenant_id, "Expecting a Tenant Id.") - if self.tenant_manager.get(tenant_id) is None: - raise fault.ItemNotFoundFault("The tenant not found") - - dendpoint_template = self.endpoint_template_manager.get( - endpoint_template.id) - if not dendpoint_template: - raise fault.ItemNotFoundFault( - "The endpoint template could not be found") - dendpoint = models.Endpoints() - dendpoint.tenant_id = tenant_id - dendpoint.endpoint_template_id = endpoint_template.id - dendpoint = self.endpoint_manager.create(dendpoint) - dservice = self.service_manager.get(dendpoint_template.service_id) - dendpoint = Endpoint( - dendpoint.id, - dendpoint.tenant_id, - dendpoint_template.region, - dservice.name, - dservice.type, - dendpoint_template.public_url, - dendpoint_template.admin_url, - dendpoint_template.internal_url, - dendpoint_template.version_id, - dendpoint_template.version_list, - dendpoint_template.version_info - ) - return dendpoint - - @service_admin_token_validator - def delete_endpoint(self, admin_token, endpoint_id): - if self.endpoint_manager.get(endpoint_id) is None: - raise fault.ItemNotFoundFault("The Endpoint is not found.") - self.endpoint_manager.delete(endpoint_id) - return None - - #Service Operations - @service_admin_token_validator - def create_service(self, admin_token, service): - if not isinstance(service, Service): - raise fault.BadRequestFault("Expecting a Service") - - if self.service_manager.get_by_name(service.name) is not None: - raise fault.ServiceConflictFault( - "A service with that name already exists") - - user = self._validate_token(admin_token)[1] - - dservice = models.Service() - dservice.name = service.name - dservice.type = service.type - dservice.desc = service.description - dservice.owner_id = user.id - dservice = self.service_manager.create(dservice) - service.id = dservice.id - - return service - - @service_admin_token_validator - def get_services(self, admin_token, marker, limit, url): - ts = [] - dservices = self.service_manager.get_page(marker, limit) - for dservice in dservices: - ts.append(Service(dservice.id, dservice.name, dservice.type, - dservice.desc)) - prev, next = self.service_manager.get_page_markers(marker, limit) - links = self.get_links(url, prev, next, limit) - return Services(ts, links) - - @service_admin_token_validator - def get_service(self, admin_token, service_id): - dservice = self.service_manager.get(service_id) - if not dservice: - raise fault.ItemNotFoundFault("The service could not be found") - return Service(dservice.id, dservice.name, dservice.type, - dservice.desc) - - @service_admin_token_validator - def get_service_by_name(self, admin_token, service_name): - dservice = self.service_manager.get_by_name(service_name) - if not dservice: - raise fault.ItemNotFoundFault("The service could not be found") - return Service(dservice.id, dservice.name, dservice.type, - dservice.desc) - - @service_admin_token_validator - def delete_service(self, admin_token, service_id): - dservice = self.service_manager.get(service_id) - - if not dservice: - raise fault.ItemNotFoundFault("The service could not be found") - - #Delete Related Endpointtemplates and Endpoints. - endpoint_templates = self.endpoint_template_manager.get_by_service( - service_id) - if endpoint_templates is not None: - for endpoint_template in endpoint_templates: - endpoints = self.endpoint_manager.\ - endpoint_get_by_endpoint_template(endpoint_template.id) - if endpoints is not None: - for endpoint in endpoints: - self.endpoint_manager.delete(endpoint.id) - self.endpoint_template_manager.delete(endpoint_template.id) - #Delete Related Role and RoleRefs - roles = self.role_manager.get_by_service(service_id) - if roles is not None: - for role in roles: - rolegrants = self.grant_manager.rolegrant_list_by_role(role.id) - if rolegrants is not None: - for rolegrant in rolegrants: - self.grant_manager.rolegrant_delete(rolegrant.id) - self.role_manager.delete(role.id) - self.service_manager.delete(service_id) - - @admin_token_validator - def get_credentials(self, admin_token, user_id, marker, limit, url): - ts = [] - duser = self.user_manager.get(user_id) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - ts.append(PasswordCredentials(duser.name, None)) - links = [] - return Credentials(ts, links) - - @admin_token_validator - def get_password_credentials(self, admin_token, user_id): - duser = self.user_manager.get(user_id) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - if not duser.password: - raise fault.ItemNotFoundFault( - "Password credentials could not be found") - return PasswordCredentials(duser.name, None) - - @admin_token_validator - def delete_password_credentials(self, admin_token, user_id): - duser = self.user_manager.get(user_id) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - values = {'id': user_id, 'password': None} - self.user_manager.update(values) - - @admin_token_validator - def update_password_credentials(self, admin_token, user_id, - password_credentials): - duser = self.user_manager.get(user_id) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - - if (password_credentials.user_name is None - or not password_credentials.user_name.strip()): - raise fault.BadRequestFault("Expecting a username.") - duser_name = self.user_manager.get_by_name( - password_credentials.user_name) - if duser_name.id != duser.id: - raise fault.UserConflictFault( - "A user with that name already exists") - values = {'id': user_id, 'password': password_credentials.password, - 'name': password_credentials.user_name} - self.user_manager.update(values) - duser = self.user_manager.get(user_id) - return PasswordCredentials(duser.name, duser.password) - - @admin_token_validator - def create_password_credentials(self, admin_token, user_id, - password_credentials): - duser = self.user_manager.get(user_id) - if not duser: - raise fault.ItemNotFoundFault("The user could not be found") - - if password_credentials.user_name is None or\ - not password_credentials.user_name.strip(): - raise fault.BadRequestFault("Expecting a username.") - - if password_credentials.user_name != duser.name: - duser_name = self.user_manager.get_by_name( - password_credentials.user_name) - if duser_name: - raise fault.UserConflictFault( - "A user with that name already exists") - if duser.password: - raise fault.BadRequestFault( - "Password credentials already available.") - values = {'id': user_id, 'password': password_credentials.password, - 'name': password_credentials.user_name} - self.user_manager.update(values) - duser = self.user_manager.get(user_id) - return PasswordCredentials(duser.name, duser.password) - - @staticmethod - def get_links(url, prev, next, limit): - """Method to form and return pagination links.""" - links = [] - if prev: - links.append(atom.Link('prev', "%s?marker=%s&limit=%s" \ - % (url, prev, limit))) - if next: - links.append(atom.Link('next', "%s?marker=%s&limit=%s" \ - % (url, next, limit))) - return links diff --git a/keystone/logic/signer.py b/keystone/logic/signer.py deleted file mode 100644 index 4ca171be..00000000 --- a/keystone/logic/signer.py +++ /dev/null @@ -1,166 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# 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. -# -# PORTIONS OF THIS FILE ARE FROM: -# http://code.google.com/p/boto -# Copyright (c) 2006-2009 Mitch Garnaat http://garnaat.org/ -# -# Permission is hereby granted, free of charge, to any person obtaining a -# copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, dis- -# tribute, sublicense, and/or sell copies of the Software, and to permit -# persons to whom the Software is furnished to do so, subject to the fol- -# lowing conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- -# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -# IN THE SOFTWARE. - -""" -Utility class for parsing signed AMI manifests. -""" - -import base64 -import hashlib -import hmac -import logging -import urllib - - -LOG = logging.getLogger('keystone.signer') - - -class Signer(object): - """Hacked up code from boto/connection.py""" - - # pylint: disable=E1101 - def __init__(self, secret_key): - secret_key = secret_key.encode() - self.hmac = hmac.new(secret_key, digestmod=hashlib.sha1) - if hashlib.sha256: - self.hmac_256 = hmac.new(secret_key, digestmod=hashlib.sha256) - - def generate(self, credentials, s3=False): - """Generate auth string according to what SignatureVersion is given.""" - if s3: - return self._calc_signature_s3(credentials.verb, - credentials.expire, - credentials.path, - credentials.content_type, - credentials.content_md5, - credentials.xheaders) - if credentials.params['SignatureVersion'] == '0': - return self._calc_signature_0(credentials.params) - if credentials.params['SignatureVersion'] == '1': - return self._calc_signature_1(credentials.params) - if credentials.params['SignatureVersion'] == '2': - return self._calc_signature_2(credentials.params, - credentials.verb, - credentials.host, - credentials.path) - raise Exception('Unknown Signature Version: %s' % - credentials.params['SignatureVersion']) - - @staticmethod - def _get_utf8_value(value): - """Get the UTF8-encoded version of a value.""" - if not isinstance(value, str) and not isinstance(value, unicode): - value = str(value) - if isinstance(value, unicode): - return value.encode('utf-8') - else: - return value - - def _calc_signature_0(self, params): - """Generate AWS signature version 0 string.""" - s = params['Action'] + params['Timestamp'] - self.hmac.update(s) - return base64.b64encode(self.hmac.digest()) - - def _calc_signature_1(self, params): - """Generate AWS signature version 1 string.""" - keys = params.keys() - keys.sort(cmp=lambda x, y: cmp(x.lower(), y.lower())) - for key in keys: - self.hmac.update(key) - val = self._get_utf8_value(params[key]) - self.hmac.update(val) - return base64.b64encode(self.hmac.digest()) - - def _calc_signature_2(self, params, verb, server_string, path): - """Generate AWS signature version 2 string.""" - LOG.debug('using _calc_signature_2') - string_to_sign = '%s\n%s\n%s\n' % (verb, server_string, path) - if self.hmac_256: - current_hmac = self.hmac_256 - params['SignatureMethod'] = 'HmacSHA256' - else: - current_hmac = self.hmac - params['SignatureMethod'] = 'HmacSHA1' - keys = params.keys() - keys.sort() - pairs = [] - for key in keys: - val = self._get_utf8_value(params[key]) - val = urllib.quote(val, safe='-_~') - pairs.append(urllib.quote(key, safe='') + '=' + val) - qs = '&'.join(pairs) - LOG.debug('query string: %s', qs) - string_to_sign += qs - LOG.debug('string_to_sign: %s', string_to_sign) - current_hmac.update(string_to_sign) - b64 = base64.b64encode(current_hmac.digest()) - LOG.debug('len(b64)=%d', len(b64)) - LOG.debug('base64 encoded digest: %s', b64) - return b64 - - # pylint: disable=R0913 - def _calc_signature_s3(self, verb, expire, path, content_type, content_md5, - xheaders): - """Generate AWS signature for S3 string.""" - LOG.debug('using _calc_signature_s3') - string_to_sign = '%s\n%s\n%s\n%s\n' % ( - verb, content_md5, content_type, expire) - if xheaders: - xheader_keys = xheaders.keys() - xheader_keys.sort() - for key in xheader_keys: - string_to_sign += '%s:%s\n' % (key, xheaders[key]) - string_to_sign += path - current_hmac = self.hmac - LOG.debug('string_to_sign: %s', string_to_sign) - current_hmac.update(string_to_sign) - b64 = base64.b64encode(current_hmac.digest()) - LOG.debug('len(b64)=%d', len(b64)) - LOG.debug('base64 encoded digest: %s', b64) - return b64 - - -if __name__ == '__main__': - # pylint: disable=E1121 - print Signer('foo').generate({'SignatureMethod': 'HmacSHA256', - 'SignatureVersion': '2'}, - 'get', 'server', '/foo') diff --git a/keystone/logic/types/__init__.py b/keystone/logic/types/__init__.py deleted file mode 100644 index e69de29b..00000000 --- a/keystone/logic/types/__init__.py +++ /dev/null diff --git a/keystone/logic/types/atom.py b/keystone/logic/types/atom.py deleted file mode 100644 index a4e03a9f..00000000 --- a/keystone/logic/types/atom.py +++ /dev/null @@ -1,57 +0,0 @@ -# Copyright (c) 2010-2011 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. - -# pylint: disable=C0103 - -from lxml import etree - - -class Link(object): - """An atom link""" - - def __init__(self, rel, href, link_type=None, hreflang=None, title=None): - self.rel = rel - self.href = href - self.link_type = link_type - self.hreflang = hreflang - self.title = title - - def to_dict(self): - links = {} - if self.link_type: - links["link_type"] = self.link_type - if self.hreflang: - links["hreflang"] = self.hreflang - if self.title: - links["title"] = self.title - - links["rel"] = self.rel - links["href"] = self.href - return {'links': links} - - def to_dom(self): - ATOM_NAMESPACE = "http://www.w3.org/2005/Atom" - ATOM = "{%s}" % ATOM_NAMESPACE - NSMAP = {'atom': ATOM_NAMESPACE} - dom = etree.Element(ATOM + "link", nsmap=NSMAP) - if self.link_type: - dom.set("link_type", self.link_type) - if self.link_type: - dom.set("hreflang", self.hreflang) - if self.title: - dom.set("title", self.title) - dom.set("rel", self.rel) - dom.set("href", self.href) - return dom diff --git a/keystone/logic/types/auth.py b/keystone/logic/types/auth.py deleted file mode 100755 index ace6aa6b..00000000 --- a/keystone/logic/types/auth.py +++ /dev/null @@ -1,673 +0,0 @@ -# Copyright (c) 2010-2011 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. - -# pylint: disable=C0103,R0912,R0913,R0914 - -import json -from lxml import etree -from keystone.logic.types import fault -import keystone.backends.api as db_api -from keystone import utils - - -class AuthBase(object): - def __init__(self, tenant_id=None, tenant_name=None): - self.tenant_id = tenant_id - self.tenant_name = tenant_name - - @staticmethod - def _validate_auth(obj, *valid_keys): - if not 'auth' in obj: - raise fault.BadRequestFault('Expecting auth') - - auth = obj.get('auth') - - for key in auth: - if not key in valid_keys: - raise fault.BadRequestFault('Invalid attribute(s): %s' % key) - - if auth.get('tenantId') and auth.get('tenantName'): - raise fault.BadRequestFault( - 'Expecting either Tenant ID or Tenant Name, but not both') - - return auth - - @staticmethod - def _validate_key(obj, key, *required_keys): - if not key in obj: - raise fault.BadRequestFault('Expecting %s' % key) - - ret = obj[key] - - for skey in ret: - if not skey in required_keys: - raise fault.BadRequestFault('Invalid attribute(s): %s' % skey) - - for required_key in required_keys: - if not ret.get(required_key): - raise fault.BadRequestFault('Expecting %s:%s' % - (key, required_key)) - return ret - - -class AuthWithUnscopedToken(AuthBase): - def __init__(self, token_id, tenant_id=None, tenant_name=None): - super(AuthWithUnscopedToken, self).__init__(tenant_id, tenant_name) - self.token_id = token_id - - @staticmethod - def from_xml(xml_str): - try: - dom = etree.Element("root") - dom.append(etree.fromstring(xml_str)) - root = dom.find("{http://docs.openstack.org/identity/api/v2.0}" - "auth") - if root is None: - raise fault.BadRequestFault("Expecting auth") - token = root.find("{http://docs.openstack.org/identity/api/v2.0}" - "token") - if token is None: - raise fault.BadRequestFault("Expecting token") - - token_id = token.get("id") - tenant_id = root.get("tenantId") - tenant_name = root.get("tenantName") - utils.check_empty_string(token_id, "Expecting a token id.") - if tenant_id and tenant_name: - raise fault.BadRequestFault( - "Expecting either Tenant ID or Tenant Name, but not both") - - return AuthWithUnscopedToken(token_id, tenant_id, tenant_name) - except etree.LxmlError as e: - raise fault.BadRequestFault("Cannot parse password access", str(e)) - - @staticmethod - def from_json(json_str): - try: - obj = json.loads(json_str) - - auth = AuthBase._validate_auth(obj, 'tenantId', 'tenantName', - 'token') - token = AuthBase._validate_key(auth, 'token', 'id') - - return AuthWithUnscopedToken(token['id'], - auth.get('tenantId'), - auth.get('tenantName')) - except (ValueError, TypeError) as e: - raise fault.BadRequestFault("Cannot parse auth", str(e)) - - -class AuthWithPasswordCredentials(AuthBase): - def __init__(self, username, password, tenant_id=None, tenant_name=None): - super(AuthWithPasswordCredentials, self).__init__(tenant_id, - tenant_name) - self.username = username - self.password = password - - @staticmethod - def from_xml(xml_str): - try: - dom = etree.Element("root") - dom.append(etree.fromstring(xml_str)) - root = dom.find("{http://docs.openstack.org/identity/api/v2.0}" - "auth") - if root is None: - raise fault.BadRequestFault("Expecting auth") - tenant_id = root.get("tenantId") - tenant_name = root.get("tenantName") - password_credentials = \ - root.find("{http://docs.openstack.org/identity/api/v2.0}" - "passwordCredentials") - if password_credentials is None: - raise fault.BadRequestFault("Expecting passwordCredentials") - username = password_credentials.get("username") - utils.check_empty_string(username, "Expecting a username") - password = password_credentials.get("password") - utils.check_empty_string(password, "Expecting a password") - - if tenant_id and tenant_name: - raise fault.BadRequestFault( - "Expecting either Tenant ID or Tenant Name, but not both") - - return AuthWithPasswordCredentials(username, password, tenant_id, - tenant_name) - except etree.LxmlError as e: - raise fault.BadRequestFault("Cannot parse password access", str(e)) - - @staticmethod - def from_json(json_str): - try: - obj = json.loads(json_str) - - auth = AuthBase._validate_auth(obj, 'tenantId', 'tenantName', - 'passwordCredentials', 'token') - cred = AuthBase._validate_key(auth, 'passwordCredentials', - 'username', 'password') - - return AuthWithPasswordCredentials(cred['username'], - cred['password'], - auth.get('tenantId'), - auth.get('tenantName')) - except (ValueError, TypeError) as e: - raise fault.BadRequestFault("Cannot parse auth", str(e)) - - -class Ec2Credentials(object): - """Credentials based on username, access_key, signature and data. - - @type access: str - @param access: Access key for user in the form of access:project. - - @type signature: str - @param signature: Signature of the request. - - @type params: dictionary of str - @param params: Web paramaters used for the signature. - - @type verb: str - @param verb: Web request verb ('GET' or 'POST'). - - @type host: str - @param host: Web request host string (including port). - - @type path: str - @param path: Web request path. - - """ - - def __init__(self, access, signature, verb, - host, path, params): - self.access = access - self.signature = signature - self.verb = verb - self.host = host - self.path = path - self.params = params - - @staticmethod - def from_xml(xml_str): - try: - dom = etree.Element("root") - dom.append(etree.fromstring(xml_str)) - root = dom.find("{http://docs.openstack.org/identity/api/v2.0}" - "auth") - xmlns = "http://docs.openstack.org/identity/api/ext/OS-KSEC2/v1.0" - if root is None: - root = dom.find("{%s}ec2Credentials" % xmlns) - else: - root = root.find("{%s}ec2Credentials" % xmlns) - if root is None: - raise fault.BadRequestFault("Expecting ec2Credentials") - access = root.get("key") - utils.check_empty_string(access, "Expecting an access key.") - signature = root.get("signature") - utils.check_empty_string(signature, "Expecting a signature.") - verb = root.get("verb") - utils.check_empty_string(verb, "Expecting a verb.") - host = root.get("host") - utils.check_empty_string(signature, "Expecting a host.") - path = root.get("path") - utils.check_empty_string(signature, "Expecting a path.") - # TODO(vish): parse xml params - params = {} - return Ec2Credentials(access, signature, verb, host, path, params) - except etree.LxmlError as e: - raise fault.BadRequestFault("Cannot parse password credentials", - str(e)) - - @staticmethod - def from_json(json_str): - try: - root = json.loads(json_str) - if "auth" in root: - obj = root['auth'] - else: - obj = root - - if "OS-KSEC2:ec2Credentials" in obj: - cred = obj["OS-KSEC2:ec2Credentials"] - elif "ec2Credentials" in obj: - cred = obj["ec2Credentials"] - else: - raise fault.BadRequestFault("Expecting ec2Credentials") - # Check that fields are valid - invalid = [key for key in cred if key not in\ - ['username', 'access', 'signature', 'params', - 'verb', 'host', 'path']] - if invalid != []: - raise fault.BadRequestFault("Invalid attribute(s): %s" - % invalid) - if not "access" in cred: - raise fault.BadRequestFault("Expecting an access key") - access = cred["access"] - if not "signature" in cred: - raise fault.BadRequestFault("Expecting a signature") - signature = cred["signature"] - if not "verb" in cred: - raise fault.BadRequestFault("Expecting a verb") - verb = cred["verb"] - if not "host" in cred: - raise fault.BadRequestFault("Expecting a host") - host = cred["host"] - if not "path" in cred: - raise fault.BadRequestFault("Expecting a path") - path = cred["path"] - if not "params" in cred: - raise fault.BadRequestFault("Expecting params") - params = cred["params"] - return Ec2Credentials(access, signature, verb, host, path, params) - except (ValueError, TypeError) as e: - raise fault.BadRequestFault("Cannot parse password credentials", - str(e)) - - -# pylint: disable=R0902 -class S3Credentials(object): - """Credentials based on username, access_key, signature and data. - - @type access: str - @param access: Access key for user in the form of access:project. - - @type signature: str - @param signature: Signature of the request. - - @type verb: str - @param verb: Web request verb ('GET' or 'POST'). - - @type host: expire - @param host: Web request expire time. - - @type path: str - @param path: Web request path. - - @type expire: str - @param expire: Web request expire. - - @type content_type: str - @param content_type: Web request content contenttype. - - @type content_md5: str - @param content_md5: Web request content contentmd5. - - @type xheaders: str - @param xheaders: Web request content extended headers. - - """ - - def __init__(self, access, signature, verb, path, expire, content_type, - content_md5, xheaders): - self.access = access - self.signature = signature - self.verb = verb - self.path = path - self.expire = expire - self.content_type = content_type - self.content_md5 = content_md5 - self.xheaders = xheaders - - @staticmethod - def from_xml(xml_str): - try: - dom = etree.Element("root") - dom.append(etree.fromstring(xml_str)) - root = dom.find("{http://docs.openstack.org/identity/api/v2.0}" - "auth") - xmlns = "http://docs.openstack.org/identity/api/ext/OS-KSS3/v1.0" - if root is None: - root = dom.find("{%s}s3Credentials" % xmlns) - else: - root = root.find("{%s}s3Credentials" % xmlns) - - if root is None: - raise fault.BadRequestFault("Expecting s3Credentials") - access = root.get("access") - if access == None: - raise fault.BadRequestFault("Expecting an access key") - signature = root.get("signature") - if signature == None: - raise fault.BadRequestFault("Expecting a signature") - verb = root.get("verb") - if verb == None: - raise fault.BadRequestFault("Expecting a verb") - path = root.get("path") - if path == None: - raise fault.BadRequestFault("Expecting a path") - expire = root.get("expire") - if expire == None: - raise fault.BadRequestFault("Expecting a expire") - content_type = root.get("content_type", '') - content_md5 = root.get("content_md5", '') - xheaders = root.get("xheaders", None) - return S3Credentials(access, signature, verb, path, expire, - content_type, content_md5, xheaders) - except etree.LxmlError as e: - raise fault.BadRequestFault("Cannot parse password credentials", - str(e)) - - @staticmethod - def from_json(json_str): - try: - root = json.loads(json_str) - if "auth" in root: - obj = root['auth'] - else: - obj = root - - if "OS-KSS3:s3Credentials" in obj: - cred = obj["OS-KSS3:s3Credentials"] - elif "s3Credentials" in obj: - cred = obj["s3Credentials"] - else: - raise fault.BadRequestFault("Expecting s3Credentials") - - # Check that fields are valid - invalid = [key for key in cred if key not in\ - ['username', 'access', 'signature', 'verb', 'expire', - 'path', 'content_type', 'content_md5', 'xheaders']] - if invalid != []: - raise fault.BadRequestFault("Invalid attribute(s): %s" - % invalid) - if not "access" in cred: - raise fault.BadRequestFault("Expecting an access key") - access = cred["access"] - if not "signature" in cred: - raise fault.BadRequestFault("Expecting a signature") - signature = cred["signature"] - if not "verb" in cred: - raise fault.BadRequestFault("Expecting a verb") - verb = cred["verb"] - if not "path" in cred: - raise fault.BadRequestFault("Expecting a path") - path = cred["path"] - if not "expire" in cred: - raise fault.BadRequestFault("Expecting a expire") - expire = cred["expire"] - content_type = cred.get("content_type", '') - content_md5 = cred.get("content_md5", '') - xheaders = cred.get("xheaders", None) - return S3Credentials(access, signature, verb, path, expire, - content_type, content_md5, xheaders) - except (ValueError, TypeError) as e: - raise fault.BadRequestFault("Cannot parse password credentials", - str(e)) - - -class Tenant(object): - """Provides the scope of a token""" - - def __init__(self, id, name): - self.id = id - self.name = name - - -class Token(object): - """An auth token.""" - - def __init__(self, expires, token_id, tenant=None): - assert tenant is None or isinstance(tenant, Tenant) - - self.expires = expires - self.id = token_id - self.tenant = tenant - - -class User(object): - """A user.""" - - id = None - username = None - tenant_id = None - tenant_name = None - rolegrants = None - - def __init__(self, id, username, tenant_id, tenant_name, rolegrants=None): - self.id = id - self.username = username - self.tenant_id = tenant_id - self.tenant_name = tenant_name - self.rolegrants = rolegrants - - -class AuthData(object): - """Authentation Information returned upon successful login. - - This class handles rendering to JSON and XML. It renders - the token, the user data, the roles, and the service catalog. - - The list of endpoint URLs in the service catalog can be filtered by - URL type. For example, when we respond to a public call from a user - without elevated privileges, the "adminURL" is not returned. The - url_types paramater in the initializer lists the types to return. - The actual authorization is done in logic/service.py - """ - - def __init__(self, token, user, base_urls=None, url_types=None): - self.token = token - self.user = user - self.base_urls = base_urls - if url_types is None: - self.url_types = ["internal", "public", "admin"] - else: - self.url_types = url_types - self.d = {} - if self.base_urls is not None: - self.__convert_baseurls_to_dict() - - def to_xml(self): - dom = etree.Element("access", - xmlns="http://docs.openstack.org/identity/api/v2.0") - token = etree.Element("token", - expires=self.token.expires.isoformat()) - token.set("id", self.token.id) - if self.token.tenant: - tenant = etree.Element("tenant", - id=unicode(self.token.tenant.id), - name=unicode(self.token.tenant.name)) - token.append(tenant) - dom.append(token) - - user = etree.Element("user", - id=unicode(self.user.id), - name=unicode(self.user.username)) - dom.append(user) - - if self.user.rolegrants is not None: - user.append(self.user.rolegrants.to_dom()) - - if self.base_urls is not None and len(self.base_urls) > 0: - service_catalog = etree.Element("serviceCatalog") - for key, key_base_urls in self.d.items(): - dservice = db_api.SERVICE.get(key) - if not dservice: - raise fault.ItemNotFoundFault( - "The service could not be found") - service = etree.Element("service", - name=dservice.name, type=dservice.type) - for base_url in key_base_urls: - include_this_endpoint = False - endpoint = etree.Element("endpoint") - if base_url.region: - endpoint.set("region", base_url.region) - for url_kind in self.url_types: - base_url_item = getattr(base_url, url_kind + "_url") - if base_url_item: - if '%tenant_id%' in base_url_item: - if self.token.tenant: - # Don't return tenant endpoints if token - # not scoped to a tenant - endpoint.set(url_kind + "URL", - base_url_item.replace('%tenant_id%', - str(self.token.tenant.id))) - endpoint.set('tenantId', - str(self.token.tenant.id)) - include_this_endpoint = True - else: - endpoint.set(url_kind + "URL", base_url_item) - include_this_endpoint = True - if include_this_endpoint: - endpoint.set("id", str(base_url.id)) - if hasattr(base_url, "version_id"): - if base_url.version_id: - endpoint.set("versionId", - str(base_url.version_id)) - service.append(endpoint) - if service.find("endpoint") is not None: - service_catalog.append(service) - dom.append(service_catalog) - return etree.tostring(dom) - - def __convert_baseurls_to_dict(self): - for base_url in self.base_urls: - if base_url.service_id not in self.d: - self.d[base_url.service_id] = list() - self.d[base_url.service_id].append(base_url) - - def to_json(self): - token = {} - token["id"] = self.token.id - token["expires"] = self.token.expires.isoformat() - if self.token.tenant: - tenant = { - 'id': unicode(self.token.tenant.id), - 'name': unicode(self.token.tenant.name)} - token['tenant'] = tenant # v2.0/Diablo contract - token['tenants'] = [tenant] # missed use case in v2.0 - auth = {} - auth["token"] = token - auth['user'] = { - 'id': unicode(self.user.id), - 'name': unicode(self.user.username)} - - if self.user.rolegrants is not None: - auth['user']["roles"] = self.user.rolegrants.to_json_values() - - if self.base_urls is not None and len(self.base_urls) > 0: - service_catalog = [] - for key, key_base_urls in self.d.items(): - service = {} - endpoints = [] - for base_url in key_base_urls: - include_this_endpoint = False - endpoint = {} - if base_url.region: - endpoint["region"] = base_url.region - for url_kind in self.url_types: - base_url_item = getattr(base_url, url_kind + "_url") - if base_url_item: - if '%tenant_id%' in base_url_item: - if self.token.tenant: - # Don't return tenant endpoints if token - # not scoped to a tenant - endpoint[url_kind + "URL"] = \ - base_url_item.replace('%tenant_id%', - str(self.token.tenant.id)) - endpoint['tenantId'] = \ - str(self.token.tenant.id) - include_this_endpoint = True - else: - endpoint[url_kind + "URL"] = base_url_item - include_this_endpoint = True - if include_this_endpoint: - endpoint['id'] = str(base_url.id) - if hasattr(base_url, 'version_id'): - if base_url.version_id: - endpoint['versionId'] = \ - str(base_url.version_id) - endpoints.append(endpoint) - dservice = db_api.SERVICE.get(key) - if not dservice: - raise fault.ItemNotFoundFault( - "The service could not be found for" + str(key)) - if len(endpoints): - service["name"] = dservice.name - service["type"] = dservice.type - service["endpoints"] = endpoints - service_catalog.append(service) - auth["serviceCatalog"] = service_catalog - ret = {} - ret["access"] = auth - return json.dumps(ret) - - -class ValidateData(object): - """Authentation Information returned upon successful token validation.""" - - token = None - user = None - - def __init__(self, token, user): - self.token = token - self.user = user - - def to_xml(self): - dom = etree.Element("access", - xmlns="http://docs.openstack.org/identity/api/v2.0") - - token = etree.Element("token", - id=unicode(self.token.id), - expires=self.token.expires.isoformat()) - - if self.token.tenant: - tenant = etree.Element("tenant", - id=unicode(self.token.tenant.id), - name=unicode(self.token.tenant.name)) - token.append(tenant) - - user = etree.Element("user", - id=unicode(self.user.id), - name=unicode(self.user.username)) - - if self.user.tenant_id is not None: - user.set('tenantId', unicode(self.user.tenant_id)) - if self.user.tenant_name is not None: - user.set('tenantName', unicode(self.user.tenant_name)) - - if self.user.rolegrants is not None: - user.append(self.user.rolegrants.to_dom()) - - dom.append(token) - dom.append(user) - return etree.tostring(dom) - - def to_json(self): - token = { - "id": unicode(self.token.id), - "expires": self.token.expires.isoformat()} - - if self.token.tenant: - tenant = { - 'id': unicode(self.token.tenant.id), - 'name': unicode(self.token.tenant.name)} - token['tenant'] = tenant # v2.0/Diablo contract - token['tenants'] = [tenant] # missed use case in v2.0 - - user = { - "id": unicode(self.user.id), - "name": unicode(self.user.username), - # TODO(ziad) temporary until we are comfortable clients are updated - "username": unicode(self.user.username)} - - if self.user.tenant_id is not None: - user['tenantId'] = unicode(self.user.tenant_id) - if self.user.tenant_name is not None: - user['tenantName'] = unicode(self.user.tenant_name) - - if self.user.rolegrants is not None: - user["roles"] = self.user.rolegrants.to_json_values() - - return json.dumps({ - "access": { - "token": token, - "user": user}}) diff --git a/keystone/logic/types/credential.py b/keystone/logic/types/credential.py deleted file mode 100644 index 40e99f3c..00000000 --- a/keystone/logic/types/credential.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) 2010-2011 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 json -from lxml import etree - -from keystone.logic.types import fault -from keystone import utils - - -class PasswordCredentials(object): - def __init__(self, user_name, password): - self.user_name = user_name - self.password = password - - @staticmethod - def from_xml(xml_str): - try: - dom = etree.Element("root") - dom.append(etree.fromstring(xml_str)) - root = dom.find("{http://docs.openstack.org/identity/api/v2.0}" \ - "passwordCredentials") - if root is None: - raise fault.BadRequestFault("Expecting passwordCredentials") - user_name = root.get("username") - password = root.get("password") - utils.check_empty_string(password, "Expecting a password.") - return PasswordCredentials(user_name, password) - except etree.LxmlError as e: - raise fault.BadRequestFault( - "Cannot parse passwordCredentials", str(e)) - - @staticmethod - def from_json(json_str): - try: - obj = json.loads(json_str) - if not "passwordCredentials" in obj: - raise fault.BadRequestFault("Expecting passwordCredentials") - password_credentials = obj["passwordCredentials"] - - # Check that fields are valid - invalid = [key for key in password_credentials if key not in\ - ['username', 'password']] - if invalid != []: - raise fault.BadRequestFault("Invalid attribute(s): %s" - % invalid) - - user_name = password_credentials.get('username') - password = password_credentials.get('password') - utils.check_empty_string(password, "Expecting a password.") - return PasswordCredentials(user_name, password) - except (ValueError, TypeError) as e: - raise fault.BadRequestFault( - "Cannot parse passwordCredentials", str(e)) - - def to_dom(self): - dom = etree.Element("passwordCredentials", - xmlns="http://docs.openstack.org/identity/api/v2.0") - if self.user_name: - dom.set("username", unicode(self.user_name)) - if self.password: - dom.set("password", unicode(self.password)) - return dom - - def to_xml(self): - return etree.tostring(self.to_dom()) - - def to_dict(self): - password_credentials = {} - if self.user_name: - password_credentials["username"] = unicode(self.user_name) - if self.password: - password_credentials['password'] = unicode(self.password) - return {'passwordCredentials': password_credentials} - - def to_json(self): - return json.dumps(self.to_dict()) - - -class Credentials(object): - "A collection of credentials." - - def __init__(self, values, links): - self.values = values - self.links = links - - def to_xml(self): - dom = etree.Element("credentials") - dom.set(u"xmlns", "http://docs.openstack.org/identity/api/v2.0") - - for t in self.values: - dom.append(t.to_dom()) - - for t in self.links: - dom.append(t.to_dom()) - - return etree.tostring(dom) - - def to_dom(self): - dom = etree.Element("credentials") - dom.set(u"xmlns", "http://docs.openstack.org/identity/api/v2.0") - - for t in self.values: - dom.append(t.to_dom()) - - for t in self.links: - dom.append(t.to_dom()) - - return dom - - def to_json(self): - values = [t.to_dict() for t in self.values] - links = [t.to_dict()["links"] for t in self.links] - return json.dumps({"credentials": values, "credentials_links": links}) diff --git a/keystone/logic/types/endpoint.py b/keystone/logic/types/endpoint.py deleted file mode 100644 index 3b4c8d64..00000000 --- a/keystone/logic/types/endpoint.py +++ /dev/null @@ -1,363 +0,0 @@ -# Copyright (c) 2010-2011 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 json -from lxml import etree -from keystone.logic.types import fault - - -class EndpointTemplate(object): - """Document me!""" - - @staticmethod - def from_xml(xml_str): - try: - dom = etree.Element("root") - dom.append(etree.fromstring(xml_str)) - root = dom.find( - "{http://docs.openstack.org/identity"\ - "/api/ext/OS-KSCATALOG/v1.0}" \ - "endpointTemplate") - if root is None: - raise fault.BadRequestFault("Expecting endpointTemplate") - id = root.get("id") - region = root.get("region") - name = root.get("name") - type = root.get("type") - public_url = root.get("publicURL") - admin_url = root.get("adminURL") - internal_url = root.get("internalURL") - enabled = root.get("enabled") - is_global = root.get("global") - version = root.find( - "{http://docs.openstack.org/identity/"\ - "api/v2.0}" \ - "version") - version_id = None - version_info = None - version_list = None - if version is not None: - if version.get('id'): - version_id = version.get("id") - if version.get('info'): - version_info = version.get("info") - if version.get('list'): - version_list = version.get("list") - - return EndpointTemplate(id, region, - name, type, public_url, admin_url, - internal_url, enabled, is_global, - version_id, version_list, version_info) - except etree.LxmlError as e: - raise fault.BadRequestFault("Cannot parse endpointTemplate", - str(e)) - - @staticmethod - def from_json(json_str): - try: - obj = json.loads(json_str) - region = None - name = None - type = None - public_url = None - admin_url = None - internal_url = None - enabled = None - is_global = None - version_id = None - version_list = None - version_info = None - if not "OS-KSCATALOG:endpointTemplate" in obj: - raise fault.BadRequestFault( - "Expecting OS-KSCATALOG:endpointTemplate") - endpoint_template = obj["OS-KSCATALOG:endpointTemplate"] - - # Check that fields are valid - invalid = [key for key in endpoint_template if key not in - ['id', 'region', 'name', 'type', 'publicURL', - 'adminURL', 'internalURL', 'enabled', 'global', - 'versionId', 'versionInfo', 'versionList']] - if invalid != []: - raise fault.BadRequestFault("Invalid attribute(s): %s" - % invalid) - - if not "id" in endpoint_template: - id = None - else: - id = endpoint_template["id"] - - if 'region' in endpoint_template: - region = endpoint_template["region"] - if 'name' in endpoint_template: - name = endpoint_template["name"] - if 'type' in endpoint_template: - type = endpoint_template["type"] - if 'publicURL' in endpoint_template: - public_url = endpoint_template["publicURL"] - if 'adminURL' in endpoint_template: - admin_url = endpoint_template["adminURL"] - if 'internalURL' in endpoint_template: - internal_url = endpoint_template["internalURL"] - if 'enabled' in endpoint_template: - enabled = endpoint_template["enabled"] - if 'global' in endpoint_template: - is_global = endpoint_template["global"] - if 'versionId' in endpoint_template: - version_id = endpoint_template["versionId"] - else: - version_id = None - if 'versionInfo' in endpoint_template: - version_info = endpoint_template["versionInfo"] - else: - version_info = None - if 'versionList' in endpoint_template: - version_list = endpoint_template["versionList"] - else: - version_list = None - - return EndpointTemplate( - id, region, name, type, public_url, admin_url, - internal_url, enabled, is_global, version_id, - version_list, version_info) - except (ValueError, TypeError) as e: - raise fault.BadRequestFault(\ - "Cannot parse endpointTemplate", str(e)) - - def __init__(self, id, region, name, type, public_url, admin_url, - internal_url, enabled, is_global, - version_id=None, version_list=None, version_info=None): - self.id = id - self.region = region - self.name = name - self.type = type - self.public_url = public_url - self.admin_url = admin_url - self.internal_url = internal_url - self.enabled = bool(enabled) - self.is_global = bool(is_global) - self.version_id = version_id - self.version_list = version_list - self.version_info = version_info - - def to_dom(self): - dom = etree.Element("endpointTemplate", - xmlns="http://docs.openstack.org/" - "identity/api/ext/OS-KSCATALOG/v1.0") - if self.id: - dom.set("id", str(self.id)) - if self.region: - dom.set("region", self.region) - if self.name: - dom.set("name", str(self.name)) - if self.type: - dom.set("type", str(self.type)) - if self.public_url: - dom.set("publicURL", self.public_url) - if self.admin_url: - dom.set("adminURL", self.admin_url) - if self.internal_url: - dom.set("internalURL", self.internal_url) - if self.enabled: - dom.set("enabled", str(self.enabled).lower()) - if self.is_global: - dom.set("global", str(self.is_global).lower()) - version = etree.Element("version", - xmlns="http://docs.openstack.org" - "/identity/api/v2.0") - if self.version_id: - version.set("id", self.version_id) - if self.version_info: - version.set("info", self.version_info) - if self.version_list: - version.set("list", self.version_list) - dom.append(version) - return dom - - def to_xml(self): - return etree.tostring(self.to_dom()) - - def to_dict(self): - endpoint_template = {} - if self.id: - endpoint_template["id"] = unicode(self.id) - if self.region: - endpoint_template["region"] = self.region - if self.name: - endpoint_template["name"] = self.name - if self.type: - endpoint_template["type"] = self.type - if self.public_url: - endpoint_template["publicURL"] = self.public_url - if self.admin_url: - endpoint_template["adminURL"] = self.admin_url - if self.internal_url: - endpoint_template["internalURL"] = self.internal_url - if self.enabled: - endpoint_template["enabled"] = self.enabled - if self.is_global: - endpoint_template["global"] = self.is_global - if self.version_id: - endpoint_template["versionId"] = self.version_id - if self.version_info: - endpoint_template["versionInfo"] = self.version_info - if self.version_list: - endpoint_template["versionList"] = self.version_list - return {'OS-KSCATALOG:endpointTemplate': endpoint_template} - - def to_json(self): - return json.dumps(self.to_dict()) - - -class EndpointTemplates(object): - """A collection of endpointTemplates.""" - - def __init__(self, values, links): - self.values = values - self.links = links - - def to_xml(self): - dom = etree.Element("endpointTemplates") - dom.set(u"xmlns", - "http://docs.openstack.org/identity/api/ext/OS-KSCATALOG/v1.0") - - for t in self.values: - dom.append(t.to_dom()) - - for t in self.links: - dom.append(t.to_dom()) - - return etree.tostring(dom) - - def to_json(self): - values = [t.to_dict()["OS-KSCATALOG:endpointTemplate"] - for t in self.values] - links = [t.to_dict()["links"] for t in self.links] - return json.dumps({"OS-KSCATALOG:endpointTemplates": values, - "OS-KSCATALOG:endpointTemplates_links": links}) - - -class Endpoint(object): - """Document me!""" - - def __init__(self, id, tenant_id, region, - name, type, public_url, admin_url, - internal_url, version_id=None, - version_list=None, version_info=None): - self.id = id - self.tenant_id = tenant_id - self.region = region - self.name = name - self.type = type - self.public_url = self.substitute_tenant_id(public_url) - self.admin_url = self.substitute_tenant_id(admin_url) - self.internal_url = self.substitute_tenant_id(internal_url) - self.version_id = version_id - self.version_list = version_list - self.version_info = version_info - - def to_dom(self): - dom = etree.Element("endpoint", - xmlns="http://docs.openstack.org/identity/api/v2.0") - if self.id: - dom.set("id", str(self.id)) - if self.tenant_id: - dom.set("tenantId", str(self.tenant_id)) - if self.region: - dom.set("region", self.region) - if self.name: - dom.set("name", str(self.name)) - if self.type: - dom.set("type", str(self.type)) - if self.public_url: - dom.set("publicURL", self.public_url) - if self.admin_url: - dom.set("adminURL", self.admin_url) - if self.internal_url: - dom.set("internalURL", self.internal_url) - version = etree.Element("version", - xmlns="http://docs.openstack.org" - "/identity/api/v2.0") - if self.version_id: - version.set("id", self.version_id) - if self.version_info: - version.set("info", self.version_info) - if self.version_list: - version.set("list", self.version_list) - dom.append(version) - return dom - - def to_xml(self): - return etree.tostring(self.to_dom()) - - def to_dict(self): - endpoint = {} - if self.id: - endpoint["id"] = self.id - if self.tenant_id: - endpoint["tenantId"] = self.tenant_id - if self.region: - endpoint["region"] = self.region - if self.name: - endpoint["name"] = self.name - if self.type: - endpoint["type"] = self.type - if self.public_url: - endpoint["publicURL"] = self.public_url - if self.admin_url: - endpoint["adminURL"] = self.admin_url - if self.internal_url: - endpoint["internalURL"] = self.internal_url - if self.version_id: - endpoint["versionId"] = self.version_id - if self.version_info: - endpoint["versionInfo"] = self.version_info - if self.version_list: - endpoint["versionList"] = self.version_list - return {'endpoint': endpoint} - - def substitute_tenant_id(self, url): - if url: - return url.replace('%tenant_id%', - str(self.tenant_id)) - return url - - def to_json(self): - return json.dumps(self.to_dict()) - - -class Endpoints(object): - """A collection of endpoints.""" - - def __init__(self, values, links): - self.values = values - self.links = links - - def to_xml(self): - dom = etree.Element("endpoints") - dom.set(u"xmlns", - "http://docs.openstack.org/identity/api/v2.0") - - for t in self.values: - dom.append(t.to_dom()) - - for t in self.links: - dom.append(t.to_dom()) - - return etree.tostring(dom) - - def to_json(self): - values = [t.to_dict()["endpoint"] for t in self.values] - links = [t.to_dict()["links"] for t in self.links] - return json.dumps({"endpoints": values, "endpoints_links": links}) diff --git a/keystone/logic/types/extension.py b/keystone/logic/types/extension.py deleted file mode 100644 index bc8bab59..00000000 --- a/keystone/logic/types/extension.py +++ /dev/null @@ -1,12 +0,0 @@ -class Extensions(object): - """An extensions type to hold static extensions content.""" - - def __init__(self, json_content, xml_content): - self.xml_content = xml_content - self.json_content = json_content - - def to_json(self): - return self.json_content - - def to_xml(self): - return self.xml_content diff --git a/keystone/logic/types/fault.py b/keystone/logic/types/fault.py deleted file mode 100755 index 9ef4f8f0..00000000 --- a/keystone/logic/types/fault.py +++ /dev/null @@ -1,165 +0,0 @@ -# Copyright (c) 2010-2011 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 json -from lxml import etree - - -class IdentityFault(Exception): - """Base Exception type for all auth exceptions""" - - def __init__(self, msg, details=None, code=500): - self.args = (code, msg, details) - self.code = code - self.msg = msg - self.details = details - self.key = "IdentityFault" - - @property - def message(self): - return self.msg - - def to_xml(self): - dom = etree.Element(self.key, - xmlns="http://docs.openstack.org/identity/api/v2.0") - dom.set("code", str(self.code)) - msg = etree.Element("message") - msg.text = self.msg - dom.append(msg) - if self.details and len(self.details.strip()): - desc = etree.Element("details") - desc.text = self.details - dom.append(desc) - return etree.tostring(dom) - - def to_json(self): - fault = {} - fault["message"] = self.msg - fault["code"] = str(self.code) - if self.details and len(self.details.strip()): - fault["details"] = self.details - ret = {} - ret[self.key] = fault - return json.dumps(ret) - - -class ServiceUnavailableFault(IdentityFault): - """The auth service is unavailable""" - - def __init__(self, msg, details=None, code=503): - super(ServiceUnavailableFault, self).__init__(msg, details, code) - self.key = "serviceUnavailable" - - -class BadRequestFault(IdentityFault): - """Bad user request""" - - def __init__(self, msg, details=None, code=400): - super(BadRequestFault, self).__init__(msg, details, code) - self.key = "badRequest" - - -class UnauthorizedFault(IdentityFault): - """User is unauthorized""" - - def __init__(self, msg, details=None, code=401): - super(UnauthorizedFault, self).__init__(msg, details, code) - self.key = "unauthorized" - - -class ForbiddenFault(IdentityFault): - """The user is forbidden""" - - def __init__(self, msg, details=None, code=403): - super(ForbiddenFault, self).__init__(msg, details, code) - self.key = "forbidden" - - -class ItemNotFoundFault(IdentityFault): - """The item is not found""" - - def __init__(self, msg, details=None, code=404): - super(ItemNotFoundFault, self).__init__(msg, details, code) - self.key = "itemNotFound" - - -class TenantDisabledFault(IdentityFault): - """The tenant is disabled""" - - def __init__(self, msg, details=None, code=403): - super(TenantDisabledFault, self).__init__(msg, details, code) - self.key = "tenantDisabled" - - -class TenantConflictFault(IdentityFault): - """The tenant already exists?""" - - def __init__(self, msg, details=None, code=409): - super(TenantConflictFault, self).__init__(msg, details, code) - self.key = "tenantConflict" - - -class OverlimitFault(IdentityFault): - """A limit has been exceeded""" - - def __init__(self, msg, details=None, code=409, retry_at=None): - super(OverlimitFault, self).__init__(msg, details, code) - self.args = (code, msg, details, retry_at) - self.retry_at = retry_at - self.key = "overLimit" - - -class UserConflictFault(IdentityFault): - """The User already exists?""" - - def __init__(self, msg, details=None, code=409): - super(UserConflictFault, self).__init__(msg, details, code) - self.key = "userConflict" - - -class UserDisabledFault(IdentityFault): - """The user is disabled""" - - def __init__(self, msg, details=None, code=403): - super(UserDisabledFault, self).__init__(msg, details, code) - self.key = "userDisabled" - - -class EmailConflictFault(IdentityFault): - """The Email already exists?""" - - def __init__(self, msg, details=None, code=409): - super(EmailConflictFault, self).__init__(msg, details, code) - self.key = "emailConflict" - - -class RoleConflictFault(IdentityFault): - """The User already exists?""" - - def __init__(self, msg, details=None, code=409): - super(RoleConflictFault, self).__init__(msg, details, code) - self.key = "roleConflict" - - -class ServiceConflictFault(IdentityFault): - """The Service already exists?""" - - def __init__(self, msg, details=None, code=409): - super(ServiceConflictFault, self).__init__(msg, details, code) - self.key = "serviceConflict" - - -class DatabaseMigrationError(IdentityFault): - message = _("There was an error migrating the database.") diff --git a/keystone/logic/types/tenant.py b/keystone/logic/types/tenant.py deleted file mode 100644 index ca6e024b..00000000 --- a/keystone/logic/types/tenant.py +++ /dev/null @@ -1,150 +0,0 @@ -# Copyright (c) 2010-2011 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 json -from lxml import etree - -from keystone.logic.types import fault -from keystone import models - - -class Tenant(object): - """Describes a tenant in the auth system""" - id = None - name = None - description = None - enabled = None - - def __init__(self, id=None, name=None, description=None, enabled=None): - self.id = id # pylint: disable=C0103 - self.name = name - self.description = description - if enabled is not None: - self.enabled = bool(enabled) - else: - self.enabled = None - - @staticmethod - def from_xml(xml_str): - try: - dom = etree.Element("root") - dom.append(etree.fromstring(xml_str)) - root = dom.find( - "{http://docs.openstack.org/identity/api/v2.0}tenant") - if root is None: - raise fault.BadRequestFault("Expecting Tenant") - id = root.get("id") - name = root.get("name") - enabled = root.get("enabled") - if enabled is None or enabled == "true" or enabled == "yes": - set_enabled = True - elif enabled == "false" or enabled == "no": - set_enabled = False - else: - raise fault.BadRequestFault("Bad enabled attribute!") - desc = root.find("{http://docs.openstack.org/identity/api/v2.0}" - "description") - if desc is None: - description = None - else: - description = desc.text - return models.Tenant(id=id, name=name, description=description, - enabled=set_enabled) - except etree.LxmlError as e: - raise fault.BadRequestFault("Cannot parse Tenant", str(e)) - - @staticmethod - def from_json(json_str): - try: - obj = json.loads(json_str) - if not "tenant" in obj: - raise fault.BadRequestFault("Expecting tenant") - tenant = obj["tenant"] - - # Check that fields are valid - invalid = [key for key in tenant if key not in\ - ['id', 'name', 'enabled', 'description']] - if invalid != []: - raise fault.BadRequestFault("Invalid attribute(s): %s" - % invalid) - - id = tenant.get("id", None) - name = tenant.get("name", None) - set_enabled = True - if "enabled" in tenant: - set_enabled = tenant["enabled"] - if not isinstance(set_enabled, bool): - raise fault.BadRequestFault("Bad enabled attribute!") - description = tenant.get("description") - return Tenant(id=id, name=name, description=description, - enabled=set_enabled) - except (ValueError, TypeError) as e: - raise fault.BadRequestFault("Cannot parse Tenant", str(e)) - - def to_dom(self): - dom = etree.Element("tenant", - xmlns="http://docs.openstack.org/identity/api/v2.0", - enabled=str(self.enabled).lower()) - if self.id: - dom.set("id", unicode(self.id)) - if self.name: - dom.set("name", unicode(self.name)) - desc = etree.Element("description") - if self.description: - desc.text = unicode(self.description) - dom.append(desc) - return dom - - def to_xml(self): - return etree.tostring(self.to_dom()) - - def to_dict(self): - tenant = { - "enabled": self.enabled} - if self.description: - tenant['description'] = unicode(self.description) - if self.id: - tenant["id"] = unicode(self.id) - if self.name: - tenant["name"] = unicode(self.name) - return {"tenant": tenant} - - def to_json(self): - return json.dumps(self.to_dict()) - - -class Tenants(object): - """A collection of tenants.""" - - def __init__(self, values, links): - self.values = values - self.links = links - - def to_xml(self): - dom = etree.Element("tenants") - dom.set(u"xmlns", "http://docs.openstack.org/identity/api/v2.0") - - for t in self.values: - dom.append(t.to_dom()) - - for t in self.links: - dom.append(t.to_dom()) - - return etree.tostring(dom) - - def to_json(self): - values = [t.to_dict()["tenant"] for t in self.values] - links = [t.to_dict()["links"] for t in self.links] - return json.dumps({"tenants": values, "tenants_links": links}) diff --git a/keystone/logic/types/user.py b/keystone/logic/types/user.py deleted file mode 100755 index 5be5d488..00000000 --- a/keystone/logic/types/user.py +++ /dev/null @@ -1,280 +0,0 @@ -# Copyright (c) 2010-2011 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 json -from lxml import etree - -from keystone.logic.types import fault -from keystone import utils - - -class User(object): - """Document me!""" - - def __init__(self, password=None, id=None, name=None, tenant_id=None, - email=None, enabled=None, tenant_roles=None): - self.id = id - self.name = name - self.tenant_id = tenant_id - self.password = password - self.email = email - self.enabled = enabled and True or False - self.tenant_roles = tenant_roles - - @staticmethod - def from_xml(xml_str): - try: - dom = etree.Element("root") - dom.append(etree.fromstring(xml_str)) - root = dom.find("{http://docs.openstack.org/identity/api/v2.0}" \ - "user") - if root is None: - raise fault.BadRequestFault("Expecting User") - - name = root.get("name") - tenant_id = root.get("tenantId") - email = root.get("email") - password = root.get("password") - enabled = root.get("enabled") - utils.check_empty_string(name, "Expecting User Name") - utils.check_empty_string(password, "Expecting User Password") - utils.check_empty_string(email, "Expecting User email") - enabled = enabled is None or enabled.lower() in ["true", "yes"] - - return User(password, id, name, tenant_id, email, enabled) - except etree.LxmlError as e: - raise fault.BadRequestFault("Cannot parse User", str(e)) - - @staticmethod - def from_json(json_str): - try: - obj = json.loads(json_str) - if not "user" in obj: - raise fault.BadRequestFault("Expecting User") - user = obj["user"] - - # Check that fields are valid - invalid = [key for key in user if key not in - ['id', 'name', 'password', 'tenantId', 'email', - 'enabled']] - if invalid != []: - raise fault.BadRequestFault("Invalid attribute(s): %s" - % invalid) - - id = user.get('id', None) - name = user.get('name', None) - - if not "password" in user: - raise fault.BadRequestFault("Expecting User Password") - password = user["password"] - - if "tenantId" in user: - tenant_id = user["tenantId"] - else: - tenant_id = None - if "email" not in user: - raise fault.BadRequestFault("Expecting User Email") - email = user["email"] - utils.check_empty_string(name, "Expecting User Name") - utils.check_empty_string(password, "Expecting User Password") - utils.check_empty_string(email, "Expecting User email") - if "enabled" in user: - set_enabled = user["enabled"] - if not isinstance(set_enabled, bool): - raise fault.BadRequestFault("Bad enabled attribute!") - else: - set_enabled = True - return User(password, id, name, tenant_id, email, set_enabled) - except (ValueError, TypeError) as e: - raise fault.BadRequestFault("Cannot parse User", str(e)) - - def to_dom(self): - dom = etree.Element("user", - xmlns="http://docs.openstack.org/identity/api/v2.0") - if self.email: - dom.set("email", unicode(self.email)) - if self.tenant_id: - dom.set("tenantId", unicode(self.tenant_id)) - if self.id: - dom.set("id", unicode(self.id)) - if self.name: - dom.set("name", unicode(self.name)) - if self.enabled: - dom.set("enabled", unicode(self.enabled).lower()) - if self.password: - dom.set("password", unicode(self.password)) - if self.tenant_roles: - dom_roles = etree.Element("tenantRoles") - for role in self.tenant_roles: - dom_role = etree.Element("tenantRole") - dom_role.text = role - dom_roles.append(dom_role) - dom.append(dom_roles) - return dom - - def to_xml(self): - return etree.tostring(self.to_dom()) - - def to_dict(self): - user = {} - - if self.id: - user["id"] = unicode(self.id) - if self.name: - user["name"] = unicode(self.name) - if self.tenant_id: - user["tenantId"] = unicode(self.tenant_id) - if self.password: - user["password"] = unicode(self.password) - user["email"] = unicode(self.email) - user["enabled"] = self.enabled - if self.tenant_roles: - user["tenantRoles"] = list(self.tenant_roles) - return {'user': user} - - def to_json(self): - return json.dumps(self.to_dict()) - - -class User_Update(object): - """Document me!""" - - def __init__(self, password=None, id=None, name=None, tenant_id=None, - email=None, enabled=None): - self.id = id - self.name = name - self.tenant_id = tenant_id - self.password = password - self.email = email - self.enabled = bool(enabled) if enabled is not None else None - - @staticmethod - def from_xml(xml_str): - try: - dom = etree.Element("root") - dom.append(etree.fromstring(xml_str)) - root = dom.find("{http://docs.openstack.org/identity/api/v2.0}" \ - "user") - if root is None: - raise fault.BadRequestFault("Expecting User") - id = root.get("id") - name = root.get("name") - tenant_id = root.get("tenantId") - email = root.get("email") - password = root.get("password") - enabled = root.get("enabled") - if enabled is None or enabled == "true" or enabled == "yes": - set_enabled = True - elif enabled == "false" or enabled == "no": - set_enabled = False - else: - raise fault.BadRequestFault("Bad enabled attribute!") - - return User(password=password, id=id, name=name, - tenant_id=tenant_id, email=email, enabled=set_enabled) - except etree.LxmlError as e: - raise fault.BadRequestFault("Cannot parse User", str(e)) - - @staticmethod - def from_json(json_str): - try: - obj = json.loads(json_str) - if not "user" in obj: - raise fault.BadRequestFault("Expecting User") - user = obj["user"] - - # Check that fields are valid - invalid = [key for key in user if key not in - ['id', 'name', 'password', 'tenantId', 'email', - 'enabled']] - if invalid != []: - raise fault.BadRequestFault("Invalid attribute(s): %s" - % invalid) - - id = user.get('id', None) - name = user.get('name', None) - password = user.get('password', None) - tenant_id = user.get('tenantId', None) - email = user.get('email', None) - enabled = user.get('enabled', True) - - if not isinstance(enabled, bool): - raise fault.BadRequestFault("Bad enabled attribute!") - - return User(password, id, name, tenant_id, email, enabled) - except (ValueError, TypeError) as e: - raise fault.BadRequestFault("Cannot parse Tenant", str(e)) - - def to_dom(self): - dom = etree.Element("user", - xmlns="http://docs.openstack.org/identity/api/v2.0") - if self.email: - dom.set("email", unicode(self.email)) - if self.tenant_id: - dom.set("tenantId", unicode(self.tenant_id)) - if self.id: - dom.set("id", unicode(self.id)) - if self.name: - dom.set("name", unicode(self.name)) - if self.enabled is not None: - dom.set("enabled", unicode(self.enabled).lower()) - if self.password: - dom.set("password", unicode(self.password)) - return dom - - def to_xml(self): - return etree.tostring(self.to_dom()) - - def to_dict(self): - user = {} - - if self.id: - user["id"] = unicode(self.id) - if self.name: - user["name"] = unicode(self.name) - if self.tenant_id: - user["tenantId"] = unicode(self.tenant_id) - if self.password: - user["password"] = unicode(self.password) - if self.email: - user["email"] = unicode(self.email) - if self.enabled is not None: - user["enabled"] = self.enabled - return {'user': user} - - def to_json(self): - return json.dumps(self.to_dict()) - - -class Users(object): - """A collection of users.""" - - def __init__(self, values, links): - self.values = values - self.links = links - - def to_xml(self): - dom = etree.Element("users") - dom.set(u"xmlns", "http://docs.openstack.org/identity/api/v2.0") - for t in self.values: - dom.append(t.to_dom()) - for t in self.links: - dom.append(t.to_dom()) - return etree.tostring(dom) - - def to_json(self): - values = [t.to_dict()["user"] for t in self.values] - links = [t.to_dict()["links"] for t in self.links] - return json.dumps({"users": values, "users_links": links}) diff --git a/keystone/manage/__init__.py b/keystone/manage/__init__.py deleted file mode 100644 index 3b9d0f91..00000000 --- a/keystone/manage/__init__.py +++ /dev/null @@ -1,507 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# 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. - -""" -Keystone Identity Server - CLI Management Interface -""" - -import logging -import optparse # deprecated in 2.7, in favor of argparse -import os -import sys -import tempfile - -from keystone import version -import keystone.backends as db -from keystone.backends.sqlalchemy import migration -# Need to give it a different alias -from keystone import config as new_config -from keystone.common import config -from keystone.logic.types import fault -from keystone.manage import api -from keystone import utils - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - -# We're using two config systems here, so we need to be clear -# which one we're working with. -CONF = new_config.CONF - - -# CLI feature set -OBJECTS = ['user', 'tenant', 'role', 'service', - 'endpointTemplates', 'token', 'endpoint', 'credentials', 'database'] -ACTIONS = ['add', 'list', 'disable', 'delete', 'grant', 'revoke', - 'sync', 'downgrade', 'upgrade', 'version_control', 'version', - 'goto'] - - -# Messages -OBJECT_NOT_SPECIFIED = 'No object type specified for first argument' -ACTION_NOT_SPECIFIED = 'No action specified for second argument' -ID_NOT_SPECIFIED = 'No ID specified for third argument' -SUPPORTED_OBJECTS = "Supported objects: %s" % (", ".join(OBJECTS)) -SUPPORTED_ACTIONS = "Supported actions: %s" % (", ".join(ACTIONS)) -ACTION_NOT_SUPPORTED = 'Action not supported for %s' - - -class RaisingOptionParser(optparse.OptionParser): - def error(self, msg): - self.print_usage(sys.stderr) - raise optparse.OptParseError(msg) - - -def parse_args(args=None): - usage = """ - Usage: keystone-manage [options] type action [id [attributes]] - type : %s - action : %s - id : name or id - attributes : depending on type... - users : password, tenant - tokens : user, tenant, expiration - - role list [tenant] will list roles granted on that tenant - database [sync | downgrade | upgrade | version_control | version] - - options - -c | --config-file : config file to use - -d | --debug : debug mode - - Example: keystone-manage user add Admin P@ssw0rd - """ % (", ".join(OBJECTS), ", ".join(ACTIONS)) - - # Initialize a parser for our configuration paramaters - parser = RaisingOptionParser(usage, version='%%prog %s' - % version.version()) - config.add_common_options(parser) - config.add_log_options(parser) - - # Parse command-line and load config - (options, args) = config.parse_options(parser, args) - - if not args or args[0] != 'database': - # Use the legacy code to find the config file - config_file = config.find_config_file(options, sys.argv[1:]) - # Now init the CONF for the backends - CONF(config_files=[config_file]) - - db.configure_backends() - return args - - -def get_options(args=None): - # Initialize a parser for our configuration paramaters - parser = RaisingOptionParser() - config.add_common_options(parser) - config.add_log_options(parser) - - # Parse command-line and load config - (options, args) = config.parse_options(parser, list(args)) - - _config_file, conf = config.load_paste_config('admin', options, args) - conf.global_conf.update(conf.local_conf) - - return conf.global_conf - - -# pylint: disable=R0912,R0915 -def process(*args): - # Check arguments - if len(args) == 0: - raise optparse.OptParseError(OBJECT_NOT_SPECIFIED) - else: - object_type = args[0] - if object_type not in OBJECTS: - raise optparse.OptParseError(SUPPORTED_OBJECTS) - - if len(args) == 1: - raise optparse.OptParseError(ACTION_NOT_SPECIFIED) - else: - action = args[1] - if action not in ACTIONS: - raise optparse.OptParseError(SUPPORTED_ACTIONS) - - if action not in ['list', 'sync', 'version_control', 'version']: - if len(args) == 2: - raise optparse.OptParseError(ID_NOT_SPECIFIED) - else: - object_id = args[2] - - # Helper functions - def require_args(args, min, msg): - """Ensure there are at least `min` arguments""" - if len(args) < min: - raise optparse.OptParseError(msg) - - def optional_arg(args, index): - return ((len(args) > index) and str(args[index]).strip()) or None - - if object_type == 'database': - options = get_options(args) - - # Execute command - if (object_type, action) == ('user', 'add'): - require_args(args, 4, 'No password specified for fourth argument') - if api.add_user(name=object_id, password=args[3], - tenant=optional_arg(args, 4)): - print ("SUCCESS: User %s created." % object_id) - - elif (object_type, action) == ('user', 'list'): - print (Table('Users', ['id', 'name', 'enabled', 'tenant'], - api.list_users())) - - elif (object_type, action) == ('user', 'disable'): - if api.disable_user(name=object_id): - print ("SUCCESS: User %s disabled." % object_id) - - elif object_type == 'user': - raise optparse.OptParseError(ACTION_NOT_SUPPORTED % ('users')) - - elif (object_type, action) == ('tenant', 'add'): - if api.add_tenant(name=object_id): - print ("SUCCESS: Tenant %s created." % object_id) - - elif (object_type, action) == ('tenant', 'list'): - print Table('Tenants', ['id', 'name', 'enabled'], api.list_tenants()) - - elif (object_type, action) == ('tenant', 'disable'): - if api.disable_tenant(name=object_id): - print ("SUCCESS: Tenant %s disabled." % object_id) - - elif object_type == 'tenant': - raise optparse.OptParseError(ACTION_NOT_SUPPORTED % ('tenants')) - - elif (object_type, action) == ('role', 'add'): - if api.add_role(name=object_id, service_name=optional_arg(args, 3)): - print ("SUCCESS: Role %s created successfully." % object_id) - - elif (object_type, action) == ('role', 'list'): - tenant = optional_arg(args, 2) - if tenant: - # print with users - print (Table('Role assignments for tenant %s' % - tenant, ['User', 'Role'], - api.list_roles(tenant=tenant))) - else: - # print without tenants - print (Table('Roles', ['id', 'name', 'service_id', 'description'], - api.list_roles())) - - elif (object_type, action) == ('role', 'grant'): - require_args(args, 4, "Missing arguments: role grant 'role' 'user' " - "'tenant (optional)'") - tenant = optional_arg(args, 4) - if api.grant_role(object_id, args[3], tenant): - print ("SUCCESS: Granted %s the %s role on %s." % - (args[3], object_id, tenant)) - - elif object_type == 'role': - raise optparse.OptParseError(ACTION_NOT_SUPPORTED % ('roles')) - - elif (object_type, action) == ('endpointTemplates', 'add'): - require_args(args, 9, "Missing arguments: endpointTemplates add " - "'region' 'service_name' 'publicURL' 'adminURL' 'internalURL' " - "'enabled' 'global'") - version_id = optional_arg(args, 9) - version_list = optional_arg(args, 10) - version_info = optional_arg(args, 11) - if api.add_endpoint_template(region=args[2], service=args[3], - public_url=args[4], admin_url=args[5], internal_url=args[6], - enabled=args[7], is_global=args[8], - version_id=version_id, version_list=version_list, - version_info=version_info): - print ("SUCCESS: Created EndpointTemplates for %s " - "pointing to %s." % (args[3], args[4])) - - elif (object_type, action) == ('endpointTemplates', 'list'): - tenant = optional_arg(args, 2) - if tenant: - print Table('Endpoints for tenant %s' % tenant, - ['id', 'service', 'region', 'Public URL'], - api.list_tenant_endpoints(tenant)) - else: - print Table('All EndpointTemplates', ['id', 'service', 'type', - 'region', 'enabled', 'is_global', 'Public URL', - 'Admin URL'], - api.list_endpoint_templates()) - - elif (object_type, action) == ('endpoint', 'add'): - require_args(args, 4, "Missing arguments: endPoint add tenant " - "endPointTemplate") - if api.add_endpoint(tenant=args[2], endpoint_template=args[3]): - print ("SUCCESS: Endpoint %s added to tenant %s." % - (args[3], args[2])) - - elif object_type == 'endpoint': - raise optparse.OptParseError(ACTION_NOT_SUPPORTED % ('endpoints')) - - elif (object_type, action) == ('token', 'add'): - require_args(args, 6, 'Creating a token requires a token id, user, ' - 'tenant, and expiration') - if api.add_token(token=object_id, user=args[3], tenant=args[4], - expires=args[5]): - print ("SUCCESS: Token %s created." % object_id) - - elif (object_type, action) == ('token', 'list'): - print Table('Tokens', ('token', 'user', 'expiration', 'tenant'), - api.list_tokens()) - - elif (object_type, action) == ('token', 'delete'): - if api.delete_token(token=object_id): - print ('SUCCESS: Token %s deleted.' % (object_id,)) - - elif object_type == 'token': - raise optparse.OptParseError(ACTION_NOT_SUPPORTED % ('tokens')) - - elif (object_type, action) == ('service', 'add'): - require_args(args, 4, "Missing arguments: service add <name> " \ - "[type] [desc] [owner_id]" - "type") - type = optional_arg(args, 3) - desc = optional_arg(args, 4) - owner_id = optional_arg(args, 5) - - if api.add_service(name=object_id, type=type, desc=desc, - owner_id=owner_id): - print ("SUCCESS: Service %s created successfully." - % (object_id,)) - - elif (object_type, action) == ('service', 'list'): - print (Table('Services', ('id', 'name', 'type', 'owner_id', - 'description'), api.list_services())) - - elif object_type == 'service': - raise optparse.OptParseError(ACTION_NOT_SUPPORTED % ('services')) - - elif (object_type, action) == ('credentials', 'add'): - require_args(args, 6, 'Creating a credentials requires a type, key, ' - 'secret, and tenant_id (id is user_id)') - if api.add_credentials(user=object_id, type=args[3], key=args[4], - secrete=args[5], tenant=optional_arg(args, 6)): - print ("SUCCESS: Credentials %s created." % - (object_id,)) - - elif object_type == 'credentials': - raise optparse.OptParseError(ACTION_NOT_SUPPORTED % ('credentials')) - - elif (object_type, action) == ('database', 'sync'): - require_args(args, 1, 'Syncing database requires a version #') - backend_names = options.get('backends', None) - if backend_names: - if 'keystone.backends.sqlalchemy' in backend_names.split(','): - do_db_sync(options['keystone.backends.sqlalchemy'], - args) - else: - raise optparse.OptParseError( - 'SQL alchemy backend not specified in config') - - elif (object_type, action) == ('database', 'upgrade'): - require_args(args, 1, 'Upgrading database requires a version #') - backend_names = options.get('backends', None) - if backend_names: - if 'keystone.backends.sqlalchemy' in backend_names.split(','): - do_db_upgrade(options['keystone.backends.sqlalchemy'], - args) - else: - raise optparse.OptParseError( - 'SQL alchemy backend not specified in config') - - elif (object_type, action) == ('database', 'downgrade'): - require_args(args, 1, 'Downgrading database requires a version #') - backend_names = options.get('backends', None) - if backend_names: - if 'keystone.backends.sqlalchemy' in backend_names.split(','): - do_db_downgrade(options['keystone.backends.sqlalchemy'], - args) - else: - raise optparse.OptParseError( - 'SQL alchemy backend not specified in config') - - elif (object_type, action) == ('database', 'version_control'): - backend_names = options.get('backends', None) - if backend_names: - if 'keystone.backends.sqlalchemy' in backend_names.split(','): - do_db_version_control(options['keystone.backends.sqlalchemy']) - else: - raise optparse.OptParseError( - 'SQL alchemy backend not specified in config') - - elif (object_type, action) == ('database', 'version'): - backend_names = options.get('backends', None) - if backend_names: - if 'keystone.backends.sqlalchemy' in backend_names.split(','): - do_db_version(options['keystone.backends.sqlalchemy']) - else: - raise optparse.OptParseError( - 'SQL alchemy backend not specified in config') - - elif (object_type, action) == ('database', 'goto'): - require_args(args, 1, 'Jumping database versions requires a ' - 'version #') - backend_names = options.get('backends', None) - if backend_names: - if 'keystone.backends.sqlalchemy' in backend_names.split(','): - do_db_goto_version(options['keystone.backends.sqlalchemy'], - target_version=args[2]) - else: - raise optparse.OptParseError( - 'SQL alchemy backend not specified in config') - - else: - # Command recognized but not handled: should never reach this - raise NotImplementedError() - - -# -# Database Migration commands (from Glance-manage) -# -def do_db_version(options): - """Print database's current migration level""" - print (migration.db_version(options['sql_connection'])) - - -def do_db_goto_version(options, target_version): - """Override the database's current migration level""" - if migration.db_goto_version(options['sql_connection'], target_version): - msg = ('Jumped to version=%s (without performing intermediate ' - 'migrations)') % target_version - print (msg) - - -def do_db_upgrade(options, args): - """Upgrade the database's migration level""" - try: - db_version = args[2] - except IndexError: - db_version = None - - print ("Upgrading database to version %s" % db_version) - migration.upgrade(options['sql_connection'], version=db_version) - - -def do_db_downgrade(options, args): - """Downgrade the database's migration level""" - try: - db_version = args[2] - except IndexError: - raise Exception("downgrade requires a version argument") - - migration.downgrade(options['sql_connection'], version=db_version) - - -def do_db_version_control(options): - """Place a database under migration control""" - migration.version_control(options['sql_connection']) - print ("Database now under version control") - - -def do_db_sync(options, args): - """Place a database under migration control and upgrade""" - try: - db_version = args[2] - except IndexError: - db_version = None - migration.db_sync(options['sql_connection'], version=db_version) - - -class Table: - """Prints data in table for console output - - Syntax print Table("This is the title", - ["Header1","Header2","H3"], - [[1,2,3],["Hi","everybody","How are you??"],[None,True,[1,2]]]) - - """ - def __init__(self, title=None, headers=None, rows=None): - self.title = title - self.headers = headers if headers is not None else [] - self.rows = rows if rows is not None else [] - self.nrows = len(self.rows) - self.fieldlen = [] - - ncols = len(headers) - - for h in range(ncols): - max = 0 - for row in rows: - if len(str(row[h])) > max: - max = len(str(row[h])) - self.fieldlen.append(max) - - for i in range(len(headers)): - if len(str(headers[i])) > self.fieldlen[i]: - self.fieldlen[i] = len(str(headers[i])) - - self.width = sum(self.fieldlen) + (ncols - 1) * 3 + 4 - - def __str__(self): - hbar = "-" * self.width - if self.title: - title = "| " + self.title + " " * \ - (self.width - 3 - (len(self.title))) + "|" - out = [hbar, title, hbar] - else: - out = [] - header = "" - for i in range(len(self.headers)): - header += "| %s" % (str(self.headers[i])) + " " * \ - (self.fieldlen[i] - len(str(self.headers[i]))) + " " - header += "|" - out.append(header) - out.append(hbar) - for i in self.rows: - line = "" - for j in range(len(i)): - line += "| %s" % (str(i[j])) + " " * \ - (self.fieldlen[j] - len(str(i[j]))) + " " - out.append(line + "|") - - out.append(hbar) - return "\r\n".join(out) - - -def main(args=None): - try: - process(*parse_args(args)) - except (optparse.OptParseError, fault.DatabaseMigrationError) as exc: - print >> sys.stderr, exc - sys.exit(2) - except Exception as exc: - logstr = str(exc) - loginfo = None - if len(exc.args) > 1: - logstr = exc.args[0] - loginfo = exc.args[1] - - errmsg = "ERROR: %s" % logstr - if loginfo: - errmsg += ": %s" % loginfo - - print errmsg - logger.error(logstr, exc_info=loginfo) - raise - - -if __name__ == '__main__': - try: - main() - except StandardError: - sys.exit(1) diff --git a/keystone/manage/api.py b/keystone/manage/api.py deleted file mode 100644 index de5d2d56..00000000 --- a/keystone/manage/api.py +++ /dev/null @@ -1,210 +0,0 @@ -import datetime - -import keystone.backends.api as db_api -import keystone.backends.models as db_models -import keystone.models as models - - -def add_user(name, password, tenant=None): - if tenant: - tenant = db_api.TENANT.get_by_name(tenant).id - - obj = models.User() - obj.name = name - obj.password = password - obj.enabled = True - obj.tenant_id = tenant - return db_api.USER.create(obj) - - -def disable_user(name): - user = db_api.USER.get_by_name(name) - if user is None: - raise IndexError("User %s not found" % name) - user.enabled = False - return db_api.USER.update(user.id, user) - - -def list_users(): - objects = db_api.USER.get_all() - if objects is None: - raise IndexError("No users found") - return [[o.id, o.name, o.enabled, o.tenant_id] for o in objects] - - -def add_tenant(name): - obj = models.Tenant() - obj.name = name - obj.enabled = True - db_api.TENANT.create(obj) - - -def list_tenants(): - objects = db_api.TENANT.get_all() - if objects is None: - raise IndexError("Tenants not found") - return [[o.id, o.name, o.enabled] for o in objects] - - -def disable_tenant(name): - obj = db_api.TENANT.get_by_name(name) - if obj is None: - raise IndexError("Tenant %s not found" % name) - obj.enabled = False - return db_api.TENANT.update(obj.id, obj) - - -def add_role(name, service_name=None): - obj = models.Role() - obj.name = name - - names = name.split(":") - if len(names) == 2: - service_name = names[0] or service_name - if service_name: - # we have a role with service prefix, fill in the service ID - service = db_api.SERVICE.get_by_name(name=service_name) - obj.service_id = service.id - return db_api.ROLE.create(obj) - - -def list_role_assignments(tenant): - objects = db_api.TENANT.get_role_assignments(tenant) - if objects is None: - raise IndexError("Assignments not found") - return [[o.user.name, o.role.name] for o in objects] - - -def list_roles(tenant=None): - if tenant: - tenant = db_api.TENANT.get_by_name(tenant).id - return list_role_assignments(tenant) - else: - objects = db_api.ROLE.get_all() - if objects is None: - raise IndexError("Roles not found") - return [[o.id, o.name, o.service_id, o.description] for o in objects] - - -def grant_role(role, user, tenant=None): - """Grants `role` to `user` (and optionally, on `tenant`)""" - role = db_api.ROLE.get_by_name(name=role).id - user = db_api.USER.get_by_name(name=user).id - - if tenant: - tenant = db_api.TENANT.get_by_name(name=tenant).id - - obj = db_models.UserRoleAssociation() - obj.role_id = role - obj.user_id = user - obj.tenant_id = tenant - - return db_api.USER.user_role_add(obj) - - -def add_endpoint_template(region, service, public_url, admin_url, internal_url, - enabled, is_global, version_id, version_list, version_info): - db_service = db_api.SERVICE.get_by_name(service) - if db_service is None: - raise IndexError("Service %s not found" % service) - obj = db_models.EndpointTemplates() - obj.region = region - obj.service_id = db_service.id - obj.public_url = public_url - obj.admin_url = admin_url - obj.internal_url = internal_url - obj.enabled = enabled - obj.is_global = is_global - obj.version_id = version_id - obj.version_list = version_list - obj.version_info = version_info - return db_api.ENDPOINT_TEMPLATE.create(obj) - - -def list_tenant_endpoints(tenant): - objects = db_api.ENDPOINT_TEMPLATE.endpoint_get_by_tenant(tenant) - if objects is None: - raise IndexError("URLs not found") - return [[db_api.SERVICE.get(o.service_id).name, - o.region, o.public_url] for o in objects] - - -def list_endpoint_templates(): - objects = db_api.ENDPOINT_TEMPLATE.get_all() - if objects is None: - raise IndexError("URLs not found") - return [[o.id, - db_api.SERVICE.get(o.service_id).name, - db_api.SERVICE.get(o.service_id).type, - o.region, o.enabled, o.is_global, - o.public_url, o.admin_url] for o in objects] - - -def add_endpoint(tenant, endpoint_template): - tenant = db_api.TENANT.get_by_name(name=tenant).id - endpoint_template = db_api.ENDPOINT_TEMPLATE.get(id=endpoint_template).id - - obj = db_models.Endpoints() - obj.tenant_id = tenant - obj.endpoint_template_id = endpoint_template - db_api.ENDPOINT_TEMPLATE.endpoint_add(obj) - return obj - - -def add_token(token, user, tenant, expires): - user = db_api.USER.get_by_name(name=user).id - if tenant: - tenant = db_api.TENANT.get_by_name(name=tenant).id - - obj = models.Token() - obj.id = token - obj.user_id = user - obj.tenant_id = tenant - obj.expires = datetime.datetime.strptime(expires.replace("-", ""), - "%Y%m%dT%H:%M") - return db_api.TOKEN.create(obj) - - -def list_tokens(): - objects = db_api.TOKEN.get_all() - if objects is None: - raise IndexError("Tokens not found") - return [[o.id, o.user_id, o.expires, o.tenant_id] for o in objects] - - -def delete_token(token): - obj = db_api.TOKEN.get(token) - if obj is None: - raise IndexError("Token %s not found" % (token,)) - return db_api.TOKEN.delete(token) - - -def add_service(name, type, desc, owner_id): - obj = models.Service() - obj.name = name - obj.type = type - obj.description = desc - obj.owner_id = owner_id - return db_api.SERVICE.create(obj) - - -def list_services(): - objects = db_api.SERVICE.get_all() - if objects is None: - raise IndexError("Services not found") - return [[o.id, o.name, o.type, o.owner_id, o.description] for o in objects] - - -def add_credentials(user, type, key, secrete, tenant=None): - user = db_api.USER.get_by_name(user).id - - if tenant: - tenant = db_api.TENANT.get_by_name(tenant).id - - obj = models.Credentials() - obj.user_id = user - obj.type = type - obj.key = key - obj.secret = secrete - obj.tenant_id = tenant - return db_api.CREDENTIALS.create(obj) diff --git a/keystone/manage2/__init__.py b/keystone/manage2/__init__.py deleted file mode 100644 index 4336730b..00000000 --- a/keystone/manage2/__init__.py +++ /dev/null @@ -1,107 +0,0 @@ -"""OpenStack Identity (Keystone) Management""" - - -import argparse -import optparse -import os -import pkgutil -import sys - -from keystone.common import config as legacy_config -from keystone import config -from keystone.manage2 import commands - -CONF = config.CONF - -# builds a complete path to the commands package -PACKAGE_PATH = os.path.dirname(commands.__file__) - -# builds a list of modules in the commands package -MODULES = [tupl for tupl in pkgutil.iter_modules([PACKAGE_PATH])] - - -def load_module(module_name): - """Imports a module given the module name""" - try: - module_loader, name, _is_package = [md for md in MODULES - if md[1] == module_name][0] - except IndexError: - raise ValueError("No module found named '%s'" % module_name) - - loader = module_loader.find_module(name) - module = loader.load_module(name) - return module - - -# pylint: disable=W0612 -def init_config(): - """Uses legacy config module to parse out legacy settings and provide - them to the new cfg.py parser. - - This is a hack until we find a better way to have cfg.py ignore - unknown arguments - """ - - class SilentOptParser(optparse.OptionParser): - """ Class used to prevent OptionParser from exiting when it detects - invalid options coming in """ - def exit(self, status=0, msg=None): - pass - - def error(self, msg): - pass - - # Initialize a parser for our configuration paramaters - parser = SilentOptParser() - legacy_config.add_common_options(parser) - legacy_config.add_log_options(parser) - - # Parse command-line and load config - (options, args) = legacy_config.parse_options(parser) - (legacy_args, unknown_args) = parser.parse_args() - - cfgfile = getattr(legacy_args, 'config_file', None) - if cfgfile: - known_args = ['--config-file', cfgfile] - else: - known_args = [] - - # Use the legacy code to find the config file - config_file = legacy_config.find_config_file(options, known_args) - - # Now init the CONF for the backends using only known args - old_args = sys.argv[:] - sys.argv = known_args - try: - CONF(config_files=[config_file]) - except StandardError: - raise - finally: - sys.argv = old_args - - -def main(): - # discover command modules - module_names = [name for _, name, _ in MODULES] - - # build an argparser for keystone manage itself - parser = argparse.ArgumentParser(description=__doc__) - subparsers = parser.add_subparsers(dest='command', - help='Management commands') - - # append each command as a subparser - for module_name in module_names: - module = load_module(module_name) - subparser = subparsers.add_parser(module_name, - help=module.Command.__doc__) - - module.Command.append_parser(subparser) - - # actually parse the command line args or print help - args = parser.parse_args() - - # configure and run command - init_config() - module = load_module(args.command) - cmd = module.Command() - exit(cmd.run(args)) diff --git a/keystone/manage2/base.py b/keystone/manage2/base.py deleted file mode 100644 index bfd5babd..00000000 --- a/keystone/manage2/base.py +++ /dev/null @@ -1,111 +0,0 @@ -import argparse - -from keystone import config -from keystone.manage2 import common - - -class BaseCommand(object): - """Provides a common pattern for keystone-manage commands""" - - # pylint: disable=W0613 - def __init__(self, *args, **kwargs): - self.parser = argparse.ArgumentParser(prog=self.__module__, - description=self.__doc__) - self.append_parser(self.parser) - - def true_or_false(self, args, positive, negative): - """Evaluates a complementary pair of args to determine True/False. - - Fails if both args were provided. - - """ - - if getattr(args, positive) and getattr(args, negative): - self.parser.error("Unable to apply both: --%s and --%s" % ( - tuple([x.replace('_', '-') for x in (positive, negative)]))) - - return getattr(args, positive) and not getattr(args, negative) - - @classmethod - def append_parser(cls, parser): - """Appends this command's arguments to an argparser - - :param parser: argparse.ArgumentParser - """ - args = getattr(cls, '_args', {}) - - for name in args.keys(): - try: - parser.add_argument(name, **args[name]) - except TypeError: - print "Unable to add argument (%s) %s" % (name, args[name]) - raise - - def run(self, args): - """Handles argparse args and prints command results to stdout - - :param args: argparse Namespace - """ - raise NotImplementedError() - - -# pylint: disable=W0223 -class BaseSqlalchemyCommand(BaseCommand): - """Common functionality for database management commands""" - - def __init__(self, *args, **kwargs): - super(BaseSqlalchemyCommand, self).__init__(*args, **kwargs) - - @staticmethod - def _get_connection_string(): - sqla = config.CONF['keystone.backends.sqlalchemy'] - return sqla.sql_connection - - -# pylint: disable=E1101,W0223 -class BaseBackendCommand(BaseCommand): - """Common functionality for commands requiring backend access""" - - def __init__(self, managers=None, *args, **kwargs): - super(BaseBackendCommand, self).__init__(*args, **kwargs) - - # we may need to initialize our own managers - managers = managers or common.init_managers() - - # managers become available as self.attributes - for name, manager in managers.iteritems(): - setattr(self, name, manager) - - @staticmethod - def _get(obj_name, manager, id=None): - """Get an object from a manager, or fail if not found""" - if id is not None: - obj = manager.get(id) - - if obj is None: - raise KeyError("%s ID not found: %s" % (obj_name, id)) - - return obj - - def get_user(self, id): - return BaseBackendCommand._get("User", self.user_manager, id) - - def get_tenant(self, id): - return BaseBackendCommand._get("Tenant", self.tenant_manager, id) - - def get_token(self, id): - return BaseBackendCommand._get("Token", self.token_manager, id) - - def get_credential(self, id): - return BaseBackendCommand._get("Credential", self.credential_manager, - id) - - def get_role(self, id): - return BaseBackendCommand._get("Role", self.role_manager, id) - - def get_service(self, id): - return BaseBackendCommand._get("Service", self.service_manager, id) - - def get_endpoint_template(self, id): - return BaseBackendCommand._get("Endpoint Template", - self.endpoint_template_manager, id) diff --git a/keystone/manage2/commands/__init__.py b/keystone/manage2/commands/__init__.py deleted file mode 100644 index e69de29b..00000000 --- a/keystone/manage2/commands/__init__.py +++ /dev/null diff --git a/keystone/manage2/commands/create_credential.py b/keystone/manage2/commands/create_credential.py deleted file mode 100644 index d65bb597..00000000 --- a/keystone/manage2/commands/create_credential.py +++ /dev/null @@ -1,43 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common -from keystone.manage2 import mixins -from keystone.backends import models - - -@common.arg('--user-id', - required=True, - help='identifies the user who can authenticate with this credential') -@common.arg('--tenant-id', - required=False, - help='identifies the tenant upon which the crednetial is valid') -@common.arg('--type', - required=True, - help="credential type (e.g. 'EC2')") -@common.arg('--key', - required=True) -@common.arg('--secret', - required=True) -class Command(base.BaseBackendCommand, mixins.DateTimeMixin): - """Creates a new credential.""" - - # pylint: disable=E1101,R0913 - def create_credential(self, user_id, credential_type, key, secret, - tenant_id=None): - self.get_user(user_id) - self.get_tenant(tenant_id) - - obj = models.Credentials() - obj.user_id = user_id - obj.tenant_id = tenant_id - obj.type = credential_type - obj.key = key - obj.secret = secret - - return self.credential_manager.create(obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - credential = self.create_credential(user_id=args.user_id, - tenant_id=args.tenant_id, credential_type=args.type, - key=args.key, secret=args.secret) - print credential.id diff --git a/keystone/manage2/commands/create_endpoint_template.py b/keystone/manage2/commands/create_endpoint_template.py deleted file mode 100644 index dfb58294..00000000 --- a/keystone/manage2/commands/create_endpoint_template.py +++ /dev/null @@ -1,62 +0,0 @@ -from keystone import models -from keystone.manage2 import base -from keystone.manage2 import common -from keystone.manage2 import mixins - - -@common.arg('--region', - required=True, - help='identifies the region where the endpoint exists') -@common.arg('--service-id', - required=True, - help='references the service that owns the endpoint, by ID') -@common.arg('--public-url', - required=True, - help='url to access the endpoint over a public network (e.g. the ' - 'internet)') -@common.arg('--admin-url', - required=True, - help='url to access service administrator api') -@common.arg('--internal-url', - required=True, - help='url to access the endpoint over a high bandwidth, low latency, ' - 'unmetered network (e.g. LAN)') -@common.arg('--global', - action='store_true', - required=False, - default=False, - help='indicates whether the endpoint should be mapped to tenants ' - '(tenant-specific) or not (global)') -@common.arg('--disabled', - action='store_true', - required=False, - default=False, - help="create the endpoint in a disabled state (endpoints are enabled by " - "default)") -class Command(base.BaseBackendCommand, mixins.DateTimeMixin): - """Creates a new endpoint template.""" - - # pylint: disable=E1101,R0913 - def create_endpoint_template(self, region, service_id, public_url, - admin_url, internal_url, is_global=False, is_enabled=True): - self.get_service(service_id) - - obj = models.EndpointTemplate() - obj.region = region - obj.service_id = service_id - obj.public_url = public_url - obj.admin_url = admin_url - obj.internal_url = internal_url - obj.is_global = is_global - obj.enabled = is_enabled - - return self.endpoint_template_manager.create(obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - endpoint_template = self.create_endpoint_template(region=args.region, - service_id=args.service_id, public_url=args.public_url, - admin_url=args.admin_url, internal_url=args.internal_url, - is_global=getattr(args, 'global'), - is_enabled=(not args.disabled)) - print endpoint_template.id diff --git a/keystone/manage2/commands/create_role.py b/keystone/manage2/commands/create_role.py deleted file mode 100644 index 0460da69..00000000 --- a/keystone/manage2/commands/create_role.py +++ /dev/null @@ -1,36 +0,0 @@ -from keystone import models -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--name', - required=True, - help='a unique role name') -@common.arg('--description', - required=False, - help='describe the role') -@common.arg('--service-id', - required=False, - help='service which owns the role') -class Command(base.BaseBackendCommand): - """Creates a new role. - - Optionally, specify a service to own the role. - """ - - # pylint: disable=E1101 - def create_role(self, name, description=None, service_id=None): - self.get_service(service_id) - - obj = models.Role() - obj.name = name - obj.description = description - obj.service_id = service_id - - return self.role_manager.create(obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - role = self.create_role(name=args.name, - description=args.description, service_id=args.service_id) - print role.id diff --git a/keystone/manage2/commands/create_service.py b/keystone/manage2/commands/create_service.py deleted file mode 100644 index 5bbbdb41..00000000 --- a/keystone/manage2/commands/create_service.py +++ /dev/null @@ -1,42 +0,0 @@ -from keystone.manage2 import common -from keystone.manage2 import base -from keystone.backends import models - - -@common.arg('--name', - required=True, - help='unique service name') -@common.arg('--type', - required=True, - help='service type (e.g. identity, compute, object-storage, etc)') -@common.arg('--description', - required=False, - help='describe the service') -@common.arg('--owner-id', - required=False, - help='user who owns the service') -class Command(base.BaseBackendCommand): - """Creates a new service. - - Optionally, specify a user to own the service. - """ - - # pylint: disable=E1101,R0913 - def create_service(self, name, service_type, description=None, - owner_id=None): - self.get_user(owner_id) - - obj = models.Service() - obj.name = name - obj.type = service_type - obj.owner_id = owner_id - obj.desc = description - - return self.service_manager.create(obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - service = self.create_service(name=args.name, - service_type=args.type, description=args.description, - owner_id=args.owner_id) - print service.id diff --git a/keystone/manage2/commands/create_tenant.py b/keystone/manage2/commands/create_tenant.py deleted file mode 100644 index 3fed3089..00000000 --- a/keystone/manage2/commands/create_tenant.py +++ /dev/null @@ -1,38 +0,0 @@ -from keystone.manage2 import common -from keystone.manage2 import base -from keystone.backends import models - - -@common.arg('--id', - required=False, - help='a unique identifier used in URLs') -@common.arg('--name', - required=True, - help='a unique name') -@common.arg('--disabled', - action='store_true', - required=False, - default=False, - help="create the tenant in a disabled state (tenants are enabled by " - "default)") -class Command(base.BaseBackendCommand): - """Creates a new tenant, enabled by default. - - The tenant is enabled by default, but can optionally be disabled upon - creation. - """ - - # pylint: disable=E1101 - def create_tenant(self, name, id=None, enabled=True): - obj = models.Tenant() - obj.id = id - obj.name = name - obj.enabled = enabled - - return self.tenant_manager.create(obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - tenant = self.create_tenant(id=args.id, name=args.name, - enabled=(not args.disabled)) - print tenant.id diff --git a/keystone/manage2/commands/create_token.py b/keystone/manage2/commands/create_token.py deleted file mode 100644 index 14778b34..00000000 --- a/keystone/manage2/commands/create_token.py +++ /dev/null @@ -1,59 +0,0 @@ -import uuid - -from keystone.backends import models -from keystone.manage2 import base -from keystone.manage2 import common -from keystone.manage2 import mixins - - -@common.arg('--id', - required=False, - help='a unique token ID') -@common.arg('--user-id', - required=True, - help='identifies the user who can authenticate with this token') -@common.arg('--tenant-id', - required=False, - help='identifies the tenant upon which the token is valid') -@common.arg('--expires', - required=False, - help='identifies the POSIX date/time until which the token is valid ' - '(e.g. 1999-01-31T23:59)') -class Command(base.BaseBackendCommand, mixins.DateTimeMixin): - """Creates a new token. - - If a token ID is not provided, one will be generated automatically. - - If a tenant ID is not provided, the token will be unscoped. - - If an expiration datetime is not provided, the token will expires 24 - hours after creation. - """ - - # pylint: disable=E1101,R0913 - def create_token(self, user_id, token_id=None, tenant_id=None, - expires=None): - self.get_user(user_id) - self.get_tenant(tenant_id) - - obj = models.Token() - obj.user_id = user_id - obj.tenant_id = tenant_id - - if token_id is not None: - obj.id = token_id - else: - obj.id = uuid.uuid4().hex - - if expires is not None: - obj.expires = self.str_to_datetime(expires) - else: - obj.expires = Command.get_datetime_tomorrow() - - return self.token_manager.create(obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - token = self.create_token(token_id=args.id, user_id=args.user_id, - tenant_id=args.tenant_id, expires=args.expires) - print token.id diff --git a/keystone/manage2/commands/create_user.py b/keystone/manage2/commands/create_user.py deleted file mode 100644 index efe5b131..00000000 --- a/keystone/manage2/commands/create_user.py +++ /dev/null @@ -1,55 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common -from keystone.backends import models - - -@common.arg('--id', - required=False, - help='a unique identifier used in URLs') -@common.arg('--name', - required=True, - help='a unique username used for authentication') -@common.arg('--email', - required=False, - help='a unique email address') -@common.arg('--password', - required=True, - help='used for authentication') -@common.arg('--tenant-id', - required=False, - help='default tenant ID') -@common.arg('--disabled', - action='store_true', - required=False, - default=False, - help="create the user in a disabled state (users are enabled by " - "default)") -class Command(base.BaseBackendCommand): - """Creates a new user, enabled by default. - - Optionally, specify a default tenant for the user. - The user is enabled by default, but can be disabled upon creation as - well. - """ - - # pylint: disable=E1101,R0913 - def create_user(self, name, password, id=None, email=None, tenant_id=None, - enabled=True): - self.get_tenant(tenant_id) - - obj = models.User() - obj.id = id - obj.name = name - obj.password = password - obj.email = email - obj.enabled = enabled - obj.tenant_id = tenant_id - - return self.user_manager.create(obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - user = self.create_user(id=args.id, name=args.name, - password=args.password, email=args.email, - tenant_id=args.tenant_id, enabled=(not args.disabled)) - print user.id diff --git a/keystone/manage2/commands/delete_credential.py b/keystone/manage2/commands/delete_credential.py deleted file mode 100644 index 50f0c41b..00000000 --- a/keystone/manage2/commands/delete_credential.py +++ /dev/null @@ -1,18 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--where-id', - required=True, - help='identify the credential to be deleted by ID') -class Command(base.BaseBackendCommand): - """Deletes the specified credential.""" - - # pylint: disable=E1101 - def delete_credential(self, id): - credential = self.get_credential(id) - self.credential_manager.delete(credential.id) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.delete_credential(id=args.where_id) diff --git a/keystone/manage2/commands/delete_endpoint_template.py b/keystone/manage2/commands/delete_endpoint_template.py deleted file mode 100644 index b39bed1e..00000000 --- a/keystone/manage2/commands/delete_endpoint_template.py +++ /dev/null @@ -1,18 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--where-id', - required=True, - help='identify the endpoint template to be deleted by ID') -class Command(base.BaseBackendCommand): - """Deletes the specified endpoint_template.""" - - # pylint: disable=E1101 - def delete_endpoint_template(self, id): - endpoint_template = self.get_endpoint_template(id) - self.endpoint_template_manager.delete(endpoint_template.id) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.delete_endpoint_template(id=args.where_id) diff --git a/keystone/manage2/commands/delete_role.py b/keystone/manage2/commands/delete_role.py deleted file mode 100644 index d841efb9..00000000 --- a/keystone/manage2/commands/delete_role.py +++ /dev/null @@ -1,18 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--where-id', - required=True, - help='identify the role to be deleted by ID') -class Command(base.BaseBackendCommand): - """Deletes the specified role.""" - - # pylint: disable=E1101 - def delete_role(self, id): - role = self.get_role(id) - self.role_manager.delete(role.id) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.delete_role(id=args.where_id) diff --git a/keystone/manage2/commands/delete_service.py b/keystone/manage2/commands/delete_service.py deleted file mode 100644 index 8119659f..00000000 --- a/keystone/manage2/commands/delete_service.py +++ /dev/null @@ -1,18 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--where-id', - required=True, - help='identify the service to be deleted by ID') -class Command(base.BaseBackendCommand): - """Deletes the specified service.""" - - # pylint: disable=E1101 - def delete_service(self, id): - service = self.get_service(id) - self.service_manager.delete(service.id) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.delete_service(id=args.where_id) diff --git a/keystone/manage2/commands/delete_tenant.py b/keystone/manage2/commands/delete_tenant.py deleted file mode 100644 index 2adbf904..00000000 --- a/keystone/manage2/commands/delete_tenant.py +++ /dev/null @@ -1,21 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--where-id', - required=True, - help='identify the tenant to be deleted') -class Command(base.BaseBackendCommand): - """Deletes the specified tenant. - - This command is irreversible! To simply disable a tenant, - use `update_tenant --disable`.""" - - # pylint: disable=E1101 - def delete_tenant(self, id): - tenant = self.get_tenant(id) - self.tenant_manager.delete(tenant.id) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.delete_tenant(id=args.where_id) diff --git a/keystone/manage2/commands/delete_token.py b/keystone/manage2/commands/delete_token.py deleted file mode 100644 index 544855c8..00000000 --- a/keystone/manage2/commands/delete_token.py +++ /dev/null @@ -1,18 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--where-id', - required=True, - help='identify the token to be deleted by ID') -class Command(base.BaseBackendCommand): - """Deletes the specified token.""" - - # pylint: disable=E1101 - def delete_token(self, id): - token = self.get_token(id) - self.token_manager.delete(token.id) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.delete_token(id=args.where_id) diff --git a/keystone/manage2/commands/delete_user.py b/keystone/manage2/commands/delete_user.py deleted file mode 100644 index 701df76f..00000000 --- a/keystone/manage2/commands/delete_user.py +++ /dev/null @@ -1,21 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--where-id', - required=True, - help='identify the user to be deleted by ID') -class Command(base.BaseBackendCommand): - """Deletes the specified user. - - This command is irreversible! To simply disable a user, - use `update_user --disable`.""" - - # pylint: disable=E1101 - def delete_user(self, id): - user = self.get_user(id) - self.user_manager.delete(user.id) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.delete_user(id=args.where_id) diff --git a/keystone/manage2/commands/downgrade_database.py b/keystone/manage2/commands/downgrade_database.py deleted file mode 100644 index 3bfbcc20..00000000 --- a/keystone/manage2/commands/downgrade_database.py +++ /dev/null @@ -1,19 +0,0 @@ -from keystone.backends.sqlalchemy import migration -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--version', - required=True, - help='specify the desired database version') -class Command(base.BaseSqlalchemyCommand): - """Downgrades the database to the specified version""" - - @staticmethod - def downgrade_database(version): - """Downgrade database to the specified version""" - migration.downgrade(Command._get_connection_string(), version=version) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.downgrade_database(version=args.version) diff --git a/keystone/manage2/commands/goto_database.py b/keystone/manage2/commands/goto_database.py deleted file mode 100644 index d419c1c7..00000000 --- a/keystone/manage2/commands/goto_database.py +++ /dev/null @@ -1,26 +0,0 @@ -from keystone.backends.sqlalchemy import migration -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--version', - required=True, - help='specify the desired database version') -class Command(base.BaseSqlalchemyCommand): - """Jumps to the specified database version without running migrations. - - Useful for initializing your version control at a version other than zero - (e.g. you have an existing post-diablo database). - - """ - - @staticmethod - def goto_database_version(version): - """Override database's current migration level""" - if not migration.db_goto_version(Command._get_connection_string(), - version): - raise Exception("Unable to jump to specified version") - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.goto_database_version(version=args.version) diff --git a/keystone/manage2/commands/grant_role.py b/keystone/manage2/commands/grant_role.py deleted file mode 100644 index 770d9887..00000000 --- a/keystone/manage2/commands/grant_role.py +++ /dev/null @@ -1,45 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common -from keystone.backends import models - - -@common.arg('--user-id', - required=True, - help='identify the user to grant the role to by ID') -@common.arg('--role-id', - required=True, - help='identify the role to be granted by ID') -@common.arg('--tenant-id', - required=False, - help='identify the tenant for the granted role is valid (the role is ' - 'global if a tenant is not specified)') -class Command(base.BaseBackendCommand): - """Grants a role to a user, and optionally, for a specific tenant. - - If a tenant is not specified, the role is granted globally.""" - - # pylint: disable=E1101,R0913 - def grant_role(self, user_id, role_id, tenant_id=None): - self.get_user(user_id) - self.get_role(role_id) - self.get_tenant(tenant_id) - - # this is a bit of a hack to validate that the grant doesn't exist - grant = self.grant_manager.rolegrant_get_by_ids(user_id, role_id, - tenant_id) - if grant is not None: - raise KeyError('Grant already exists for User ID %s, ' - 'Role ID %s and Tenant ID %s' % (user_id, role_id, - tenant_id)) - - obj = models.UserRoleAssociation() - obj.user_id = user_id - obj.role_id = role_id - obj.tenant_id = tenant_id - - self.user_manager.user_role_add(obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.grant_role(user_id=args.user_id, role_id=args.role_id, - tenant_id=args.tenant_id) diff --git a/keystone/manage2/commands/list_credentials.py b/keystone/manage2/commands/list_credentials.py deleted file mode 100644 index 4bc8a5b2..00000000 --- a/keystone/manage2/commands/list_credentials.py +++ /dev/null @@ -1,23 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import mixins - - -class Command(base.BaseBackendCommand, mixins.ListMixin, - mixins.DateTimeMixin): - """Lists all credentials in the system.""" - - # pylint: disable=E1101 - def get_credentials(self): - return self.credential_manager.get_all() - - def run(self, args): - """Process argparse args, and print results to stdout""" - table = self.build_table(["ID", "User ID", "Tenant ID", - "Type", "Key", "Secret"]) - - for obj in self.get_credentials(): - row = [obj.id, obj.user_id, obj.tenant_id, - obj.type, obj.key, obj.secret] - table.add_row(row) - - self.print_table(table) diff --git a/keystone/manage2/commands/list_endpoint_templates.py b/keystone/manage2/commands/list_endpoint_templates.py deleted file mode 100644 index fbfc5341..00000000 --- a/keystone/manage2/commands/list_endpoint_templates.py +++ /dev/null @@ -1,23 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import mixins - - -class Command(base.BaseBackendCommand, mixins.ListMixin): - """Lists all endpoint templates in the system.""" - - # pylint: disable=E1101 - def get_endpoint_templates(self): - return self.endpoint_template_manager.get_all() - - def run(self, args): - """Process argparse args, and print results to stdout""" - table = self.build_table(['ID', 'Service ID', 'Region', 'Enabled', - 'Global', 'Public URL', 'Admin URL', 'Internal URL']) - - for obj in self.get_endpoint_templates(): - row = [obj.id, obj.service_id, obj.region, obj.enabled, - obj.is_global, obj.public_url, obj.admin_url, - obj.internal_url] - table.add_row(row) - - self.print_table(table) diff --git a/keystone/manage2/commands/list_endpoints.py b/keystone/manage2/commands/list_endpoints.py deleted file mode 100644 index dfebb410..00000000 --- a/keystone/manage2/commands/list_endpoints.py +++ /dev/null @@ -1,20 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import mixins - - -class Command(base.BaseBackendCommand, mixins.ListMixin): - """Lists all roles in the system.""" - - # pylint: disable=E1101 - def get_roles(self): - return self.endpoint_manager.get_all() - - def run(self, args): - """Process argparse args, and print results to stdout""" - table = self.build_table(["Endpoint Template ID", "Tenant ID"]) - - for obj in self.get_roles(): - row = [obj.endpoint_template_id, obj.tenant_id] - table.add_row(row) - - self.print_table(table) diff --git a/keystone/manage2/commands/list_role_grants.py b/keystone/manage2/commands/list_role_grants.py deleted file mode 100644 index cd69ed33..00000000 --- a/keystone/manage2/commands/list_role_grants.py +++ /dev/null @@ -1,50 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common -from keystone.manage2 import mixins - - -@common.arg('--where-user-id', - required=False, - help='lists roles granted to a specific user') -@common.arg('--where-role-id', - required=False, - help='lists users and tenants a role has been granted to') -@common.arg('--where-tenant-id', - required=False, - help='lists roles granted on a specific tenant') -@common.arg('--where-global', - action='store_true', - required=False, - default=False, - help="lists roles that have been granted globally") -class Command(base.BaseBackendCommand, mixins.ListMixin): - """Lists the users and tenants a role has been granted to.""" - - # pylint: disable=E1101,R0913 - def list_role_grants(self, role_id=None, user_id=None, tenant_id=None, - is_global=False): - self.get_user(user_id) - self.get_role(role_id) - self.get_tenant(tenant_id) - - if is_global: - tenant_id = False - - return self.grant_manager.list_role_grants(user_id=user_id, - role_id=role_id, tenant_id=tenant_id) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.true_or_false(args, 'where_tenant_id', 'where_global') - - table = self.build_table(["Role ID", "User ID", "Tenant ID", - "Global"]) - - for obj in self.list_role_grants(role_id=args.where_role_id, - user_id=args.where_user_id, tenant_id=args.where_tenant_id, - is_global=args.where_global): - row = [obj.role_id, obj.user_id, obj.tenant_id, - obj.tenant_id is None] - table.add_row(row) - - self.print_table(table) diff --git a/keystone/manage2/commands/list_roles.py b/keystone/manage2/commands/list_roles.py deleted file mode 100644 index 9e381fc7..00000000 --- a/keystone/manage2/commands/list_roles.py +++ /dev/null @@ -1,20 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import mixins - - -class Command(base.BaseBackendCommand, mixins.ListMixin): - """Lists all roles in the system.""" - - # pylint: disable=E1101 - def get_roles(self): - return self.role_manager.get_all() - - def run(self, args): - """Process argparse args, and print results to stdout""" - table = self.build_table(["ID", "Name", "Service ID", "Description"]) - - for obj in self.get_roles(): - row = [obj.id, obj.name, obj.service_id, obj.desc] - table.add_row(row) - - self.print_table(table) diff --git a/keystone/manage2/commands/list_services.py b/keystone/manage2/commands/list_services.py deleted file mode 100644 index 0496c50b..00000000 --- a/keystone/manage2/commands/list_services.py +++ /dev/null @@ -1,21 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import mixins - - -class Command(base.BaseBackendCommand, mixins.ListMixin): - """Lists all services in the system.""" - - # pylint: disable=E1101 - def get_services(self): - return self.service_manager.get_all() - - def run(self, args): - """Process argparse args, and print results to stdout""" - table = self.build_table(["ID", "Name", "Type", "Owner ID", - "Description"]) - - for obj in self.get_services(): - row = [obj.id, obj.name, obj.type, obj.owner_id, obj.desc] - table.add_row(row) - - self.print_table(table) diff --git a/keystone/manage2/commands/list_tenants.py b/keystone/manage2/commands/list_tenants.py deleted file mode 100644 index 81a15ca4..00000000 --- a/keystone/manage2/commands/list_tenants.py +++ /dev/null @@ -1,22 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import mixins - - -class Command(base.BaseBackendCommand, mixins.ListMixin): - """Lists all tenants in the system.""" - - # pylint: disable=E1101 - def get_tenants(self): - return self.tenant_manager.get_all() - - def run(self, args): - """Process argparse args, and print results to stdout""" - - table = self.build_table(["ID", "Name", "Enabled"]) - - # populate the table - for tenant in self.get_tenants(): - row = [tenant.id, tenant.name, tenant.enabled] - table.add_row(row) - - self.print_table(table) diff --git a/keystone/manage2/commands/list_tokens.py b/keystone/manage2/commands/list_tokens.py deleted file mode 100644 index 9bdc3063..00000000 --- a/keystone/manage2/commands/list_tokens.py +++ /dev/null @@ -1,23 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import mixins - - -class Command(base.BaseBackendCommand, mixins.ListMixin, - mixins.DateTimeMixin): - """Lists all tokens in the system.""" - - # pylint: disable=E1101 - def get_tokens(self): - return self.token_manager.get_all() - - def run(self, args): - """Process argparse args, and print results to stdout""" - table = self.build_table(["ID", "User ID", "Tenant ID", - "Expiration"]) - - for obj in self.get_tokens(): - row = [obj.id, obj.user_id, obj.tenant_id, - self.datetime_to_str(obj.expires)] - table.add_row(row) - - self.print_table(table) diff --git a/keystone/manage2/commands/list_users.py b/keystone/manage2/commands/list_users.py deleted file mode 100644 index a23202dc..00000000 --- a/keystone/manage2/commands/list_users.py +++ /dev/null @@ -1,23 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import mixins - - -class Command(base.BaseBackendCommand, mixins.ListMixin): - """Lists all users in the system.""" - - # pylint: disable=E1101 - def get_users(self): - return self.user_manager.get_all() - - def run(self, args): - """Process argparse args, and print results to stdout""" - table = self.build_table(["ID", "Name", "Email", "Default Tenant ID", - "Enabled"]) - - for user in self.get_users(): - row = [user.id, user.name, user.email, user.tenant_id, - user.enabled] - table.add_row(row) - - # TODO(dolph): sort order and subsets could become CLI options - self.print_table(table) diff --git a/keystone/manage2/commands/map_endpoint.py b/keystone/manage2/commands/map_endpoint.py deleted file mode 100644 index ab63ec9d..00000000 --- a/keystone/manage2/commands/map_endpoint.py +++ /dev/null @@ -1,37 +0,0 @@ -from keystone import models -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--tenant-id', - required=True, - help='identify the tenant to be mapped by ID') -@common.arg('--endpoint-template-id', - required=True, - help='identify the endpoint to be mapped by ID') -class Command(base.BaseBackendCommand): - """Maps a non-global endpoint to a tenant. - - If a mapping exists between a tenant and an endpoint template, then - the endpoint will appear in the tenant's service catalog, customized - for the tenant. - - Global endpoints are already available to all tenants and therefore don't - need to be mapped. - """ - - # pylint: disable=E1101,R0913 - def create_endpoint(self, endpoint_template_id, tenant_id): - self.get_endpoint_template(endpoint_template_id) - self.get_tenant(tenant_id) - - obj = models.Endpoint() - obj.endpoint_template_id = endpoint_template_id - obj.tenant_id = tenant_id - - self.endpoint_manager.create(obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.create_endpoint(endpoint_template_id=args.endpoint_template_id, - tenant_id=args.tenant_id) diff --git a/keystone/manage2/commands/revoke_role.py b/keystone/manage2/commands/revoke_role.py deleted file mode 100644 index 6b403d7d..00000000 --- a/keystone/manage2/commands/revoke_role.py +++ /dev/null @@ -1,40 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--user-id', - required=True, - help='identify the user to revoke the role from by ID') -@common.arg('--role-id', - required=True, - help='identify the role to be revoked by ID') -@common.arg('--tenant-id', - required=False, - help='identify the tenant for the role to be revoked from by ID (the ' - 'role is assumed to be global if a tenant is not specified)') -class Command(base.BaseBackendCommand): - """Revoke a role from a user, and optionally, from a specific tenant. - - If a tenant is not specified, then the role is assumed to be global, - and revoked as a global role. - """ - - # pylint: disable=E1101,R0913 - def revoke_role(self, user_id, role_id, tenant_id=None): - self.get_user(user_id) - self.get_role(role_id) - self.get_tenant(tenant_id) - - grant = self.grant_manager.rolegrant_get_by_ids(user_id, role_id, - tenant_id) - - if grant is None: - raise KeyError('Grant not found for User ID %s, Role ID %s and ' - 'Tenant ID %s' % (user_id, role_id, tenant_id)) - - self.grant_manager.rolegrant_delete(grant.id) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.revoke_role(user_id=args.user_id, role_id=args.role_id, - tenant_id=args.tenant_id) diff --git a/keystone/manage2/commands/sync_database.py b/keystone/manage2/commands/sync_database.py deleted file mode 100644 index 62092d50..00000000 --- a/keystone/manage2/commands/sync_database.py +++ /dev/null @@ -1,19 +0,0 @@ -from keystone.backends.sqlalchemy import migration -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--version', - required=False, - help='specify the desired database version') -class Command(base.BaseSqlalchemyCommand): - """Upgrades the database to the latest schema.""" - - @staticmethod - def sync_database(version=None): - """Place database under migration control & automatically upgrade""" - migration.db_sync(Command._get_connection_string(), version=version) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.sync_database(version=args.version) diff --git a/keystone/manage2/commands/unmap_endpoint.py b/keystone/manage2/commands/unmap_endpoint.py deleted file mode 100644 index 991e597e..00000000 --- a/keystone/manage2/commands/unmap_endpoint.py +++ /dev/null @@ -1,28 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--tenant-id', - required=True, - help='identify the tenant to be unmapped by ID') -@common.arg('--endpoint-template-id', - required=True, - help='identify the endpoint template to be unmapped by ID') -class Command(base.BaseBackendCommand): - """Unmap an endpoint template from a tenant.""" - - # pylint: disable=E1101,R0913 - def delete_endpoint(self, endpoint_template_id, tenant_id): - obj = self.endpoint_manager.get_by_ids(endpoint_template_id, tenant_id) - - if obj is None: - raise KeyError("Endpoint mapping not found for " - "endpoint_template_id=%s, tenant_id=%s" % ( - endpoint_template_id, tenant_id)) - - self.endpoint_manager.delete(obj.id) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.delete_endpoint(endpoint_template_id=args.endpoint_template_id, - tenant_id=args.tenant_id) diff --git a/keystone/manage2/commands/update_credential.py b/keystone/manage2/commands/update_credential.py deleted file mode 100644 index eb15ef03..00000000 --- a/keystone/manage2/commands/update_credential.py +++ /dev/null @@ -1,55 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common -from keystone.manage2 import mixins - - -@common.arg('--where-id', - required=True, - help='identifies the credential to update by ID') -@common.arg('--user-id', - required=False, - help='change the user the credential applies to, by ID') -@common.arg('--tenant-id', - required=False, - help='change the tenant this credential applies to, by ID') -@common.arg('--type', - required=True, - help="change the credential type (e.g. 'EC2')") -@common.arg('--key', - required=True, - help="change the credential key") -@common.arg('--secret', - required=True, - help="change the credential secret") -class Command(base.BaseBackendCommand, mixins.DateTimeMixin): - """Updates the specified credential.""" - - # pylint: disable=E1101,R0913 - def update_credential(self, id, user_id=None, tenant_id=None, - cred_type=None, secret=None, key=None): - obj = self.get_credential(id) - self.get_user(user_id) - self.get_tenant(tenant_id) - - if user_id is not None: - obj.user_id = user_id - - if tenant_id is not None: - obj.tenant_id = tenant_id - - if cred_type is not None: - obj.type = cred_type - - if key is not None: - obj.key = key - - if secret is not None: - obj.secret = secret - - self.credential_manager.update(id, obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.update_credential(id=args.where_id, user_id=args.user_id, - tenant_id=args.tenant_id, cred_type=args.type, - key=args.key, secret=args.secret) diff --git a/keystone/manage2/commands/update_endpoint_template.py b/keystone/manage2/commands/update_endpoint_template.py deleted file mode 100644 index 3f0f6893..00000000 --- a/keystone/manage2/commands/update_endpoint_template.py +++ /dev/null @@ -1,88 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common -from keystone.manage2 import mixins - - -@common.arg('--where-id', - required=True, - help='identifies the endpoint template to update by ID') -@common.arg('--region', - required=True, - help='identifies the region where the endpoint exists') -@common.arg('--service-id', - required=True, - help='references the service that owns the endpoint, by ID') -@common.arg('--public-url', - required=True, - help='url to access the endpoint over a public network (e.g. the ' - 'internet)') -@common.arg('--admin-url', - required=True, - help='url to access service administrator api') -@common.arg('--internal-url', - required=True, - help='url to access the endpoint over a high bandwidth, low latency, ' - 'unmetered network (e.g. LAN)') -@common.arg('--global', - action='store_true', - required=False, - default=False, - help='indicates whether the endpoint should apply to all tenants') -@common.arg('--non-global', - action='store_true', - required=False, - default=False, - help='indicates whether the endpoint should be mapped to specific tenants') -@common.arg('--enable', - action='store_true', - required=False, - default=False, - help="enable the endpoint template") -@common.arg('--disable', - action='store_true', - required=False, - default=False, - help="disable the endpoint template") -class Command(base.BaseBackendCommand, mixins.DateTimeMixin): - """Updates an existing endpoint template.""" - - # pylint: disable=E1101,R0913 - def update_endpoint_template(self, id, region, service_id, public_url, - admin_url, internal_url, is_global=False, is_enabled=True): - obj = self.get_endpoint_template(id) - - self.get_service(service_id) - - if region is not None: - obj.region = region - - if service_id is not None: - obj.service_id = service_id - - if public_url is not None: - obj.public_url = public_url - - if admin_url is not None: - obj.admin_url = admin_url - - if internal_url is not None: - obj.internal_url = internal_url - - if is_global is not None: - obj.is_global = is_global - - if is_enabled is not None: - obj.enabled = is_enabled - - self.endpoint_template_manager.update(obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - is_global = self.true_or_false(args, 'global', 'non_global') - enabled = self.true_or_false(args, 'enable', 'disable') - - self.update_endpoint_template(id=args.where_id, - region=args.region, service_id=args.service_id, - public_url=args.public_url, admin_url=args.admin_url, - internal_url=args.internal_url, is_global=is_global, - is_enabled=enabled) diff --git a/keystone/manage2/commands/update_role.py b/keystone/manage2/commands/update_role.py deleted file mode 100644 index 2f459661..00000000 --- a/keystone/manage2/commands/update_role.py +++ /dev/null @@ -1,40 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--where-id', - required=True, - help='identifies the role to update by ID') -@common.arg('--name', - required=False, - help='a unique role name') -@common.arg('--description', - required=False, - help='describe the role') -@common.arg('--service-id', - required=False, - help='service which owns the role') -class Command(base.BaseBackendCommand): - """Updates the specified role.""" - - # pylint: disable=E1101,R0913 - def update_role(self, id, name=None, description=None, service_id=None): - obj = self.get_role(id) - - if name is not None: - obj.name = name - - if description is not None: - obj.description = description - - if service_id is not None: - service = self.get_service(service_id) - obj.service_id = service.id - - self.role_manager.update(obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.update_role(id=args.where_id, name=args.name, - description=args.description, - service_id=args.service_id) diff --git a/keystone/manage2/commands/update_service.py b/keystone/manage2/commands/update_service.py deleted file mode 100644 index 7c7c3dac..00000000 --- a/keystone/manage2/commands/update_service.py +++ /dev/null @@ -1,47 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--where-id', - required=True, - help='identifies the service to update by ID') -@common.arg('--name', - required=False, - help='unique service name') -@common.arg('--type', - required=False, - help='service type (e.g. identity, compute, object-storage, etc)') -@common.arg('--description', - required=False, - help='describe the service') -@common.arg('--owner-id', - required=False, - help='user who owns the service') -class Command(base.BaseBackendCommand): - """Updates the specified service.""" - - # pylint: disable=E1101,R0913 - def update_service(self, id, name=None, service_type=None, - description=None, owner_id=None): - obj = self.get_service(id) - - if name is not None: - obj.name = name - - if service_type is not None: - obj.type = service_type - - if description is not None: - obj.description = description - - if owner_id is not None: - owner = self.get_user(owner_id) - obj.owner_id = owner.id - - self.service_manager.update(obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.update_service(id=args.where_id, name=args.name, - service_type=args.type, - description=args.description, owner_id=args.owner_id) diff --git a/keystone/manage2/commands/update_tenant.py b/keystone/manage2/commands/update_tenant.py deleted file mode 100644 index b2f844f1..00000000 --- a/keystone/manage2/commands/update_tenant.py +++ /dev/null @@ -1,40 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--where-id', - required=True, - help='identifies the tenant to update by ID') -@common.arg('--name', - required=False, - help="change the tenant's name") -@common.arg('--enable', - action='store_true', - required=False, - default=False, - help="enable the tenant") -@common.arg('--disable', - action='store_true', - required=False, - default=False, - help="disable the tenant") -class Command(base.BaseBackendCommand): - """Updates the specified tenant.""" - - # pylint: disable=E1101 - def update_tenant(self, id, name=None, enabled=None): - tenant = self.get_tenant(id) - - if name is not None: - tenant.name = name - - if enabled is not None: - tenant.enabled = enabled - - self.tenant_manager.update(tenant) - - def run(self, args): - """Process argparse args, and print results to stdout""" - enabled = self.true_or_false(args, 'enable', 'disable') - - self.update_tenant(id=args.where_id, name=args.name, enabled=enabled) diff --git a/keystone/manage2/commands/update_token.py b/keystone/manage2/commands/update_token.py deleted file mode 100644 index f2c78328..00000000 --- a/keystone/manage2/commands/update_token.py +++ /dev/null @@ -1,42 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common -from keystone.manage2 import mixins - - -@common.arg('--where-id', - required=True, - help='identifies the token to update by ID') -@common.arg('--user-id', - required=False, - help='change the user the token applies to, by ID') -@common.arg('--tenant-id', - required=False, - help='change the tenant this token applies to, by ID') -@common.arg('--expires', - required=False, - help="change the token's expiration date") -class Command(base.BaseBackendCommand, mixins.DateTimeMixin): - """Updates the specified token.""" - - # pylint: disable=E1101,R0913 - def update_token(self, id, user_id=None, tenant_id=None, - expires=None): - obj = self.get_token(id) - self.get_user(user_id) - self.get_tenant(tenant_id) - - if user_id is not None: - obj.user_id = user_id - - if tenant_id is not None: - obj.tenant_id = tenant_id - - if expires is not None: - obj.expires = self.str_to_datetime(expires) - - self.token_manager.update(id, obj) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.update_token(id=args.where_id, user_id=args.user_id, - tenant_id=args.tenant_id, expires=args.expires) diff --git a/keystone/manage2/commands/update_user.py b/keystone/manage2/commands/update_user.py deleted file mode 100644 index dafce642..00000000 --- a/keystone/manage2/commands/update_user.py +++ /dev/null @@ -1,62 +0,0 @@ -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--where-id', - required=True, - help='identifies the user to update by ID') -@common.arg('--name', - required=False, - help="change the user's name") -@common.arg('--password', - required=False, - help="change the user's password") -@common.arg('--email', - required=False, - help="change the user's email address") -@common.arg('--tenant_id', - required=False, - help="change the user's default tenant") -@common.arg('--enable', - action='store_true', - required=False, - default=False, - help="enable the user") -@common.arg('--disable', - action='store_true', - required=False, - default=False, - help="disable the user") -class Command(base.BaseBackendCommand): - """Updates the specified user.""" - - # pylint: disable=E1101,R0913 - def update_user(self, id, name=None, password=None, email=None, - tenant_id=None, enabled=None): - user = self.get_user(id) - - if name is not None: - user.name = name - - if password is not None: - user.password = password - - if email is not None: - user.email = email - - if tenant_id is not None: - tenant = self.get_tenant(tenant_id) - user.tenant = tenant.id - - if enabled is not None: - user.enabled = enabled - - self.user_manager.update(user) - - def run(self, args): - """Process argparse args, and print results to stdout""" - enabled = self.true_or_false(args, 'enable', 'disable') - - self.update_user(id=args.where_id, name=args.name, - password=args.password, email=args.email, - tenant_id=args.tenant_id, enabled=enabled) diff --git a/keystone/manage2/commands/upgrade_database.py b/keystone/manage2/commands/upgrade_database.py deleted file mode 100644 index 94884442..00000000 --- a/keystone/manage2/commands/upgrade_database.py +++ /dev/null @@ -1,19 +0,0 @@ -from keystone.backends.sqlalchemy import migration -from keystone.manage2 import base -from keystone.manage2 import common - - -@common.arg('--version', - required=True, - help='specify the desired database version') -class Command(base.BaseSqlalchemyCommand): - """Upgrades the database to the specified version.""" - - @staticmethod - def upgrade_database(version): - """Upgrade database to the specified version""" - migration.upgrade(Command._get_connection_string(), version=version) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.upgrade_database(version=args.version) diff --git a/keystone/manage2/commands/version.py b/keystone/manage2/commands/version.py deleted file mode 100644 index 8e97346f..00000000 --- a/keystone/manage2/commands/version.py +++ /dev/null @@ -1,53 +0,0 @@ -from keystone.backends.sqlalchemy import migration -from keystone import version -from keystone.manage2 import base -from keystone.manage2 import common -from keystone.logic.types import fault - - -@common.arg('--api', action='store_true', - default=False, - help='only print the API version') -@common.arg('--implementation', action='store_true', - default=False, - help='only print the implementation version') -@common.arg('--database', action='store_true', - default=False, - help='only print the database version') -class Command(base.BaseSqlalchemyCommand): - """Returns keystone version data. - - Provides the latest API version, implementation version, database version, - or all of the above, if none is specified. - """ - - @staticmethod - def get_api_version(): - """Returns a complete API version string""" - return ' '.join([version.API_VERSION, version.API_VERSION_STATUS]) - - @staticmethod - def get_implementation_version(): - """Returns a complete implementation version string""" - return version.version() - - @staticmethod - def get_database_version(): - """Returns database's current migration level""" - return migration.db_version(Command._get_connection_string()) - - def run(self, args): - """Process argparse args, and print results to stdout""" - show_all = not (args.api or args.implementation or args.database) - - if args.api or show_all: - print 'API v%s' % Command.get_api_version() - if args.implementation or show_all: - print 'Implementation v%s' % Command.get_implementation_version() - if args.database or show_all: - try: - version_str = 'v%s' % (self.get_database_version()) - except fault.DatabaseMigrationError: - version_str = 'not under version control' - - print 'Database %s' % (version_str) diff --git a/keystone/manage2/commands/version_control_database.py b/keystone/manage2/commands/version_control_database.py deleted file mode 100644 index f69e206c..00000000 --- a/keystone/manage2/commands/version_control_database.py +++ /dev/null @@ -1,15 +0,0 @@ -from keystone.backends.sqlalchemy import migration -from keystone.manage2 import base - - -class Command(base.BaseSqlalchemyCommand): - """Places an existing database under version control.""" - - @staticmethod - def version_control_database(): - """Place database under migration control""" - migration.version_control(Command._get_connection_string()) - - def run(self, args): - """Process argparse args, and print results to stdout""" - self.version_control_database() diff --git a/keystone/manage2/common.py b/keystone/manage2/common.py deleted file mode 100644 index 55624109..00000000 --- a/keystone/manage2/common.py +++ /dev/null @@ -1,64 +0,0 @@ -import optparse -import sys - -from keystone import backends -from keystone import config as new_config -from keystone import version -from keystone.common import config -from keystone.managers.credential import Manager as CredentialManager -from keystone.managers.endpoint import Manager as EndpointManager -from keystone.managers.endpoint_template import Manager as \ - EndpointTemplateManager -from keystone.managers.grant import Manager as GrantManager -from keystone.managers.role import Manager as RoleManager -from keystone.managers.service import Manager as ServiceManager -from keystone.managers.tenant import Manager as TenantManager -from keystone.managers.token import Manager as TokenManager -from keystone.managers.user import Manager as UserManager - - -def arg(name, **kwargs): - """Decorate the command class with an argparse argument""" - def _decorator(cls): - if not hasattr(cls, '_args'): - setattr(cls, '_args', {}) - args = getattr(cls, '_args') - args[name] = kwargs - return cls - return _decorator - - -def get_options(): - # Initialize a parser for our configuration paramaters - parser = optparse.OptionParser("Usage", version='%%prog %s' - % version.version()) - config.add_common_options(parser) - config.add_log_options(parser) - - # Parse command-line and load config - (options, args) = config.parse_options(parser, []) # pylint: disable=W0612 - - return options - - -def init_managers(): - """Initializes backend storage and return managers""" - if new_config.CONF.backends is None: - # Get merged config and CLI options and admin-specific settings - options = get_options() - config_file = config.find_config_file(options, sys.argv[1:]) - new_config.CONF(config_files=[config_file]) - - backends.configure_backends() - - managers = {} - managers['credential_manager'] = CredentialManager() - managers['token_manager'] = TokenManager() - managers['tenant_manager'] = TenantManager() - managers['endpoint_manager'] = EndpointManager() - managers['endpoint_template_manager'] = EndpointTemplateManager() - managers['user_manager'] = UserManager() - managers['role_manager'] = RoleManager() - managers['grant_manager'] = GrantManager() - managers['service_manager'] = ServiceManager() - return managers diff --git a/keystone/manage2/mixins.py b/keystone/manage2/mixins.py deleted file mode 100644 index 67d3b4fd..00000000 --- a/keystone/manage2/mixins.py +++ /dev/null @@ -1,42 +0,0 @@ -import datetime -import prettytable - - -class ListMixin(object): - """Implements common patterns for list_* commands""" - - @staticmethod - def build_table(fields): - table = prettytable.PrettyTable(fields) - - # set default alignment - for field in fields: - table.set_field_align(field, "l") - - return table - - @staticmethod - def print_table(table): - if "Name" in table.fields: - table.printt(sortby="Name") - else: - table.printt() - - -class DateTimeMixin(object): - datetime_format = '%Y-%m-%dT%H:%M' - - def datetime_to_str(self, dt): - """Return a string representing the given datetime""" - return dt.strftime(self.datetime_format) - - def str_to_datetime(self, string): - """Return a datetime representing the given string""" - return datetime.datetime.strptime(string, self.datetime_format) - - @staticmethod - def get_datetime_tomorrow(): - """Returns a datetime representing 24 hours from now""" - today = datetime.datetime.utcnow() - tomorrow = today + datetime.timedelta(days=1) - return tomorrow diff --git a/keystone/managers/__init__.py b/keystone/managers/__init__.py deleted file mode 100644 index e69de29b..00000000 --- a/keystone/managers/__init__.py +++ /dev/null diff --git a/keystone/managers/credential.py b/keystone/managers/credential.py deleted file mode 100644 index 0fc8388a..00000000 --- a/keystone/managers/credential.py +++ /dev/null @@ -1,46 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (C) 2011 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. - -""" Credential manager module """ - -import logging - -import keystone.backends.api as api - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class Manager(object): - def __init__(self): - self.driver = api.CREDENTIALS - - def create(self, token): - return self.driver.create(token) - - def update(self, id, credential): - return self.driver.update(id, credential) - - def get(self, credential_id): - return self.driver.get(credential_id) - - def get_all(self): - return self.driver.get_all() - - def get_by_access(self, access): - return self.driver.get_by_access(access) - - def delete(self, credential_id): - return self.driver.delete(credential_id) diff --git a/keystone/managers/endpoint.py b/keystone/managers/endpoint.py deleted file mode 100644 index 06401a0d..00000000 --- a/keystone/managers/endpoint.py +++ /dev/null @@ -1,65 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (C) 2011 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. - -""" Endpoint manager module """ - -import logging - -import keystone.backends.api as api - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class Manager(object): - def __init__(self): - self.driver = api.ENDPOINT_TEMPLATE - - def endpoint_get_by_endpoint_template(self, endpoint_template_id): - """ Get all endpoints by endpoint template """ - return self.driver.endpoint_get_by_endpoint_template( - endpoint_template_id) - - def delete(self, endpoint_id): - """ Delete Endpoint """ - self.driver.endpoint_delete(endpoint_id) - - def endpoint_get_by_tenant_get_page(self, tenant_id, marker, limit): - """ Get endpoints by tenant """ - return self.driver.endpoint_get_by_tenant_get_page( - tenant_id, marker, limit) - - def endpoint_get_by_tenant_get_page_markers(self, tenant_id, marker, - limit): - return self.driver.endpoint_get_by_tenant_get_page_markers( - tenant_id, marker, limit) - - def create(self, endpoint): - """ Create a new Endpoint """ - return self.driver.endpoint_add(endpoint) - - def get(self, endpoint_id): - """ Returns Endpoint by ID """ - return self.driver.endpoint_get(endpoint_id) - - # pylint: disable=E1103 - def get_by_ids(self, endpoint_template_id, tenant_id): - """ Returns Endpoint by ID """ - return self.driver.endpoint_get_by_ids(endpoint_template_id, tenant_id) - - # pylint: disable=E1103 - def get_all(self): - """ Returns all Endpoint Templates """ - return self.driver.endpoint_get_all() diff --git a/keystone/managers/endpoint_template.py b/keystone/managers/endpoint_template.py deleted file mode 100644 index 9be49e72..00000000 --- a/keystone/managers/endpoint_template.py +++ /dev/null @@ -1,69 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (C) 2011 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. - -""" EndpointTemplate manager module """ - -import logging - -import keystone.backends.api as api - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class Manager(object): - def __init__(self): - self.driver = api.ENDPOINT_TEMPLATE - - def create(self, obj): - """ Create a new Endpoint Template """ - return self.driver.create(obj) - - def get_all(self): - """ Returns all endpoint templates """ - return self.driver.get_all() - - def get(self, endpoint_template_id): - """ Returns Endpoint Template by ID """ - return self.driver.get(endpoint_template_id) - - def get_page(self, marker, limit): - """ Get one page of endpoint template list """ - return self.driver.get_page(marker, limit) - - def get_page_markers(self, marker, limit): - """ Calculate pagination markers for endpoint template list """ - return self.driver.get_page_markers(marker, limit) - - def get_by_service(self, service_id): - """ Returns Endpoint Templates by service """ - return self.driver.get_by_service(service_id) - - def get_by_service_get_page(self, service_id, marker, limit): - """ Get one page of endpoint templates by service""" - return self.driver.get_by_service_get_page(service_id, marker, limit) - - def get_by_service_get_page_markers(self, service_id, marker, limit): - """ Calculate pagination markers for endpoint templates by service """ - return self.driver.get_by_service_get_page_markers(service_id, marker, - limit) - - def update(self, endpoint_template): - """ Update Endpoint Template """ - return self.driver.update(endpoint_template['id'], endpoint_template) - - def delete(self, endpoint_template_id): - """ Delete Endpoint Template """ - self.driver.delete(endpoint_template_id) diff --git a/keystone/managers/grant.py b/keystone/managers/grant.py deleted file mode 100644 index 41104ce6..00000000 --- a/keystone/managers/grant.py +++ /dev/null @@ -1,59 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (C) 2011 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. - -""" Role-Grant manager module """ - -import logging - -import keystone.backends.api as api - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class Manager(object): - def __init__(self): - self.driver = api.ROLE - - # - # Role-Grant Methods - # - def rolegrant_get_page(self, user_id, tenant_id, marker, limit): - """ Get one page of role grant list """ - return self.driver.rolegrant_get_page(user_id, tenant_id, marker, - limit) - - def rolegrant_get_page_markers(self, user_id, tenant_id, marker, limit): - """ Calculate pagination markers for role grants list """ - return self.driver.rolegrant_get_page_markers(user_id, tenant_id, - marker, limit) - - def list_global_roles_for_user(self, user_id): - return self.driver.list_global_roles_for_user(user_id) - - def list_tenant_roles_for_user(self, user_id, tenant_id): - return self.driver.list_tenant_roles_for_user(user_id, tenant_id) - - def rolegrant_list_by_role(self, role_id): - return self.driver.rolegrant_list_by_role(role_id) - - def rolegrant_get_by_ids(self, user_id, role_id, tenant_id): - return self.driver.rolegrant_get_by_ids(user_id, role_id, tenant_id) - - def rolegrant_delete(self, grant_id): - return self.driver.rolegrant_delete(grant_id) - - def list_role_grants(self, role_id, user_id, tenant_id): - return self.driver.list_role_grants(role_id, user_id, tenant_id) diff --git a/keystone/managers/role.py b/keystone/managers/role.py deleted file mode 100644 index 0c3782e5..00000000 --- a/keystone/managers/role.py +++ /dev/null @@ -1,74 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (C) 2011 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. - -""" Role manager module """ - -import logging - -import keystone.backends.api as api - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class Manager(object): - def __init__(self): - self.driver = api.ROLE - - def create(self, role): - """ Create a new role """ - return self.driver.create(role) - - def get(self, role_id): - """ Returns role by ID """ - return self.driver.get(role_id) - - def get_by_name(self, name): - """ Returns role by name """ - return self.driver.get_by_name(name=name) - - def get_all(self): - """ Returns all roles """ - return self.driver.get_all() - - def get_page(self, marker, limit): - """ Get one page of roles list """ - return self.driver.get_page(marker, limit) - - def get_page_markers(self, marker, limit): - """ Calculate pagination markers for roles list """ - return self.driver.get_page_markers(marker, limit) - - def get_by_service(self, service_id): - """ Returns role by service """ - return self.driver.get_by_service(service_id) - - def get_by_service_get_page(self, service_id, marker, limit): - """ Get one page of roles by service""" - return self.driver.get_by_service_get_page(service_id, marker, limit) - - def get_by_service_get_page_markers(self, service_id, marker, limit): - """ Calculate pagination markers for roles by service """ - return self.driver.get_by_service_get_page_markers(service_id, marker, - limit) - - # pylint: disable=E1103 - def update(self, role): - """ Update role """ - return self.driver.update(role['id'], role) - - def delete(self, role_id): - """ Delete role """ - self.driver.delete(role_id) diff --git a/keystone/managers/service.py b/keystone/managers/service.py deleted file mode 100644 index c3063be1..00000000 --- a/keystone/managers/service.py +++ /dev/null @@ -1,65 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (C) 2011 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. - -""" Service manager module """ - -import logging - -import keystone.backends.api as api - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class Manager(object): - def __init__(self): - self.driver = api.SERVICE - - def create(self, service): - """ Create a new service """ - return self.driver.create(service) - - def get(self, service_id): - """ Returns service by ID """ - return self.driver.get(service_id) - - def get_by_name(self, name): - """ Returns service by name """ - return self.driver.get_by_name(name=name) - - def get_all(self): - """ Returns all services """ - return self.driver.get_all() - - def get_page(self, marker, limit): - """ Get one page of services list """ - return self.driver.get_page(marker, limit) - - def get_page_markers(self, marker, limit): - """ Calculate pagination markers for services list """ - return self.driver.get_page_markers(marker, limit) - - def get_by_name_and_type(self, name, service_type): - """ Returns service by name and type """ - return self.driver.get_by_name_and_type(name, service_type) - - # pylint: disable=E1103 - def update(self, service): - """ Update service """ - return self.driver.update(service['id'], service) - - def delete(self, service_id): - """ Delete service """ - self.driver.delete(service_id) diff --git a/keystone/managers/tenant.py b/keystone/managers/tenant.py deleted file mode 100644 index d69c089f..00000000 --- a/keystone/managers/tenant.py +++ /dev/null @@ -1,75 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (C) 2011 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. - -""" Tenant manager module - -TODO: move functionality into here. Ex: - - def get_tenant(self, context, tenant_id): - '''Return info for a tenant if it is valid.''' - return self.driver.get(tenant_id) -""" - -import logging - -import keystone.backends.api as api - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class Manager(object): - def __init__(self): - self.driver = api.TENANT - - def create(self, tenant): - return self.driver.create(tenant) - - def get(self, tenant_id): - """ Returns tenant by ID """ - return self.driver.get(tenant_id) - - def get_by_name(self, name): - """ Returns tenant by name """ - return self.driver.get_by_name(name=name) - - def get_all(self): - """ Returns all tenants """ - return self.driver.get_all() - - def get_page(self, marker, limit): - """ Get one page of tenants """ - return self.driver.get_page(marker, limit) - - def get_page_markers(self, marker, limit): - """ Calculate pagination markers for tenant list """ - return self.driver.get_page_markers(marker, limit) - - def list_for_user_get_page(self, user_id, marker, limit): - return self.driver.list_for_user_get_page(user_id, marker, limit) - - def list_for_user_get_page_markers(self, user_id, marker, limit): - return self.driver.list_for_user_get_page_markers(user_id, marker, - limit) - - def update(self, tenant): - """ Update tenant """ - return self.driver.update(tenant['id'], tenant) - - def delete(self, tenant_id): - self.driver.delete(tenant_id) - - def get_all_endpoints(self, tenant_id): - return self.driver.get_all_endpoints(tenant_id) diff --git a/keystone/managers/token.py b/keystone/managers/token.py deleted file mode 100644 index 9f21b35a..00000000 --- a/keystone/managers/token.py +++ /dev/null @@ -1,61 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (C) 2011 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. - -""" Token manager module """ - -import logging - -import keystone.backends.api as api - -LOG = logging.getLogger(__name__) - - -class Manager(object): - def __init__(self): - self.driver = api.TOKEN - - def create(self, token): - return self.driver.create(token) - - # pylint: disable=E1103 - def update(self, id, token): - return self.driver.update(id, token) - - def get(self, token_id): - """ Returns token by ID """ - return self.driver.get(token_id) - - def get_all(self): - """ Returns all tokens """ - return self.driver.get_all() - - def find(self, user_id, tenant_id=None): - """ Finds token by user ID and, optionally, tenant ID - - :param user_id: user id as a string - :param tenant_id: tenant id as a string (optional) - :returns: Token object or None - :raises: RuntimeError is user_id is None - """ - if user_id is None: - raise RuntimeError("User ID is required when looking up tokens") - if tenant_id: - return self.driver.get_for_user_by_tenant(user_id, tenant_id) - else: - return self.driver.get_for_user(user_id) - - def delete(self, token_id): - self.driver.delete(token_id) diff --git a/keystone/managers/user.py b/keystone/managers/user.py deleted file mode 100644 index 6ed049d0..00000000 --- a/keystone/managers/user.py +++ /dev/null @@ -1,84 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (C) 2011 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. - -""" User manager module """ - -import logging - -import keystone.backends.api as api - -LOG = logging.getLogger(__name__) - - -class Manager(object): - def __init__(self): - self.driver = api.USER - - def create(self, user): - """ Create user from dict or model, assign id if not there """ - return self.driver.create(user) - - def get(self, user_id): - """ Returns user by ID """ - return self.driver.get(user_id) - - def get_by_name(self, name): - """ Returns user by name """ - return self.driver.get_by_name(name=name) - - def get_by_email(self, email): - """ Returns user by email """ - return self.driver.get_by_email(email=email) - - def get_all(self): - """ Returns all users """ - return self.driver.get_all() - - def users_get_page(self, marker, limit): - """ Get one page of users list """ - return self.driver.users_get_page(marker, limit) - - def users_get_page_markers(self, marker, limit): - """ Calculate pagination markers for users list """ - return self.driver.users_get_page_markers(marker, limit) - - def get_by_tenant(self, user_id, tenant_id): - """ Get user if associated with tenant, else None """ - return self.driver.get_by_tenant(user_id, tenant_id) - - def users_get_by_tenant_get_page(self, tenant_id, role_id, marker, limit): - """ Get one page of users list for a tenant """ - return self.driver.users_get_by_tenant_get_page( - tenant_id, role_id, marker, limit) - - def users_get_by_tenant_get_page_markers(self, tenant_id, role_id, - marker, limit): - """ Calculate pagination markers for users list on a tenant """ - return self.driver.users_get_by_tenant_get_page_markers( - tenant_id, role_id, marker, limit) - - def update(self, user): - """ Update user """ - return self.driver.update(user['id'], user) - - def delete(self, user_id): - self.driver.delete(user_id) - - def check_password(self, user_id, password): - return self.driver.check_password(user_id, password) - - def user_role_add(self, values): - self.driver.user_role_add(values) diff --git a/keystone/middleware/__init__.py b/keystone/middleware/__init__.py index e69de29b..e2e9a993 100644 --- a/keystone/middleware/__init__.py +++ b/keystone/middleware/__init__.py @@ -0,0 +1 @@ +from keystone.middleware.core import * diff --git a/keystone/middleware/auth_basic.py b/keystone/middleware/auth_basic.py deleted file mode 100644 index 3a5ce416..00000000 --- a/keystone/middleware/auth_basic.py +++ /dev/null @@ -1,190 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-2011 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. - - -""" -BASIC AUTH MIDDLEWARE - STUB - -This WSGI component should perform multiple jobs: - -* validate incoming basic claims -* perform all basic auth interactions with clients -* collect and forward identity information from the authentication process - such as user name, groups, etc... - -This is an Auth component as per: http://wiki.openstack.org/openstack-authn - -""" - -import eventlet -from eventlet import wsgi -import os -import logging -from paste.deploy import loadapp -import urlparse -from webob.exc import Request, Response -from webob.exc import HTTPUnauthorized - -from keystone.common.bufferedhttp import http_connect_raw as http_connect - -PROTOCOL_NAME = "Basic Authentication" - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -def _decorate_request_headers(header, value, proxy_headers, env): - proxy_headers[header] = value - env["HTTP_%s" % header] = value - - -class AuthProtocol(object): - """Auth Middleware that handles authenticating client calls""" - - def __init__(self, app, conf): - logger.info("Starting the %s component", PROTOCOL_NAME) - - self.conf = conf - self.app = app - #if app is set, then we are in a WSGI pipeline and requests get passed - # on to app. If it is not set, this component should forward requests - - # where to find the OpenStack service (if not in local WSGI chain) - # these settings are only used if this component is acting as a proxy - # and the OpenSTack service is running remotely - self.service_protocol = conf.get('service_protocol', 'https') - self.service_host = conf.get('service_host') - self.service_port = int(conf.get('service_port')) - self.service_url = '%s://%s:%s' % (self.service_protocol, - self.service_host, - self.service_port) - # used to verify this component with the OpenStack service or PAPIAuth - self.service_pass = conf.get('service_pass') - - # delay_auth_decision means we still allow unauthenticated requests - # through and we let the downstream service make the final decision - self.delay_auth_decision = int(conf.get('delay_auth_decision', 0)) - - def __call__(self, env, start_response): - def custom_start_response(status, headers): - if self.delay_auth_decision: - headers.append(('WWW-Authenticate', - "Basic realm='Use guest/guest'")) - return start_response(status, headers) - - #Prep headers to proxy request to remote service - proxy_headers = env.copy() - user = '' - - #Look for authentication - if 'HTTP_AUTHORIZATION' not in env: - #No credentials were provided - if self.delay_auth_decision: - _decorate_request_headers("X_IDENTITY_STATUS", "Invalid", - proxy_headers, env) - else: - # If the user isn't authenticated, we reject the request and - # return 401 indicating we need Basic Auth credentials. - ret = HTTPUnauthorized("Authentication required", - [('WWW-Authenticate', - 'Basic realm="Use guest/guest"')]) - return ret(env, start_response) - else: - # Claims were provided - validate them - import base64 - auth_header = env['HTTP_AUTHORIZATION'] - _auth_type, encoded_creds = auth_header.split(None, 1) - user, password = base64.b64decode(encoded_creds).split(':', 1) - if not self.validateCreds(user, password): - #Claims were rejected - if not self.delay_auth_decision: - # Reject request (or ask for valid claims) - ret = HTTPUnauthorized("Authentication required", - [('WWW-Authenticate', - 'Basic realm="Use guest/guest"')]) - return ret(env, start_response) - else: - # Claims are valid, forward request - _decorate_request_headers("X_IDENTITY_STATUS", "Invalid", - proxy_headers, env) - - # TODO(Ziad): add additional details we may need, - # like tenant and group info - _decorate_request_headers('X_AUTHORIZATION', "Proxy %s" % user, - proxy_headers, env) - _decorate_request_headers("X_IDENTITY_STATUS", "Confirmed", - proxy_headers, env) - _decorate_request_headers('X_TENANT', 'blank', - proxy_headers, env) - #Auth processed, headers added now decide how to pass on the call - if self.app: - # Pass to downstream WSGI component - env['HTTP_AUTHORIZATION'] = "Basic %s" % self.service_pass - return self.app(env, custom_start_response) - - proxy_headers['AUTHORIZATION'] = "Basic %s" % self.service_pass - # We are forwarding to a remote service (no downstream WSGI app) - req = Request(proxy_headers) - parsed = urlparse(req.url) - conn = http_connect(self.service_host, self.service_port, \ - req.method, parsed.path, \ - proxy_headers, \ - ssl=(self.service_protocol == 'https')) - resp = conn.getresponse() - data = resp.read() - #TODO(ziad): use a more sophisticated proxy - # we are rewriting the headers now - return Response(status=resp.status, body=data)(env, start_response) - - def validateCreds(self, username, password): - #stub for password validation. - # import ConfigParser - # import hashlib - #usersConfig = ConfigParser.ConfigParser() - #usersConfig.readfp(open('/etc/openstack/users.ini')) - #password = hashlib.sha1(password).hexdigest() - #for un, pwd in usersConfig.items('users'): - #TODO(Ziad): add intelligent credential validation (instead of hard - # coded) - if username == 'guest' and password == 'guest': - return True - return False - - -def filter_factory(global_conf, ** local_conf): - """Returns a WSGI filter app for use with paste.deploy.""" - conf = global_conf.copy() - conf.update(local_conf) - - def auth_filter(app): - return AuthProtocol(app, conf) - return auth_filter - - -def app_factory(global_conf, ** local_conf): - conf = global_conf.copy() - conf.update(local_conf) - return AuthProtocol(None, conf) - -if __name__ == "__main__": - app = loadapp("config:" + \ - os.path.join(os.path.abspath(os.path.dirname(__file__)), - os.pardir, - os.pardir, - "examples/paste/auth_basic.ini"), - global_conf={"log_name": "auth_basic.log"}) - wsgi.server(eventlet.listen(('', 8090)), app) diff --git a/keystone/middleware/auth_openid.py b/keystone/middleware/auth_openid.py deleted file mode 100644 index 3debe2a4..00000000 --- a/keystone/middleware/auth_openid.py +++ /dev/null @@ -1,125 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-2011 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. - -""" -OPENID AUTH MIDDLEWARE - STUB - -This WSGI component should perform multiple jobs: -- validate incoming openid claims -- perform all openid interactions with clients -- collect and forward identity information from the openid authentication - such as user name, groups, etc... - -This is an Auth component as per: http://wiki.openstack.org/openstack-authn -""" - -import logging -import eventlet -from eventlet import wsgi -import os -from paste.deploy import loadapp -import urlparse -from webob.exc import Request, Response - -from keystone.common.bufferedhttp import http_connect_raw as http_connect - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - -PROTOCOL_NAME = "OpenID Authentication" - - -class AuthProtocol(object): - """Auth Middleware that handles authenticating client calls""" - - def __init__(self, app, conf): - logger.info("Starting the %s component", PROTOCOL_NAME) - - self.conf = conf - self.app = app - #if app is set, then we are in a WSGI pipeline and requests get passed - # on to app. If it is not set, this component should forward requests - - # where to find the OpenStack service (if not in local WSGI chain) - # these settings are only used if this component is acting as a proxy - # and the OpenSTack service is running remotely - self.service_protocol = conf.get('service_protocol', 'http') - self.service_host = conf.get('service_host', '127.0.0.1') - self.service_port = int(conf.get('service_port', 8090)) - self.service_url = '%s://%s:%s' % (self.service_protocol, - self.service_host, - self.service_port) - # used to verify this component with the OpenStack service or PAPIAuth - self.service_pass = conf.get('service_pass', 'dTpw') - - # delay_auth_decision means we still allow unauthenticated requests - # through and we let the downstream service make the final decision - self.delay_auth_decision = int(conf.get('delay_auth_decision', 0)) - - def __call__(self, env, start_response): - def custom_start_response(status, headers): - if self.delay_auth_decision: - headers.append(('WWW-Authenticate', "Basic realm='API Realm'")) - return start_response(status, headers) - - #TODO(Rasib): PERFORM OPENID AUTH - - #Auth processed, headers added now decide how to pass on the call - if self.app: - # Pass to downstream WSGI component - env['HTTP_AUTHORIZATION'] = "Basic %s" % self.service_pass - return self.app(env, custom_start_response) - - proxy_headers = [] - proxy_headers['AUTHORIZATION'] = "Basic %s" % self.service_pass - # We are forwarding to a remote service (no downstream WSGI app) - req = Request(proxy_headers) - parsed = urlparse(req.url) - conn = http_connect(self.service_host, self.service_port, \ - req.method, parsed.path, \ - proxy_headers, \ - ssl=(self.service_protocol == 'https')) - resp = conn.getresponse() - data = resp.read() - #TODO(ziad): use a more sophisticated proxy - # we are rewriting the headers now - return Response(status=resp.status, body=data)(env, start_response) - - -def filter_factory(global_conf, **local_conf): - """Returns a WSGI filter app for use with paste.deploy.""" - conf = global_conf.copy() - conf.update(local_conf) - - def auth_filter(app): - return AuthProtocol(app, conf) - return auth_filter - - -def app_factory(global_conf, **local_conf): - conf = global_conf.copy() - conf.update(local_conf) - return AuthProtocol(None, conf) - -if __name__ == "__main__": - app = loadapp("config:" + \ - os.path.join(os.path.abspath(os.path.dirname(__file__)), - os.pardir, - os.pardir, - "examples/paste/auth_openid.ini"), - global_conf={"log_name": "auth_openid.log"}) - wsgi.server(eventlet.listen(('', 8090)), app) diff --git a/keystone/middleware/auth_token.py b/keystone/middleware/auth_token.py index 8cde88a6..4a0d501a 100644..100755 --- a/keystone/middleware/auth_token.py +++ b/keystone/middleware/auth_token.py @@ -1,6 +1,5 @@ -#!/usr/bin/env python # vim: tabstop=4 shiftwidth=4 softtabstop=4 -# + # Copyright (c) 2010-2011 OpenStack, LLC. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,17 +18,18 @@ """ TOKEN-BASED AUTH MIDDLEWARE -This WSGI component: +This WSGI component performs multiple jobs: -* Verifies that incoming client requests have valid tokens by validating +* it verifies that incoming client requests have valid tokens by verifying tokens with the auth service. -* Rejects unauthenticated requests UNLESS it is in 'delay_auth_decision' +* it will reject unauthenticated requests UNLESS it is in 'delay_auth_decision' mode, which means the final decision is delegated to the downstream WSGI component (usually the OpenStack service) -* Collects and forwards identity information based on a valid token - such as user name, tenant, etc +* it will collect and forward identity information from a valid token + such as user name etc... + +Refer to: http://wiki.openstack.org/openstack-authn -Refer to: http://keystone.openstack.org/middleware_architecture.html HEADERS ------- @@ -41,11 +41,11 @@ Coming in from initial call from client or customer ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ HTTP_X_AUTH_TOKEN - The client token being passed in. + the client token being passed in HTTP_X_STORAGE_TOKEN - The client token being passed in (legacy Rackspace use) to support - swift/cloud files + the client token being passed in (legacy Rackspace use) to support + cloud files Used for communication between components ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -60,93 +60,35 @@ What we add to the request for use by the OpenStack service ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ HTTP_X_AUTHORIZATION - The client identity being passed in - -HTTP_X_IDENTITY_STATUS - 'Confirmed' or 'Invalid' - The underlying service will only see a value of 'Invalid' if the Middleware - is configured to run in 'delay_auth_decision' mode - -HTTP_X_TENANT - *Deprecated* in favor of HTTP_X_TENANT_ID and HTTP_X_TENANT_NAME - Keystone-assigned unique identifier, deprecated - -HTTP_X_TENANT_ID - Identity service managed unique identifier, string - -HTTP_X_TENANT_NAME - Unique tenant identifier, string - -HTTP_X_USER - *Deprecated* in favor of HTTP_X_USER_ID and HTTP_X_USER_NAME - Unique user name, string - -HTTP_X_USER_ID - Identity-service managed unique identifier, string - -HTTP_X_USER_NAME - Unique user identifier, string - -HTTP_X_ROLE - *Deprecated* in favor of HTTP_X_ROLES - This is being renamed, and the new header contains the same data. - -HTTP_X_ROLES - Comma delimited list of case-sensitive Roles + the client identity being passed in """ - -from datetime import datetime -from dateutil import parser -import errno -import eventlet -from eventlet import wsgi import httplib import json -# memcache is imported in __init__ if memcache caching is configured -import logging import os -from paste.deploy import loadapp -import time -import urllib + +import eventlet +from eventlet import wsgi +from paste import deploy from urlparse import urlparse +import webob +import webob.exc from webob.exc import HTTPUnauthorized -from webob.exc import Request, Response from keystone.common.bufferedhttp import http_connect_raw as http_connect -logger = logging.getLogger(__name__) # pylint: disable=C0103 - -PROTOCOL_NAME = "Token Authentication" -# The time format of the 'expires' property of a token -EXPIRE_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f" -MAX_CACHE_TIME = 86400 - - -class ValidationFailed(Exception): - pass - - -class TokenExpired(Exception): - pass - - -class KeystoneUnreachable(Exception): - pass +PROTOCOL_NAME = 'Token Authentication' class AuthProtocol(object): """Auth Middleware that handles authenticating client calls""" - # pylint: disable=W0613 def _init_protocol_common(self, app, conf): - """ Common initialization code - - When we eventually superclass this, this will be the superclass - initialization code that applies to all protocols - """ - logger.info("Starting the %s component", PROTOCOL_NAME) + """ Common initialization code""" + print 'Starting the %s component' % PROTOCOL_NAME + self.conf = conf + self.app = app #if app is set, then we are in a WSGI pipeline and requests get passed # on to app. If it is not set, this component should forward requests @@ -155,18 +97,10 @@ class AuthProtocol(object): # and the OpenSTack service is running remotely self.service_protocol = conf.get('service_protocol', 'https') self.service_host = conf.get('service_host') - service_port = conf.get('service_port') - service_ids = conf.get('service_ids') - self.service_id_querystring = '' - if service_ids: - self.service_id_querystring = '?HP-IDM-serviceId=%s' % \ - (urllib.quote(service_ids)) - if service_port: - self.service_port = int(service_port) + self.service_port = int(conf.get('service_port')) self.service_url = '%s://%s:%s' % (self.service_protocol, self.service_host, self.service_port) - self.service_timeout = conf.get('service_timeout', 30) # used to verify this component with the OpenStack service or PAPIAuth self.service_pass = conf.get('service_pass') @@ -181,94 +115,27 @@ class AuthProtocol(object): self.auth_host = conf.get('auth_host') self.auth_port = int(conf.get('auth_port')) self.auth_protocol = conf.get('auth_protocol', 'https') - self.auth_timeout = float(conf.get('auth_timeout', 30)) # where to tell clients to find the auth service (default to url # constructed based on endpoint we have for the service to use) self.auth_location = conf.get('auth_uri', - "%s://%s:%s" % (self.auth_protocol, + '%s://%s:%s' % (self.auth_protocol, self.auth_host, self.auth_port)) - logger.debug("Authentication Service:%s", self.auth_location) # Credentials used to verify this component with the Auth service since # validating tokens is a privileged call self.admin_token = conf.get('admin_token') - self.admin_user = conf.get('admin_user', None) - self.admin_password = conf.get('admin_password', None) - # Certificate file and key file used to authenticate with Keystone - # server - self.cert_file = conf.get('certfile', None) - self.key_file = conf.get('keyfile', None) - # Caching - self.cache = conf.get('cache', None) - self.memcache_hosts = conf.get('memcache_hosts', None) - if self.memcache_hosts: - if self.cache is None: - self.cache = "keystone.cache" - self.tested_for_osksvalidate = False - self.last_test_for_osksvalidate = None - self.osksvalidate = self._supports_osksvalidate() def __init__(self, app, conf): """ Common initialization code """ + #TODO(ziad): maybe we refactor this into a superclass - # Defining instance variables here for improving pylint score - # NOTE(salvatore-orlando): the following vars are assigned values - # either in init_protocol or init_protocol_common. We should not - # worry about them being initialized to None - self.conf = conf - self.app = app - self.admin_password = None - self.admin_token = None - self.admin_user = None - self.auth_api_version = None - self.auth_host = None - self.auth_location = None - self.auth_port = None - self.auth_protocol = None - self.auth_timeout = None - self.cert_file = None - self.key_file = None - self.delay_auth_decision = None - self.service_pass = None - self.service_host = None - self.service_port = None - self.service_protocol = None - self.service_timeout = None - self.service_url = None - self.service_id_querystring = None - self.osksvalidate = None - self.tested_for_osksvalidate = None - self.last_test_for_osksvalidate = None - self.cache = None - self.memcache_hosts = None self._init_protocol_common(app, conf) # Applies to all protocols self._init_protocol(conf) # Specific to this protocol def __call__(self, env, start_response): """ Handle incoming request. Authenticate. And send downstream. """ - logger.debug("entering AuthProtocol.__call__") - # Initialize caching client - if self.memcache_hosts: - # This will only be used if the configuration calls for memcache - import memcache - - if env.get(self.cache, None) is None: - memcache_client = memcache.Client([self.memcache_hosts]) - env[self.cache] = memcache_client - - # Check if we're set up to use OS-KSVALIDATE periodically if not on - if self.tested_for_osksvalidate != True: - if self.last_test_for_osksvalidate is None or \ - (time.time() - self.last_test_for_osksvalidate) > 60: - # Try test again every 60 seconds if failed - # this also handles if middleware was started before - # the keystone server - try: - self.osksvalidate = self._supports_osksvalidate() - except (httplib.HTTPException, StandardError): - pass #Prep headers to forward request to local or remote downstream service proxy_headers = env.copy() @@ -278,410 +145,230 @@ class AuthProtocol(object): del proxy_headers[header] #Look for authentication claims - token = self._get_claims(env) - if not token: - logger.debug("No claims provided") + claims = self._get_claims(env) + if not claims: + #No claim(s) provided if self.delay_auth_decision: #Configured to allow downstream service to make final decision. #So mark status as Invalid and forward the request downstream - logger.debug("delay_auth_decision is %s, so sending request " - "down the pipeline" % self.delay_auth_decision) - self._decorate_request("X_IDENTITY_STATUS", - "Invalid", env, proxy_headers) + self._decorate_request('X_IDENTITY_STATUS', + 'Invalid', env, proxy_headers) else: #Respond to client as appropriate for this auth protocol return self._reject_request(env, start_response) else: # this request is presenting claims. Let's validate them - try: - claims = self._verify_claims(env, token) - except (ValidationFailed, TokenExpired): + valid = self._validate_claims(claims) + if not valid: # Keystone rejected claim if self.delay_auth_decision: # Downstream service will receive call still and decide - self._decorate_request("X_IDENTITY_STATUS", - "Invalid", env, proxy_headers) + self._decorate_request('X_IDENTITY_STATUS', + 'Invalid', env, proxy_headers) else: #Respond to client as appropriate for this auth protocol return self._reject_claims(env, start_response) else: - self._decorate_request("X_IDENTITY_STATUS", - "Confirmed", env, proxy_headers) + self._decorate_request('X_IDENTITY_STATUS', + 'Confirmed', env, proxy_headers) + + #Collect information about valid claims + if valid: + claims = self._expound_claims(claims) # Store authentication data if claims: - self._decorate_request('X_AUTHORIZATION', "Proxy %s" % - claims['user']['name'], env, proxy_headers) - - self._decorate_request('X_TENANT_ID', - claims['tenant']['id'], env, proxy_headers) - self._decorate_request('X_TENANT_NAME', - claims['tenant']['name'], env, proxy_headers) - - self._decorate_request('X_USER_ID', - claims['user']['id'], env, proxy_headers) - self._decorate_request('X_USER_NAME', - claims['user']['name'], env, proxy_headers) - - roles = ','.join(claims['roles']) - self._decorate_request('X_ROLES', - roles, env, proxy_headers) + self._decorate_request('X_AUTHORIZATION', 'Proxy %s' % + claims['user'], env, proxy_headers) - # Deprecated in favor of X_TENANT_ID and _NAME + # For legacy compatibility before we had ID and Name self._decorate_request('X_TENANT', - claims['tenant']['id'], env, proxy_headers) + claims['tenant'], env, proxy_headers) - # Deprecated in favor of X_USER_ID and _NAME - # TODO(zns): documentation says this should be the username - # the user logged in with. We've been returning the id... - self._decorate_request('X_USER', - claims['user']['id'], env, proxy_headers) + # Services should use these + self._decorate_request('X_TENANT_NAME', + claims.get('tenant_name', claims['tenant']), + env, proxy_headers) + self._decorate_request('X_TENANT_ID', + claims['tenant'], env, proxy_headers) - # Deprecated in favor of X_ROLES - self._decorate_request('X_ROLE', - roles, env, proxy_headers) + self._decorate_request('X_USER', + claims['user'], env, proxy_headers) + if 'roles' in claims and len(claims['roles']) > 0: + if claims['roles'] != None: + roles = '' + for role in claims['roles']: + if len(roles) > 0: + roles += ',' + roles += role + self._decorate_request('X_ROLE', + roles, env, proxy_headers) + + # NOTE(todd): unused + self.expanded = True #Send request downstream return self._forward_request(env, start_response, proxy_headers) - @staticmethod - def _convert_date(date): - """ Convert datetime to unix timestamp for caching """ - return time.mktime(parser.parse(date).utctimetuple()) - - # pylint: disable=W0613 - @staticmethod - def _protect_claims(token, claims): - """ encrypt or mac claims if necessary """ - return claims + # NOTE(todd): unused + def get_admin_auth_token(self, username, password): + """ + This function gets an admin auth token to be used by this service to + validate a user's token. Validate_token is a priviledged call so + it needs to be authenticated by a service that is calling it + """ + headers = {'Content-type': 'application/json', + 'Accept': 'application/json'} + params = {'passwordCredentials': {'username': username, + 'password': password, + 'tenantId': '1'}} + conn = httplib.HTTPConnection('%s:%s' \ + % (self.auth_host, self.auth_port)) + conn.request('POST', '/v2.0/tokens', json.dumps(params), \ + headers=headers) + response = conn.getresponse() + data = response.read() + return data - # pylint: disable=W0613 - @staticmethod - def _unprotect_claims(token, pclaims): - """ decrypt or demac claims if necessary """ - return pclaims - - def _cache_put(self, env, token, claims, valid): - """ Put a claim into the cache """ - cache = self._cache(env) - if cache and claims: - key = 'tokens/%s' % (token) - if "timeout" in cache.set.func_code.co_varnames: - # swift cache - expires = self._convert_date(claims['expires']) - claims = self._protect_claims(token, claims) - cache.set(key, (claims, expires, valid), - timeout=expires - time.time()) - else: - # normal memcache client - expires = self._convert_date(claims['expires']) - timeout = expires - time.time() - if timeout > MAX_CACHE_TIME or not valid: - # Limit cache to one day (and cache bad tokens for a day) - timeout = MAX_CACHE_TIME - claims = self._protect_claims(token, claims) - cache.set(key, (claims, expires, valid), time=timeout) - - def _cache_get(self, env, token): - """ Return claim and relevant information (expiration and validity) - from cache """ - cache = self._cache(env) - if cache: - key = 'tokens/%s' % (token) - cached_claims = cache.get(key) - if cached_claims: - claims, expires, valid = cached_claims - if valid: - if "timeout" in cache.set.func_code.co_varnames: - if expires > time.time(): - claims = self._unprotect_claims(token, claims) - else: - if expires > time.time(): - claims = self._unprotect_claims(token, claims) - return (claims, expires, valid) - return None - - def _cache(self, env): - """ Return a cache to use for token caching, or none """ - if self.cache is not None: - return env.get(self.cache, None) - return None - - @staticmethod - def _get_claims(env): + def _get_claims(self, env): """Get claims from request""" - logger.debug("Looking for authentication claims in _get_claims") claims = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN')) return claims def _reject_request(self, env, start_response): """Redirect client to auth server""" - logger.debug("Rejecting request - authentication required") - return HTTPUnauthorized("Authentication required", - [("WWW-Authenticate", + return webob.exc.HTTPUnauthorized('Authentication required', + [('WWW-Authenticate', "Keystone uri='%s'" % self.auth_location)])(env, start_response) - @staticmethod - def _reject_claims(env, start_response): + def _reject_claims(self, env, start_response): """Client sent bad claims""" - logger.debug("Rejecting request - bad claim or token") - return HTTPUnauthorized()(env, + return webob.exc.HTTPUnauthorized()(env, start_response) - def _build_token_uri(self): - return '/v2.0/tokens/%s' % self.service_id_querystring - - def _get_admin_auth_token(self, username, password): - """ - This function gets an admin auth token to be used by this service to - validate a user's token. Validate_token is a priviledged call so - it needs to be authenticated by a service that is calling it - """ - headers = { - "Content-type": "application/json", - "Accept": "application/json"} - params = { - "auth": { - "passwordCredentials": { - "username": username, - "password": password, - } - } - } - if self.auth_protocol == "http": - conn = httplib.HTTPConnection(self.auth_host, self.auth_port) - else: - conn = httplib.HTTPSConnection(self.auth_host, self.auth_port, - cert_file=self.cert_file) - conn.request("POST", self._build_token_uri(), json.dumps(params), - headers=headers) - response = conn.getresponse() - data = response.read() - return data - - def _verify_claims(self, env, claims, retry=True): - """Verify claims and extract identity information, if applicable.""" - - cached_claims = self._cache_get(env, claims) - if cached_claims: - logger.debug("Found cached claims") - claims, expires, valid = cached_claims - if not valid: - logger.debug("Claims not valid (according to cache)") - raise ValidationFailed() - if expires <= time.time(): - logger.debug("Claims (token) expired (according to cache)") - raise TokenExpired() - return claims + def _validate_claims(self, claims): + """Validate claims, and provide identity information isf applicable """ # Step 1: We need to auth with the keystone service, so get an # admin token - if not self.admin_token: - auth = self._get_admin_auth_token(self.admin_user, - self.admin_password) - self.admin_token = json.loads(auth)["access"]["token"]["id"] + #TODO(ziad): Need to properly implement this, where to store creds + # for now using token from ini + #auth = self.get_admin_auth_token('admin', 'secrete', '1') + #admin_token = json.loads(auth)['auth']['token']['id'] # Step 2: validate the user's token with the auth service # since this is a priviledged op,m we need to auth ourselves # by using an admin token - headers = {"Content-type": "application/json", - "Accept": "application/json", - "X-Auth-Token": self.admin_token} - if self.osksvalidate: - headers['X-Subject-Token'] = claims - path = '/v2.0/OS-KSVALIDATE/token/validate/%s' % \ - self.service_id_querystring - logger.debug("Connecting to %s://%s:%s to check claims using the" - "OS-KSVALIDATE extension" % (self.auth_protocol, - self.auth_host, self.auth_port)) - else: - path = '/v2.0/tokens/%s%s' % (claims, self.service_id_querystring) - logger.debug("Connecting to %s://%s:%s to check claims" % ( - self.auth_protocol, self.auth_host, self.auth_port)) + headers = {'Content-type': 'application/json', + 'Accept': 'application/json', + 'X-Auth-Token': self.admin_token} + ##TODO(ziad):we need to figure out how to auth to keystone + #since validate_token is a priviledged call + #Khaled's version uses creds to get a token + # 'X-Auth-Token': admin_token} + # we're using a test token from the ini file for now + conn = http_connect(self.auth_host, self.auth_port, 'GET', + '/v2.0/tokens/%s' % claims, headers=headers) + resp = conn.getresponse() + # data = resp.read() + conn.close() - try: - conn = http_connect(self.auth_host, self.auth_port, 'GET', - path, - headers=headers, - ssl=(self.auth_protocol == 'https'), - key_file=self.key_file, - cert_file=self.cert_file, - timeout=self.auth_timeout) - resp = conn.getresponse() - data = resp.read() - except EnvironmentError as exc: - if exc.errno == errno.ECONNREFUSED: - logger.error("Keystone server not responding on %s://%s:%s " - "to check claims" % (self.auth_protocol, - self.auth_host, - self.auth_port)) - raise KeystoneUnreachable("Unable to connect to authentication" - " server") - else: - logger.exception(exc) - raise + if not str(resp.status).startswith('20'): + # Keystone rejected claim + return False + else: + #TODO(Ziad): there is an optimization we can do here. We have just + #received data from Keystone that we can use instead of making + #another call in _expound_claims + return True + + def _expound_claims(self, claims): + # Valid token. Get user data and put it in to the call + # so the downstream service can use it + headers = {'Content-type': 'application/json', + 'Accept': 'application/json', + 'X-Auth-Token': self.admin_token} + ##TODO(ziad):we need to figure out how to auth to keystone + #since validate_token is a priviledged call + #Khaled's version uses creds to get a token + # 'X-Auth-Token': admin_token} + # we're using a test token from the ini file for now + conn = http_connect(self.auth_host, self.auth_port, 'GET', + '/v2.0/tokens/%s' % claims, headers=headers) + resp = conn.getresponse() + data = resp.read() + conn.close() - logger.debug("Response received: %s" % resp.status) if not str(resp.status).startswith('20'): - # Cache it if there is a cache available - if self.cache: - logger.debug("Caching that results were invalid") - self._cache_put(env, claims, - claims={'expires': - datetime.strftime(time.time(), - EXPIRE_TIME_FORMAT)}, - valid=False) - if retry: - self.admin_token = None - return self._verify_claims(env, claims, False) - else: - # Keystone rejected claim - logger.debug("Failing the validation") - raise ValidationFailed() + raise LookupError('Unable to locate claims: %s' % resp.status) token_info = json.loads(data) + roles = [] + role_refs = token_info['access']['user']['roles'] + if role_refs != None: + for role_ref in role_refs: + # Nova looks for the non case-sensitive role 'Admin' + # to determine admin-ness + roles.append(role_ref['name']) - roles = [role['name'] for role in token_info[ - "access"]["user"]["roles"]] - - # in diablo, there were two ways to get tenant data - tenant = token_info['access']['token'].get('tenant') - if tenant: - # post diablo - tenant_id = tenant['id'] - tenant_name = tenant['name'] - else: - # diablo only - tenant_id = token_info['access']['user'].get('tenantId') + try: + tenant = token_info['access']['token']['tenant']['id'] + tenant_name = token_info['access']['token']['tenant']['name'] + except: + tenant = None + tenant_name = None + if not tenant: + tenant = token_info['access']['user'].get('tenantId') tenant_name = token_info['access']['user'].get('tenantName') - logger.debug("Tenant identified: id=%s, name=%s" % (tenant_id, - tenant_name)) - - verified_claims = { - 'user': { - 'id': token_info['access']['user']['id'], - 'name': token_info['access']['user']['name'], - }, - 'tenant': { - 'id': tenant_id, - 'name': tenant_name - }, - 'roles': roles, - 'expires': token_info['access']['token']['expires']} - logger.debug("User identified: id=%s, name=%s" % ( - token_info['access']['user']['id'], - token_info['access']['user']['name'])) - - expires = self._convert_date(verified_claims['expires']) - if expires <= time.time(): - logger.debug("Claims (token) expired: %s" % str(expires)) - # Cache it if there is a cache available (we also cached bad - # claims) - if self.cache: - logger.debug("Caching expired claim (token)") - self._cache_put(env, claims, verified_claims, valid=False) - raise TokenExpired() - - # Cache it if there is a cache available - if self.cache: - logger.debug("Caching validated claim") - self._cache_put(env, claims, verified_claims, valid=True) - logger.debug("Returning successful validation") + verified_claims = {'user': token_info['access']['user']['username'], + 'tenant': tenant, + 'roles': roles} + if tenant_name: + verified_claims['tenantName'] = tenant_name return verified_claims - @staticmethod - def _decorate_request(index, value, env, proxy_headers): + def _decorate_request(self, index, value, env, proxy_headers): """Add headers to request""" - logger.debug("Decorating request with HTTP_%s=%s" % (index, value)) proxy_headers[index] = value - env["HTTP_%s" % index] = value + env['HTTP_%s' % index] = value def _forward_request(self, env, start_response, proxy_headers): """Token/Auth processed & claims added to headers""" self._decorate_request('AUTHORIZATION', - "Basic %s" % self.service_pass, env, proxy_headers) + 'Basic %s' % self.service_pass, env, proxy_headers) #now decide how to pass on the call if self.app: # Pass to downstream WSGI component - logger.debug("Sending request to next app in WSGI pipeline") return self.app(env, start_response) #.custom_start_response) else: # We are forwarding to a remote service (no downstream WSGI app) - logger.debug("Sending request to %s" % self.service_url) - req = Request(proxy_headers) + req = webob.Request(proxy_headers) parsed = urlparse(req.url) - # pylint: disable=E1101 conn = http_connect(self.service_host, self.service_port, req.method, parsed.path, proxy_headers, - ssl=(self.service_protocol == 'https'), - timeout=self.service_timeout) + ssl=(self.service_protocol == 'https')) resp = conn.getresponse() data = resp.read() - logger.debug("Response was %s" % resp.status) #TODO(ziad): use a more sophisticated proxy # we are rewriting the headers now - if resp.status in (401, 305): + if resp.status == 401 or resp.status == 305: # Add our own headers to the list - headers = [("WWW_AUTHENTICATE", + headers = [('WWW_AUTHENTICATE', "Keystone uri='%s'" % self.auth_location)] - return Response(status=resp.status, body=data, - headerlist=headers)(env, - start_response) + return webob.Response(status=resp.status, + body=data, + headerlist=headers)(env, start_response) else: - return Response(status=resp.status, body=data)(env, - start_response) - - def _supports_osksvalidate(self): - """Check if target Keystone server supports OS-KSVALIDATE.""" - if self.tested_for_osksvalidate: - return self.osksvalidate - - headers = {"Accept": "application/json"} - logger.debug("Connecting to %s://%s:%s to check extensions" % ( - self.auth_protocol, self.auth_host, self.auth_port)) - try: - self.last_test_for_osksvalidate = time.time() - conn = http_connect(self.auth_host, self.auth_port, 'GET', - '/v2.0/extensions/', - headers=headers, - ssl=(self.auth_protocol == 'https'), - key_file=self.key_file, - cert_file=self.cert_file, - timeout=self.auth_timeout) - resp = conn.getresponse() - data = resp.read() - - logger.debug("Response received: %s" % resp.status) - if not str(resp.status).startswith('20'): - logger.debug("Failed to detect extensions. " - "Falling back to core API") - return False - except EnvironmentError as exc: - if exc.errno == errno.ECONNREFUSED: - logger.warning("Keystone server not responding. Extension " - "detection will be retried later.") - else: - logger.exception("Unexpected error trying to detect " - "extensions.") - logger.debug("Falling back to core API behavior (using tokens in " - "URL)") - return False - except httplib.HTTPException as exc: - logger.exception("Error trying to detect extensions.") - logger.debug("Falling back to core API behavior (using tokens in " - "URL)") - return False - - self.tested_for_osksvalidate = True - return "OS-KSVALIDATE" in data + return webob.Response(status=resp.status, + body=data)(env, start_response) def filter_factory(global_conf, **local_conf): @@ -689,8 +376,8 @@ def filter_factory(global_conf, **local_conf): conf = global_conf.copy() conf.update(local_conf) - def auth_filter(filteredapp): - return AuthProtocol(filteredapp, conf) + def auth_filter(app): + return AuthProtocol(app, conf) return auth_filter @@ -699,20 +386,11 @@ def app_factory(global_conf, **local_conf): conf.update(local_conf) return AuthProtocol(None, conf) - -def main(): - """Called when the middleware is started up separately (as in a remote - proxy configuration) - """ - config_file = os.path.join(os.path.abspath(os.path.dirname(__file__)), +if __name__ == '__main__': + app = deploy.loadapp('config:' + \ + os.path.join(os.path.abspath(os.path.dirname(__file__)), os.pardir, os.pardir, - "examples/paste/auth_token.ini") - logger.debug("Initializing with config file: %s" % config_file) - wsgiapp = loadapp("config:%s" % config_file, - global_conf={"log_name": "auth_token.log"}) - wsgi.server(eventlet.listen(('', 8090)), wsgiapp) - - -if __name__ == "__main__": - main() + 'examples/paste/auth_token.ini'), + global_conf={'log_name': 'auth_token.log'}) + wsgi.server(eventlet.listen(('', 8090)), app) diff --git a/keystone/middleware/core.py b/keystone/middleware/core.py new file mode 100644 index 00000000..09b86bbf --- /dev/null +++ b/keystone/middleware/core.py @@ -0,0 +1,112 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import json + +import webob.exc + +from keystone import config +from keystone.common import wsgi + + +CONF = config.CONF + + +# Header used to transmit the auth token +AUTH_TOKEN_HEADER = 'X-Auth-Token' + + +# Environment variable used to pass the request context +CONTEXT_ENV = 'openstack.context' + + +# Environment variable used to pass the request params +PARAMS_ENV = 'openstack.params' + + +class TokenAuthMiddleware(wsgi.Middleware): + def process_request(self, request): + token = request.headers.get(AUTH_TOKEN_HEADER) + context = request.environ.get(CONTEXT_ENV, {}) + context['token_id'] = token + request.environ[CONTEXT_ENV] = context + + +class AdminTokenAuthMiddleware(wsgi.Middleware): + """A trivial filter that checks for a pre-defined admin token. + + Sets 'is_admin' to true in the context, expected to be checked by + methods that are admin-only. + + """ + + def process_request(self, request): + token = request.headers.get(AUTH_TOKEN_HEADER) + context = request.environ.get(CONTEXT_ENV, {}) + context['is_admin'] = (token == CONF.admin_token) + request.environ[CONTEXT_ENV] = context + + +class PostParamsMiddleware(wsgi.Middleware): + """Middleware to allow method arguments to be passed as POST parameters. + + Filters out the parameters `self`, `context` and anything beginning with + an underscore. + + """ + + def process_request(self, request): + params_parsed = request.params + params = {} + for k, v in params_parsed.iteritems(): + if k in ('self', 'context'): + continue + if k.startswith('_'): + continue + params[k] = v + + request.environ[PARAMS_ENV] = params + + +class JsonBodyMiddleware(wsgi.Middleware): + """Middleware to allow method arguments to be passed as serialized JSON. + + Accepting arguments as JSON is useful for accepting data that may be more + complex than simple primitives. + + In this case we accept it as urlencoded data under the key 'json' as in + json=<urlencoded_json> but this could be extended to accept raw JSON + in the POST body. + + Filters out the parameters `self`, `context` and anything beginning with + an underscore. + + """ + def process_request(self, request): + # Ignore unrecognized content types. Empty string indicates + # the client did not explicitly set the header + if not request.content_type in ('application/json', ''): + return + + params_json = request.body + if not params_json: + return + + params_parsed = {} + try: + params_parsed = json.loads(params_json) + except ValueError: + msg = "Malformed json in request body" + raise webob.exc.HTTPBadRequest(explanation=msg) + finally: + if not params_parsed: + params_parsed = {} + + params = {} + for k, v in params_parsed.iteritems(): + if k in ('self', 'context'): + continue + if k.startswith('_'): + continue + params[k] = v + + request.environ[PARAMS_ENV] = params diff --git a/keystone/middleware/crypt.py b/keystone/middleware/crypt.py deleted file mode 100644 index bb25620d..00000000 --- a/keystone/middleware/crypt.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# 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. - -""" -Routines for URL-safe encrypting/decrypting - -Keep this file in sync with all copies: -- glance/common/crypt.py -- keystone/middleware/crypt.py -- keystone/common/crypt.py - -""" - -import base64 - -from Crypto.Cipher import AES -from Crypto import Random -from Crypto.Random import random - - -def urlsafe_encrypt(key, plaintext, blocksize=16): - """ - Encrypts plaintext. Resulting ciphertext will contain URL-safe characters - :param key: AES secret key - :param plaintext: Input text to be encrypted - :param blocksize: Non-zero integer multiple of AES blocksize in bytes (16) - - :returns : Resulting ciphertext - """ - def pad(text): - """ - Pads text to be encrypted - """ - pad_length = (blocksize - len(text) % blocksize) - sr = random.StrongRandom() - pad = ''.join(chr(sr.randint(1, 0xFF)) for i in range(pad_length - 1)) - # We use chr(0) as a delimiter between text and padding - return text + chr(0) + pad - - # random initial 16 bytes for CBC - init_vector = Random.get_random_bytes(16) - cypher = AES.new(key, AES.MODE_CBC, init_vector) - padded = cypher.encrypt(pad(str(plaintext))) - return base64.urlsafe_b64encode(init_vector + padded) - - -def urlsafe_decrypt(key, ciphertext): - """ - Decrypts URL-safe base64 encoded ciphertext - :param key: AES secret key - :param ciphertext: The encrypted text to decrypt - - :returns : Resulting plaintext - """ - # Cast from unicode - ciphertext = base64.urlsafe_b64decode(str(ciphertext)) - cypher = AES.new(key, AES.MODE_CBC, ciphertext[:16]) - padded = cypher.decrypt(ciphertext[16:]) - return padded[:padded.rfind(chr(0))] diff --git a/keystone/middleware/ec2_token.py b/keystone/middleware/ec2_token.py new file mode 100644 index 00000000..cc3094a3 --- /dev/null +++ b/keystone/middleware/ec2_token.py @@ -0,0 +1,92 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# All Rights Reserved. +# +# 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. +""" +Starting point for routing EC2 requests. + +""" + +from urlparse import urlparse + +from eventlet.green import httplib +import webob.dec +import webob.exc + +from nova import flags +from nova import utils +from nova import wsgi + + +FLAGS = flags.FLAGS +flags.DEFINE_string('keystone_ec2_url', + 'http://localhost:5000/v2.0/ec2tokens', + 'URL to get token from ec2 request.') + + +class EC2Token(wsgi.Middleware): + """Authenticate an EC2 request with keystone and convert to token.""" + + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + # Read request signature and access id. + try: + signature = req.params['Signature'] + access = req.params['AWSAccessKeyId'] + except KeyError: + raise webob.exc.HTTPBadRequest() + + # Make a copy of args for authentication and signature verification. + auth_params = dict(req.params) + # Not part of authentication args + auth_params.pop('Signature') + + # Authenticate the request. + creds = {'ec2Credentials': {'access': access, + 'signature': signature, + 'host': req.host, + 'verb': req.method, + 'path': req.path, + 'params': auth_params, + }} + creds_json = utils.dumps(creds) + headers = {'Content-Type': 'application/json'} + + # Disable 'has no x member' pylint error + # for httplib and urlparse + # pylint: disable-msg=E1101 + o = urlparse(FLAGS.keystone_ec2_url) + if o.scheme == 'http': + conn = httplib.HTTPConnection(o.netloc) + else: + conn = httplib.HTTPSConnection(o.netloc) + conn.request('POST', o.path, body=creds_json, headers=headers) + response = conn.getresponse().read() + conn.close() + + # NOTE(vish): We could save a call to keystone by + # having keystone return token, tenant, + # user, and roles from this call. + + result = utils.loads(response) + try: + token_id = result['access']['token']['id'] + except (AttributeError, KeyError): + raise webob.exc.HTTPBadRequest() + + # Authenticated! + req.headers['X-Auth-Token'] = token_id + return self.application diff --git a/keystone/middleware/glance_auth_token.py b/keystone/middleware/glance_auth_token.py index cc689bc8..6bef1390 100644 --- a/keystone/middleware/glance_auth_token.py +++ b/keystone/middleware/glance_auth_token.py @@ -30,12 +30,9 @@ middleware. Example: examples/paste/glance-api.conf, examples/paste/glance-registry.conf """ -import logging from glance.common import context -logger = logging.getLogger(__name__) # pylint: disable=C0103 - class KeystoneContextMiddleware(context.ContextMiddleware): """Glance keystone integration middleware.""" @@ -47,8 +44,6 @@ class KeystoneContextMiddleware(context.ContextMiddleware): """ # Only accept the authentication information if the identity # has been confirmed--presumably by upstream - logger.debug('X_IDENTITY_STATUS=%s' % - req.headers.get('X_IDENTITY_STATUS')) if req.headers.get('X_IDENTITY_STATUS', 'Invalid') != 'Confirmed': # Use the default empty context req.context = self.make_context(read_only=True) @@ -57,7 +52,7 @@ class KeystoneContextMiddleware(context.ContextMiddleware): # OK, let's extract the information we need auth_tok = req.headers.get('X_AUTH_TOKEN', req.headers.get('X_STORAGE_TOKEN')) - user = req.headers.get('X_USER_ID') or req.headers.get('X_USER') + user = req.headers.get('X_USER') tenant = req.headers.get('X_TENANT') roles = [r.strip() for r in req.headers.get('X_ROLE', '').split(',')] is_admin = 'Admin' in roles @@ -74,11 +69,10 @@ def filter_factory(global_conf, **local_conf): """ Factory method for paste.deploy """ - context_opts = context.cfg.ConfigOpts() conf = global_conf.copy() conf.update(local_conf) def filter(app): - return KeystoneContextMiddleware(app, context_opts, **conf) + return KeystoneContextMiddleware(app, conf) return filter diff --git a/keystone/middleware/nova_auth_token.py b/keystone/middleware/nova_auth_token.py new file mode 100644 index 00000000..ad6bcbb8 --- /dev/null +++ b/keystone/middleware/nova_auth_token.py @@ -0,0 +1,104 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2010-2011 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. + + +""" +NOVA LAZY PROVISIONING AUTH MIDDLEWARE + +This WSGI component allows keystone act as an identity service for nova by +lazy provisioning nova projects/users as authenticated by auth_token. + +Use by applying after auth_token in the nova paste config. +Example: docs/nova-api-paste.ini +""" + +from nova import auth +from nova import context +from nova import flags +from nova import utils +from nova import wsgi +from nova import exception +import webob.dec +import webob.exc + + +FLAGS = flags.FLAGS + + +class KeystoneAuthShim(wsgi.Middleware): + """Lazy provisioning nova project/users from keystone tenant/user""" + + def __init__(self, application, db_driver=None): + if not db_driver: + db_driver = FLAGS.db_driver + self.db = utils.import_object(db_driver) + self.auth = auth.manager.AuthManager() + super(KeystoneAuthShim, self).__init__(application) + + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + # find or create user + try: + user_id = req.headers['X_USER'] + except: + return webob.exc.HTTPUnauthorized() + try: + user_ref = self.auth.get_user(user_id) + except: + user_ref = self.auth.create_user(user_id) + + # get the roles + roles = [r.strip() for r in req.headers.get('X_ROLE', '').split(',')] + + # set user admin-ness to keystone admin-ness + # FIXME: keystone-admin-role value from keystone.conf is not + # used neither here nor in glance_auth_token! + roles = [r.strip() for r in req.headers.get('X_ROLE', '').split(',')] + is_admin = 'Admin' in roles + if user_ref.is_admin() != is_admin: + self.auth.modify_user(user_ref, admin=is_admin) + + # create a project for tenant + if 'X_TENANT_ID' in req.headers: + # This is the new header since Keystone went to ID/Name + project_id = req.headers['X_TENANT_ID'] + else: + # This is for legacy compatibility + project_id = req.headers['X_TENANT'] + + if project_id: + try: + project_ref = self.auth.get_project(project_id) + except: + project_ref = self.auth.create_project(project_id, user_id) + # ensure user is a member of project + if not self.auth.is_project_member(user_id, project_id): + self.auth.add_to_project(user_id, project_id) + else: + project_ref = None + + # Get the auth token + auth_token = req.headers.get('X_AUTH_TOKEN', + req.headers.get('X_STORAGE_TOKEN')) + + # Build a context, including the auth_token... + ctx = context.RequestContext(user_id, project_id, + is_admin=('Admin' in roles), + auth_token=auth_token) + + req.environ['nova.context'] = ctx + return self.application diff --git a/keystone/middleware/nova_keystone_context.py b/keystone/middleware/nova_keystone_context.py new file mode 100644 index 00000000..5c41bc87 --- /dev/null +++ b/keystone/middleware/nova_keystone_context.py @@ -0,0 +1,69 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 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. +""" +Nova Auth Middleware. + +""" + +import webob.dec +import webob.exc + +from nova import context +from nova import flags +from nova import wsgi + + +FLAGS = flags.FLAGS +flags.DECLARE('use_forwarded_for', 'nova.api.auth') + + +class NovaKeystoneContext(wsgi.Middleware): + """Make a request context from keystone headers""" + + @webob.dec.wsgify(RequestClass=wsgi.Request) + def __call__(self, req): + try: + user_id = req.headers['X_USER'] + except KeyError: + return webob.exc.HTTPUnauthorized() + # get the roles + roles = [r.strip() for r in req.headers.get('X_ROLE', '').split(',')] + + if 'X_TENANT_ID' in req.headers: + # This is the new header since Keystone went to ID/Name + project_id = req.headers['X_TENANT_ID'] + else: + # This is for legacy compatibility + project_id = req.headers['X_TENANT'] + + # Get the auth token + auth_token = req.headers.get('X_AUTH_TOKEN', + req.headers.get('X_STORAGE_TOKEN')) + + # Build a context, including the auth_token... + remote_address = getattr(req, 'remote_address', '127.0.0.1') + remote_address = req.remote_addr + if FLAGS.use_forwarded_for: + remote_address = req.headers.get('X-Forwarded-For', remote_address) + ctx = context.RequestContext(user_id, + project_id, + roles=roles, + auth_token=auth_token, + strategy='keystone', + remote_address=remote_address) + + req.environ['nova.context'] = ctx + return self.application diff --git a/keystone/middleware/quantum_auth_token.py b/keystone/middleware/quantum_auth_token.py deleted file mode 100755 index 6d027c41..00000000 --- a/keystone/middleware/quantum_auth_token.py +++ /dev/null @@ -1,461 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010-2011 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. - - -""" -TOKEN-BASED AUTH MIDDLEWARE - -This WSGI component performs multiple jobs: - -- it verifies that incoming client requests have valid tokens by verifying - tokens with the auth service. -- it will reject unauthenticated requests UNLESS it is in 'delay_auth_decision' - mode, which means the final decision is delegated to the downstream WSGI - component (usually the OpenStack service) -- it will collect and forward identity information from a valid token - such as user name, groups, etc... - -Refer to: http://wiki.openstack.org/openstack-authn - -This WSGI component has been derived from Keystone's auth_token -middleware module. It contains some specialization for Quantum. - -HEADERS -======= - -Headers starting with ``HTTP_`` is a standard http header -Headers starting with ``HTTP_X`` is an extended http header - -Coming in from initial call from client or customer ---------------------------------------------------- - -HTTP_X_AUTH_TOKEN - The client token being passed in - -HTTP_X_STORAGE_TOKEN - The client token being passed in (legacy Rackspace use) to support - cloud files - -Used for communication between components ------------------------------------------ - -www-Authenticate - Only used if this component is being used remotely - -HTTP_AUTHORIZATION - Basic auth password used to validate the connection - -What we add to the request for use by the OpenStack service ------------------------------------------------------------ - -HTTP_X_AUTHORIZATION - The client identity being passed in - -""" - -import httplib -import json -import logging -import urllib -from urlparse import urlparse -from webob.exc import HTTPUnauthorized, Request, Response - -from keystone.common.bufferedhttp import http_connect_raw as http_connect - -PROTOCOL_NAME = "Quantum Token Authentication" -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -# pylint: disable=R0902 -class AuthProtocol(object): - """Auth Middleware that handles authenticating client calls""" - - def _init_protocol_common(self, app, conf): - """ Common initialization code""" - logger.info("Starting the %s component", PROTOCOL_NAME) - - self.conf = conf - self.app = app - #if app is set, then we are in a WSGI pipeline and requests get passed - # on to app. If it is not set, this component should forward requests - - # where to find the Quantum service (if not in local WSGI chain) - # these settings are only used if this component is acting as a proxy - # and the OpenSTack service is running remotely - if not self.app: - self.service_protocol = conf.get('quantum_protocol', 'https') - self.service_host = conf.get('quantum_host') - self.service_port = int(conf.get('quantum_port')) - self.service_url = '%s://%s:%s' % (self.service_protocol, - self.service_host, - self.service_port) - - # delay_auth_decision means we still allow unauthenticated requests - # through and we let the downstream service make the final decision - self.delay_auth_decision = int(conf.get('delay_auth_decision', 0)) - - def _init_protocol(self, _app, conf): - """ Protocol specific initialization """ - - # where to find the auth service (we use this to validate tokens) - self.auth_host = conf.get('auth_host') - self.auth_port = int(conf.get('auth_port')) - self.auth_protocol = conf.get('auth_protocol', 'http') - self.cert_file = conf.get('certfile', None) - self.key_file = conf.get('keyfile', None) - self.auth_timeout = conf.get('auth_timeout', 30) - self.auth_api_version = conf.get('auth_version', '2.0') - self.auth_location = "%s://%s:%s" % (self.auth_protocol, - self.auth_host, - self.auth_port) - self.auth_uri = conf.get('auth_uri', self.auth_location) - logger.debug("Authentication Service:%s", self.auth_location) - # Credentials used to verify this component with the Auth service - # since validating tokens is a privileged call - self.admin_user = conf.get('admin_user') - self.admin_password = conf.get('admin_password') - self.admin_token = conf.get('admin_token') - # bind to one or more service instances - service_ids = conf.get('service_ids') - self.serviceId_qs = '' - if service_ids: - self.serviceId_qs = '?HP-IDM-serviceId=%s' % \ - (urllib.quote(service_ids)) - - def _build_token_uri(self, claims=None): - claim_str = "/%s" % claims if claims else "" - return "/v%s/tokens%s%s" % (self.auth_api_version, claim_str, - self.serviceId_qs or '') - - def __init__(self, app, conf): - """ Common initialization code """ - # Defining instance variables here for improving pylint score - # NOTE(salvatore-orlando): the following vars are assigned values - # either in init_protocol or init_protocol_common. We should not - # worry about them being initialized to None - self.admin_password = None - self.admin_token = None - self.admin_user = None - self.auth_api_version = None - self.auth_host = None - self.auth_location = None - self.auth_uri = None - self.auth_port = None - self.auth_protocol = None - self.auth_timeout = None - self.cert_file = None - self.key_file = None - self.service_host = None - self.service_port = None - self.service_protocol = None - self.service_url = None - self.proxy_headers = None - self.start_response = None - self.app = None - self.conf = None - self.env = None - self.delay_auth_decision = None - self.expanded = None - self.claims = None - - self._init_protocol_common(app, conf) # Applies to all protocols - self._init_protocol(app, conf) # Specific to this protocol - - # pylint: disable=R0912 - def __call__(self, env, start_response): - """ Handle incoming request. Authenticate. And send downstream. """ - logger.debug("entering AuthProtocol.__call__") - logger.debug("start response:%s", start_response) - self.start_response = start_response - self.env = env - - #Prep headers to forward request to local or remote downstream service - self.proxy_headers = env.copy() - for header in self.proxy_headers.iterkeys(): - if header[0:5] == 'HTTP_': - self.proxy_headers[header[5:]] = self.proxy_headers[header] - del self.proxy_headers[header] - - #Look for authentication claims - logger.debug("Looking for authentication claims") - self.claims = self._get_claims(env) - if not self.claims: - #No claim(s) provided - logger.debug("No claims provided") - if self.delay_auth_decision: - #Configured to allow downstream service to make final decision. - #So mark status as Invalid and forward the request downstream - self._decorate_request("X_IDENTITY_STATUS", "Invalid") - else: - #Respond to client as appropriate for this auth protocol - return self._reject_request() - else: - # this request is presenting claims. Let's validate them - logger.debug("Claims found. Validating.") - valid = self._validate_claims(self.claims) - if not valid: - # Keystone rejected claim - if self.delay_auth_decision: - # Downstream service will receive call still and decide - self._decorate_request("X_IDENTITY_STATUS", "Invalid") - else: - #Respond to client as appropriate for this auth protocol - return self._reject_claims() - else: - self._decorate_request("X_IDENTITY_STATUS", "Confirmed") - - #Collect information about valid claims - if valid: - logger.debug("Validation successful") - claims = self._expound_claims() - - # Store authentication data - if claims: - # TODO(Ziad): add additional details we may need, - # like tenant and group info - self._decorate_request('X_AUTHORIZATION', "Proxy %s" % - claims['user']) - - self._decorate_request('X_TENANT_ID', - claims['tenant']['id'],) - self._decorate_request('X_TENANT_NAME', - claims['tenant']['name']) - - self._decorate_request('X_USER_ID', - claims['user']['id']) - self._decorate_request('X_USER_NAME', - claims['user']['name']) - - self._decorate_request('X_TENANT', claims['tenant']['id']) - self._decorate_request('X_USER', claims['user']['id']) - - if 'group' in claims: - self._decorate_request('X_GROUP', claims['group']) - if 'roles' in claims and len(claims['roles']) > 0: - if claims['roles'] is not None: - roles = '' - for role in claims['roles']: - if len(roles) > 0: - roles += ',' - roles += role - self._decorate_request('X_ROLE', roles) - - # NOTE(todd): unused - self.expanded = True - logger.debug("About to forward request") - #Send request downstream - return self._forward_request() - - # NOTE(salvatore-orlando): this function is now used again - def get_admin_auth_token(self, username, password): - """ - This function gets an admin auth token to be used by this service to - validate a user's token. Validate_token is a priviledged call so - it needs to be authenticated by a service that is calling it - """ - headers = { - "Content-type": "application/json", - "Accept": "application/json"} - params = { - "auth": - { - "passwordCredentials": - { - "username": username, - "password": password - } - } - } - if self.auth_protocol == "http": - conn = httplib.HTTPConnection(self.auth_host, self.auth_port) - else: - conn = httplib.HTTPSConnection(self.auth_host, self.auth_port, - cert_file=self.cert_file) - conn.request("POST", self._build_token_uri(), json.dumps(params), \ - headers=headers) - response = conn.getresponse() - data = response.read() - return data - - @staticmethod - def _get_claims(env): - """Get claims from request""" - claims = env.get('HTTP_X_AUTH_TOKEN', env.get('HTTP_X_STORAGE_TOKEN')) - return claims - - def _reject_request(self): - """Redirect client to auth server""" - return HTTPUnauthorized("Authentication required", - [("WWW-Authenticate", - "Keystone uri='%s'" % self.auth_uri)])(self.env, - self.start_response) - - def _reject_claims(self): - """Client sent bad claims""" - return HTTPUnauthorized()(self.env, self.start_response) - - def _validate_claims(self, claims, retry=False): - """Validate claims, and provide identity information if applicable """ - - # Step 1: We need to auth with the keystone service, so get an - # admin token - # TODO(ziad): Need to properly implement this, where to store creds - # for now using token from ini - # NOTE(salvatore-orlando): Temporarily restoring auth token retrieval, - # with credentials in configuration file - if not self.admin_token: - auth = self.get_admin_auth_token(self.admin_user, - self.admin_password) - self.admin_token = json.loads(auth)["access"]["token"]["id"] - - # Step 2: validate the user's token with the auth service - # since this is a priviledged op,m we need to auth ourselves - # by using an admin token - headers = {"Content-type": "application/json", - "Accept": "application/json", - "X-Auth-Token": self.admin_token} - conn = http_connect(self.auth_host, self.auth_port, 'GET', - self._build_token_uri(claims), headers=headers, - ssl=(self.auth_protocol == 'https'), - key_file=self.key_file, cert_file=self.cert_file, - timeout=self.auth_timeout) - resp = conn.getresponse() - # pylint: disable=E1103 - conn.close() - - if not str(resp.status).startswith('20'): - # Keystone rejected claim - # In case a 404 error it might just be that the token has expired - # Therefore try and get a new token - # of course assuming admin credentials have been specified - # Note(salvatore-orlando): the 404 here is not really - # what should be returned - if self.admin_user and self.admin_password and \ - not retry and str(resp.status) == '404': - logger.warn("Unable to validate token." + - "Admin token possibly expired.") - self.admin_token = None - return self._validate_claims(claims, True) - return False - else: - #TODO(Ziad): there is an optimization we can do here. We have just - #received data from Keystone that we can use instead of making - #another call in _expound_claims - logger.info("Claims successfully validated") - return True - - def _expound_claims(self): - # Valid token. Get user data and put it in to the call - # so the downstream service can use it - headers = {"Content-type": "application/json", - "Accept": "application/json", - "X-Auth-Token": self.admin_token} - conn = http_connect(self.auth_host, self.auth_port, 'GET', - self._build_token_uri(self.claims), - headers=headers, - ssl=(self.auth_protocol == 'https'), - key_file=self.key_file, cert_file=self.cert_file, - timeout=self.auth_timeout) - resp = conn.getresponse() - data = resp.read() - # pylint: disable=E1103 - conn.close() - - if not str(resp.status).startswith('20'): - raise LookupError('Unable to locate claims: %s' % resp.status) - - token_info = json.loads(data) - #TODO(Ziad): make this more robust - #first_group = token_info['auth']['user']['groups']['group'][0] - roles = [] - rolegrants = token_info["access"]["user"]["roles"] - if rolegrants is not None: - roles = [rolegrant["id"] for rolegrant in rolegrants] - - token_info = json.loads(data) - - roles = [role['name'] for role in token_info[ - "access"]["user"]["roles"]] - - # in diablo, there were two ways to get tenant data - tenant = token_info['access']['token'].get('tenant') - if tenant: - # post diablo - tenant_id = tenant['id'] - tenant_name = tenant['name'] - else: - # diablo only - tenant_id = token_info['access']['user'].get('tenantId') - tenant_name = token_info['access']['user'].get('tenantName') - - verified_claims = { - 'user': { - 'id': token_info['access']['user']['id'], - 'name': token_info['access']['user']['name'], - }, - 'tenant': { - 'id': tenant_id, - 'name': tenant_name - }, - 'roles': roles} - - return verified_claims - - def _decorate_request(self, index, value): - """Add headers to request""" - self.proxy_headers[index] = value - self.env["HTTP_%s" % index] = value - - def _forward_request(self): - """Token/Auth processed & claims added to headers""" - #now decide how to pass on the call - if self.app: - # Pass to downstream WSGI component - return self.app(self.env, self.start_response) - #.custom_start_response) - else: - # We are forwarding to a remote service (no downstream WSGI app) - req = Request(self.proxy_headers) - # pylint: disable=E1101 - parsed = urlparse(req.url) - conn = http_connect(self.service_host, - self.service_port, - req.method, - parsed.path, - self.proxy_headers, - ssl=(self.service_protocol == 'https')) - resp = conn.getresponse() - data = resp.read() - return Response(status=resp.status, body=data)(self.proxy_headers, - self.start_response) - - -def filter_factory(global_conf, **local_conf): - """Returns a WSGI filter app for use with paste.deploy.""" - conf = global_conf.copy() - conf.update(local_conf) - - def auth_filter(application): - return AuthProtocol(application, conf) - return auth_filter - - -def app_factory(global_conf, **local_conf): - conf = global_conf.copy() - conf.update(local_conf) - return AuthProtocol(None, conf) diff --git a/keystone/middleware/remoteauth.py b/keystone/middleware/remoteauth.py deleted file mode 100644 index 07ba7032..00000000 --- a/keystone/middleware/remoteauth.py +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010 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. - - -""" -Auth Middleware that handles auth for a service - -This module can be installed as a filter in front of your service to validate -that requests are coming from a trusted component that has handled -authenticating the call. If a call comes from an untrusted source, it will -redirect it back to be properly authenticated. This is done by sending our a -305 proxy redirect response with the URL for the auth service. - -The auth service settings are specified in the INI file (keystone.ini). The ini -file is passed in as the WSGI config file when starting the service. For this -proof of concept, the ini file is in echo/echo/echo.ini. - -In the current implementation use a basic auth password to verify that the -request is coming from a valid auth component or service - -Refer to: http://wiki.openstack.org/openstack-authn - - -HEADERS -------- - -* HTTP\_ is a standard http header -* HTTP_X is an extended http header - -Coming in from initial call -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -HTTP_X_AUTH_TOKEN - the client token being passed in - -HTTP_X_STORAGE_TOKEN - the client token being passed in (legacy Rackspace use) to support - cloud files - -Used for communication between components -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -www-authenticate - only used if this component is being used remotely - -HTTP_AUTHORIZATION - basic auth password used to validate the connection - -What we add to the request for use by the OpenStack service -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -HTTP_X_AUTHORIZATION - the client identity being passed in - - -""" -import logging -from webob.exc import HTTPUseProxy, HTTPUnauthorized - -PROTOCOL_NAME = "Remote Proxy Authentication" -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class RemoteAuth(object): - - # app is the downstream WSGI component, usually the OpenStack service - # - # if app is not provided, the assumption is this filter is being run - # from a separate server. - - def __init__(self, app, conf): - logger.info("Starting the %s component", PROTOCOL_NAME) - # app is the next app in WSGI chain - eventually the OpenStack service - self.app = app - self.conf = conf - # where to redirect untrusted requests to - self.proxy_location = conf.get('proxy_location') - # secret that will tell us a request is coming from a trusted auth - # component - self.remote_auth_pass = conf.get('remote_auth_pass') - - def __call__(self, env, start_response): - # Validate the request is trusted - # Authenticate the Auth component itself. - headers = [('www-authenticate', 'Basic realm="API Auth"')] - if 'HTTP_AUTHORIZATION' not in env: - logger.debug("Unauthenticated: redirecting to %s" % - self.proxy_location) - # Redirect to proxy (auth component) and show that basic auth is - # required - return HTTPUseProxy(location=self.proxy_location, - headers=headers)(env, start_response) - else: - auth_type, encoded_creds = env['HTTP_AUTHORIZATION'].split(None, 1) - if encoded_creds != self.remote_auth_pass: - logger.warn('Bad proxy credentials received') - return HTTPUnauthorized(headers=headers)(env, start_response) - - # Make sure that the user has been authenticated by the Auth Service - if 'HTTP_X_AUTHORIZATION' not in env: - return HTTPUnauthorized()(env, start_response) - - return self.app(env, start_response) - - -def filter_factory(global_conf, **local_conf): - """Returns a WSGI filter app for use with paste.deploy.""" - conf = global_conf.copy() - conf.update(local_conf) - - def auth_filter(app): - return RemoteAuth(app, conf) - return auth_filter diff --git a/keystone/middleware/s3_token.py b/keystone/middleware/s3_token.py index 31114b7c..202e2c27 100644 --- a/keystone/middleware/s3_token.py +++ b/keystone/middleware/s3_token.py @@ -20,33 +20,27 @@ # This source code is based ./auth_token.py and ./ec2_token.py. # See them for their copyright. -""" -Starting point for routing S3 requests. - -""" +"""Starting point for routing S3 requests.""" import httplib import json -from webob.dec import wsgify -from urlparse import urlparse + +import webob + +from swift.common import utils as swift_utils + PROTOCOL_NAME = "S3 Token Authentication" class S3Token(object): - """Auth Middleware that handles S3 authenticating client calls""" - - def _init_protocol_common(self, app, conf): - """ Common initialization code""" - print "Starting the %s component" % PROTOCOL_NAME + """Auth Middleware that handles S3 authenticating client calls.""" - self.conf = conf + def __init__(self, app, conf): + """Common initialization code.""" self.app = app - #if app is set, then we are in a WSGI pipeline and requests get passed - # on to app. If it is not set, this component should forward requests - - def _init_protocol(self, conf): - """ Protocol specific initialization """ + self.logger = swift_utils.get_logger(conf, log_route='s3_token') + self.logger.debug('Starting the %s component' % PROTOCOL_NAME) # where to find the auth service (we use this to validate tokens) self.auth_host = conf.get('auth_host') @@ -56,68 +50,38 @@ class S3Token(object): # where to tell clients to find the auth service (default to url # constructed based on endpoint we have for the service to use) self.auth_location = conf.get('auth_uri', - "%s://%s:%s" % (self.auth_protocol, - self.auth_host, - self.auth_port)) + '%s://%s:%s' % (self.auth_protocol, + self.auth_host, + self.auth_port)) # Credentials used to verify this component with the Auth service since # validating tokens is a privileged call self.admin_token = conf.get('admin_token') - def __init__(self, app, conf): - """ Common initialization code """ - - #TODO(ziad): maybe we refactor this into a superclass - self._init_protocol_common(app, conf) # Applies to all protocols - self._init_protocol(conf) # Specific to this protocol - self.app = None - self.auth_port = None - self.auth_protocol = None - self.auth_location = None - self.auth_host = None - self.admin_token = None - self.conf = None - - #@webob.dec.wsgify(RequestClass=webob.exc.Request) - # pylint: disable=R0914 - @wsgify - def __call__(self, req): - """ Handle incoming request. Authenticate. And send downstream. """ + def __call__(self, environ, start_response): + """Handle incoming request. authenticate and send downstream.""" + req = webob.Request(environ) + parts = swift_utils.split_path(req.path, 1, 4, True) + version, account, container, obj = parts # Read request signature and access id. if not 'Authorization' in req.headers: - return self.app - try: - account, signature = \ - req.headers['Authorization'].split(' ')[-1].rsplit(':', 1) - #del(req.headers['Authorization']) - except StandardError: - return self.app + return self.app(environ, start_response) + token = req.headers.get('X-Auth-Token', + req.headers.get('X-Storage-Token')) - #try: - # account, tenant = access.split(':') - #except Exception: - # account = access + auth_header = req.headers['Authorization'] + access, signature = auth_header.split(' ')[-1].rsplit(':', 1) # Authenticate the request. - creds = {'OS-KSS3:s3Credentials': {'access': account, - 'signature': signature, - 'verb': req.method, - 'path': req.path, - 'expire': req.headers['Date'], - }} - - if req.headers.get('Content-Type'): - creds['s3Credentials']['content_type'] = \ - req.headers['Content-Type'] - if req.headers.get('Content-MD5'): - creds['s3Credentials']['content_md5'] = req.headers['Content-MD5'] - xheaders = {} - for key, value in req.headers.iteritems(): - if key.startswith('X-Amz'): - xheaders[key.lower()] = value - if xheaders: - creds['s3Credentials']['xheaders'] = xheaders + creds = {'credentials': {'access': access, + 'token': token, + 'signature': signature, + 'host': req.host, + 'verb': req.method, + 'path': req.path, + 'expire': req.headers['Date'], + }} creds_json = json.dumps(creds) headers = {'Content-Type': 'application/json'} @@ -126,29 +90,40 @@ class S3Token(object): else: conn = httplib.HTTPSConnection(self.auth_host, self.auth_port) - conn.request('POST', '/v2.0/tokens', body=creds_json, headers=headers) - response = conn.getresponse().read() + conn.request('POST', '/v2.0/s3tokens', + body=creds_json, + headers=headers) + resp = conn.getresponse() + if resp.status < 200 or resp.status >= 300: + raise Exception('Keystone reply error: status=%s reason=%s' % ( + resp.status, + resp.reason)) + + # NOTE(vish): We could save a call to keystone by having + # keystone return token, tenant, user, and roles + # from this call. + # + # NOTE(chmou): We still have the same problem we would need to + # change token_auth to detect if we already + # identified and not doing a second query and just + # pass it through to swiftauth in this case. + # identity_info = json.loads(response) + output = resp.read() conn.close() - - # NOTE(vish): We could save a call to keystone by - # having keystone return token, tenant, - # user, and roles from this call. - result = json.loads(response) - endpoint_path = '' + identity_info = json.loads(output) try: - token_id = str(result['access']['token']['id']) - for endpoint in result['access']['serviceCatalog']: - if endpoint['type'] == 'Swift Service': - ep = urlparse(endpoint['endpoints'][0]['internalURL']) - endpoint_path = str(ep.path) # pylint: disable=E1101 - break - except KeyError: - return self.app - - # Authenticated! + token_id = str(identity_info['access']['token']['id']) + tenant = (identity_info['access']['token']['tenant']['id'], + identity_info['access']['token']['tenant']['name']) + except (KeyError, IndexError): + self.logger.debug('Error getting keystone reply: %s' % + (str(output))) + raise + req.headers['X-Auth-Token'] = token_id - req.headers['X-Endpoint-Path'] = endpoint_path - return self.app + environ['PATH_INFO'] = environ['PATH_INFO'].replace( + account, 'AUTH_%s' % tenant[0]) + return self.app(environ, start_response) def filter_factory(global_conf, **local_conf): diff --git a/keystone/middleware/swift_auth.py b/keystone/middleware/swift_auth.py index 56fa0e09..d12ac162 100644 --- a/keystone/middleware/swift_auth.py +++ b/keystone/middleware/swift_auth.py @@ -1,4 +1,6 @@ -# Copyright (c) 2011 OpenStack, LLC. +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 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. @@ -13,17 +15,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -from webob.exc import HTTPForbidden, HTTPNotFound, HTTPUnauthorized +import webob -from swift.common.utils import get_logger, split_path, get_remote_client -from swift.common.middleware.acl import clean_acl, parse_acl, referrer_allowed +from swift.common import utils as swift_utils +from swift.common.middleware import acl as swift_acl class SwiftAuth(object): - """ - Keystone to Swift authorization system. + """Swift middleware to Keystone authorization system. - Add to your pipeline in proxy-server.conf, such as:: + In Swift's proxy-server.conf add the middleware to your pipeline:: [pipeline:main] pipeline = catch_errors cache tokenauth swiftauth proxy-server @@ -37,8 +38,8 @@ class SwiftAuth(object): [filter:swiftauth] use = egg:keystone#swiftauth - keystone_swift_operator_roles = Admin, SwiftOperator - keystone_tenant_user_admin = true + operator_roles = admin, SwiftOperator + is_admin = true If Swift memcache is to be used for caching tokens, add the additional property in the tokenauth filter: @@ -51,11 +52,11 @@ class SwiftAuth(object): This maps tenants to account in Swift. The user whose able to give ACL / create Containers permissions - will be the one that are inside the keystone_swift_operator_roles + will be the one that are inside the operator_roles setting which by default includes the Admin and the SwiftOperator roles. - The option keystone_tenant_user_admin if set to true will allow the + The option is_admin if set to true will allow the username that has the same name as the account name to be the owner. Example: If we have the account called hellocorp with a user @@ -68,23 +69,20 @@ class SwiftAuth(object): def __init__(self, app, conf): self.app = app self.conf = conf - self.logger = get_logger(conf, log_route='keystone') + self.logger = swift_utils.get_logger(conf, log_route='keystoneauth') self.reseller_prefix = conf.get('reseller_prefix', 'AUTH').strip() - self.keystone_swift_operator_roles = \ - conf.get('keystone_swift_operator_roles', 'Admin, SwiftOperator') - self.keystone_tenant_user_admin = \ - conf.get('keystone_tenant_user_admin', "false").lower() in \ - ('true', 't', '1', 'on', 'yes', 'y') - self.allowed_sync_hosts = [h.strip() - for h in conf.get('allowed_sync_hosts', '127.0.0.1').split(',') - if h.strip()] + self.operator_roles = conf.get('operator_roles', + 'admin, SwiftOperator') + config_is_admin = conf.get('is_admin', "false").lower() + self.is_admin = config_is_admin in ('true', 't', '1', 'on', 'yes', 'y') + cfg_synchosts = conf.get('allowed_sync_hosts', '127.0.0.1') + self.allowed_sync_hosts = [h.strip() for h in cfg_synchosts.split(',') + if h.strip()] def __call__(self, environ, start_response): - self.logger.debug('Initialise keystone middleware') identity = self._keystone_identity(environ) if not identity: - #TODO: non authenticated access allow via refer environ['swift.authorize'] = self.denied_response return self.app(environ, start_response) @@ -92,24 +90,24 @@ class SwiftAuth(object): environ['keystone.identity'] = identity environ['REMOTE_USER'] = identity.get('tenant') environ['swift.authorize'] = self.authorize - environ['swift.clean_acl'] = clean_acl + environ['swift.clean_acl'] = swift_acl.clean_acl return self.app(environ, start_response) def _keystone_identity(self, environ): - """ Extract the identity from the Keystone auth component """ - if (environ.get('HTTP_X_IDENTITY_STATUS') != 'Confirmed'): - return None + """Extract the identity from the Keystone auth component.""" + if environ.get('HTTP_X_IDENTITY_STATUS') != 'Confirmed': + return roles = [] - if ('HTTP_X_ROLES' in environ): - roles = environ.get('HTTP_X_ROLES').split(',') - identity = {'user': environ.get('HTTP_X_USER_NAME'), + if 'HTTP_X_ROLE' in environ: + roles = environ['HTTP_X_ROLE'].split(',') + identity = {'user': environ.get('HTTP_X_USER'), 'tenant': (environ.get('HTTP_X_TENANT_ID'), environ.get('HTTP_X_TENANT_NAME')), 'roles': roles} return identity def _reseller_check(self, account, tenant_id): - """ Check reseller prefix """ + """Check reseller prefix.""" return account == '%s_%s' % (self.reseller_prefix, tenant_id) def authorize(self, req): @@ -118,77 +116,82 @@ class SwiftAuth(object): tenant = env_identity.get('tenant') try: - version, account, container, obj = split_path(req.path, 1, 4, True) + part = swift_utils.split_path(req.path, 1, 4, True) + version, account, container, obj = part except ValueError: - return HTTPNotFound(request=req) + return webob.exc.HTTPNotFound(request=req) if not self._reseller_check(account, tenant[0]): - self.logger.debug('tenant mismatch') + log_msg = 'tenant mismatch: %s != %s' % (account, tenant[0]) + self.logger.debug(log_msg) return self.denied_response(req) user_groups = env_identity.get('roles', []) - # If user is in the swift operator group then make the owner of it. - for _group in self.keystone_swift_operator_roles.split(','): - _group = _group.strip() - if _group in user_groups: - self.logger.debug( - "User in group: %s allow to manage this account" % \ - (_group)) + # Check the groups the user is belonging to. If the user is + # part of the group defined in the config variable + # operator_roles (like Admin) then it will be + # promoted as an Admin of the account/tenant. + for group in self.operator_roles.split(','): + group = group.strip() + if group in user_groups: + log_msg = "allow user in group %s as account admin" % group + self.logger.debug(log_msg) req.environ['swift_owner'] = True - return None + return # If user is of the same name of the tenant then make owner of it. user = env_identity.get('user', '') - if self.keystone_tenant_user_admin and user == tenant[1]: - self.logger.debug("user: %s == %s tenant and option "\ - "keystone_tenant_user_admin is set" % \ - (user, tenant)) + if self.is_admin and user == tenant[1]: req.environ['swift_owner'] = True - return None - - # Allow container sync - if (req.environ.get('swift_sync_key') and - req.environ['swift_sync_key'] == - req.headers.get('x-container-sync-key', None) and - 'x-timestamp' in req.headers and - (req.remote_addr in self.allowed_sync_hosts or - get_remote_client(req) in self.allowed_sync_hosts)): - self.logger.debug('allowing container-sync') - return None - - # Check if Referrer allow it - referrers, groups = parse_acl(getattr(req, 'acl', None)) - if referrer_allowed(req.referer, referrers): + return + + # Allow container sync. + if (req.environ.get('swift_sync_key') + and req.environ['swift_sync_key'] == + req.headers.get('x-container-sync-key', None) + and 'x-timestamp' in req.headers + and (req.remote_addr in self.allowed_sync_hosts + or swift_utils.get_remote_client(req) + in self.allowed_sync_hosts)): + log_msg = 'allowing proxy %s for container-sync' % req.remote_addr + self.logger.debug(log_msg) + return + + # Check if referrer is allowed. + referrers, groups = swift_acl.parse_acl(getattr(req, 'acl', None)) + if swift_acl.referrer_allowed(req.referer, referrers): if obj or '.rlistings' in groups: - self.logger.debug('authorizing via ACL') - return None + log_msg = 'authorizing %s via referer ACL' % req.referrer + self.logger.debug(log_msg) + return return self.denied_response(req) # Allow ACL at individual user level (tenant:user format) if '%s:%s' % (tenant[0], user) in groups: - self.logger.debug('user explicitly allowed in ACL authorizing') - return None + log_msg = 'user %s:%s allowed in ACL authorizing' + self.logger.debug(log_msg % (tenant[0], user)) + return # Check if we have the group in the usergroups and allow it for user_group in user_groups: if user_group in groups: - self.logger.debug('user in group which is allowed in' \ - ' ACL: %s authorizing' % (user_group)) - return None + log_msg = 'user %s:%s allowed in ACL: %s authorizing' + self.logger.debug(log_msg % (tenant[0], user, user_group)) + return - # last but not least retun deny return self.denied_response(req) def denied_response(self, req): - """ + """Deny WSGI Response. + Returns a standard WSGI response callable with the status of 403 or 401 depending on whether the REMOTE_USER is set or not. """ if req.remote_user: - return HTTPForbidden(request=req) + return webob.exc.HTTPForbidden(request=req) else: - return HTTPUnauthorized(request=req) + return webob.exc.HTTPUnauthorized(request=req) def filter_factory(global_conf, **local_conf): diff --git a/keystone/middleware/url.py b/keystone/middleware/url.py deleted file mode 100644 index d75313a1..00000000 --- a/keystone/middleware/url.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2010 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. - -""" -DEPRECATED - moved to keystone.frontends.normalizer - -This file only exists to maintain compatibility with configuration files -that load keystone.middleware.url -""" - -import logging - -from keystone.frontends.normalizer import filter_factory\ - as new_filter_factory - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -def filter_factory(global_conf, **local_conf): - """Returns a WSGI filter app for use with paste.deploy. - - In this case, we return the class that has been moved""" - logger.warning("'%s' has been moved to 'keystone.frontends.normalizer'. " - "Update your configuration file to reflect the change" % - __name__) - return new_filter_factory(global_conf, **local_conf) diff --git a/keystone/models.py b/keystone/models.py deleted file mode 100644 index 0f8006ce..00000000 --- a/keystone/models.py +++ /dev/null @@ -1,813 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (C) 2011 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. - -""" Module that contains all object models - -The models are used to hold Keystone 'business' objects and their validation, -serialization, and backend interaction code. - -The models are based off of python's dict. - -The uses supported are: - # can be initialized with static properties - tenant = Tenant(name='A1000') - - # handles writing to correct backend - tenant.save() - - # static properties - id = tenant.id - tenant = None - - # Acts as a dict - tenant is a dict - tenant.dict points to the data dict (i.e. tenant["tenant"]) - - # can be retrieved by static property - tenant_by_name = Tenant.get(name='A1000') - - # can be retrieved by id default, so name not needed - tenant_by_id = Tenant.get(id) - assertIsEquals(tenant_by_id, tenant_by_name) - - # handles serialization - print tenant_by_id - print tenant_by_id.to_json() # Keystone latest contract - print tenant_by_id.to_json_20() # Keystone 2.0 contract - - Serialization routines can take hints in this format: - { - "contract_attributes": ["id", "name", ...], - "types": [("id", int), (...)] - } - attribute/value can be: - contract_attributes: list of contract attributes (see initializer) - format is a list of attributes names (ex ['id', 'name']) - types: list of attribute/type mappings - format is a list of name/type tuples (ex [('id', int)]) - tags: list of attributes that go into XML tags - format is a list of attribute names(ex ['description'] - maps: list of attributes to rename - format is from/to values (ex {'serviceId": "service_id",}) - Default hints can be stored in the class as cls.hints -""" - -import json -from lxml import etree - -from keystone import utils -from keystone.utils import fault - - -class AttrDict(dict): - """Lets us do setattr and getattr since dict does not allow it""" - pass - - -class Resource(AttrDict): - """ Base class for models - - Provides basic functionality that can be overridden """ - - hints = {} - xmlns = None - - def __init__(self, *args, **kw): - """ Initialize object - kwargs contain static properties - """ - super(Resource, self).__init__(*args, **kw) - # attributes that can be used as attributes. Example: - # tenant.id - here id is a contract attribute - super(Resource, self).__setattr__("contract_attributes", []) - if kw: - self.contract_attributes.extend(kw.keys()) - for name, value in kw.iteritems(): - self[name] = value - - # - # model properties - # - # Override built-in classes to allow for user.id (as well as user["id"]) - # for attributes defined in the Keystone contract - # - def __repr__(self): - return "<%s(%s)>" % (self.__class__.__name__, ', '.join(['%s=%s' % - (attrib, self[attrib].__repr__()) for attrib in - self.contract_attributes])) - - def __str__(self): - """Returns string representation including the class name.""" - return str(self.to_dict()) - - def __getattr__(self, name): - """ Supports reading contract attributes (ex. tenant.id) - - This should only be called if the original call did not match - an attribute (Python's rules)""" - if name in self.contract_attributes: - if name in self: - return self[name] - return None - elif name == 'desc': # TODO(zns): deprecate this - # We need to maintain this compatibility with this nasty attribute - # until we're done refactoring - return self.description - else: - if hasattr(super(Resource, self), name): - return getattr(super(Resource, self), name) - else: - raise AttributeError("'%s' not found on object of class '%s'" - % (name, self.__class__.__name__)) - - def __setattr__(self, name, value): - """ Supports setting contract attributes (ex. tenant.name = 'A1') """ - - if name in self.contract_attributes: - # Put those into the dict (and not as attrs) - if value is not None: - self[name] = value - else: - super(Resource, self).__setattr__(name, value) - - def __getitem__(self, name): - if name in self.contract_attributes: - if super(Resource, self).__contains__(name): - return super(Resource, self).__getitem__(name) - return None - elif name == 'desc': # TODO(zns): deprecate this - # We need to maintain this compatibility with this nasty attribute - # until we're done refactoring - return self.description - elif name == self.__class__.__name__.lower(): - # Supports using dict syntax to access the attributes of the - # class. Ex: Resource(id=1)['resource']['id'] - return self - else: - return super(Resource, self).__getitem__(name) - - def __contains__(self, key): - if key in self.contract_attributes: - return True - return super(Resource, self).__contains__(key) - - # - # Serialization Functions - may be moved to a different class - # - def to_dict(self, model_name=None, hints=None): - """ For compatibility with logic.types """ - if model_name is None: - model_name = self.__class__.__name__.lower() - result = self.strip_null_fields(self.copy()) - if hints is None: - hints = self.hints - if hints: - if "types" in hints: - Resource.apply_type_mappings( - result, - hints["types"]) - if "maps" in hints: - Resource.apply_name_mappings( - result, - hints["maps"]) - return {model_name: result} - - def to_json(self, hints=None, model_name=None): - """ Serializes object to json - implies latest Keystone contract """ - d = self.to_dict(model_name=model_name) - if hints is None: - hints = self.hints - if hints: - if "types" in hints: - Resource.apply_type_mappings( - d[model_name or self.__class__.__name__.lower()], - hints["types"]) - if "maps" in hints: - Resource.apply_name_mappings( - d[model_name or self.__class__.__name__.lower()], - hints["maps"]) - return json.dumps(d) - - def to_xml(self, hints=None, model_name=None): - """ Serializes object to XML string - - implies latest Keystone contract - :param hints: see introduction on format""" - if hints is None: - hints = self.hints - dom = self.to_dom(hints=hints, model_name=model_name) - return etree.tostring(dom) - - def to_dom(self, xmlns=None, hints=None, model_name=None): - """ Serializes object to XML objec - - implies latest Keystone contract - :param xmlns: accepts an optional namespace for XML - :param tags: accepts a list of attribute names that should go into XML - tags instead of attributes - """ - if xmlns is None: - xmlns = self.xmlns - if hints is None: - hints = self.hints - if model_name is None: - model_name = self.__class__.__name__.lower() - if xmlns: - dom = etree.Element(model_name, xmlns=xmlns) - else: - dom = etree.Element(model_name) - Resource.write_dict_to_xml(self, dom, hints) - return dom - - # - # Deserialization functions - # - @classmethod - def from_json(cls, json_str, hints=None, model_name=None): - """ Deserializes object from json - assumes latest Keystone - contract - """ - if hints is None: - hints = cls.hints - try: - obj = json.loads(json_str) - if model_name is None: - model_name = cls.__name__.lower() - if model_name in obj: - # Ignore class name if it is there - obj = obj[model_name] - if hints and ('maps' in hints): - name_mappings = hints['maps'] - if name_mappings: - Resource.reverse_name_mappings(obj, name_mappings) - model_object = None - if hints: - if 'contract_attributes' in hints: - # build mapping and instantiate object with - # contract_attributes provided - params = {} - for name in hints['contract_attributes']: - if name in obj: - params[name] = obj[name] - else: - params[name] = None - model_object = cls(**params) - if model_object is None: - model_object = cls() - model_object.update(obj) - - if hints and ('types' in hints): - type_mappings = hints['types'] - Resource.apply_type_mappings(model_object, type_mappings) - return model_object - except (ValueError, TypeError) as e: - raise fault.BadRequestFault("Cannot parse '%s' json" % \ - cls.__name__, str(e)) - - @classmethod - def from_xml(cls, xml_str, hints=None): - """ Deserializes object from XML - assumes latest Keystone - contract """ - if hints is None: - hints = cls.hints - try: - object = etree.fromstring(xml_str) - model_object = None - type_mappings = None - name_mappings = None - if hints: - if 'types' in hints: - type_mappings = hints['types'] - if 'contract_attributes' in hints: - # build mapping and instantiate object with - # contract_attributes provided - params = {} - for name in hints['contract_attributes']: - params[name] = object.get(name, None) - model_object = cls(**params) - if 'maps' in hints: - name_mappings = hints['maps'] - if model_object is None: - model_object = cls() - cls.write_xml_to_dict(object, model_object) - if type_mappings: - Resource.apply_type_mappings(model_object, type_mappings) - if name_mappings: - Resource.reverse_name_mappings(model_object, name_mappings) - return model_object - except etree.LxmlError as e: - raise fault.BadRequestFault("Cannot parse '%s' xml" % cls.__name__, - str(e)) - - # - # Validation calls - # - def validate(self): - """ Validates object attributes. Raises error if object not valid - - This calls inspect() in fail_fast mode, so it gets back the first - validation error and raises it. It is up to the code in inspect() - to determine what validations take precedence and are returned - first - - :returns: True if no validation errors raise""" - errors = self.inspect(fail_fast=True) - if errors: - raise errors[0][0](errors[0][1]) - return True - - # pylint: disable=W0613, R0201 - def inspect(self, fail_fast=None): - """ Validates and retuns validation results without raising any errors - :param fail_fast" return after first validation failure - - :returns: [(faultClass, message), ...], ordered by relevance - - if None, then no errors found - """ - return [] - - # - # Formatting, hint processing functions - # - - @staticmethod - def strip_null_fields(dict_object): - """ Strips null fields from dict""" - for k, v in dict_object.items(): - if v is None: - del dict_object[k] - return dict_object - - @staticmethod - def write_dict_to_xml(dict_object, xml, hints=None): - """ Attempts to convert a dict into XML as best as possible. - Converts named keys and attributes and recursively calls for - any values are are embedded dicts - - :param hints: handles tags (a list of attribute names that should go - into XML tags instead of attributes) and maps - """ - tags = [] - rename = [] - maps = {} - if hints is not None: - if 'tags' in hints: - tags = hints['tags'] - if 'maps' in hints: - maps = hints['maps'] - rename = maps.values() - for name, value in dict_object.iteritems(): - if name in rename: - name = maps.keys()[rename.index(name)] - if isinstance(value, dict): - element = etree.SubElement(xml, name) - Resource.write_dict_to_xml(value, element) - elif name in tags: - element = xml.find(name) - if isinstance(value, dict): - if element is None: - element = etree.SubElement(xml, name) - Resource.write_dict_to_xml(value, element) - else: - if value is not None: - if element is None: - element = etree.SubElement(xml, name) - element.text = str(value) - else: - if value is not None: - if isinstance(value, dict): - Resource.write_dict_to_xml(value, xml) - elif isinstance(value, bool): - xml.set(name, str(value).lower()) - else: - xml.set(name, str(value)) - else: - if name in xml: - del xml.attrib[name] - - @staticmethod - def write_xml_to_dict(xml, dict_object): - """ Attempts to update a dict with XML as best as possible.""" - for key, value in xml.items(): - dict_object[key] = value - for element in xml.iterdescendants(): - name = element.tag - if "}" in name: - #trim away namespace if it is there - name = name[name.rfind("}") + 1:] - if element.attrib == {}: - dict_object[name] = element.text - else: - dict_object[name] = {} - Resource.write_xml_to_dict(element, dict_object[element.tag]) - - @staticmethod - def apply_type_mappings(target, type_mappings): - """Applies type mappings to dict values""" - if type_mappings: - for name, type in type_mappings: - if type is int: - target[name] = int(target[name]) - elif issubclass(type, basestring): - # Move sub to string - if name in target: - value = target[name] - if isinstance(value, dict): - value = value[0] - if value: - target[name] = str(value) - elif type is bool: - target[name] = str(target[name]).lower() not in ['0', - 'false'] - else: - raise NotImplementedError("Model type mappings cannot \ - handle '%s' types" % type.__name__) - - @staticmethod - def apply_name_mappings(target, name_mappings): - """ Applies name mappings to dict values """ - if name_mappings: - for outside, inside in name_mappings.iteritems(): - if inside in target: - target[outside] = target.pop(inside) - - @staticmethod - def reverse_name_mappings(target, name_mappings): - """ Extracts names from mappings to dict values """ - if name_mappings: - for outside, inside in name_mappings.iteritems(): - if outside in target: - target[inside] = target.pop(outside) - - # - # Backend management - # - def save(self): - """ Handles finding correct backend and writing to it - Supports both saving new object (create) and updating an existing one - """ - #if self.status == 'new': - # #backends[find the class].create(self) - #elif self.status == 'existing': - # #backends[find the class].update(self) - pass - - def delete(self): - """ Handles finding correct backend and deleting object from it """ - pass - - @classmethod - def get(cls, id=None, *args, **kw): - # backends[find the class].get(id, *args, **kw) - return cls(*args, **kw) - - -class Service(Resource): - """ Service model """ - def __init__(self, id=None, name=None, type=None, description=None, - owner_id=None, *args, **kw): - super(Service, self).__init__(id=id, name=name, type=type, - description=description, - owner_id=owner_id, *args, **kw) - # pylint: disable=E0203 - if isinstance(self.id, int): - self.id = str(self.id) - - def to_dict(self, model_name=None): - if model_name is None: - model_name = 'OS-KSADM:service' - return super(Service, self).to_dict(model_name=model_name) - - def to_dom(self, xmlns=None, hints=None, model_name=None): - if xmlns is None: - xmlns = "http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0" - return super(Service, self).to_dom(xmlns, hints=hints, - model_name=model_name) - - @classmethod - def from_json(cls, json_str, hints=None, model_name=None): - if model_name is None: - model_name = 'OS-KSADM:service' - result = super(Service, cls).from_json(json_str, hints=hints, - model_name=model_name) - result.validate() # TODO(zns): remove; compatibility with logic.types - return result - - @classmethod - def from_xml(cls, xml_str, hints=None): - result = super(Service, cls).from_xml(xml_str, hints=hints) - result.validate() # TODO(zns): remove; compatibility with logic.types - return result - - def inspect(self, fail_fast=None): - result = super(Service, self).inspect(fail_fast) - if fail_fast and result: - return result - - # Check that fields are valid - invalid = [key for key in result if key not in - ['id', 'name', 'type', 'description', 'owner_id']] - if invalid: - result.append((fault.BadRequestFault, "Invalid attribute(s): %s" - % invalid)) - if fail_fast: - return result - - if utils.is_empty_string(self.name): - result.append((fault.BadRequestFault, "Expecting Service Name")) - if fail_fast: - return result - - if utils.is_empty_string(self.type): - result.append((fault.BadRequestFault, "Expecting Service Type")) - if fail_fast: - return result - return result - - -class Services(object): - "A collection of services." - - def __init__(self, values, links): - self.values = values - self.links = links - - def to_xml(self, model_name=None): - dom = etree.Element("services") - dom.set(u"xmlns", - "http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0") - - for t in self.values: - dom.append(t.to_dom(model_name=model_name)) - - for t in self.links: - dom.append(t.to_dom(model_name=model_name)) - - return etree.tostring(dom) - - def to_json(self, model_name=None): - services = [t.to_dict()["OS-KSADM:service"] - for t in self.values] - services_links = [t.to_dict()["links"] - for t in self.links] - return json.dumps({"OS-KSADM:services": services, - "OS-KSADM:services_links": services_links}) - - -class Tenant(Resource): - """ Tenant model """ - # pylint: disable=E0203,C0103 - def __init__(self, id=None, name=None, description=None, enabled=None, - *args, **kw): - super(Tenant, self).__init__(id=id, name=name, - description=description, enabled=enabled, - *args, **kw) - if isinstance(self.id, int): - self.id = str(self.id) - if isinstance(self.enabled, basestring): - self.enabled = self.enabled.lower() == 'true' - - @classmethod - def from_xml(cls, xml_str, hints=None): - if hints is None: - hints = {} - if 'contract_attributes' not in hints: - hints['contract_attributes'] = ['id', 'name', 'description', - 'enabled'] - if 'tags' not in hints: - hints['tags'] = ["description"] - return super(Tenant, cls).from_xml(xml_str, hints=hints) - - def to_dom(self, xmlns=None, hints=None, model_name=None): - if hints is None: - hints = {} - if 'tags' not in hints: - hints['tags'] = ["description"] - if xmlns is None: - xmlns = "http://docs.openstack.org/identity/api/v2.0" - - return super(Tenant, self).to_dom(xmlns=xmlns, hints=hints, - model_name=model_name) - - def to_xml(self, hints=None, model_name=None): - if hints is None: - hints = {} - if 'tags' not in hints: - hints['tags'] = ["description"] - return super(Tenant, self).to_xml(hints=hints, model_name=model_name) - - def to_json(self, hints=None, model_name=None): - if hints is None: - hints = {} - return super(Tenant, self).to_json(hints=hints, model_name=model_name) - - -class User(Resource): - """ User model - - Attribute Notes: - default_tenant_id (formerly tenant_id): this attribute can be enabled or - disabled by configuration. When enabled, any authentication call - without a tenant gets authenticated to this tenant. - """ - # pylint: disable=R0913 - def __init__(self, id=None, password=None, name=None, - tenant_id=None, - email=None, enabled=None, - *args, **kw): - super(User, self).__init__(id=id, password=password, name=name, - tenant_id=tenant_id, email=email, - enabled=enabled, *args, **kw) - - def to_json(self, hints=None, model_name=None): - results = super(User, self).to_json(hints, model_name=model_name) - assert 'password":' not in results - return results - - def to_xml(self, hints=None): - results = super(User, self).to_xml(hints) - assert 'password"=' not in results - return results - - -class EndpointTemplate(Resource): - """ EndpointTemplate model """ - # pylint: disable=R0913 - def __init__(self, id=None, region=None, service_id=None, public_url=None, - admin_url=None, internal_url=None, enabled=None, is_global=None, - version_id=None, version_list=None, version_info=None, *args, - **kw): - super(EndpointTemplate, self).__init__(id=id, region=region, - service_id=service_id, public_url=public_url, - admin_url=admin_url, internal_url=internal_url, - enabled=enabled, is_global=is_global, version_id=version_id, - version_list=version_list, version_info=version_info, *args, - **kw) - - -class Endpoint(Resource): - """ Endpoint model """ - # pylint: disable=R0913 - def __init__(self, id=None, endpoint_template_id=None, tenant_id=None, - *args, **kw): - super(Endpoint, self).__init__(id=id, tenant_id=tenant_id, - endpoint_template_id=endpoint_template_id, *args, **kw) - - -class Role(Resource): - """ Role model """ - hints = {"maps": - {"userId": "user_id", - "roleId": "role_id", - "serviceId": "service_id", - "tenantId": "tenant_id"}, - "contract_attributes": ['id', 'name', 'service_id', - 'tenant_id', 'description'], - "types": [('id', basestring), ('service_id', basestring)], - } - xmlns = "http://docs.openstack.org/identity/api/v2.0" - - def __init__(self, id=None, name=None, description=None, service_id=None, - tenant_id=None, *args, **kw): - super(Role, self).__init__(id=id, name=name, - description=description, - service_id=service_id, - tenant_id=tenant_id, - *args, **kw) - # pylint: disable=E0203 - if isinstance(self.id, int): - self.id = str(self.id) - # pylint: disable=E0203 - if isinstance(self.service_id, int): - self.service_id = str(self.service_id) - - @classmethod - def from_json(cls, json_str, hints=None, model_name=None): - # Check that fields are valid - role = json.loads(json_str) - if model_name is None: - model_name = "role" - if model_name in role: - role = role[model_name] - - invalid = [key for key in role if key not in - ['id', 'name', 'description', 'serviceId', - # TODO(zns): remove those when we separate grants - # from Roles - 'tenantId', 'userId']] - if invalid: - raise fault.BadRequestFault("Invalid attribute(s): %s" - % invalid) - - return super(Role, cls).from_json(json_str, hints=hints, - model_name=model_name) - - -class Roles(object): - "A collection of roles." - - def __init__(self, values, links): - self.values = values - self.links = links - - def to_xml(self): - dom = etree.Element("roles") - dom.set(u"xmlns", "http://docs.openstack.org/identity/api/v2.0") - - for t in self.values: - dom.append(t.to_dom()) - - for t in self.links: - dom.append(t.to_dom()) - - return etree.tostring(dom) - - def to_dom(self): - dom = etree.Element("roles") - dom.set(u"xmlns", "http://docs.openstack.org/identity/api/v2.0") - - if self.values: - for t in self.values: - dom.append(t.to_dom()) - - if self.links: - for t in self.links: - dom.append(t.to_dom()) - - return dom - - def to_json(self): - values = [t.to_dict()["role"] - for t in self.values] - links = [t.to_dict()["links"] - for t in self.links] - model_name = "roles" - return json.dumps({model_name: values, - ("%s_links" % model_name): links}) - - def to_json_values(self): - values = [t.to_dict()["role"] for t in self.values] - return values - - -class Token(Resource): - """ Token model """ - def __init__(self, id=None, user_id=None, expires=None, tenant_id=None, - *args, **kw): - super(Token, self).__init__(id=id, user_id=user_id, expires=expires, - tenant_id=tenant_id, *args, **kw) - - -class UserRoleAssociation(Resource): - """ Role Grant model """ - - hints = { - 'contract_attributes': ['id', 'role_id', 'user_id', 'tenant_id'], - 'types': [('user_id', basestring), ('tenant_id', basestring)], - 'maps': {'userId': 'user_id', 'roleId': 'role_id', - 'tenantId': 'tenant_id'} - } - - def __init__(self, user_id=None, role_id=None, tenant_id=None, - *args, **kw): - # pylint: disable=E0203 - super(UserRoleAssociation, self).__init__(user_id=user_id, - role_id=role_id, tenant_id=tenant_id, - *args, **kw) - if isinstance(self.user_id, int): - # pylint: disable=E0203 - self.user_id = str(self.user_id) - if isinstance(self.tenant_id, int): - self.tenant_id = str(self.tenant_id) - - def to_json(self, hints=None, model_name=None): - if model_name is None: - model_name = "role" - return super(UserRoleAssociation, self).to_json(hints=hints, - model_name=model_name) - - def to_xml(self, hints=None, model_name=None): - if model_name is None: - model_name = "role" - return super(UserRoleAssociation, self).to_xml(hints=hints, - model_name=model_name) - - -class Credentials(Resource): - # pylint: disable=R0913 - def __init__(self, id=None, user_id=None, tenant_id=None, type=None, - key=None, secret=None, *args, **kw): - super(Credentials, self).__init__(id=id, user_id=user_id, - tenant_id=tenant_id, type=type, key=key, secret=secret, *args, - **kw) diff --git a/keystone/policy/__init__.py b/keystone/policy/__init__.py new file mode 100644 index 00000000..d16de59f --- /dev/null +++ b/keystone/policy/__init__.py @@ -0,0 +1 @@ +from keystone.policy.core import * diff --git a/keystone/contrib/extensions/service/osec2/__init__.py b/keystone/policy/backends/__init__.py index e69de29b..e69de29b 100644 --- a/keystone/contrib/extensions/service/osec2/__init__.py +++ b/keystone/policy/backends/__init__.py diff --git a/keystone/policy/backends/simple.py b/keystone/policy/backends/simple.py new file mode 100644 index 00000000..ec4840fe --- /dev/null +++ b/keystone/policy/backends/simple.py @@ -0,0 +1,23 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + + +from keystone.common import logging + + +class TrivialTrue(object): + def can_haz(self, target, credentials): + return True + + +class SimpleMatch(object): + def can_haz(self, target, credentials): + """Check whether key-values in target are present in credentials.""" + # TODO(termie): handle ANDs, probably by providing a tuple instead of a + # string + for requirement in target: + key, match = requirement.split(':', 1) + check = credentials.get(key) + if check is None or isinstance(check, basestring): + check = [check] + if match in check: + return True diff --git a/keystone/policy/core.py b/keystone/policy/core.py new file mode 100644 index 00000000..694f6285 --- /dev/null +++ b/keystone/policy/core.py @@ -0,0 +1,21 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +"""Main entry point into the Policy service.""" + +from keystone import config +from keystone.common import manager + + +CONF = config.CONF + + +class Manager(manager.Manager): + """Default pivot point for the Policy 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.policy.driver) diff --git a/keystone/routers/__init__.py b/keystone/routers/__init__.py deleted file mode 100644 index e69de29b..00000000 --- a/keystone/routers/__init__.py +++ /dev/null diff --git a/keystone/routers/admin.py b/keystone/routers/admin.py deleted file mode 100755 index 63ceb4f0..00000000 --- a/keystone/routers/admin.py +++ /dev/null @@ -1,145 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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 logging -import routes - -from keystone.common import wsgi -from keystone.controllers.token import TokenController -from keystone.controllers.roles import RolesController -from keystone.controllers.staticfiles import StaticFilesController -from keystone.controllers.tenant import TenantController -from keystone.controllers.user import UserController -from keystone.controllers.version import VersionController -from keystone.controllers.extensions import ExtensionsController -import keystone.contrib.extensions.admin as extension - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class AdminApi(wsgi.Router): - """WSGI entry point for admin Keystone API requests.""" - - def __init__(self): - mapper = routes.Mapper() - - # Load extensions first so they can override core if they need to - extension.get_extension_configurer().configure(mapper) - - # Token Operations - auth_controller = TokenController() - mapper.connect("/tokens", controller=auth_controller, - action="authenticate", - conditions=dict(method=["POST"])) - mapper.connect("/tokens/{token_id}", controller=auth_controller, - action="validate_token", - conditions=dict(method=["GET"])) - mapper.connect("/tokens/{token_id}", controller=auth_controller, - action="check_token", - conditions=dict(method=["HEAD"])) - # Do we need this. API doesn't have delete token. - mapper.connect("/tokens/{token_id}", controller=auth_controller, - action="delete_token", - conditions=dict(method=["DELETE"])) - mapper.connect("/tokens/{token_id}/endpoints", - controller=auth_controller, - action="endpoints", - conditions=dict(method=["GET"])) - - # Tenant Operations - tenant_controller = TenantController() - mapper.connect("/tenants", controller=tenant_controller, - action="get_tenants", conditions=dict(method=["GET"])) - mapper.connect("/tenants/{tenant_id}", - controller=tenant_controller, - action="get_tenant", conditions=dict(method=["GET"])) - roles_controller = RolesController() - mapper.connect("/tenants/{tenant_id}/users/{user_id}/roles", - controller=roles_controller, action="get_user_roles", - conditions=dict(method=["GET"])) - # User Operations - user_controller = UserController() - mapper.connect("/users/{user_id}", - controller=user_controller, - action="get_user", - conditions=dict(method=["GET"])) - mapper.connect("/users/{user_id}/roles", - controller=roles_controller, action="get_user_roles", - conditions=dict(method=["GET"])) - # Miscellaneous Operations - version_controller = VersionController() - mapper.connect("/", controller=version_controller, - action="get_version_info", file="admin/version", - conditions=dict(method=["GET"])) - - extensions_controller = ExtensionsController() - mapper.connect("/extensions", - controller=extensions_controller, - action="get_extensions_info", - conditions=dict(method=["GET"])) - - # Static Files Controller - static_files_controller = StaticFilesController() - mapper.connect("/identityadminguide.pdf", - controller=static_files_controller, - action="get_pdf_contract", - root="content/admin/", pdf="identityadminguide.pdf", - conditions=dict(method=["GET"])) - mapper.connect("/identity-admin.wadl", - controller=static_files_controller, - action="get_wadl_contract", - root="content/admin/", wadl="identity-admin.wadl", - conditions=dict(method=["GET"])) - mapper.connect("/common.ent", - controller=static_files_controller, - action="get_wadl_contract", - root="content/common/", wadl="common.ent", - conditions=dict(method=["GET"])) - mapper.connect("/xsd/{xsd}", - controller=static_files_controller, - action="get_xsd_contract", - root="content/common/", - conditions=dict(method=["GET"])) - mapper.connect("/xsd/atom/{xsd}", - controller=static_files_controller, - action="get_xsd_atom_contract", - root="content/common/", - conditions=dict(method=["GET"])) - mapper.connect("/xslt/{file:.*}", - controller=static_files_controller, - action="get_static_file", - root="content/common/", path="xslt/", - mimetype="application/xml", - conditions=dict(method=["GET"])) - mapper.connect("/js/{file:.*}", - controller=static_files_controller, - action="get_static_file", - root="content/common/", path="js/", - mimetype="application/javascript", - conditions=dict(method=["GET"])) - mapper.connect("/style/{file:.*}", - controller=static_files_controller, - action="get_static_file", - root="content/common/", path="style/", - mimetype="application/css", - conditions=dict(method=["GET"])) - mapper.connect("/samples/{file:.*}", - controller=static_files_controller, - action="get_static_file", - root="content/common/", path="samples/", - conditions=dict(method=["GET"])) - super(AdminApi, self).__init__(mapper) diff --git a/keystone/routers/service.py b/keystone/routers/service.py deleted file mode 100644 index 529e5b44..00000000 --- a/keystone/routers/service.py +++ /dev/null @@ -1,117 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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 logging -import routes - -from keystone.common import wsgi -from keystone.controllers.token import TokenController -from keystone.controllers.tenant import TenantController -from keystone.controllers.version import VersionController -from keystone.controllers.staticfiles import StaticFilesController -from keystone.controllers.extensions import ExtensionsController - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -class ServiceApi(wsgi.Router): - """WSGI entry point for Keystone Service API requests.""" - - def __init__(self): - mapper = routes.Mapper() - - # Token Operations - auth_controller = TokenController() - mapper.connect("/tokens", controller=auth_controller, - action="authenticate", - conditions=dict(method=["POST"])) - # TODO(zns): this should be deprecated - mapper.connect("/ec2tokens", controller=auth_controller, - action="authenticate_ec2", - conditions=dict(method=["POST"])) - - tenant_controller = TenantController(True) - mapper.connect("/tenants", - controller=tenant_controller, - action="get_tenants", - conditions=dict(method=["GET"])) - - # Miscellaneous Operations - version_controller = VersionController() - mapper.connect("/", - controller=version_controller, - action="get_version_info", file="service/version", - conditions=dict(method=["GET"])) - - extensions_controller = ExtensionsController(True) - mapper.connect("/extensions", - controller=extensions_controller, - action="get_extensions_info", - conditions=dict(method=["GET"])) - - # Static Files Controller - static_files_controller = StaticFilesController() - mapper.connect("/identitydevguide.pdf", - controller=static_files_controller, - action="get_pdf_contract", - root="content/service/", pdf="identitydevguide.pdf", - conditions=dict(method=["GET"])) - mapper.connect("/identity.wadl", - controller=static_files_controller, - action="get_wadl_contract", - root="content/service/", wadl="identity.wadl", - conditions=dict(method=["GET"])) - mapper.connect("/common.ent", - controller=static_files_controller, - action="get_wadl_contract", - wadl="common.ent", root="content/common/", - conditions=dict(method=["GET"])) - mapper.connect("/xslt/{file:.*}", - controller=static_files_controller, - action="get_static_file", path="common/xslt/", - mimetype="application/xml", - conditions=dict(method=["GET"])) - mapper.connect("/style/{file:.*}", - controller=static_files_controller, - action="get_static_file", path="common/style/", - mimetype="application/css", - conditions=dict(method=["GET"])) - mapper.connect("/js/{file:.*}", - controller=static_files_controller, - action="get_static_file", path="common/js/", - mimetype="application/javascript", - conditions=dict(method=["GET"])) - mapper.connect("/samples/{file:.*}", - controller=static_files_controller, - action="get_static_file", path="common/samples/", - conditions=dict(method=["GET"])) - mapper.connect("/xsd/{xsd:.*}", - controller=static_files_controller, - action="get_xsd_contract", root="content/common/", - conditions=dict(method=["GET"])) - mapper.connect("/xsd/atom/{xsd}", - controller=static_files_controller, - action="get_xsd_atom_contract", - root="content/common/", - conditions=dict(method=["GET"])) - mapper.connect("/xsd/atom/{xsd}", - controller=static_files_controller, - action="get_xsd_atom_contract", - root="content/common/", - conditions=dict(method=["GET"])) - - super(ServiceApi, self).__init__(mapper) diff --git a/keystone/server.py b/keystone/server.py deleted file mode 100755 index de040948..00000000 --- a/keystone/server.py +++ /dev/null @@ -1,186 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-2011 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. - - -""" -Service that stores identities and issues and manages tokens - -HEADERS -------- - -* HTTP\_ is a standard http header -* HTTP_X is an extended http header - -Coming in from initial call -^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -HTTP_X_AUTH_TOKEN - the client token being passed in - -HTTP_X_STORAGE_TOKEN - the client token being passed in (legacy Rackspace use) to support - cloud files - -Used for communication between components -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -www-authenticate - only used if this component is being used remotely - -HTTP_AUTHORIZATION - basic auth password used to validate the connection - -What we add to the request for use by the OpenStack SERVICE -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -HTTP_X_AUTHORIZATION - the client identity being passed in - -""" - -# pylint: disable=W0613 - -import logging - -from keystone import config -from keystone.common import config as common_config -from keystone.common import wsgi -from keystone.routers.service import ServiceApi -from keystone.routers.admin import AdminApi - -CONF = config.CONF - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - - -def service_app_factory(global_conf, **local_conf): - """paste.deploy app factory for creating OpenStack API server apps""" - return ServiceApi() - - -def admin_app_factory(global_conf, **local_conf): - """paste.deploy app factory for creating OpenStack API server apps""" - return AdminApi() - - -# pylint: disable=R0902 -class Server(): - """Used to start and stop Keystone servers - - This class is called from shell and command-line scripts and is the - entry-point for starting and stopping Keystone servers. - - The initializer can take option and argument overrides, but otherwise will - parse arguments and configuration files itself to determine how to start - the server. - """ - - def __init__(self, name='admin', config_name=None, args=None): - """Initizalizer which takes the following paramaters: - - :param name: A cosmetic name for the server (ex. Admin API) - :param config: the paste config name to look for when starting the - server - :param args: override for sys.argv (otherwise sys.argv is used) - """ - logger.debug("Init server '%s' with config=%s" % (name, config_name)) - - self.name = name - self.config = config_name or self.name - self.args = args - self.key = None - self.server = None - self.port = None - self.host = None - self.protocol = None - self.options = CONF.to_dict() - - def start(self, host=None, port=None, wait=True): - """Starts the Keystone server - - :param host: the IP address to listen on - :param port: the TCP/IP port to listen on - :param wait: whether to wait (block) for the server to terminate or - return to the caller without waiting - """ - logger.debug("Starting API server") - conf, app = common_config.load_paste_app(self.config, self.options, - self.args) - - debug = CONF.debug in [True, "True", "1"] - verbose = CONF.verbose in [True, "True", "1"] - - if debug or verbose: - config_file = common_config.find_config_file(self.options, - self.args) - logger.info("Starting '%s' with config: %s" % - (self.config, config_file)) - - if port is None: - if self.config == 'admin': - # Legacy - port = int(CONF.admin_port or 35357) - else: - port = int(CONF.service_port or CONF.bind_port or 5000) - if host is None: - host = CONF.bind_host or CONF.service_host or "0.0.0.0" - - self.key = "%s-%s:%s" % (self.name, host, port) - - # Safely get SSL options - service_ssl = CONF.service_ssl in [True, "True", "1"] - - # Load the server - if service_ssl: - cert_required = conf.get('cert_required', False) - cert_required = cert_required in [True, "True", "1"] - certfile = conf.get('certfile') - keyfile = conf.get('keyfile') - ca_certs = conf.get('ca_certs') - - self.server = wsgi.SslServer() - self.server.start(app, port, host, - certfile=certfile, keyfile=keyfile, - ca_certs=ca_certs, - cert_required=cert_required, - key=self.key) - self.protocol = 'https' - else: - self.server = wsgi.Server() - self.server.start(app, port, host, - key="%s-%s:%s" % (self.config, host, port)) - self.protocol = 'http' - - self.port = port - self.host = host - - logger.info("%s listening on %s://%s:%s" % ( - self.name, ['http', 'https'][service_ssl], host, port)) - - # Wait until done - if wait: - self.server.wait() - - def stop(self): - """Stops the Keystone server - - This should be called always to release the network socket - """ - if self.server is not None: - if self.key in self.server.threads: - logger.debug("Killing %s" % self.key) - self.server.threads[self.key].kill() - self.server = None diff --git a/keystone/service.py b/keystone/service.py new file mode 100644 index 00000000..2c361e40 --- /dev/null +++ b/keystone/service.py @@ -0,0 +1,448 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import uuid + +import routes +import webob.dec +import webob.exc + +from keystone import catalog +from keystone import exception +from keystone import identity +from keystone import policy +from keystone import token +from keystone.common import logging +from keystone.common import utils +from keystone.common import wsgi + + +class AdminRouter(wsgi.ComposingRouter): + def __init__(self): + mapper = routes.Mapper() + + # Token Operations + auth_controller = TokenController() + mapper.connect('/tokens', + controller=auth_controller, + action='authenticate', + conditions=dict(method=['POST'])) + mapper.connect('/tokens/{token_id}', + controller=auth_controller, + action='validate_token', + conditions=dict(method=['GET'])) + mapper.connect('/tokens/{token_id}', + controller=auth_controller, + action='delete_token', + conditions=dict(method=['DELETE'])) + mapper.connect('/tokens/{token_id}/endpoints', + controller=auth_controller, + action='endpoints', + conditions=dict(method=['GET'])) + + # Miscellaneous Operations + extensions_controller = ExtensionsController() + mapper.connect('/extensions', + controller=extensions_controller, + action='get_extensions_info', + conditions=dict(method=['GET'])) + identity_router = identity.AdminRouter() + routers = [identity_router] + super(AdminRouter, self).__init__(mapper, routers) + + +class PublicRouter(wsgi.ComposingRouter): + def __init__(self): + mapper = routes.Mapper() + + noop_controller = NoopController() + mapper.connect('/', + controller=noop_controller, + action='noop') + + # Token Operations + auth_controller = TokenController() + mapper.connect('/tokens', + controller=auth_controller, + action='authenticate', + conditions=dict(method=['POST'])) + + # Miscellaneous + extensions_controller = ExtensionsController() + mapper.connect('/extensions', + controller=extensions_controller, + action='get_extensions_info', + conditions=dict(method=['GET'])) + + identity_router = identity.PublicRouter() + routers = [identity_router] + + super(PublicRouter, self).__init__(mapper, routers) + + +class PublicVersionRouter(wsgi.ComposingRouter): + def __init__(self): + mapper = routes.Mapper() + version_controller = VersionController('public') + mapper.connect('/', + controller=version_controller, + action='get_versions') + routers = [] + super(PublicVersionRouter, self).__init__(mapper, routers) + + +class AdminVersionRouter(wsgi.ComposingRouter): + def __init__(self): + mapper = routes.Mapper() + version_controller = VersionController('admin') + mapper.connect('/', + controller=version_controller, + action='get_versions') + routers = [] + super(AdminVersionRouter, self).__init__(mapper, routers) + + +class VersionController(wsgi.Application): + def __init__(self, version_type): + self.catalog_api = catalog.Manager() + self.url_key = "%sURL" % version_type + super(VersionController, self).__init__() + + def _get_identity_url(self, context): + catalog_ref = self.catalog_api.get_catalog( + context=context, + user_id=None, + tenant_id=None) + for region, region_ref in catalog_ref.iteritems(): + for service, service_ref in region_ref.iteritems(): + if service == 'identity': + return service_ref[self.url_key] + + raise NotImplemented() + + def get_versions(self, context): + identity_url = self._get_identity_url(context) + if not identity_url.endswith('/'): + identity_url = identity_url + '/' + return { + "versions": { + "values": [{ + "id": "v2.0", + "status": "beta", + "updated": "2011-11-19T00:00:00Z", + "links": [{ + "rel": "self", + "href": identity_url, + }, { + "rel": "describedby", + "type": "text/html", + "href": "http://docs.openstack.org/api/openstack-" + "identity-service/2.0/content/" + }, { + "rel": "describedby", + "type": "application/pdf", + "href": "http://docs.openstack.org/api/openstack-" + "identity-service/2.0/identity-dev-guide-" + "2.0.pdf" + }], + "media-types": [{ + "base": "application/json", + "type": "application/vnd.openstack.identity-v2.0" + "+json" + }] + }] + } + } + + +class NoopController(wsgi.Application): + def __init__(self): + super(NoopController, self).__init__() + + def noop(self, context): + return {} + + +class TokenController(wsgi.Application): + def __init__(self): + self.catalog_api = catalog.Manager() + self.identity_api = identity.Manager() + self.token_api = token.Manager() + self.policy_api = policy.Manager() + super(TokenController, self).__init__() + + def authenticate(self, context, auth=None): + """Authenticate credentials and return a token. + + Accept auth as a dict that looks like:: + + { + "auth":{ + "passwordCredentials":{ + "username":"test_user", + "password":"mypass" + }, + "tenantName":"customer-x" + } + } + + In this case, tenant is optional, if not provided the token will be + considered "unscoped" and can later be used to get a scoped token. + + Alternatively, this call accepts auth with only a token and tenant + that will return a token that is scoped to that tenant. + """ + + token_id = uuid.uuid4().hex + if 'passwordCredentials' in auth: + username = auth['passwordCredentials'].get('username', '') + password = auth['passwordCredentials'].get('password', '') + tenant_name = auth.get('tenantName', None) + + if username: + user_ref = self.identity_api.get_user_by_name( + context=context, user_name=username) + user_id = user_ref['id'] + else: + user_id = auth['passwordCredentials'].get('userId', None) + + # more compat + if tenant_name: + tenant_ref = self.identity_api.get_tenant_by_name( + context=context, tenant_name=tenant_name) + tenant_id = tenant_ref['id'] + else: + tenant_id = auth.get('tenantId', None) + + try: + (user_ref, tenant_ref, metadata_ref) = \ + self.identity_api.authenticate(context=context, + user_id=user_id, + password=password, + tenant_id=tenant_id) + + # If the user is disabled don't allow them to authenticate + if not user_ref.get('enabled', True): + raise webob.exc.HTTPForbidden('User has been disabled') + except AssertionError as e: + raise webob.exc.HTTPForbidden(e.message) + + token_ref = self.token_api.create_token( + context, token_id, dict(id=token_id, + user=user_ref, + tenant=tenant_ref, + metadata=metadata_ref)) + if tenant_ref: + catalog_ref = self.catalog_api.get_catalog( + context=context, + user_id=user_ref['id'], + tenant_id=tenant_ref['id'], + metadata=metadata_ref) + else: + catalog_ref = {} + + elif 'token' in auth: + token = auth['token'].get('id', None) + + tenant_name = auth.get('tenantName') + + # more compat + if tenant_name: + tenant_ref = self.identity_api.get_tenant_by_name( + context=context, tenant_name=tenant_name) + tenant_id = tenant_ref['id'] + else: + tenant_id = auth.get('tenantId', None) + + try: + old_token_ref = self.token_api.get_token(context=context, + token_id=token) + except exception.NotFound: + raise exception.Unauthorized() + + user_ref = old_token_ref['user'] + + tenants = self.identity_api.get_tenants_for_user(context, + user_ref['id']) + if tenant_id: + assert tenant_id in tenants + + tenant_ref = self.identity_api.get_tenant(context=context, + tenant_id=tenant_id) + if tenant_ref: + metadata_ref = self.identity_api.get_metadata( + context=context, + user_id=user_ref['id'], + tenant_id=tenant_ref['id']) + catalog_ref = self.catalog_api.get_catalog( + context=context, + user_id=user_ref['id'], + tenant_id=tenant_ref['id'], + metadata=metadata_ref) + else: + metadata_ref = {} + catalog_ref = {} + + token_ref = self.token_api.create_token( + context, token_id, dict(id=token_id, + user=user_ref, + tenant=tenant_ref, + metadata=metadata_ref)) + + # TODO(termie): optimize this call at some point and put it into the + # the return for metadata + # fill out the roles in the metadata + roles_ref = [] + for role_id in metadata_ref.get('roles', []): + roles_ref.append(self.identity_api.get_role(context, role_id)) + logging.debug('TOKEN_REF %s', token_ref) + return self._format_authenticate(token_ref, roles_ref, catalog_ref) + + # admin only + def validate_token(self, context, token_id, belongs_to=None): + """Check that a token is valid. + + Optionally, also ensure that it is owned by a specific tenant. + + """ + # TODO(termie): this stuff should probably be moved to middleware + self.assert_admin(context) + + token_ref = self.token_api.get_token(context=context, + token_id=token_id) + + if belongs_to: + assert token_ref['tenant']['id'] == belongs_to + + # TODO(termie): optimize this call at some point and put it into the + # the return for metadata + # fill out the roles in the metadata + metadata_ref = token_ref['metadata'] + roles_ref = [] + for role_id in metadata_ref.get('roles', []): + roles_ref.append(self.identity_api.get_role(context, role_id)) + return self._format_token(token_ref, roles_ref) + + def delete_token(self, context, token_id): + """Delete a token, effectively invalidating it for authz.""" + # TODO(termie): this stuff should probably be moved to middleware + self.assert_admin(context) + + self.token_api.delete_token(context=context, token_id=token_id) + + def endpoints(self, context, token_id): + """Return service catalog endpoints.""" + try: + token_ref = self.token_api.get_token(context=context, + token_id=token_id) + except exception.NotFound: + raise exception.Unauthorized() + + catalog_ref = self.catalog_api.get_catalog(context, + token_ref['user']['id'], + token_ref['tenant']['id']) + return {'token': {'serviceCatalog': self._format_catalog(catalog_ref)}} + + def _format_authenticate(self, token_ref, roles_ref, catalog_ref): + o = self._format_token(token_ref, roles_ref) + o['access']['serviceCatalog'] = self._format_catalog(catalog_ref) + return o + + def _format_token(self, token_ref, roles_ref): + user_ref = token_ref['user'] + metadata_ref = token_ref['metadata'] + expires = token_ref['expires'] + if expires is not None: + expires = utils.isotime(expires) + o = {'access': {'token': {'id': token_ref['id'], + 'expires': expires, + }, + 'user': {'id': user_ref['id'], + 'name': user_ref['name'], + 'username': user_ref['name'], + 'roles': roles_ref, + 'roles_links': metadata_ref.get('roles_links', + []) + } + } + } + if 'tenant' in token_ref and token_ref['tenant']: + token_ref['tenant']['enabled'] = True + o['access']['token']['tenant'] = token_ref['tenant'] + return o + + def _format_catalog(self, catalog_ref): + """Munge catalogs from internal to output format + Internal catalogs look like: + + {$REGION: { + {$SERVICE: { + $key1: $value1, + ... + } + } + } + + The legacy api wants them to look like + + [{'name': $SERVICE[name], + 'type': $SERVICE, + 'endpoints': [{ + 'tenantId': $tenant_id, + ... + 'region': $REGION, + }], + 'endpoints_links': [], + }] + + """ + if not catalog_ref: + return {} + + services = {} + for region, region_ref in catalog_ref.iteritems(): + for service, service_ref in region_ref.iteritems(): + new_service_ref = services.get(service, {}) + new_service_ref['name'] = service_ref.pop('name') + new_service_ref['type'] = service + new_service_ref['endpoints_links'] = [] + service_ref['region'] = region + + endpoints_ref = new_service_ref.get('endpoints', []) + endpoints_ref.append(service_ref) + + new_service_ref['endpoints'] = endpoints_ref + services[service] = new_service_ref + + return services.values() + + +class ExtensionsController(wsgi.Application): + def __init__(self): + super(ExtensionsController, self).__init__() + + def get_extensions_info(self, context): + raise NotImplemented() + + +def public_app_factory(global_conf, **local_conf): + conf = global_conf.copy() + conf.update(local_conf) + return PublicRouter() + + +def admin_app_factory(global_conf, **local_conf): + conf = global_conf.copy() + conf.update(local_conf) + return AdminRouter() + + +def public_version_app_factory(global_conf, **local_conf): + conf = global_conf.copy() + conf.update(local_conf) + return PublicVersionRouter() + + +def admin_version_app_factory(global_conf, **local_conf): + conf = global_conf.copy() + conf.update(local_conf) + return AdminVersionRouter() diff --git a/keystone/test.py b/keystone/test.py new file mode 100644 index 00000000..f28ce6f7 --- /dev/null +++ b/keystone/test.py @@ -0,0 +1,248 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import os +import unittest +import subprocess +import sys +import time + +from paste import deploy + +from keystone import config +from keystone.common import kvs +from keystone.common import logging +from keystone.common import utils +from keystone.common import wsgi + + +ROOTDIR = os.path.dirname(os.path.dirname(__file__)) +VENDOR = os.path.join(ROOTDIR, 'vendor') +TESTSDIR = os.path.join(ROOTDIR, 'tests') +ETCDIR = os.path.join(ROOTDIR, 'etc') +CONF = config.CONF + + +cd = os.chdir + + +def rootdir(*p): + return os.path.join(ROOTDIR, *p) + + +def etcdir(*p): + return os.path.join(ETCDIR, *p) + + +def testsdir(*p): + return os.path.join(TESTSDIR, *p) + + +def checkout_vendor(repo, rev): + # TODO(termie): this function is a good target for some optimizations :PERF + name = repo.split('/')[-1] + if name.endswith('.git'): + name = name[:-4] + + working_dir = os.getcwd() + revdir = os.path.join(VENDOR, '%s-%s' % (name, rev.replace('/', '_'))) + modcheck = os.path.join(VENDOR, '.%s-%s' % (name, rev.replace('/', '_'))) + try: + if os.path.exists(modcheck): + mtime = os.stat(modcheck).st_mtime + if int(time.time()) - mtime < 10000: + return revdir + + if not os.path.exists(revdir): + utils.git('clone', repo, revdir) + + cd(revdir) + utils.git('checkout', '-q', 'master') + utils.git('pull', '-q') + utils.git('checkout', '-q', rev) + + # write out a modified time + with open(modcheck, 'w') as fd: + fd.write('1') + except subprocess.CalledProcessError as e: + logging.warning('Failed to checkout %s', repo) + cd(working_dir) + return revdir + + +class TestClient(object): + def __init__(self, app=None, token=None): + self.app = app + self.token = token + + def request(self, method, path, headers=None, body=None): + if headers is None: + headers = {} + + if self.token: + headers.setdefault('X-Auth-Token', self.token) + + req = wsgi.Request.blank(path) + req.method = method + for k, v in headers.iteritems(): + req.headers[k] = v + if body: + req.body = body + return req.get_response(self.app) + + def get(self, path, headers=None): + return self.request('GET', path=path, headers=headers) + + def post(self, path, headers=None, body=None): + return self.request('POST', path=path, headers=headers, body=body) + + def put(self, path, headers=None, body=None): + return self.request('PUT', path=path, headers=headers, body=body) + + +class TestCase(unittest.TestCase): + def __init__(self, *args, **kw): + super(TestCase, self).__init__(*args, **kw) + self._paths = [] + self._memo = {} + + def setUp(self): + super(TestCase, self).setUp() + self.config() + + def config(self): + CONF(config_files=[etcdir('keystone.conf'), + testsdir('test_overrides.conf')]) + + def tearDown(self): + for path in self._paths: + if path in sys.path: + sys.path.remove(path) + kvs.INMEMDB.clear() + CONF.reset() + super(TestCase, self).tearDown() + + def load_backends(self): + """Hacky shortcut to load the backends for data manipulation.""" + self.identity_api = utils.import_object(CONF.identity.driver) + self.token_api = utils.import_object(CONF.token.driver) + self.catalog_api = utils.import_object(CONF.catalog.driver) + + def load_fixtures(self, fixtures): + """Hacky basic and naive fixture loading based on a python module. + + Expects that the various APIs into the various services are already + defined on `self`. + + """ + # TODO(termie): doing something from json, probably based on Django's + # loaddata will be much preferred. + for tenant in fixtures.TENANTS: + rv = self.identity_api.create_tenant(tenant['id'], tenant) + setattr(self, 'tenant_%s' % tenant['id'], rv) + + for user in fixtures.USERS: + user_copy = user.copy() + tenants = user_copy.pop('tenants') + rv = self.identity_api.create_user(user['id'], user_copy.copy()) + for tenant_id in tenants: + self.identity_api.add_user_to_tenant(tenant_id, user['id']) + setattr(self, 'user_%s' % user['id'], user_copy) + + for role in fixtures.ROLES: + rv = self.identity_api.create_role(role['id'], role) + setattr(self, 'role_%s' % role['id'], rv) + + for metadata in fixtures.METADATA: + metadata_ref = metadata.copy() + # TODO(termie): these will probably end up in the model anyway, + # so this may be futile + del metadata_ref['user_id'] + del metadata_ref['tenant_id'] + rv = self.identity_api.create_metadata(metadata['user_id'], + metadata['tenant_id'], + metadata_ref) + setattr(self, + 'metadata_%s%s' % (metadata['user_id'], + metadata['tenant_id']), rv) + + def _paste_config(self, config): + if not config.startswith('config:'): + test_path = os.path.join(TESTSDIR, config) + etc_path = os.path.join(ROOTDIR, 'etc', config) + for path in [test_path, etc_path]: + if os.path.exists('%s.conf' % path): + return 'config:%s.conf' % path + return config + + def loadapp(self, config, name='main'): + return deploy.loadapp(self._paste_config(config), name=name) + + def appconfig(self, config): + return deploy.appconfig(self._paste_config(config)) + + def serveapp(self, config, name=None): + app = self.loadapp(config, name=name) + server = wsgi.Server(app, 0) + server.start(key='socket') + + # Service catalog tests need to know the port we ran on. + port = server.socket_info['socket'][1] + CONF.public_port = port + CONF.admin_port = port + return server + + def client(self, app, *args, **kw): + return TestClient(app, *args, **kw) + + def add_path(self, path): + sys.path.insert(0, path) + self._paths.append(path) + + def clear_module(self, module): + for x in sys.modules.keys(): + if x.startswith(module): + del sys.modules[x] + + def assertListEquals(self, actual, expected): + copy = expected[:] + #print expected, actual + self.assertEquals(len(actual), len(expected)) + while copy: + item = copy.pop() + matched = False + for x in actual: + #print 'COMPARE', item, x, + try: + self.assertDeepEquals(x, item) + matched = True + #print 'MATCHED' + break + except AssertionError as e: + #print e + pass + if not matched: + raise AssertionError('Expected: %s\n Got: %s' % (expected, + actual)) + + def assertDictEquals(self, actual, expected): + for k in expected: + self.assertTrue(k in actual, + 'Expected key %s not in %s.' % (k, actual)) + self.assertDeepEquals(expected[k], actual[k]) + + for k in actual: + self.assertTrue(k in expected, + 'Unexpected key %s in %s.' % (k, actual)) + + def assertDeepEquals(self, actual, expected): + try: + if type(expected) is type([]) or type(expected) is type(tuple()): + # assert items equal, ignore order + self.assertListEquals(actual, expected) + elif type(expected) is type({}): + self.assertDictEquals(actual, expected) + else: + self.assertEquals(actual, expected) + except AssertionError as e: + raise + raise AssertionError('Expected: %s\n Got: %s' % (expected, actual)) diff --git a/keystone/test/EchoSOAPUI.xml b/keystone/test/EchoSOAPUI.xml deleted file mode 100644 index 8a74eee0..00000000 --- a/keystone/test/EchoSOAPUI.xml +++ /dev/null @@ -1,2 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<con:soapui-project name="Echo" resourceRoot="" soapui-version="3.6.1" abortOnError="false" runType="SEQUENTIAL" xmlns:con="http://eviware.com/soapui/config"><con:settings/><con:interface xsi:type="con:RestService" wadlVersion="http://wadl.dev.java.net/2009/02" name="Echo" type="rest" basePath="" definitionUrl="file:/Users/jorgew/projects/keystone/echo/echo.wadl" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:settings/><con:definitionCache/><con:endpoints><con:endpoint>http://localhost:8090</con:endpoint></con:endpoints><con:resource name="/" path="/"><con:settings/><con:parameters><con:parameter required="true"><con:name>X-Auth-Token</con:name><con:value xsi:nil="true"/><con:style>HEADER</con:style><con:type xmlns:xs="http://www.w3.org/2001/XMLSchema">xs:string</con:type><con:default xsi:nil="true"/></con:parameter></con:parameters><con:method name="GET - get" method="GET"><con:settings/><con:parameters/><con:representation type="REQUEST" id=""><con:mediaType>*/*</con:mediaType><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/echo/api/v1.0">v1:echo</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1" mediaType="*/*"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8090</con:endpoint><con:request/><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:request></con:method><con:method name="PUT - put" method="PUT"><con:settings/><con:parameters/><con:representation type="REQUEST" id=""><con:mediaType>*/*</con:mediaType><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/echo/api/v1.0">v1:echo</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1"><con:settings/><con:endpoint>http://localhost:8090</con:endpoint><con:parameters/></con:request></con:method><con:method name="POST - post" method="POST"><con:settings/><con:parameters/><con:representation type="REQUEST" id=""><con:mediaType>*/*</con:mediaType><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/echo/api/v1.0">v1:echo</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1"><con:settings/><con:endpoint>http://localhost:8090</con:endpoint><con:parameters/></con:request></con:method><con:method name="DELETE - delete" method="DELETE"><con:settings/><con:parameters/><con:representation type="REQUEST" id=""><con:mediaType>*/*</con:mediaType><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/echo/api/v1.0">v1:echo</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1"><con:settings/><con:endpoint>http://localhost:8090</con:endpoint><con:parameters/></con:request></con:method><con:method name="HEAD - head" method="HEAD"><con:settings/><con:parameters/><con:representation type="REQUEST" id=""><con:mediaType>*/*</con:mediaType><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/echo/api/v1.0">v1:echo</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1"><con:settings/><con:endpoint>http://localhost:8090</con:endpoint><con:parameters/></con:request></con:method><con:method name="OPTIONS - options" method="OPTIONS"><con:settings/><con:parameters/><con:representation type="REQUEST" id=""><con:mediaType>*/*</con:mediaType><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/echo/api/v1.0">v1:echo</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1"><con:settings/><con:endpoint>http://localhost:8090</con:endpoint><con:parameters/></con:request></con:method></con:resource></con:interface><con:properties/><con:wssContainer/><con:databaseConnectionContainer/><con:reporting><con:xmlTemplates/><con:parameters/></con:reporting></con:soapui-project>
\ No newline at end of file diff --git a/keystone/test/IdentitySOAPUI.xml b/keystone/test/IdentitySOAPUI.xml deleted file mode 100644 index de072afb..00000000 --- a/keystone/test/IdentitySOAPUI.xml +++ /dev/null @@ -1,1355 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<con:soapui-project name="Keystone" resourceRoot="" soapui-version="3.6.1" abortOnError="false" runType="SEQUENTIAL" xmlns:con="http://eviware.com/soapui/config"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.actions.iface.tools.soapui.TestRunnerAction@values-local"><![CDATA[<xml-fragment xmlns:con="http://eviware.com/soapui/config"> - <con:entry key="Global Properties" value=""/> - <con:entry key="TestSuite" value="Keystone Tests"/> - <con:entry key="Report to Generate" value=""/> - <con:entry key="Password" value=""/> - <con:entry key="soapui-setings.xml Password" value=""/> - <con:entry key="TestRunner Path" value=""/> - <con:entry key="Tool Args" value=""/> - <con:entry key="Ignore Errors" value="false"/> - <con:entry key="Host:Port" value=""/> - <con:entry key="WSS Password Type" value=""/> - <con:entry key="Save Project" value="false"/> - <con:entry key="Enable UI" value="true"/> - <con:entry key="System Properties" value=""/> - <con:entry key="Domain" value=""/> - <con:entry key="Coverage Report" value="false"/> - <con:entry key="Export JUnit Results" value="false"/> - <con:entry key="Open Report" value="false"/> - <con:entry key="Project Properties" value=""/> - <con:entry key="Project Password" value=""/> - <con:entry key="Export All" value="false"/> - <con:entry key="Report Format(s)" value=""/> - <con:entry key="TestCase" value="<all>"/> - <con:entry key="Print Report" value="false"/> - <con:entry key="Username" value=""/> - <con:entry key="Root Folder" value=""/> - <con:entry key="Save After" value="false"/> - <con:entry key="Add Settings" value="false"/> - <con:entry key="Endpoint" value=""/> -</xml-fragment>]]></con:setting></con:settings><con:interface xsi:type="con:RestService" wadlVersion="http://wadl.dev.java.net/2009/02" name="Keystone" type="rest" basePath="" definitionUrl="file:/Users/jorgew/projects/keystone/keystone/identity.wadl" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:settings/><con:definitionCache type="TEXT" rootPart="file:/Users/jorgew/projects/keystone/keystone/identity.wadl"><con:part><con:url>file:/Users/jorgew/projects/keystone/keystone/identity.wadl</con:url><con:content><![CDATA[<application xsi:schemaLocation="http://docs.openstack.org/identity/api/v2.0 xsd/api.xsd http://docs.openstack.org/common/api/v1.0 xsd/api-common.xsd " xmlns="http://wadl.dev.java.net/2009/02" xmlns:identity="http://docs.openstack.org/identity/api/v2.0" xmlns:capi="http://docs.openstack.org/common/api/v1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> - <grammars> - <include href="xsd/api.xsd"/> - <include href="xsd/api-common.xsd"/> - </grammars> - <!--We should use SSL in production--> - <resources base="http://localhost:5000"> - <resource id="version" path="v1.0"> - <method href="#getVersionInfo"/> - <resource id="extensions" path="extensions"> - <method href="#getExtensions"/> - <resource id="alias" path="{alias}"> - <param name="alias" style="template" type="xsd:string"/> - <method href="#getExtension"/> - </resource> - </resource> - <resource id="token" path="token"> - <method href="#authenticate"/> - <resource id="tokenId" path="{tokenId}"> - <param name="X-Auth-Token" style="header" type="xsd:string" required="true"/> - <param name="tokenId" style="template" type="xsd:string"/> - <method href="#validateToken"/> - <method href="#revokeToken"/> - </resource> - </resource> - <resource id="tenants" path="tenants"> - <param name="X-Auth-Token" style="header" type="xsd:string" required="true"/> - <method href="#getTenants"/> - <method href="#createTenant"/> - <resource id="tenantId" path="{tenantId}"> - <param name="tenantId" style="template" type="xsd:string"/> - <method href="#getTenant"/> - <method href="#updateTenant"/> - <method href="#deleteTenant"/> - </resource> - </resource> - </resource> - </resources> - <!--Extensions--> - <method name="GET" id="getExtensions"> - <response status="200 203"> - <representation mediaType="application/xml" element="capi:extensions"/> - <representation mediaType="application/json"/> - </response> - <response status="400"> - <representation mediaType="application/xml" element="identity:badRequest"/> - </response> - <response status="500"> - <representation mediaType="application/xml" element="identity:identityFault"/> - </response> - <response status="503"> - <representation mediaType="application/xml" element="identity:serviceUnavailable"/> - </response> - <response status="400 500 503"> - <representation mediaType="application/json"/> - </response> - </method> - <method name="GET" id="getExtension"> - <response status="200 203"> - <representation mediaType="application/xml" element="capi:extension"/> - <representation mediaType="application/json"/> - </response> - <response status="400"> - <representation mediaType="application/xml" element="identity:badRequest"/> - </response> - <response status="404"> - <representation mediaType="application/xml" element="identity:itemNotFound"/> - </response> - <response status="500"> - <representation mediaType="application/xml" element="identity:identityFault"/> - </response> - <response status="503"> - <representation mediaType="application/xml" element="identity:serviceUnavailable"/> - </response> - <response status="400 404 500 503"> - <representation mediaType="application/json"/> - </response> - </method> - <!--Version Info--> - <method name="GET" id="getVersionInfo"> - <response status="200 203"> - <representation mediaType="application/xml" element="capi:version"/> - <representation mediaType="application/json"/> - </response> - <response status="400"> - <representation mediaType="application/xml" element="identity:badRequest"/> - </response> - <response status="500"> - <representation mediaType="application/xml" element="identity:identityFault"/> - </response> - <response status="503"> - <representation mediaType="application/xml" element="identity:serviceUnavailable"/> - </response> - <response status="400 500 503"> - <representation mediaType="application/json"/> - </response> - </method> - <!--Token Operations--> - <method name="POST" id="authenticate"> - <request> - <representation mediaType="application/xml" element="identity:passwordCredentials"/> - <representation mediaType="application/json"/> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:auth"/> - <representation mediaType="application/json"/> - </response> - <response status="401"> - <representation mediaType="application/xml" element="identity:unauthorized"/> - </response> - <response status="403"> - <representation mediaType="application/xml" element="identity:userDisabled"/> - </response> - <response status="400"> - <representation mediaType="application/xml" element="identity:badRequest"/> - </response> - <response status="500"> - <representation mediaType="application/xml" element="identity:identityFault"/> - </response> - <response status="503"> - <representation mediaType="application/xml" element="identity:serviceUnavailable"/> - </response> - <response status="401 403 400 500 503"> - <representation mediaType="application/json"/> - </response> - </method> - <method name="GET" id="validateToken"> - <request> - <param name="belongsTo" style="query" required="false" type="xsd:string"/> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:auth"/> - <representation mediaType="application/json"/> - </response> - <response status="401"> - <representation mediaType="application/xml" element="identity:unauthorized"/> - </response> - <response status="403"> - <representation mediaType="application/xml" element="identity:forbidden"/> - <representation mediaType="application/xml" element="identity:userDisabled"/> - </response> - <response status="400"> - <representation mediaType="application/xml" element="identity:badRequest"/> - </response> - <response status="404"> - <representation mediaType="application/xml" element="identity:itemNotFound"/> - </response> - <response status="500"> - <representation mediaType="application/xml" element="identity:identityFault"/> - </response> - <response status="503"> - <representation mediaType="application/xml" element="identity:serviceUnavailable"/> - </response> - <response status="400 401 403 404 500 503"> - <representation mediaType="application/json"/> - </response> - </method> - <method name="DELETE" id="revokeToken"> - <response status="204"/> - <response status="401"> - <representation mediaType="application/xml" element="identity:unauthorized"/> - </response> - <response status="403"> - <representation mediaType="application/xml" element="identity:forbidden"/> - </response> - <response status="400"> - <representation mediaType="application/xml" element="identity:badRequest"/> - </response> - <response status="404"> - <representation mediaType="application/xml" element="identity:itemNotFound"/> - </response> - <response status="500"> - <representation mediaType="application/xml" element="identity:identityFault"/> - </response> - <response status="503"> - <representation mediaType="application/xml" element="identity:serviceUnavailable"/> - </response> - <response status="400 401 403 404 500 503"> - <representation mediaType="application/json"/> - </response> - </method> - <!--Tenant Operations--> - <method name="GET" id="getTenants"> - <request> - <param name="marker" style="query" required="false" type="xsd:string"/> - <param name="limit" style="query" required="false" type="xsd:int"/> - </request> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:tenants"/> - <representation mediaType="application/json"/> - </response> - <response status="401"> - <representation mediaType="application/xml" element="identity:unauthorized"/> - </response> - <response status="403"> - <representation mediaType="application/xml" element="identity:forbidden"/> - </response> - <response status="400"> - <representation mediaType="application/xml" element="identity:badRequest"/> - </response> - <response status="404"> - <representation mediaType="application/xml" element="identity:itemNotFound"/> - </response> - <response status="500"> - <representation mediaType="application/xml" element="identity:identityFault"/> - </response> - <response status="503"> - <representation mediaType="application/xml" element="identity:serviceUnavailable"/> - </response> - <response status="400 401 403 404 500 503"> - <representation mediaType="application/json"/> - </response> - </method> - <method name="POST" id="createTenant"> - <request> - <representation mediaType="application/xml" element="identity:tenant"/> - <representation mediaType="application/json"/> - </request> - <response status="201"> - <representation mediaType="application/xml" element="identity:tenant"/> - <representation mediaType="application/json"/> - </response> - <response status="401"> - <representation mediaType="application/xml" element="identity:unauthorized"/> - </response> - <response status="403"> - <representation mediaType="application/xml" element="identity:forbidden"/> - </response> - <response status="409"> - <representation mediaType="application/xml" element="identity:tenantConflict"/> - </response> - <response status="400"> - <representation mediaType="application/xml" element="identity:badRequest"/> - </response> - <response status="500"> - <representation mediaType="application/xml" element="identity:identityFault"/> - </response> - <response status="503"> - <representation mediaType="application/xml" element="identity:serviceUnavailable"/> - </response> - <response status="401 403 400 409 500 503"> - <representation mediaType="application/json"/> - </response> - </method> - <method name="GET" id="getTenant"> - <response status="200 203"> - <representation mediaType="application/xml" element="identity:tenant"/> - <representation mediaType="application/json"/> - </response> - <response status="401"> - <representation mediaType="application/xml" element="identity:unauthorized"/> - </response> - <response status="403"> - <representation mediaType="application/xml" element="identity:forbidden"/> - </response> - <response status="400"> - <representation mediaType="application/xml" element="identity:badRequest"/> - </response> - <response status="404"> - <representation mediaType="application/xml" element="identity:itemNotFound"/> - </response> - <response status="500"> - <representation mediaType="application/xml" element="identity:identityFault"/> - </response> - <response status="503"> - <representation mediaType="application/xml" element="identity:serviceUnavailable"/> - </response> - <response status="400 401 403 404 500 503"> - <representation mediaType="application/json"/> - </response> - </method> - <method name="PUT" id="updateTenant"> - <request> - <representation mediaType="application/xml" element="identity:tenant"/> - <representation mediaType="application/json"/> - </request> - <response status="200"> - <representation mediaType="application/xml" element="identity:tenant"/> - <representation mediaType="application/json"/> - </response> - <response status="401"> - <representation mediaType="application/xml" element="identity:unauthorized"/> - </response> - <response status="403"> - <representation mediaType="application/xml" element="identity:forbidden"/> - </response> - <response status="404"> - <representation mediaType="application/xml" element="identity:itemNotFound"/> - </response> - <response status="400"> - <representation mediaType="application/xml" element="identity:badRequest"/> - </response> - <response status="500"> - <representation mediaType="application/xml" element="identity:identityFault"/> - </response> - <response status="503"> - <representation mediaType="application/xml" element="identity:serviceUnavailable"/> - </response> - <response status="401 403 404 400 500 503"> - <representation mediaType="application/json"/> - </response> - </method> - <method name="DELETE" id="deleteTenant"> - <response status="204"/> - <response status="401"> - <representation mediaType="application/xml" element="identity:unauthorized"/> - </response> - <response status="403"> - <representation mediaType="application/xml" element="identity:forbidden"/> - </response> - <response status="400"> - <representation mediaType="application/xml" element="identity:badRequest"/> - </response> - <response status="404"> - <representation mediaType="application/xml" element="identity:itemNotFound"/> - </response> - <response status="500"> - <representation mediaType="application/xml" element="identity:identityFault"/> - </response> - <response status="503"> - <representation mediaType="application/xml" element="identity:serviceUnavailable"/> - </response> - <response status="400 401 403 404 500 503"> - <representation mediaType="application/json"/> - </response> - </method> -</application>]]></con:content><con:type>http://wadl.dev.java.net/2009/02</con:type></con:part><con:part><con:url>file:/Users/jorgew/projects/keystone/keystone/xsd/api.xsd</con:url><con:content><schema elementFormDefault="qualified" attributeFormDefault="unqualified" targetNamespace="http://docs.openstack.org/identity/api/v2.0" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:identity="http://docs.openstack.org/identity/api/v2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> - <include schemaLocation="token.xsd"/> - <include schemaLocation="tenant.xsd"/> - <include schemaLocation="fault.xsd"/> -</schema></con:content><con:type>http://www.w3.org/2001/XMLSchema</con:type></con:part><con:part><con:url>file:/Users/jorgew/projects/keystone/keystone/xsd/token.xsd</con:url><con:content><![CDATA[<schema elementFormDefault="qualified" attributeFormDefault="unqualified" targetNamespace="http://docs.openstack.org/identity/api/v2.0" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:identity="http://docs.openstack.org/identity/api/v2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> - <!--Elements--> - <element name="passwordCredentials" type="identity:PasswordCredentials"/> - <element name="auth" type="identity:AuthData"/> - <!--Complex Types--> - <complexType name="Credentials" abstract="true"/> - <complexType name="PasswordCredentials"> - <complexContent> - <extension base="identity:Credentials"> - <sequence> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> - </sequence> - <attribute name="password" type="xsd:string" use="required"/> - <attribute name="username" type="xsd:string" use="required"/> - <attribute name="tenantId" type="xsd:string" use="optional"/> - <anyAttribute namespace="##other" processContents="lax"/> - </extension> - </complexContent> - </complexType> - <complexType name="AuthData"> - <sequence> - <element name="token" type="identity:Token"/> - <element name="user" type="identity:User"/> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> - </sequence> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - <complexType name="Token"> - <sequence> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> - </sequence> - <attribute name="expires" type="xsd:dateTime" use="required"/> - <attribute name="id" type="xsd:string" use="required"/> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - <complexType name="User"> - <sequence> - <element name="groups" type="identity:Groups"/> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> - </sequence> - <attribute name="tenantId" type="xsd:string"/> - <attribute name="username" type="xsd:string"/> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - <complexType name="Groups"> - <sequence> - <element name="group" type="identity:Group" maxOccurs="1000"/> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> - </sequence> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - <complexType name="Group"> - <attribute name="id" type="xsd:string" use="required"/> - <attribute name="tenantId" type="xsd:string" use="optional"/> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> -</schema>]]></con:content><con:type>http://www.w3.org/2001/XMLSchema</con:type></con:part><con:part><con:url>file:/Users/jorgew/projects/keystone/keystone/xsd/tenant.xsd</con:url><con:content><![CDATA[<schema elementFormDefault="qualified" attributeFormDefault="unqualified" targetNamespace="http://docs.openstack.org/identity/api/v2.0" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:identity="http://docs.openstack.org/identity/api/v2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" xmlns:atom="http://www.w3.org/2005/Atom"> - <!--Import ATOM specific schema definitions--> - <import vc:minVersion="1.1" namespace="http://www.w3.org/2005/Atom" schemaLocation="./atom/atom.xsd"/> - <!--Elements--> - <element name="tenant" type="identity:Tenant"/> - <element name="tenants" type="identity:Tenants"/> - <!--Complex Types--> - <complexType name="Tenants"> - <sequence> - <element name="tenant" type="identity:Tenant" maxOccurs="1000"/> - <element vc:minVersion="1.1" ref="atom:link" minOccurs="0" maxOccurs="unbounded"/> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> - </sequence> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - <complexType name="Tenant"> - <sequence> - <element name="description" type="xsd:string"/> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> - </sequence> - <attribute name="enabled" type="xsd:boolean" use="optional" default="true"/> - <attribute name="id" type="xsd:string" use="optional"/> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> -</schema>]]></con:content><con:type>http://www.w3.org/2001/XMLSchema</con:type></con:part><con:part><con:url>file:/Users/jorgew/projects/keystone/keystone/xsd/atom/atom.xsd</con:url><con:content><![CDATA[<xs:schema elementFormDefault="qualified" attributeFormDefault="unqualified" targetNamespace="http://www.w3.org/2005/Atom" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:xs="http://www.w3.org/2001/XMLSchema"> - <xs:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="xml.xsd"/> - <xs:simpleType name="relation"> - <xs:restriction base="xs:string"> - <xs:enumeration value="alternate"/> - <xs:enumeration value="appendix"/> - <xs:enumeration value="archives"/> - <xs:enumeration value="author"/> - <xs:enumeration value="bookmark"/> - <xs:enumeration value="chapter"/> - <xs:enumeration value="contents"/> - <xs:enumeration value="copyright"/> - <xs:enumeration value="current"/> - <xs:enumeration value="describedby"/> - <xs:enumeration value="edit"/> - <xs:enumeration value="edit-media"/> - <xs:enumeration value="first"/> - <xs:enumeration value="glossary"/> - <xs:enumeration value="help"/> - <xs:enumeration value="hub"/> - <xs:enumeration value="icon"/> - <xs:enumeration value="index"/> - <xs:enumeration value="last"/> - <xs:enumeration value="latest-version"/> - <xs:enumeration value="license"/> - <xs:enumeration value="monitor"/> - <xs:enumeration value="monitor-group"/> - <xs:enumeration value="next"/> - <xs:enumeration value="next-arvhice"/> - <xs:enumeration value="nofollow"/> - <xs:enumeration value="payment"/> - <xs:enumeration value="predecessor-version"/> - <xs:enumeration value="prefetch"/> - <xs:enumeration value="prev"/> - <xs:enumeration value="previous"/> - <xs:enumeration value="prev-archive"/> - <xs:enumeration value="replies"/> - <xs:enumeration value="search"/> - <xs:enumeration value="section"/> - <xs:enumeration value="self"/> - <xs:enumeration value="service"/> - <xs:enumeration value="start"/> - <xs:enumeration value="stylesheet"/> - <xs:enumeration value="subsection"/> - <xs:enumeration value="successor-version"/> - <xs:enumeration value="up"/> - <xs:enumeration value="version-history"/> - <xs:enumeration value="via"/> - <xs:enumeration value="working-copy"/> - <xs:enumeration value="working-copy-of"/> - </xs:restriction> - </xs:simpleType> - <xs:element name="link" type="atom:link"/> - <xs:complexType name="link"> - <xs:annotation> - <xs:documentation> - <html:p> - See section 3.4 of the ATOM RFC - <html:a href="http://tools.ietf.org/html/rfc4287">RFC4287</html:a> - </html:p> - </xs:documentation> - </xs:annotation> - <xs:attribute name="rel" use="required" type="atom:relation"> - <xs:annotation> - <xs:documentation> - <html:p>TODO</html:p> - </xs:documentation> - </xs:annotation> - </xs:attribute> - <xs:attribute name="type" use="optional" type="xs:string"> - <xs:annotation> - <xs:documentation> - <html:p>TODO</html:p> - </xs:documentation> - </xs:annotation> - </xs:attribute> - <xs:attribute name="href" use="required" type="xs:anyURI"> - <xs:annotation> - <xs:documentation> - <html:p>TODO</html:p> - </xs:documentation> - </xs:annotation> - </xs:attribute> - <xs:attribute name="hreflang" use="optional" type="xs:NMTOKEN"> - <xs:annotation> - <xs:documentation> - <html:p>TODO</html:p> - </xs:documentation> - </xs:annotation> - </xs:attribute> - <xs:attribute name="title" use="optional" type="xs:string"> - <xs:annotation> - <xs:documentation> - <html:p>TODO</html:p> - </xs:documentation> - </xs:annotation> - </xs:attribute> - <xs:attribute ref="xml:base"/> - <xs:attribute ref="xml:lang"/> - </xs:complexType> -</xs:schema>]]></con:content><con:type>http://www.w3.org/2001/XMLSchema</con:type></con:part><con:part><con:url>file:/Users/jorgew/projects/keystone/keystone/xsd/atom/xml.xsd</con:url><con:content><![CDATA[<?xml-stylesheet href="../2008/09/xsd.xsl" type="text/xsl"?> -<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace" xml:lang="en" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.w3.org/1999/xhtml"> - <xs:annotation> - <xs:documentation> - <div> - <h1>About the XML namespace</h1> - <div class="bodytext"> - <p>This schema document describes the XML namespace, in a form - suitable for import by other schema documents.</p> - <p> - See - <a href="http://www.w3.org/XML/1998/namespace.html">http://www.w3.org/XML/1998/namespace.html</a> - and - <a href="http://www.w3.org/TR/REC-xml">http://www.w3.org/TR/REC-xml</a> - for information - about this namespace. - </p> - <p>Note that local names in this namespace are intended to be - defined only by the World Wide Web Consortium or its subgroups. - The names currently defined in this namespace are listed below. - They should not be used with conflicting semantics by any Working - Group, specification, or document instance.</p> - <p> - See further below in this document for more information about - <a href="#usage">how to refer to this schema document from your own - XSD schema documents</a> - and about - <a href="#nsversioning">the - namespace-versioning policy governing this schema document</a> - . - </p> - </div> - </div> - </xs:documentation> - </xs:annotation> - <xs:attribute name="lang"> - <xs:annotation> - <xs:documentation> - <div> - <h3>lang (as an attribute name)</h3> - <p>denotes an attribute whose value - is a language code for the natural language of the content of - any element; its value is inherited. This name is reserved - by virtue of its definition in the XML specification.</p> - </div> - <div> - <h4>Notes</h4> - <p>Attempting to install the relevant ISO 2- and 3-letter - codes as the enumerated possible values is probably never - going to be a realistic possibility.</p> - <p> - See BCP 47 at - <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt">http://www.rfc-editor.org/rfc/bcp/bcp47.txt</a> - and the IANA language subtag registry at - <a href="http://www.iana.org/assignments/language-subtag-registry">http://www.iana.org/assignments/language-subtag-registry</a> - for further information. - </p> - <p>The union allows for the 'un-declaration' of xml:lang with - the empty string.</p> - </div> - </xs:documentation> - </xs:annotation> - <xs:simpleType> - <xs:union memberTypes="xs:language"> - <xs:simpleType> - <xs:restriction base="xs:string"> - <xs:enumeration value=""/> - </xs:restriction> - </xs:simpleType> - </xs:union> - </xs:simpleType> - </xs:attribute> - <xs:attribute name="space"> - <xs:annotation> - <xs:documentation> - <div> - <h3>space (as an attribute name)</h3> - <p>denotes an attribute whose - value is a keyword indicating what whitespace processing - discipline is intended for the content of the element; its - value is inherited. This name is reserved by virtue of its - definition in the XML specification.</p> - </div> - </xs:documentation> - </xs:annotation> - <xs:simpleType> - <xs:restriction base="xs:NCName"> - <xs:enumeration value="default"/> - <xs:enumeration value="preserve"/> - </xs:restriction> - </xs:simpleType> - </xs:attribute> - <xs:attribute name="base" type="xs:anyURI"> - <xs:annotation> - <xs:documentation> - <div> - <h3>base (as an attribute name)</h3> - <p>denotes an attribute whose value - provides a URI to be used as the base for interpreting any - relative URIs in the scope of the element on which it - appears; its value is inherited. This name is reserved - by virtue of its definition in the XML Base specification.</p> - <p> - See - <a href="http://www.w3.org/TR/xmlbase/">http://www.w3.org/TR/xmlbase/</a> - for information about this attribute. - </p> - </div> - </xs:documentation> - </xs:annotation> - </xs:attribute> - <xs:attribute name="id" type="xs:ID"> - <xs:annotation> - <xs:documentation> - <div> - <h3>id (as an attribute name)</h3> - <p>denotes an attribute whose value - should be interpreted as if declared to be of type ID. - This name is reserved by virtue of its definition in the - xml:id specification.</p> - <p> - See - <a href="http://www.w3.org/TR/xml-id/">http://www.w3.org/TR/xml-id/</a> - for information about this attribute. - </p> - </div> - </xs:documentation> - </xs:annotation> - </xs:attribute> - <xs:attributeGroup name="specialAttrs"> - <xs:attribute ref="xml:base"/> - <xs:attribute ref="xml:lang"/> - <xs:attribute ref="xml:space"/> - <xs:attribute ref="xml:id"/> - </xs:attributeGroup> - <xs:annotation> - <xs:documentation> - <div> - <h3>Father (in any context at all)</h3> - <div class="bodytext"> - <p>denotes Jon Bosak, the chair of - the original XML Working Group. This name is reserved by - the following decision of the W3C XML Plenary and - XML Coordination groups:</p> - <blockquote> - <p>In appreciation for his vision, leadership and - dedication the W3C XML Plenary on this 10th day of - February, 2000, reserves for Jon Bosak in perpetuity - the XML name "xml:Father".</p> - </blockquote> - </div> - </div> - </xs:documentation> - </xs:annotation> - <xs:annotation> - <xs:documentation> - <div xml:id="usage" id="usage"> - <h2> - <a name="usage">About this schema document</a> - </h2> - <div class="bodytext"> - <p> - This schema defines attributes and an attribute group suitable - for use by schemas wishing to allow - <code>xml:base</code> - , - <code>xml:lang</code> - , - <code>xml:space</code> - or - <code>xml:id</code> - attributes on elements they define. - </p> - <p>To enable this, such a schema must import this schema for - the XML namespace, e.g. as follows:</p> - <pre><schema . . .> - . . . - <import namespace="http://www.w3.org/XML/1998/namespace" - schemaLocation="http://www.w3.org/2001/xml.xsd"/></pre> - <p>or</p> - <pre><import namespace="http://www.w3.org/XML/1998/namespace" - schemaLocation="http://www.w3.org/2009/01/xml.xsd"/></pre> - <p>Subsequently, qualified reference to any of the attributes or the - group defined below will have the desired effect, e.g.</p> - <pre><type . . .> - . . . - <attributeGroup ref="xml:specialAttrs"/></pre> - <p>will define a type which will schema-validate an instance element - with any of those attributes.</p> - </div> - </div> - </xs:documentation> - </xs:annotation> - <xs:annotation> - <xs:documentation> - <div id="nsversioning" xml:id="nsversioning"> - <h2> - <a name="nsversioning">Versioning policy for this schema document</a> - </h2> - <div class="bodytext"> - <p> - In keeping with the XML Schema WG's standard versioning - policy, this schema document will persist at - <a href="http://www.w3.org/2009/01/xml.xsd">http://www.w3.org/2009/01/xml.xsd</a> - . - </p> - <p> - At the date of issue it can also be found at - <a href="http://www.w3.org/2001/xml.xsd">http://www.w3.org/2001/xml.xsd</a> - . - </p> - <p> - The schema document at that URI may however change in the future, - in order to remain compatible with the latest version of XML - Schema itself, or with the XML namespace itself. In other words, - if the XML Schema or XML namespaces change, the version of this - document at - <a href="http://www.w3.org/2001/xml.xsd">http://www.w3.org/2001/xml.xsd</a> - will change accordingly; the version at - <a href="http://www.w3.org/2009/01/xml.xsd">http://www.w3.org/2009/01/xml.xsd</a> - will not change. - </p> - <p>Previous dated (and unchanging) versions of this schema - document are at:</p> - <ul> - <li> - <a href="http://www.w3.org/2009/01/xml.xsd">http://www.w3.org/2009/01/xml.xsd</a> - </li> - <li> - <a href="http://www.w3.org/2007/08/xml.xsd">http://www.w3.org/2007/08/xml.xsd</a> - </li> - <li> - <a href="http://www.w3.org/2004/10/xml.xsd">http://www.w3.org/2004/10/xml.xsd</a> - </li> - <li> - <a href="http://www.w3.org/2001/03/xml.xsd">http://www.w3.org/2001/03/xml.xsd</a> - </li> - </ul> - </div> - </div> - </xs:documentation> - </xs:annotation> -</xs:schema>]]></con:content><con:type>http://www.w3.org/2001/XMLSchema</con:type></con:part><con:part><con:url>file:/Users/jorgew/projects/keystone/keystone/xsd/fault.xsd</con:url><con:content><![CDATA[<schema elementFormDefault="qualified" attributeFormDefault="unqualified" targetNamespace="http://docs.openstack.org/identity/api/v2.0" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:identity="http://docs.openstack.org/identity/api/v2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> - <!--Fault Elements--> - <element name="identityFault" type="identity:IdentityFault"/> - <element name="serviceUnavailable" type="identity:ServiceUnavailableFault"/> - <element name="badRequest" type="identity:BadRequestFault"/> - <element name="unauthorized" type="identity:UnauthorizedFault"/> - <element name="overLimit" type="identity:OverLimitFault"/> - <element name="userDisabled" type="identity:UserDisabledFault"/> - <element name="forbidden" type="identity:ForbiddenFault"/> - <element name="itemNotFound" type="identity:ItemNotFoundFault"/> - <element name="tenantConflict" type="identity:TenantConflictFault"/> - <!--Fault Types--> - <complexType name="IdentityFault"> - <sequence> - <element name="message" type="xsd:string"> - <annotation> - <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml" xmlns:xs="http://www.w3.org/2001/XMLSchema"> - <p>A human readable message that is appropriate for display - to the end user.</p> - </xsd:documentation> - </annotation> - </element> - <element name="details" type="xsd:string" minOccurs="0"> - <annotation> - <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml" xmlns:xs="http://www.w3.org/2001/XMLSchema"> - <p>The optional <details> element may contain useful - information for tracking down errors (e.g a stack - trace). This information may or may not be appropriate - for display to an end user.</p> - </xsd:documentation> - </annotation> - </element> - <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> - </sequence> - <attribute name="code" type="xsd:int" use="required"> - <annotation> - <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml" xmlns:xs="http://www.w3.org/2001/XMLSchema"> - <p>The HTTP status code associated with the current fault.</p> - </xsd:documentation> - </annotation> - </attribute> - <anyAttribute namespace="##other" processContents="lax"/> - </complexType> - <complexType name="ServiceUnavailableFault"> - <complexContent> - <extension base="identity:IdentityFault"></extension> - </complexContent> - </complexType> - <complexType name="BadRequestFault"> - <complexContent> - <extension base="identity:IdentityFault"></extension> - </complexContent> - </complexType> - <complexType name="UnauthorizedFault"> - <complexContent> - <extension base="identity:IdentityFault"></extension> - </complexContent> - </complexType> - <complexType name="UserDisabledFault"> - <complexContent> - <extension base="identity:IdentityFault"></extension> - </complexContent> - </complexType> - <complexType name="ForbiddenFault"> - <complexContent> - <extension base="identity:IdentityFault"></extension> - </complexContent> - </complexType> - <complexType name="ItemNotFoundFault"> - <complexContent> - <extension base="identity:IdentityFault"></extension> - </complexContent> - </complexType> - <complexType name="TenantConflictFault"> - <complexContent> - <extension base="identity:IdentityFault"></extension> - </complexContent> - </complexType> - <complexType name="OverLimitFault"> - <complexContent> - <extension base="identity:IdentityFault"> - <attribute name="retryAt" type="xsd:dateTime" use="optional"> - <annotation> - <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml" xmlns:xs="http://www.w3.org/2001/XMLSchema"> - <p>An optional dateTime denoting when an operation should - be retried.</p> - </xsd:documentation> - </annotation> - </attribute> - </extension> - </complexContent> - </complexType> -</schema>]]></con:content><con:type>http://www.w3.org/2001/XMLSchema</con:type></con:part><con:part><con:url>file:/Users/jorgew/projects/keystone/keystone/xsd/api-common.xsd</con:url><con:content><![CDATA[<?xml-stylesheet type="text/xsl" href="../xslt/schema.xslt"?> -<!--(C) 2009-2011 Rackspace Hosting, All Rights Reserved--> -<schema elementFormDefault="qualified" attributeFormDefault="unqualified" targetNamespace="http://docs.openstack.org/common/api/v1.0" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:capi="http://docs.openstack.org/common/api/v1.0" xmlns:xsdxt="http://docs.rackspacecloud.com/xsd-ext/v1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> - <annotation> - <xsd:appinfo xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml" xmlns:xs="http://www.w3.org/2001/XMLSchema"> - <xsdxt:title>Open Stack Common API Schema Types 1.0</xsdxt:title> - <xsdxt:link rev="index" href="extensions.xsd"/> - <xsdxt:link rev="index" href="limits.xsd"/> - <xsdxt:link rev="index" href="version.xsd"/> - </xsd:appinfo> - <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml" xmlns:xs="http://www.w3.org/2001/XMLSchema"> - <p>This is the main index XML Schema document - for Common API Schema Types Version 1.0.</p> - </xsd:documentation> - </annotation> - <include schemaLocation="extensions.xsd"> - <annotation> - <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml" xmlns:xs="http://www.w3.org/2001/XMLSchema"> - <p>Types related to extensions.</p> - </xsd:documentation> - </annotation> - </include> - <include schemaLocation="version.xsd"> - <annotation> - <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml" xmlns:xs="http://www.w3.org/2001/XMLSchema"> - <p>Types related to API version details.</p> - </xsd:documentation> - </annotation> - </include> -</schema>]]></con:content><con:type>http://www.w3.org/2001/XMLSchema</con:type></con:part><con:part><con:url>file:/Users/jorgew/projects/keystone/keystone/xsd/extensions.xsd</con:url><con:content><![CDATA[<xsd:schema elementFormDefault="qualified" attributeFormDefault="unqualified" targetNamespace="http://docs.openstack.org/common/api/v1.0" xmlns:ext="http://docs.openstack.org/common/api/v1.0" xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> - <!--Import ATOM specific schema definitions--> - <xsd:import namespace="http://www.w3.org/2005/Atom" schemaLocation="./atom/atom.xsd"/> - <xsd:element name="extensions" type="ext:Extensions"/> - <xsd:element name="extension" type="ext:Extension"/> - <xsd:complexType name="Extensions"> - <xsd:sequence> - <xsd:element name="extension" type="ext:Extension" minOccurs="0" maxOccurs="unbounded"/> - <xsd:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> - </xsd:sequence> - <xsd:anyAttribute namespace="##other" processContents="lax"/> - </xsd:complexType> - <xsd:complexType name="Extension"> - <xsd:sequence> - <xsd:element name="description" type="xsd:string" minOccurs="1"/> - <xsd:element vc:minVersion="1.1" ref="atom:link" minOccurs="0" maxOccurs="unbounded"/> - <xsd:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> - </xsd:sequence> - <xsd:attribute name="name" type="xsd:string" use="required"/> - <xsd:attribute name="namespace" type="xsd:anyURI" use="required"/> - <xsd:attribute name="alias" type="ext:Alias" use="required"/> - <xsd:attribute name="updated" type="xsd:dateTime" use="optional"/> - <xsd:anyAttribute namespace="##other" processContents="lax"/> - <xsd:assert vc:minVersion="1.1" test="atom:link[@rel='describedby']"> - <xsd:annotation> - <xsd:documentation xml:lang="EN" xmlns="http://www.w3.org/1999/xhtml"> - <p>There should be at least one atom link - with a describedby relation.</p> - </xsd:documentation> - </xsd:annotation> - </xsd:assert> - </xsd:complexType> - <xsd:simpleType name="Alias"> - <xsd:restriction base="xsd:string"> - <xsd:pattern value="\w+\-\w+"/> - </xsd:restriction> - </xsd:simpleType> -</xsd:schema>]]></con:content><con:type>http://www.w3.org/2001/XMLSchema</con:type></con:part><con:part><con:url>file:/Users/jorgew/projects/keystone/keystone/xsd/version.xsd</con:url><con:content><![CDATA[<xs:schema elementFormDefault="qualified" attributeFormDefault="unqualified" targetNamespace="http://docs.openstack.org/common/api/v1.0" xmlns:vers="http://docs.openstack.org/common/api/v1.0" xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:xs="http://www.w3.org/2001/XMLSchema"> - <!--Import ATOM specific schema definitions--> - <xs:import namespace="http://www.w3.org/2005/Atom" schemaLocation="./atom/atom.xsd"/> - <!--Multiple choices--> - <xs:element name="choices" type="vers:VersionChoiceList"/> - <!--Versioning--> - <xs:element name="versions" type="vers:VersionChoiceList"/> - <xs:element name="version" type="vers:VersionChoice" vc:minVersion="1.0" vc:maxVersion="1.1"/> - <xs:element name="version" type="vers:VersionChoiceRoot" vc:minVersion="1.1"/> - <!--Types--> - <xs:simpleType name="VersionStatus"> - <xs:annotation> - <xs:documentation> - <html:p>The VersionStatus type describes a service's operational status.</html:p> - </xs:documentation> - </xs:annotation> - <xs:restriction base="xs:string"> - <xs:enumeration value="DEPRECATED"/> - <xs:enumeration value="ALPHA"/> - <xs:enumeration value="BETA"/> - <xs:enumeration value="CURRENT"/> - </xs:restriction> - </xs:simpleType> - <xs:complexType name="VersionChoiceList"> - <xs:annotation> - <xs:documentation> - <html:p>A version choice list outlines a collection of service version choices.</html:p> - </xs:documentation> - </xs:annotation> - <xs:sequence> - <xs:element name="version" type="vers:VersionChoice" minOccurs="1" maxOccurs="unbounded"/> - <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> - </xs:sequence> - <xs:anyAttribute namespace="##other" processContents="lax"/> - <xs:assert vc:minVersion="1.1" test="every $v in vers:version satisfies $v/atom:link[@rel='self']"> - <xs:annotation> - <xs:documentation> - <html:p>In version lists, every single version must - contain at least one self link.</html:p> - </xs:documentation> - </xs:annotation> - </xs:assert> - </xs:complexType> - <xs:complexType name="VersionChoiceRoot" vc:minVersion="1.1"> - <xs:complexContent> - <xs:extension base="vers:VersionChoice"> - <xs:assert test="atom:link[@rel='describedby']"> - <xs:annotation> - <xs:documentation> - <html:p>When used as a root element, a version choice - must contain at least one describedby link.</html:p> - </xs:documentation> - </xs:annotation> - </xs:assert> - </xs:extension> - </xs:complexContent> - </xs:complexType> - <xs:complexType name="VersionChoice"> - <xs:annotation> - <xs:documentation> - <html:p>A version choice contains relevant information about an available service - that a user can then use to target a specific version of the service. Note - that both the descriptive media types and the atom link references are - not manditory and are offered as message enrichment elements rather - than message requirements.</html:p> - </xs:documentation> - </xs:annotation> - <xs:sequence> - <xs:element name="media-types" type="vers:MediaTypeList" minOccurs="0" maxOccurs="1"/> - <xs:element vc:minVersion="1.1" ref="atom:link" minOccurs="0" maxOccurs="unbounded"/> - <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> - </xs:sequence> - <xs:attribute name="id" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> - <html:p>The ID of a version choice represents the service version's unique - identifier. This ID is guaranteed to be unique only among the - service version choices outlined in the VersionChoiceList.</html:p> - </xs:documentation> - </xs:annotation> - </xs:attribute> - <xs:attribute name="status" type="vers:VersionStatus" use="required"> - <xs:annotation> - <xs:documentation> - <html:p>A version choice's status describes the current operational state of - the given service version. The operational status is captured in a - simple type enumeration called VersionStatus.</html:p> - </xs:documentation> - </xs:annotation> - </xs:attribute> - <xs:attribute name="updated" type="xs:dateTime" use="optional"> - <xs:annotation> - <xs:documentation> - <html:p> - A version choice's updated attribute describes - the time when the version was updated. The - time should be updated anytime - <html:strong>anything</html:strong> - in the - version has changed: documentation, - extensions, bug fixes. - </html:p> - </xs:documentation> - </xs:annotation> - </xs:attribute> - <xs:anyAttribute namespace="##other" processContents="lax"/> - </xs:complexType> - <xs:complexType name="MediaTypeList"> - <xs:annotation> - <xs:documentation> - <html:p>A MediaTypeList outlines a collection of valid media types for a given - service version.</html:p> - </xs:documentation> - </xs:annotation> - <xs:sequence> - <xs:element name="media-type" type="vers:MediaType" minOccurs="1" maxOccurs="unbounded"/> - <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> - </xs:sequence> - <xs:anyAttribute namespace="##other" processContents="lax"/> - </xs:complexType> - <xs:complexType name="MediaType"> - <xs:annotation> - <xs:documentation> - <html:p>A MediaType describes what content types the service version understands.</html:p> - </xs:documentation> - </xs:annotation> - <xs:sequence> - <xs:any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/> - </xs:sequence> - <xs:attribute name="base" type="xs:string" use="optional" default=""> - <xs:annotation> - <xs:documentation> - <html:p>The base of a given media type describes the simple MIME type - that then a more complicated media type can be derived from. These - types are basic and provide no namespace or version specific - data are are only provided as a convenience. Because of this the - base attribute is declared as optional.</html:p> - </xs:documentation> - </xs:annotation> - </xs:attribute> - <xs:attribute name="type" type="xs:string" use="required"> - <xs:annotation> - <xs:documentation> - <html:p> - The type attribute of a MediaType describes the MIME specific - identifier of the media type in question. This identifier should include - a vendor namespace ( - <html:a href="http://tools.ietf.org/html/rfc2048">See RFC 2048</html:a> - ) - as well as a version suffix. - </html:p> - </xs:documentation> - </xs:annotation> - </xs:attribute> - <xs:anyAttribute namespace="##other" processContents="lax"/> - </xs:complexType> -</xs:schema>]]></con:content><con:type>http://www.w3.org/2001/XMLSchema</con:type></con:part></con:definitionCache><con:endpoints><con:endpoint>http://localhost:5000</con:endpoint></con:endpoints><con:resource name="v1.0" path="v1.0"><con:settings/><con:parameters/><con:resource name="extensions" path="extensions"><con:settings/><con:parameters/><con:resource name="{alias}" path="{alias}"><con:settings/><con:parameters><con:parameter><con:name>alias</con:name><con:value xsi:nil="true"/><con:style>TEMPLATE</con:style><con:type xmlns:xs="http://www.w3.org/2001/XMLSchema">xs:string</con:type><con:default xsi:nil="true"/></con:parameter></con:parameters><con:method name="GET - getExtension" method="GET"><con:settings/><con:parameters/><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200 203</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/common/api/v1.0">v1:extension</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200 203</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>400</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:badRequest</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>404</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:itemNotFound</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>500</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:identityFault</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>503</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:serviceUnavailable</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/json</con:mediaType><con:status>400 404 500 503</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="alias" value="RAX-TEST" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:request></con:method></con:resource><con:method name="GET - getExtensions" method="GET"><con:settings/><con:parameters/><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200 203</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/common/api/v1.0">v1:extensions</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200 203</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>400</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:badRequest</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>500</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:identityFault</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>503</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:serviceUnavailable</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/json</con:mediaType><con:status>400 500 503</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:request></con:method></con:resource><con:resource name="token" path="token"><con:settings/><con:parameters/><con:resource name="{tokenId}" path="{tokenId}"><con:settings/><con:parameters><con:parameter required="true"><con:name>X-Auth-Token</con:name><con:value xsi:nil="true"/><con:style>HEADER</con:style><con:type xmlns:xs="http://www.w3.org/2001/XMLSchema">xs:string</con:type><con:default xsi:nil="true"/></con:parameter><con:parameter><con:name>tokenId</con:name><con:value xsi:nil="true"/><con:style>TEMPLATE</con:style><con:type xmlns:xs="http://www.w3.org/2001/XMLSchema">xs:string</con:type><con:default xsi:nil="true"/></con:parameter></con:parameters><con:method name="GET - validateToken" method="GET"><con:settings/><con:parameters><con:parameter><con:name>belongsTo</con:name><con:value xsi:nil="true"/><con:style>QUERY</con:style><con:type xmlns:xs="http://www.w3.org/2001/XMLSchema">xs:string</con:type><con:default xsi:nil="true"/></con:parameter></con:parameters><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200 203</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:auth</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200 203</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>401</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:unauthorized</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>403</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:forbidden</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>403</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:userDisabled</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>400</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:badRequest</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>404</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:itemNotFound</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>500</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:identityFault</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>503</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:serviceUnavailable</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/json</con:mediaType><con:status>400 401 403 404 500 503</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tokenId" value="887665443383838"/> - <con:entry key="X-Auth-Token" value="999888777666"/> - <con:entry key="belongsTo" value="1234"/> -</con:parameters></con:request></con:method><con:method name="DELETE - revokeToken" method="DELETE"><con:settings/><con:parameters/><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>401</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:unauthorized</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>403</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:forbidden</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>400</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:badRequest</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>404</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:itemNotFound</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>500</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:identityFault</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>503</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:serviceUnavailable</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/json</con:mediaType><con:status>400 401 403 404 500 503</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1" mediaType="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:5000</con:endpoint><con:request/><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tokenId" value="3u37737"/> - <con:entry key="X-Auth-Token" value="3838737726"/> -</con:parameters></con:request></con:method></con:resource><con:method name="POST - authenticate" method="POST"><con:settings/><con:parameters/><con:representation type="REQUEST" id=""><con:mediaType>application/xml</con:mediaType><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:passwordCredentials</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="REQUEST" id=""><con:mediaType>application/json</con:mediaType><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200 -203</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:auth</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200 -203</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>401</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:unauthorized</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>403</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:userDisabled</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>400</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:badRequest</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>500</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:identityFault</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>503</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:serviceUnavailable</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/json</con:mediaType><con:status>401 -403 400 500 503</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:5000</con:endpoint><con:request><passwordCredentials -password="secrete" username="joeuser" -xmlns="http://docs.openstack.org/identity/api/v2.0"/></con:request><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:request></con:method></con:resource><con:resource name="tenants" path="tenants"><con:settings/><con:parameters><con:parameter required="true"><con:name>X-Auth-Token</con:name><con:value xsi:nil="true"/><con:style>HEADER</con:style><con:type xmlns:xs="http://www.w3.org/2001/XMLSchema">xs:string</con:type><con:default xsi:nil="true"/></con:parameter></con:parameters><con:resource name="{tenantId}" path="{tenantId}"><con:settings/><con:parameters><con:parameter><con:name>tenantId</con:name><con:value xsi:nil="true"/><con:style>TEMPLATE</con:style><con:type xmlns:xs="http://www.w3.org/2001/XMLSchema">xs:string</con:type><con:default xsi:nil="true"/></con:parameter></con:parameters><con:method name="GET - getTenant" method="GET"><con:settings/><con:parameters/><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200 -203</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:tenant</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200 -203</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>401</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:unauthorized</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>403</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:forbidden</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>400</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:badRequest</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>404</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:itemNotFound</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>500</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:identityFault</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>503</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:serviceUnavailable</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/json</con:mediaType><con:status>400 -401 403 404 500 503</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tenantId" value="1234"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:request></con:method><con:method name="PUT - updateTenant" method="PUT"><con:settings/><con:parameters/><con:representation type="REQUEST" id=""><con:mediaType>application/xml</con:mediaType><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:tenant</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="REQUEST" id=""><con:mediaType>application/json</con:mediaType><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:tenant</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>401</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:unauthorized</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>403</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:forbidden</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>404</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:itemNotFound</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>400</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:badRequest</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>500</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:identityFault</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>503</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:serviceUnavailable</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/json</con:mediaType><con:status>401 -403 404 400 500 503</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1" mediaType="application/xml" postQueryString="false"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant enabled="true" xmlns:v1="http://docs.openstack.org/identity/api/v2.0"> - <v1:description>New Description</v1:description> -</v1:tenant></con:request><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tenantId" value="1234"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:request></con:method><con:method name="DELETE - deleteTenant" method="DELETE"><con:settings/><con:parameters/><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>401</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:unauthorized</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>403</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:forbidden</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>400</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:badRequest</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>404</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:itemNotFound</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>500</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:identityFault</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>503</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:serviceUnavailable</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/json</con:mediaType><con:status>400 -401 403 404 500 503</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1" mediaType="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><entry key="Accept" value="application/xml" xmlns="http://eviware.com/soapui/config"/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tenantId" value="0000"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:request></con:method></con:resource><con:method name="GET - getTenants" method="GET"><con:settings/><con:parameters><con:parameter><con:name>marker</con:name><con:value xsi:nil="true"/><con:style>QUERY</con:style><con:type xmlns:xs="http://www.w3.org/2001/XMLSchema">xs:string</con:type><con:default xsi:nil="true"/></con:parameter><con:parameter><con:name>limit</con:name><con:value xsi:nil="true"/><con:style>QUERY</con:style><con:type xmlns:xs="http://www.w3.org/2001/XMLSchema">xs:int</con:type><con:default xsi:nil="true"/></con:parameter></con:parameters><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200 -203</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:tenants</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200 -203</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>401</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:unauthorized</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>403</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:forbidden</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>400</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:badRequest</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>404</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:itemNotFound</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>500</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:identityFault</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>503</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:serviceUnavailable</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/json</con:mediaType><con:status>400 -401 403 404 500 503</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1" mediaType="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:request></con:method><con:method name="POST - createTenant" method="POST"><con:settings/><con:parameters/><con:representation type="REQUEST" id=""><con:mediaType>application/xml</con:mediaType><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:tenant</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="REQUEST" id=""><con:mediaType>application/json</con:mediaType><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>201</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:tenant</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>201</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>401</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:unauthorized</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>403</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:forbidden</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType> -<con:status>409</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:tenantConflict</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType> -<con:status>400</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:badRequest</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>500</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:identityFault</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>503</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:serviceUnavailable</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/json</con:mediaType><con:status>401 -403 400 409 500 503</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant -enabled="true" id="my_new_tenant" -xmlns:v1="http://docs.openstack.org/identity/api/v2.0"><v1:description>This -is a description of my tenant. Thank you very -much.</v1:description></v1:tenant></con:request><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:request></con:method></con:resource><con:method name="GET - getVersionInfo" method="GET"><con:settings/><con:parameters/><con:representation type="RESPONSE" id=""><con:mediaType>application/xml</con:mediaType><con:status>200 -203</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/common/api/v1.0">v1:version</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="RESPONSE" id=""><con:mediaType>application/json</con:mediaType><con:status>200 -203</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>400</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:badRequest</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>500</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:identityFault</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/xml</con:mediaType><con:status>503</con:status><con:params/><con:element xmlns:v1="http://docs.openstack.org/identity/api/v2.0">v1:serviceUnavailable</con:element><con:description xsi:nil="true"/></con:representation><con:representation type="FAULT" id=""><con:mediaType>application/json</con:mediaType><con:status>400 -500 503</con:status><con:params/><con:element xsi:nil="true"/><con:description xsi:nil="true"/></con:representation><con:request name="Request 1" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:request></con:method></con:resource></con:interface><con:testSuite name="Keystone Tests"><con:settings/><con:runType>SEQUENTIAL</con:runType><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Admin Credential Check" searchProperties="true" id="29b2fa4b-e1c3-49c4-a7e6-334724e74bb9"><con:settings/><con:testStep type="restrequest" name="GET - validateToken - Valid Token"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens/{tokenId}" methodName="GET - validateToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - validateToken - Valid Token" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains"><con:configuration><token>auth</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>token</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>user</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tokenId" value="887665443383838"/> - <con:entry key="X-Auth-Token" value="999888777666"/> - <con:entry key="belongsTo" value="1234"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - validateToken - Expired Admin Token"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens/{tokenId}" methodName="GET - validateToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - validateToken - Expired Admin Token" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains"><con:configuration><token>unauthorized</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>401</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tokenId" value="377372"/> - <con:entry key="X-Auth-Token" value="000999"/> - <con:entry key="belongsTo" value="3334"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - validateToken - Missing Admin Token"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens/{tokenId}" methodName="GET - validateToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - validateToken - Missing Admin Token" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains"><con:configuration><token>unauthorized</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>401</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tokenId" value="377372"/> - <con:entry key="X-Auth-Token" value=" "/> - <con:entry key="belongsTo" value="3334"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - validateToken - Bad Admin Token"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens/{tokenId}" methodName="GET - validateToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - validateToken - Bad Admin Token" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains"><con:configuration><token>unauthorized</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>401</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tokenId" value="377372"/> - <con:entry key="X-Auth-Token" value="976BAD"/> - <con:entry key="belongsTo" value="3334"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - validateToken - Joe User Makes Admin Call"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens/{tokenId}" methodName="GET - validateToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - validateToken - Joe User Makes Admin Call" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains"><con:configuration><token>forbidden</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>403</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tokenId" value="377372"/> - <con:entry key="X-Auth-Token" value="887665443383838"/> - <con:entry key="belongsTo" value="3334"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - validateToken - Disabled User Makes Admin Call"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens/{tokenId}" methodName="GET - validateToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - validateToken - Disabled User Makes Admin Call" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains"><con:configuration><token>userDisabled</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>403</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tokenId" value="377372"/> - <con:entry key="X-Auth-Token" value="999888777"/> - <con:entry key="belongsTo" value="3334"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:properties/><con:reportParameters/></con:testCase><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Validate Token" searchProperties="true" id="e4d9f6ba-f392-4c78-bddc-47fca1fc9b0e"><con:settings/><con:testStep type="restrequest" name="GET - validateToken - Valid Token"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens/{tokenId}" methodName="GET - validateToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - validateToken - Valid Token" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains"><con:configuration><token>auth</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>token</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>user</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tokenId" value="887665443383838"/> - <con:entry key="X-Auth-Token" value="999888777666"/> - <con:entry key="belongsTo" value="1234"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - validateToken - Valid Token no belongsTo"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens/{tokenId}" methodName="GET - validateToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - validateToken - Valid Token no belongsTo" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains"><con:configuration><token>auth</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>token</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>user</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tokenId" value="887665443383838"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - validateToken - Valid Token bad belongsTo"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens/{tokenId}" methodName="GET - validateToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - validateToken - Valid Token bad belongsTo" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains"><con:configuration><token>itemNotFound</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>404</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tokenId" value="887665443383838"/> - <con:entry key="X-Auth-Token" value="999888777666"/> - <con:entry key="belongsTo" value="998877665"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - validateToken - Bad Token"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens/{tokenId}" methodName="GET - validateToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - validateToken - Bad Token" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains"><con:configuration><token>itemNotFound</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>404</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tokenId" value="0009991919"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - validateToken - Expired Token"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens/{tokenId}" methodName="GET - validateToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - validateToken - Expired Token" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains"><con:configuration><token>itemNotFound</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>404</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tokenId" value="000999"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:properties/><con:reportParameters/></con:testCase><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Authenticate" searchProperties="true" id="6daa3ca5-8855-4181-afd4-151833f786ff"><con:settings/><con:testStep type="restrequest" name="POST - authenticate - Bad Credentials, user doesn't exist"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens" methodName="POST - authenticate" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - authenticate - Bad Credentials, user doesn't exist" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><passwordCredentials password="P@ssword1" username="testuser" xmlns="http://docs.openstack.org/identity/api/v2.0"/></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>401</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>unauthorized</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - authenticate - Disabled User"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens" methodName="POST - authenticate" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - authenticate - Disabled User" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><passwordCredentials password="1234" username="disabled" xmlns="http://docs.openstack.org/identity/api/v2.0"/></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>403</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>userDisabled</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - authenticate - Bad Credentials, bad password"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens" methodName="POST - authenticate" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - authenticate - Bad Credentials, bad password" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><passwordCredentials password="123774" username="joeuser" xmlns="http://docs.openstack.org/identity/api/v2.0"/></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>401</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>unauthorized</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - authenticate - Valid Credentials - Admin"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens" methodName="POST - authenticate" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - authenticate - Valid Credentials - Admin" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><passwordCredentials password="secrete" username="admin" xmlns="http://docs.openstack.org/identity/api/v2.0"/></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>user</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>token</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>Admin</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace auth='http://docs.openstack.org/identity/api/v2.0'; -/auth:auth/auth:user/auth:groups/auth:group/@id='Admin'</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - authenticate - Valid Credentials - Joe User"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens" methodName="POST - authenticate" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - authenticate - Valid Credentials - Joe User" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><passwordCredentials password="secrete" username="joeuser" xmlns="http://docs.openstack.org/identity/api/v2.0"/></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>user</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>token</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple NotContains"><con:configuration><token>Admin</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace auth='http://docs.openstack.org/identity/api/v2.0'; -/auth:auth/auth:user/auth:groups/auth:group/@id='Admin'</path><content>false</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:properties/><con:reportParameters/></con:testCase><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Authenticate (JSON)" searchProperties="true" id="686eabb3-4d04-4f63-a46c-fab41cd1ea62"><con:settings/><con:testStep type="restrequest" name="POST - authenticate - Bad Credentials, user doesn't exist"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens" methodName="POST - authenticate" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - authenticate - Bad Credentials, user doesn't exist" mediaType="application/json" postQueryString="false" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>{ - "passwordCredentials" : { - "username" : "testuser", - "password" : "P@ssword1" - } -}</con:request><con:assertion type="Simple Contains"><con:configuration><token>401</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>unauthorized</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - authenticate - Disabled User"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens" methodName="POST - authenticate" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - authenticate - Disabled User" mediaType="application/json" postQueryString="false" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>{ - "passwordCredentials" : { - "username" : "disabled", - "password" : "1234" - } -}</con:request><con:assertion type="Simple Contains"><con:configuration><token>403</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>userDisabled</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - authenticate - Bad Credentials, bad password"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens" methodName="POST - authenticate" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - authenticate - Bad Credentials, bad password" mediaType="application/json" postQueryString="false" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>{ - "passwordCredentials" : { - "username" : "joeuser", - "password" : "123774" - } -}</con:request><con:assertion type="Simple Contains"><con:configuration><token>401</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>unauthorized</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - authenticate - Valid Credentials - Admin"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens" methodName="POST - authenticate" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - authenticate - Valid Credentials - Admin" mediaType="application/json" postQueryString="false" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>{ - "passwordCredentials" : { - "username" : "admin", - "password" : "secrete" - } -}</con:request><con:assertion type="Simple Contains"><con:configuration><token>user</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>token</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>Admin</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - authenticate - Valid Credentials - Joe User"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens" methodName="POST - authenticate" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - authenticate - Valid Credentials - Joe User" mediaType="application/json" postQueryString="false" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>{ - "passwordCredentials" : { - "username" : "joeuser", - "password" : "secrete" - } -}</con:request><con:assertion type="Simple Contains"><con:configuration><token>user</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>token</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple NotContains"><con:configuration><token>Admin</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:properties/><con:reportParameters/></con:testCase><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Revoke Token" searchProperties="true" id="f10219b1-055a-4cf4-aeb3-4172bd596979"><con:settings/><con:testStep type="restrequest" name="DELETE - revokeToken - token doesn't exist"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens/{tokenId}" methodName="DELETE - revokeToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="DELETE - revokeToken - token doesn't exist" mediaType="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><entry key="Accept" value="application/xml" xmlns="http://eviware.com/soapui/config"/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>404</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>itemNotFound</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tokenId" value="3u37737"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - authenticate - Valid Credentials - Joe User, old Token?"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens" methodName="POST - authenticate" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - authenticate - Valid Credentials - Joe User, old Token?" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><passwordCredentials password="secrete" username="joeuser" xmlns="http://docs.openstack.org/identity/api/v2.0"/></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>user</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>token</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple NotContains"><con:configuration><token>Admin</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace auth='http://docs.openstack.org/identity/api/v2.0'; -/auth:auth/auth:user/auth:groups/auth:group/@id='Admin'</path><content>false</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace auth='http://docs.openstack.org/identity/api/v2.0'; -/auth:auth/auth:token/@id</path><content>887665443383838</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="DELETE - revokeToken - Joe User"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens/{tokenId}" methodName="DELETE - revokeToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="DELETE - revokeToken - Joe User" mediaType="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><entry key="Accept" value="application/xml" xmlns="http://eviware.com/soapui/config"/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="GroovyScriptAssertion"><con:configuration><scriptText>assert(context.response==null)</scriptText></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tokenId" value="887665443383838"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - validateToken - old Token, Bad?"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens/{tokenId}" methodName="GET - validateToken" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - validateToken - old Token, Bad?" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains"><con:configuration><token>itemNotFound</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>404</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tokenId" value="887665443383838"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - authenticate - Valid Credentials - Joe User, new Token?"><con:settings/><con:config service="Keystone" resourcePath="/v2.0/tokens" methodName="POST - authenticate" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - authenticate - Valid Credentials - Joe User, new Token?" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><passwordCredentials password="secrete" username="joeuser" xmlns="http://docs.openstack.org/identity/api/v2.0"/></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>user</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>token</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple NotContains"><con:configuration><token>Admin</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace auth='http://docs.openstack.org/identity/api/v2.0'; -/auth:auth/auth:user/auth:groups/auth:group/@id='Admin'</path><content>false</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace auth='http://docs.openstack.org/identity/api/v2.0'; -/auth:auth/auth:token/@id="887665443383838"</path><content>false</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:properties/><con:reportParameters/></con:testCase><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Tenant Create" searchProperties="true" id="ec537986-d739-4ade-a2f6-f0fca19b876e"><con:settings/><con:testStep type="restrequest" name="POST - createTenant - New Tenant"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant enabled="true" id="my_new_tenant" xmlns:v1="http://docs.openstack.org/identity/api/v2.0"><v1:description>This is a description of my tenant. Thank you very much.</v1:description></v1:tenant></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@enabled = "true" and /ns1:tenant/@id="my_new_tenant" and /ns1:tenant/ns1:description = "This is a description of my tenant. Thank you very much."</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - createTenant - New Tenant, same id"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant, same id" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant enabled="true" id="my_new_tenant" xmlns:v1="http://docs.openstack.org/identity/api/v2.0"><v1:description>This is a description of my tenant. Thank you very much.</v1:description></v1:tenant></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>tenantConflict</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>409</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - createTenant - New Disabled Tenant"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Disabled Tenant" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant enabled="false" id="mt2" xmlns:v1="http://docs.openstack.org/identity/api/v2.0"><v1:description>New Disabled Tenant</v1:description></v1:tenant></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@enabled = "false" and /ns1:tenant/@id="mt2" and /ns1:tenant/ns1:description = "New Disabled Tenant"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - createTenant - New Tenant No Enable"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant No Enable" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant id="mt3" xmlns:v1="http://docs.openstack.org/identity/api/v2.0"><v1:description>New Tenant 3</v1:description></v1:tenant></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@id="mt3" and /ns1:tenant/ns1:description = "New Tenant 3"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - createTenant - New Tenant No ID"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant No ID" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant enabled="true" xmlns:v1="http://docs.openstack.org/identity/api/v2.0"><v1:description>New Tenant No ID</v1:description></v1:tenant></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>400</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>badRequest</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - createTenant - New Tenant no Description"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant no Description" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant enabled="true" id="my_new_tenant" xmlns:v1="http://docs.openstack.org/identity/api/v2.0"></v1:tenant></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>400</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>badRequest</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:properties/><con:reportParameters/></con:testCase><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Tenant Create (JSON)" searchProperties="true" id="8e95cb90-ef1b-4f0e-a882-09a4a43dd01f"><con:settings/><con:testStep type="restrequest" name="POST - createTenant - New Tenant"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant" mediaType="application/json" postQueryString="false" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>{"tenant": - { - "id": "JGroup", - "description": "A description ...", - "enabled": true - } -} -</con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace ns1='http://localhost/v1.0/tenants'; -ns1:Response/ns1:tenant/ns1:id="JGroup" and ns1:Response/ns1:tenant/ns1:enabled="true" and ns1:Response/ns1:tenant/ns1:description="A description ..."</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - createTenant - New Tenant, same id"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant, same id" mediaType="application/json" postQueryString="false" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>{"tenant": - { - "id": "JGroup", - "description": "A description ...", - "enabled": true - } -}</con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>tenantConflict</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>409</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - createTenant - New Disabled Tenant"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Disabled Tenant" mediaType="application/json" postQueryString="false" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>{"tenant": - { - "id": "JGroup33", - "description": "A description...", - "enabled": false - } -}</con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace ns1='http://localhost/v1.0/tenants'; -ns1:Response/ns1:tenant/ns1:id = "JGroup33" and ns1:Response/ns1:tenant/ns1:enabled="false" and ns1:Response/ns1:tenant/ns1:description="A description..."</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - createTenant - New Tenant No Enable"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant No Enable" mediaType="application/json" postQueryString="false" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>{"tenant": - { - "id": "JGroup65", - "description": "A description..." - } -}</con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace ns1='http://localhost/v1.0/tenants'; -ns1:Response/ns1:tenant/ns1:id = "JGroup65" and ns1:Response/ns1:tenant/ns1:description = "A description..."</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - createTenant - New Tenant No ID"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant No ID" mediaType="application/json" postQueryString="false" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>{"tenant": - { - "description": "A description...", - "enabled" : true - } -}</con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>400</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>badRequest</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - createTenant - New Tenant no Description"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant no Description" mediaType="application/json" postQueryString="false" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>{"tenant": - { - "id": "JGroup95", - "enabled": true - } -}</con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>400</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>badRequest</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="POST - createTenant - New Tenant Bad Enabled"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant Bad Enabled" mediaType="application/json" postQueryString="false" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request>{"tenant": - { - "id": "JGroup95", - "description" : "A description...", - "enabled": "true" - } -}</con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>400</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains"><con:configuration><token>badRequest</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:properties/><con:reportParameters/></con:testCase><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Get Tenants" searchProperties="true" id="3465716c-ff9c-4dea-8444-ae70e8144571"><con:settings/><con:testStep type="restrequest" name="GET - getTenants"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="GET - getTenants" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getTenants" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -count(//ns1:tenant)</path><content>8</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getTenants (JSON)"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="GET - getTenants" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getTenants (JSON)" mediaType="application/xml" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace ns1='http://localhost/v1.0/tenants'; -count(//ns1:e)</path><content>8</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:properties/><con:reportParameters/></con:testCase><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Get Tenant" searchProperties="true" id="e66665ad-46b4-45d2-a8e4-761dbb12fd1c"><con:settings/><con:testStep type="restrequest" name="GET - getTenant"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="GET - getTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getTenant - Request 1" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -ns1:tenant/@id</path><content>1234</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath (enabled and description)"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@enabled and /ns1:tenant/ns1:description</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tenantId" value="1234"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getTenant (JSON)"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="GET - getTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getTenant (JSON)" mediaType="application/xml" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://localhost/v1.0/tenants/1234'; -ns1:Response/ns1:tenant/ns1:id</path><content>1234</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath (enabled and description)"><con:configuration><path>declare namespace ns1='http://localhost/v1.0/tenants/1234'; -/ns1:Response/ns1:tenant/ns1:enabled and /ns1:Response/ns1:tenant/ns1:description</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tenantId" value="1234"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getTenant, Not Found"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="GET - getTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getTenant, Not Found" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains" name="Contains"><con:configuration><token>404</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains" name="Contains itemNotFound"><con:configuration><token>itemNotFound</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tenantId" value="88273666219200"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:properties/></con:testCase><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Delete Tenant" searchProperties="true" id="65633524-fcae-4006-b838-e02a1077e3f7"><con:settings/><con:testStep type="restrequest" name="POST - createTenant - New Tenant"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant enabled="true" id="to_delete" - xmlns:v1="http://docs.openstack.org/identity/api/v2.0"> - <v1:description>To Be Deleted</v1:description> -</v1:tenant></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@enabled = "true" and /ns1:tenant/@id="to_delete" and /ns1:tenant/ns1:description = "To Be Deleted"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getTenant"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="GET - getTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="Copy of GET - getTenant" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -ns1:tenant/@id</path><content>to_delete</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath (enabled and description)"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@enabled and /ns1:tenant/ns1:description</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tenantId" value="to_delete"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="DELETE - deleteTenant"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="DELETE - deleteTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="DELETE - deleteTenant - Request 1" mediaType="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><entry key="Accept" value="application/xml" xmlns="http://eviware.com/soapui/config"/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="GroovyScriptAssertion" name="Script Assertion"><con:configuration><scriptText>assert(context.response == null)</scriptText></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tenantId" value="to_delete"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getTenant, Not Found"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="GET - getTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="Copy of GET - getTenant, Not Found" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains" name="Contains"><con:configuration><token>404</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains" name="Contains itemNotFound"><con:configuration><token>itemNotFound</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tenantId" value="to_delete"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="DELETE - deleteTenant, not empty - user"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="DELETE - deleteTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="DELETE - deleteTenant, not empty - user" mediaType="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><entry key="Accept" value="application/xml" xmlns="http://eviware.com/soapui/config"/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains" name="Contains - forbidden"><con:configuration><token>forbidden</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains" name="Contains - 403"><con:configuration><token>403</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tenantId" value="1234"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="DELETE - deleteTenant, not empty - group"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="DELETE - deleteTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="DELETE - deleteTenant, not empty - group" mediaType="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><entry key="Accept" value="application/xml" xmlns="http://eviware.com/soapui/config"/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Simple Contains" name="Contains - forbidden"><con:configuration><token>forbidden</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains" name="Contains - 403"><con:configuration><token>403</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tenantId" value="0000"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:properties/></con:testCase><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Update Tenant" searchProperties="true" id="60b728bc-d3d5-46f0-84bf-a12d6a443d1a"><con:settings/><con:testStep type="restrequest" name="POST - createTenant - New Tenant"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants" methodName="POST - createTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="POST - createTenant - New Tenant" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant enabled="true" id="to_update" xmlns:v1="http://docs.openstack.org/identity/api/v2.0"> - <v1:description>ToUpdate</v1:description> -</v1:tenant></con:request><con:assertion type="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@enabled = "true" and /ns1:tenant/@id="to_update" and /ns1:tenant/ns1:description = "ToUpdate"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="X-Auth-Token" value="999888777666" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getTenant"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="GET - getTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getTenant" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "true" and /ns1:tenant/@id="to_update" and /ns1:tenant/ns1:description = "ToUpdate"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tenantId" value="to_update"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="PUT - updateTenant - Description"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="PUT - updateTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="PUT - updateTenant - Description" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant enabled="true" xmlns:v1="http://docs.openstack.org/identity/api/v2.0"> - <v1:description>ToUpdate2</v1:description> -</v1:tenant></con:request><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "true" and /ns1:tenant/ns1:description = "ToUpdate2"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tenantId" value="to_update"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getTenant - New Description?"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="GET - getTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getTenant - New Description?" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "true" and /ns1:tenant/@id="to_update" and /ns1:tenant/ns1:description = "ToUpdate2"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tenantId" value="to_update"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="PUT - updateTenant - Enabled"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="PUT - updateTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="PUT - updateTenant - Enabled" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant enabled="false" xmlns:v1="http://docs.openstack.org/identity/api/v2.0"> - <v1:description>ToUpdate2</v1:description> -</v1:tenant></con:request><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "false" and /ns1:tenant/ns1:description = "ToUpdate2"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tenantId" value="to_update"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getTenant - New Enable?"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="GET - getTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getTenant - New Enable?" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "false" and /ns1:tenant/@id="to_update" and /ns1:tenant/ns1:description = "ToUpdate2"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tenantId" value="to_update"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="PUT - updateTenant - ID, should ignore"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="PUT - updateTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="PUT - updateTenant - ID, should ignore" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant id="boogabooga" enabled="false" xmlns:v1="http://docs.openstack.org/identity/api/v2.0"> - <v1:description>ToUpdate2</v1:description> -</v1:tenant></con:request><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "false" and /ns1:tenant/ns1:description = "ToUpdate2"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tenantId" value="to_update"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getTenant - New ID ignored?"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="GET - getTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getTenant - New ID ignored?" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "false" and /ns1:tenant/@id="to_update" and /ns1:tenant/ns1:description = "ToUpdate2"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tenantId" value="to_update"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="PUT - updateTenant - Enabled and Description"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="PUT - updateTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="PUT - updateTenant - Enabled and Description" mediaType="application/xml" postQueryString="false" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request><v1:tenant enabled="true" xmlns:v1="http://docs.openstack.org/identity/api/v2.0"> - <v1:description>ToUpdate3</v1:description> -</v1:tenant></con:request><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "true" and /ns1:tenant/ns1:description = "ToUpdate3"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tenantId" value="to_update"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getTenant - New Changes?"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="GET - getTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getTenant - New Changes?" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/identity/api/v2.0'; -/ns1:tenant/@id="to_update" and /ns1:tenant/@enabled = "true" and /ns1:tenant/@id="to_update" and /ns1:tenant/ns1:description = "ToUpdate3"</path><content>true</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tenantId" value="to_update"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="DELETE - deleteTenant"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/tenants/{tenantId}" methodName="DELETE - deleteTenant" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="DELETE - deleteTenant" mediaType="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><entry key="Accept" value="application/xml" xmlns="http://eviware.com/soapui/config"/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="GroovyScriptAssertion" name="Script Assertion"><con:configuration><scriptText>assert(context.response == null)</scriptText></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters> - <con:entry key="tenantId" value="to_update"/> - <con:entry key="X-Auth-Token" value="999888777666"/> -</con:parameters></con:restRequest></con:config></con:testStep><con:properties/></con:testCase><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Extensions" searchProperties="true" id="038f320e-bf1f-42e6-a953-33c166d7619e"><con:settings/><con:testStep type="restrequest" name="GET - getExtensions"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/extensions" methodName="GET - getExtensions" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getExtensions" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/common/api/v1.0'; -count(/ns1:extensions)</path><content>1</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getExtensions (JSON)"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/extensions" methodName="GET - getExtensions" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getExtensions (JSON)" mediaType="application/xml" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://localhost/v1.0/extensions'; -count(//ns1:extensions)</path><content>1</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getExtension"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/extensions/{alias}" methodName="GET - getExtension" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getExtension - Request 1" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains" name="Contains"><con:configuration><token>404</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains" name="Contains itemNotFound"><con:configuration><token>itemNotFound</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="alias" value="RAX-TEST" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getExtension (JSON)"><con:settings/><con:config service="Keystone" resourcePath="/v1.0/extensions/{alias}" methodName="GET - getExtension" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getExtension (JSON)" mediaType="application/xml" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="Simple Contains" name="Contains"><con:configuration><token>404</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:assertion type="Simple Contains" name="Contains itemNotFound"><con:configuration><token>itemNotFound</token><ignoreCase>false</ignoreCase><useRegEx>false</useRegEx></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters><entry key="alias" value="RAX-TEST" xmlns="http://eviware.com/soapui/config"/></con:parameters></con:restRequest></con:config></con:testStep><con:properties/></con:testCase><con:testCase failOnError="true" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Version Info" searchProperties="true" id="99db3dab-53de-44f5-93cd-099c8850fd97"><con:settings/><con:testStep type="restrequest" name="GET - getVersionInfo"><con:settings/><con:config service="Keystone" resourcePath="/v1.0" methodName="GET - getVersionInfo" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getVersionInfo - Request 1" mediaType="application/xml" accept="application/xml"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://docs.openstack.org/common/api/v1.0'; -count(//ns1:version)</path><content>1</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:testStep type="restrequest" name="GET - getVersionInfo (JSON)"><con:settings/><con:config service="Keystone" resourcePath="/v1.0" methodName="GET - getVersionInfo" xsi:type="con:RestRequestStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><con:restRequest name="GET - getVersionInfo (JSON)" mediaType="application/xml" accept="application/json"><con:settings><con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting></con:settings><con:endpoint>http://localhost:8080</con:endpoint><con:request/><con:assertion type="Schema Compliance" name="Schema Compliance"><con:configuration><definition/></con:configuration></con:assertion><con:assertion type="XPath Match" name="XPath Match"><con:configuration><path>declare namespace ns1='http://localhost/v1.0'; -count(//ns1:version)</path><content>1</content><allowWildcards>false</allowWildcards><ignoreNamspaceDifferences>false</ignoreNamspaceDifferences></con:configuration></con:assertion><con:jmsConfig JMSDeliveryMode="PERSISTENT"/><con:jmsPropertyConfig/><con:parameters/></con:restRequest></con:config></con:testStep><con:properties/></con:testCase><con:properties/><con:reportParameters/></con:testSuite><con:requirements/><con:properties/><con:wssContainer/><con:databaseConnectionContainer/><con:reporting><con:xmlTemplates/><con:parameters/></con:reporting></con:soapui-project>
\ No newline at end of file diff --git a/keystone/test/__init__.py b/keystone/test/__init__.py deleted file mode 100644 index 26c4d9b3..00000000 --- a/keystone/test/__init__.py +++ /dev/null @@ -1,723 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# All Rights Reserved. -# -# 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. - -# Colorizer Code is borrowed from Twisted: -# Copyright (c) 2001-2010 Twisted Matrix Laboratories. -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# -# Code copied from Nova and other OpenStack projects: -# Colorizers -# Classes starting with Nova -# other setup and initialization code -# -""" Module that handles starting the Keystone server and running -test suites""" - -import heapq -import logging -from nose import config as noseconfig -from nose import core -from nose import result -import optparse -import os -import sys -import tempfile -import time -import unittest - -import keystone -import keystone.server -import keystone.version -from keystone import config as config_module -from keystone.common import config -from keystone.test import utils -from keystone.test import client as client_tests -from keystone import utils as main_utils - -TEST_DIR = os.path.abspath(os.path.dirname(__file__)) -BASE_DIR = os.path.abspath(os.path.join(TEST_DIR, os.pardir, os.pardir)) -TEST_CERT = os.path.join(BASE_DIR, 'examples/ssl/certs/middleware-key.pem') - -logger = logging.getLogger(__name__) - -CONF = config_module.CONF - - -class _AnsiColorizer(object): - """ - A colorizer is an object that loosely wraps around a stream, allowing - callers to write text to the stream in a particular color. - - Colorizer classes must implement C{supported()} and C{write(text, color)}. - """ - _colors = dict(black=30, red=31, green=32, yellow=33, - blue=34, magenta=35, cyan=36, white=37) - - def __init__(self, stream): - self.stream = stream - - def supported(cls, stream=sys.stdout): - """ - A class method that returns True if the current platform supports - coloring terminal output using this method. Returns False otherwise. - """ - if not stream.isatty(): - return False # auto color only on TTYs - try: - import curses - except ImportError: - return False - else: - try: - try: - return curses.tigetnum("colors") > 2 - except curses.error: - curses.setupterm() - return curses.tigetnum("colors") > 2 - except: - raise - # guess false in case of error - return False - supported = classmethod(supported) - - def write(self, text, color): - """ - Write the given text to the stream in the given color. - - @param text: Text to be written to the stream. - - @param color: A string label for a color. e.g. 'red', 'white'. - """ - color = self._colors[color] - self.stream.write('\x1b[%s;1m%s\x1b[0m' % (color, text)) - - -class _Win32Colorizer(object): - """ - See _AnsiColorizer docstring. - """ - def __init__(self, stream): - from win32console import GetStdHandle, STD_OUT_HANDLE, \ - FOREGROUND_RED, FOREGROUND_BLUE, FOREGROUND_GREEN, \ - FOREGROUND_INTENSITY - red, green, blue, bold = (FOREGROUND_RED, FOREGROUND_GREEN, - FOREGROUND_BLUE, FOREGROUND_INTENSITY) - self.stream = stream - self.screenBuffer = GetStdHandle(STD_OUT_HANDLE) - self._colors = { - 'normal': red | green | blue, - 'red': red | bold, - 'green': green | bold, - 'blue': blue | bold, - 'yellow': red | green | bold, - 'magenta': red | blue | bold, - 'cyan': green | blue | bold, - 'white': red | green | blue | bold - } - - def supported(cls, stream=sys.stdout): - try: - import win32console - screenBuffer = win32console.GetStdHandle( - win32console.STD_OUT_HANDLE) - except ImportError: - return False - import pywintypes - try: - screenBuffer.SetConsoleTextAttribute( - win32console.FOREGROUND_RED | - win32console.FOREGROUND_GREEN | - win32console.FOREGROUND_BLUE) - except pywintypes.error: - return False - else: - return True - supported = classmethod(supported) - - def write(self, text, color): - color = self._colors[color] - self.screenBuffer.SetConsoleTextAttribute(color) - self.stream.write(text) - self.screenBuffer.SetConsoleTextAttribute(self._colors['normal']) - - -class _NullColorizer(object): - """ - See _AnsiColorizer docstring. - """ - def __init__(self, stream): - self.stream = stream - - def supported(cls, stream=sys.stdout): - return True - supported = classmethod(supported) - - def write(self, text, color): - self.stream.write(text) - - -def get_elapsed_time_color(elapsed_time): - if elapsed_time > 1.0: - return 'red' - elif elapsed_time > 0.25: - return 'yellow' - else: - return 'green' - - -class NovaTestResult(result.TextTestResult): - def __init__(self, *args, **kw): - self.show_elapsed = kw.pop('show_elapsed') - result.TextTestResult.__init__(self, *args, **kw) - self.num_slow_tests = 5 - self.slow_tests = [] # this is a fixed-sized heap - self._last_case = None - self.colorizer = None - # NOTE(vish): reset stdout for the terminal check - stdout = sys.stdout - sys.stdout = sys.__stdout__ - for colorizer in [_Win32Colorizer, _AnsiColorizer, _NullColorizer]: - if colorizer.supported(): - self.colorizer = colorizer(self.stream) - break - sys.stdout = stdout - - # NOTE(lorinh): Initialize start_time in case a sqlalchemy-migrate - # error results in it failing to be initialized later. Otherwise, - # _handleElapsedTime will fail, causing the wrong error message to - # be outputted. - self.start_time = time.time() - - def getDescription(self, test): - return str(test) - - def _handleElapsedTime(self, test): - self.elapsed_time = time.time() - self.start_time - item = (self.elapsed_time, test) - # Record only the n-slowest tests using heap - if len(self.slow_tests) >= self.num_slow_tests: - heapq.heappushpop(self.slow_tests, item) - else: - heapq.heappush(self.slow_tests, item) - - def _writeElapsedTime(self, test): - color = get_elapsed_time_color(self.elapsed_time) - self.colorizer.write(" %.2f" % self.elapsed_time, color) - - def _writeResult(self, test, long_result, color, short_result, success): - if self.showAll: - self.colorizer.write(long_result, color) - if self.show_elapsed and success: - self._writeElapsedTime(test) - self.stream.writeln() - elif self.dots: - self.stream.write(short_result) - self.stream.flush() - - # NOTE(vish): copied from unittest with edit to add color - def addSuccess(self, test): - unittest.TestResult.addSuccess(self, test) - self._handleElapsedTime(test) - self._writeResult(test, 'OK', 'green', '.', True) - - # NOTE(vish): copied from unittest with edit to add color - def addFailure(self, test, err): - unittest.TestResult.addFailure(self, test, err) - self._handleElapsedTime(test) - self._writeResult(test, 'FAIL', 'red', 'F', False) - - # NOTE(vish): copied from nose with edit to add color - def addError(self, test, err): - """Overrides normal addError to add support for - errorClasses. If the exception is a registered class, the - error will be added to the list for that class, not errors. - """ - self._handleElapsedTime(test) - stream = getattr(self, 'stream', None) - ec, ev, tb = err - try: - exc_info = self._exc_info_to_string(err, test) - except TypeError: - # 2.3 compat - exc_info = self._exc_info_to_string(err) - for cls, (storage, label, isfail) in self.errorClasses.items(): - if result.isclass(ec) and issubclass(ec, cls): - if isfail: - test.passed = False - storage.append((test, exc_info)) - # Might get patched into a streamless result - if stream is not None: - if self.showAll: - message = [label] - detail = result._exception_detail(err[1]) - if detail: - message.append(detail) - stream.writeln(": ".join(message)) - elif self.dots: - stream.write(label[:1]) - return - self.errors.append((test, exc_info)) - test.passed = False - if stream is not None: - self._writeResult(test, 'ERROR', 'red', 'E', False) - - def startTest(self, test): - unittest.TestResult.startTest(self, test) - self.start_time = time.time() - current_case = test.test.__class__.__name__ - - if self.showAll: - if current_case != self._last_case: - self.stream.writeln(current_case) - self._last_case = current_case - - self.stream.write( - ' %s' % str(test.test._testMethodName).ljust(60)) - self.stream.flush() - - -class NovaTestRunner(core.TextTestRunner): - def __init__(self, *args, **kwargs): - self.show_elapsed = kwargs.pop('show_elapsed') - core.TextTestRunner.__init__(self, *args, **kwargs) - - def _makeResult(self): - return NovaTestResult(self.stream, - self.descriptions, - self.verbosity, - self.config, - show_elapsed=self.show_elapsed) - - def _writeSlowTests(self, result_): - # Pare out 'fast' tests - slow_tests = [item for item in result_.slow_tests - if get_elapsed_time_color(item[0]) != 'green'] - if slow_tests: - slow_total_time = sum(item[0] for item in slow_tests) - self.stream.writeln("Slowest %i tests took %.2f secs:" - % (len(slow_tests), slow_total_time)) - for elapsed_time, test in sorted(slow_tests, reverse=True): - time_str = "%.2f" % elapsed_time - self.stream.writeln(" %s %s" % (time_str.ljust(10), test)) - - def run(self, test): - result_ = core.TextTestRunner.run(self, test) - if self.show_elapsed: - self._writeSlowTests(result_) - return result_ - - -class KeystoneTest(object): - """Primary test class for invoking keystone tests. Controls - initialization of environment with temporary configuration files, - starts keystone admin and service API WSIG servers, and then uses - :py:mod:`unittest2` to discover and iterate over existing tests. - - :py:class:`keystone.test.KeystoneTest` is expected to be - subclassed and invoked in ``run_tests.py`` where subclasses define - a config_name (that matches a template existing in - ``keystone/test/etc``) and test_files (that are cleared at the - end of test execution from the temporary space used to run these - tests). - """ - config_params = {'test_dir': TEST_DIR, 'base_dir': BASE_DIR} - isSsl = False - hpidmDisabled = False - config_name = None - test_files = None - server = None - admin_server = None - conf_fp = None - directory_base = None - - def clear_database(self): - """Remove any test databases or files generated by previous tests.""" - if self.test_files: - for fname in self.test_files: - paths = [os.path.join(os.curdir, fname), - os.path.join(os.getcwd(), fname), - os.path.join(TEST_DIR, fname)] - for fpath in paths: - if os.path.exists(fpath): - logger.debug("Removing test file %s" % fname) - os.unlink(fpath) - - def construct_temp_conf_file(self): - """Populates a configuration template, and writes to a file pointer.""" - template_fpath = os.path.join(TEST_DIR, 'etc', self.config_name) - conf_contents = open(template_fpath).read() - self.config_params['service_port'] = utils.get_unused_port() - logger.debug("Assigned port %s to service" % - self.config_params['service_port']) - self.config_params['admin_port'] = utils.get_unused_port() - logger.debug("Assigned port %s to admin" % - self.config_params['admin_port']) - - conf_contents = conf_contents % self.config_params - self.conf_fp = tempfile.NamedTemporaryFile() - self.conf_fp.write(conf_contents) - self.conf_fp.flush() - logger.debug("Create test configuration file: %s" % self.conf_fp.name) - client_tests.TEST_CONFIG_FILE_NAME = self.conf_fp.name - - def setUp(self): - pass - - def startServer(self): - """ Starts a Keystone server on random ports for testing """ - self.server = None - self.admin_server = None - - self.construct_temp_conf_file() - - # Set client certificate for test client - if self.isSsl: - logger.debug("SSL testing will use cert_file %s" % TEST_CERT) - os.environ['cert_file'] = TEST_CERT - else: - if 'cert_file' in os.environ: - del os.environ['cert_file'] - - # indicating HP-IDM is disabled - if self.hpidmDisabled: - logger.debug("HP-IDM extensions is disabled") - os.environ['HP-IDM_Disabled'] = 'True' - else: - if 'HP-IDM_Disabled' in os.environ: - del os.environ['HP-IDM_Disabled'] - - # run the keystone server - logger.info("Starting the keystone server...") - - class SilentOptParser(optparse.OptionParser): - """ Class used to prevent OptionParser from exiting when it detects - options coming in for nose/testing """ - def exit(): - pass - - def error(self, msg): - pass - - parser = SilentOptParser(version='%%prog %s' % - keystone.version.version()) - common_group = config.add_common_options(parser) - config.add_log_options(parser) - - # Handle a special argument to support starting two endpoints - common_group.add_option( - '-a', '--admin-port', dest="admin_port", metavar="PORT", - help="specifies port for Admin API to listen " - "on (default is 35357)") - - # Parse arguments and load config - (options, args) = config.parse_options(parser) - options['config_file'] = self.conf_fp.name - - # Populate the CONF module - CONF.reset() - CONF(config_files=[self.conf_fp.name]) - - try: - # Load Service API Server - service = keystone.server.Server(name="Service API", - config_name='keystone-legacy-auth', - args=args) - service.start(wait=False) - - # Client tests will use these globals to find out where - # the server is - client_tests.TEST_TARGET_SERVER_SERVICE_PROTOCOL = service.protocol - client_tests.TEST_TARGET_SERVER_SERVICE_ADDRESS = service.host - client_tests.TEST_TARGET_SERVER_SERVICE_PORT = service.port - - except RuntimeError, e: - logger.exception(e) - raise e - - try: - # Load Admin API server - port = int(CONF.admin_port or - client_tests.TEST_TARGET_SERVER_ADMIN_PORT) - host = (CONF.admin_host or - client_tests.TEST_TARGET_SERVER_ADMIN_ADDRESS) - admin = keystone.server.Server(name='Admin API', - config_name='admin', args=args) - admin.start(host=host, port=port, wait=False) - - # Client tests will use these globals to find out where - # the server is - client_tests.TEST_TARGET_SERVER_ADMIN_PROTOCOL = admin.protocol - client_tests.TEST_TARGET_SERVER_ADMIN_ADDRESS = admin.host - client_tests.TEST_TARGET_SERVER_ADMIN_PORT = admin.port - - except RuntimeError, e: - logger.exception(e) - raise e - - self.server = service - self.admin_server = admin - - # Load bootstrap data - from keystone import manage - manage_args = ['--config-file', self.conf_fp.name] - manage.parse_args(args=manage_args) - - #TODO(zns): this should end up being run by a 'bootstrap' script - fixtures = [ - ('role', 'add', CONF.keystone_admin_role), - ('user', 'add', 'admin', 'secrete'), - ('role', 'grant', CONF.keystone_admin_role, 'admin'), - ('role', 'add', CONF.keystone_service_admin_role), - ('role', 'add', 'Member'), - ] - for cmd in fixtures: - manage.process(*cmd) - - def tearDown(self): - try: - if self.server is not None: - print "Stopping the Service API..." - self.server.stop() - self.server = None - if self.admin_server is not None: - print "Stopping the Admin API..." - self.admin_server.stop() - self.admin_server = None - if self.conf_fp: - self.conf_fp.close() - self.conf_fp = None - except Exception as e: - logger.exception(e) - print "Error cleaning up %s" % e - raise e - finally: - self.clear_database() - if 'cert_file' in os.environ: - del os.environ['cert_file'] - if 'HP-IDM_Disabled' in os.environ: - del os.environ['HP-IDM_Disabled'] - reload(client_tests) - - def run(self, args=None): - try: - print 'Running test suite: %s' % self.__class__.__name__ - - self.setUp() - - # discover and run tests - - # If any argument looks like a test name but doesn't have - # "keystone.test" in front of it, automatically add that so we - # don't have to type as much - show_elapsed = True - argv = [] - if args is None: - args = sys.argv - has_base = False - for x in args: - if x.startswith(('functional', 'unit', 'client')): - argv.append('keystone.test.%s' % x) - has_base = True - elif x.startswith('--hide-elapsed'): - show_elapsed = False - elif x.startswith('-'): - argv.append(x) - else: - argv.append(x) - if x != args[0]: - has_base = True - - if not has_base and self.directory_base is not None: - argv.append(self.directory_base) - argv = ['--no-path-adjustment'] + argv[1:] - logger.debug("Running set of tests with args=%s" % argv) - - c = noseconfig.Config(stream=sys.stdout, - env=os.environ, - verbosity=3, - workingDir=TEST_DIR, - plugins=core.DefaultPluginManager(), - args=argv) - - runner = NovaTestRunner(stream=c.stream, - verbosity=c.verbosity, - config=c, - show_elapsed=show_elapsed) - - result = not core.run(config=c, testRunner=runner, argv=argv) - return int(result) # convert to values applicable to sys.exit() - except Exception, exc: - logger.exception(exc) - raise exc - finally: - self.tearDown() - - -def runtests(): - """This function can be called from 'python setup.py test'.""" - return SQLTest().run() - - -class UnitTests(KeystoneTest): - """ Class that runs unit tests """ - directory_base = 'unit' - - def run(self): - """ Run unit tests - - Filters arguments and leaves only ones relevant to unit tests - """ - - argv = [] - scoped_to_unit = False - for x in sys.argv: - if x.startswith(('functional', 'client')): - # Skip, since we're not running unit tests - return - elif x.startswith('unit'): - argv.append('keystone.test.%s' % x) - scoped_to_unit = True - else: - argv.append(x) - - if not scoped_to_unit: - argv.append('keystone.test.unit') - - return super(UnitTests, self).run(args=argv) - - -class ClientTests(KeystoneTest): - """ Class that runs client tests - - Client tests are the tests that need a running http[s] server running - and make web service calls to that server - - """ - config_name = 'sql.conf.template' - directory_base = 'client' - - def run(self): - """ Run client tests - - Filters arguments and leaves only ones relevant to client tests - """ - - argv = [] - scoped_to_client = False - for x in sys.argv: - if x.startswith(('functional', 'unit')): - # Skip, since we're not running client tests - return - elif x.startswith('client'): - argv.append('keystone.test.%s' % x) - scoped_to_client = True - else: - argv.append(x) - - if not scoped_to_client: - argv.append('keystone.test.client') - - self.startServer() - - return super(ClientTests, self).run(args=argv) - - -class SQLTest(KeystoneTest): - """Test defined using only SQLAlchemy back-end""" - config_name = 'sql.conf.template' - test_files = ('keystone.sqltest.db',) - directory_base = 'functional' - - def run(self): - """ Run client tests - - Filters arguments and leaves only ones relevant to client tests - """ - - argv = [] - scoped_to_functional = False - for x in sys.argv: - if x.startswith(('client', 'unit')): - # Skip, since we're not running functional tests - return - elif x.startswith('functional'): - argv.append('keystone.test.%s' % x) - scoped_to_functional = True - else: - argv.append(x) - - if not scoped_to_functional: - argv.append('keystone.test.functional') - - return super(SQLTest, self).run(args=argv) - - def clear_database(self): - # Disconnect the database before deleting - from keystone.backends import sqlalchemy - sqlalchemy.unregister_models() - - super(SQLTest, self).clear_database() - - -class SSLTest(ClientTests): - config_name = 'ssl.conf.template' - isSsl = True - test_files = ('keystone.ssltest.db',) - - -class MemcacheTest(SQLTest): - """Test defined using only SQLAlchemy and Memcache back-end""" - config_name = 'memcache.conf.template' - test_files = ('keystone.memcachetest.db',) - - -class LDAPTest(SQLTest): - """Test defined using only SQLAlchemy and LDAP back-end""" - config_name = 'ldap.conf.template' - test_files = ('keystone.ldaptest.db', 'ldap.db', 'ldap.db.db',) - - def clear_database(self): - super(LDAPTest, self).clear_database() - from keystone.backends.ldap.fakeldap import FakeShelve - db = FakeShelve().get_instance() - db.clear() - - -class ClientWithoutHPIDMTest(ClientTests): - """Test with HP-IDM disabled to make sure it is backward compatible""" - config_name = 'sql_no_hpidm.conf.template' - hpidmDisabled = True - test_files = ('keystone.nohpidm.db',) diff --git a/keystone/test/client/__init__.py b/keystone/test/client/__init__.py deleted file mode 100644 index 99f61885..00000000 --- a/keystone/test/client/__init__.py +++ /dev/null @@ -1,39 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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. - -""" Client Tests - -Client tests are tests that use HTTP(S) calls to a Keystone server to exercise -request/response test cases. - -In order to avoid port conflicts, client tests use the global settings below -to know which server to talk to. - -When a server is started for testing purposes (usually by the -keystone.test.KeystoneTest class) it will update these values so client tests -know where to find the server - -""" -TEST_TARGET_SERVER_ADMIN_PROTOCOL = 'http' -TEST_TARGET_SERVER_ADMIN_ADDRESS = '127.0.0.1' -TEST_TARGET_SERVER_ADMIN_PORT = 35357 - -TEST_TARGET_SERVER_SERVICE_PROTOCOL = 'http' -TEST_TARGET_SERVER_SERVICE_ADDRESS = '127.0.0.1' -TEST_TARGET_SERVER_SERVICE_PORT = 5000 - -TEST_CONFIG_FILE_NAME = None diff --git a/keystone/test/client/test_client.py b/keystone/test/client/test_client.py deleted file mode 100644 index 6e95d443..00000000 --- a/keystone/test/client/test_client.py +++ /dev/null @@ -1,101 +0,0 @@ -import unittest - -import keystone.common.exception -import keystone.client -from keystone.test.functional.common import isSsl -from keystone.test import client as client_tests - - -class TestAdminClient(unittest.TestCase): - """ - Quick functional tests for the Keystone HTTP admin client. - """ - use_server = True - - def setUp(self): - """ - Run before each test. - """ - cert_file = isSsl() - self.client = keystone.client.AdminClient( - client_tests.TEST_TARGET_SERVER_ADMIN_ADDRESS, - port=client_tests.TEST_TARGET_SERVER_ADMIN_PORT, - is_ssl=(cert_file is not None), - cert_file=cert_file, - admin_name="admin", - admin_pass="secrete") - - def test_admin_validate_token(self): - """ - Test that our admin token is valid. (HTTP GET) - """ - token = self.client.admin_token - result = self.client.validate_token(token) - self.assertEquals("admin", - result["access"]["user"]["name"]) - - def test_admin_check_token(self): - """ - Test that our admin token is valid. (HTTP HEAD) - """ - token = self.client.admin_token - self.assertTrue(self.client.check_token(token)) - - def test_admin_validate_token_fail(self): - """ - Test that validating an invalid token results in None. (HTTP GET) - """ - token = "bad_token" - self.assertTrue(self.client.validate_token(token) is None) - - def test_admin_check_token_fail(self): - """ - Test that checking an invalid token results in False. (HTTP HEAD) - """ - token = "bad_token" - self.assertFalse(self.client.check_token(token)) - - def test_admin_get_token(self): - """ - Test that we can generate a token given correct credentials. - """ - token = self.client.get_token("admin", "secrete") - self.assertEquals(self.client.admin_token, token) - - def test_admin_get_token_bad_auth(self): - """ - Test incorrect credentials generates a client error. - """ - self.assertRaises(keystone.common.exception.ClientError, - self.client.get_token, "bad_user", "bad_pass") - - -class TestServiceClient(unittest.TestCase): - """ - Quick functional tests for the Keystone HTTP service client. - """ - - def setUp(self): - """ - Run before each test. - """ - cert_file = isSsl() - self.client = keystone.client.ServiceClient( - client_tests.TEST_TARGET_SERVER_SERVICE_ADDRESS, - port=client_tests.TEST_TARGET_SERVER_SERVICE_PORT, - is_ssl=(cert_file is not None), - cert_file=cert_file) - - def test_admin_get_token(self): - """ - Test that we can generate a token given correct credentials. - """ - token = self.client.get_token("admin", "secrete") - self.assertTrue(36, len(token)) - - def test_admin_get_token_bad_auth(self): - """ - Test incorrect credentials generates a client error. - """ - self.assertRaises(keystone.common.exception.ClientError, - self.client.get_token, "bad_user", "bad_pass") diff --git a/keystone/test/client/test_d5_compat_calls.py b/keystone/test/client/test_d5_compat_calls.py deleted file mode 100644 index 865bc46d..00000000 --- a/keystone/test/client/test_d5_compat_calls.py +++ /dev/null @@ -1,169 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-2011 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 unittest2 as unittest - -from keystone.test.functional import common - - -class D5_AuthenticationTest(common.FunctionalTestCase): - """ Tests the functionality of the D5 compat module """ - use_server = True - - def setUp(self, *args, **kwargs): - super(D5_AuthenticationTest, self).setUp(*args, **kwargs) - - password = common.unique_str() - self.tenant = self.create_tenant().json['tenant'] - self.user = self.create_user(user_password=password, - tenant_id=self.tenant['id']).json['user'] - self.user['password'] = password - - self.services = {} - self.endpoint_templates = {} - self.services = self.fixture_create_service() - self.endpoint_templates = self.create_endpoint_template( - name=self.services['name'], - type=self.services['type']).\ - json['OS-KSCATALOG:endpointTemplate'] - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_templates['id']) - - def test_validate_unscoped_token(self): - """Admin should be able to validate a user's token""" - # Authenticate as user to get a token - self.service_token = self.post_token(as_json={ - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}}).\ - json['auth']['token']['id'] - - # In the real world, the service user would then pass his/her token - # to some service that depends on keystone, which would then need to - # use keystone to validate the provided token. - - # Admin independently validates the user token - r = self.get_token(self.service_token) - self.assertEqual(r.json['auth']['token']['id'], self.service_token) - self.assertTrue(r.json['auth']['token']['expires']) - self.assertEqual(r.json['auth']['user']['username'], - self.user['name']) - self.assertEqual(r.json['auth']['user']['roleRefs'], []) - - def test_validate_scoped_token(self): - """Admin should be able to validate a user's scoped token""" - # Authenticate as user to get a token - self.service_token = self.post_token(as_json={ - 'passwordCredentials': { - 'tenantId': self.tenant['id'], - 'username': self.user['name'], - 'password': self.user['password']}}).\ - json['auth']['token']['id'] - - # In the real world, the service user would then pass his/her token - # to some service that depends on keystone, which would then need to - # use keystone to validate the provided token. - - # Admin independently validates the user token - r = self.get_token(self.service_token) - self.assertEqual(r.json['auth']['token']['id'], self.service_token) - self.assertEqual(r.json['auth']['token']['tenantId'], - self.tenant['id']) - self.assertTrue(r.json['auth']['token']['expires']) - self.assertEqual(r.json['auth']['user']['username'], - self.user['name']) - self.assertEqual(r.json['auth']['user']['roleRefs'], []) - - def test_authenticate_for_a_tenant(self): - r = self.authenticate_D5(self.user['name'], self.user['password'], - self.tenant['id'], assert_status=200) - - self.assertIsNotNone(r.json['auth']['token']) - service_catalog = r.json['auth']['serviceCatalog'] - self.assertIsNotNone(service_catalog) - self.check_urls_for_regular_user(service_catalog) - - def test_authenticate_for_a_tenant_xml(self): - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<passwordCredentials xmlns="%s" tenantId="%s"' - ' username="%s" password="%s" ' - '/>') % ( - self.xmlns, self.tenant['id'], - self.user['name'], self.user['password']) - r = self.post_token(as_xml=data, assert_status=200) - - self.assertEquals(r.xml.tag, '{%s}auth' % self.xmlns) - service_catalog = r.xml.find('{%s}serviceCatalog' % self.xmlns) - self.check_urls_for_regular_user_xml(service_catalog) - - def test_authenticate_for_a_tenant_on_admin_api(self): - r = self.authenticate_D5(self.user['name'], self.user['password'], - self.tenant['id'], assert_status=200, request_type='admin') - - self.assertIsNotNone(r.json['auth']['token']) - self.assertIsNotNone(r.json['auth']['serviceCatalog']) - service_catalog = r.json['auth']['serviceCatalog'] - self.check_urls_for_regular_user(service_catalog) - - def test_authenticate_for_a_tenant_xml_on_admin_api(self): - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<passwordCredentials xmlns="%s" tenantId="%s"' - ' username="%s" password="%s" ' - '/>') % ( - self.xmlns, self.tenant['id'], - self.user['name'], self.user['password']) - r = self.post_token(as_xml=data, assert_status=200, - request_type='admin') - - self.assertEquals(r.xml.tag, '{%s}auth' % self.xmlns) - service_catalog = r.xml.find('{%s}serviceCatalog' % self.xmlns) - self.check_urls_for_regular_user_xml(service_catalog) - - def test_authenticate_user_disabled(self): - self.disable_user(self.user['id']) - self.authenticate_D5(self.user['name'], self.user['password'], - self.tenant['id'], assert_status=403) - - def test_authenticate_user_wrong(self): - data = {"passwordCredentials": { - "username-field-completely-wrong": self.user['name'], - "password": self.user['password'], - "tenantId": self.tenant['id']}} - self.post_token(as_json=data, assert_status=400) - - def test_authenticate_user_wrong_xml(self): - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<passwordCredentials ' - 'xmlns="http://docs.openstack.org/identity/api/v2.0" ' - 'usernamefieldcompletelywrong="%s" ' - 'password="%s" ' - 'tenantId="%s"/>') % ( - self.user['name'], self.user['password'], self.tenant['id']) - - self.post_token(as_xml=data, assert_status=400) - - def check_urls_for_regular_user(self, service_catalog): - self.assertIsNotNone(service_catalog) - for k in service_catalog.keys(): - endpoints = service_catalog[k] - for endpoint in endpoints: - for key in endpoint: - #Checks whether adminURL is not present. - self.assertNotEquals(key, 'adminURL') - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/client/test_extensions.py b/keystone/test/client/test_extensions.py deleted file mode 100644 index bb5d40a8..00000000 --- a/keystone/test/client/test_extensions.py +++ /dev/null @@ -1,68 +0,0 @@ -import os -import unittest2 as unittest -from keystone.test.functional import common - - -class TestExtensions(common.FunctionalTestCase): - use_server = True - - def test_extensions_json(self): - r = self.service_request(path='/extensions.json') - self.assertTrue('json' in r.getheader('Content-Type')) - content = r.json - self.assertIsNotNone(content['extensions']) - self.assertIsNotNone(content['extensions']['values']) - - def test_extensions_xml(self): - r = self.service_request(path='/extensions.xml') - self.assertTrue('xml' in r.getheader('Content-Type')) - - -class TestAdminExtensions(common.ApiTestCase): - use_server = True - - def test_extensions_json(self): - r = self.admin_request(path='/extensions.json') - self.assertTrue('json' in r.getheader('Content-Type')) - content = r.json - self.assertIsNotNone(content['extensions']) - self.assertIsNotNone(content['extensions']['values']) - found_osksadm = False - found_oskscatalog = False - found_hpidm = False - for value in content['extensions']['values']: - if value['extension']['alias'] == 'OS-KSADM': - found_osksadm = True - if value['extension']['alias'] == 'OS-KSCATALOG': - found_oskscatalog = True - if value['extension']['alias'] == 'HP-IDM': - found_hpidm = True - self.assertTrue(found_osksadm, "Missing OS-KSADM extension.") - self.assertTrue(found_oskscatalog, "Missing OS-KSCATALOG extension.") - if not common.isSsl() and 'HP-IDM_Disabled' not in os.environ: - self.assertTrue(found_hpidm, "Missing HP-IDM extension.") - - def test_extensions_xml(self): - r = self.admin_request(path='/extensions.xml') - self.assertTrue('xml' in r.getheader('Content-Type')) - content = r.xml - extensions = content.findall( - "{http://docs.openstack.org/common/api/v1.0}extension") - found_osksadm = False - found_oskscatalog = False - found_hpidm = False - for extension in extensions: - if extension.get("alias") == 'OS-KSADM': - found_osksadm = True - if extension.get("alias") == 'OS-KSCATALOG': - found_oskscatalog = True - if extension.get("alias") == 'HP-IDM': - found_hpidm = True - self.assertTrue(found_osksadm, "Missing OS-KSADM extension.") - self.assertTrue(found_oskscatalog, "Missing OS-KSCATALOG extension.") - if not common.isSsl() and 'HP-IDM_Disabled' not in os.environ: - self.assertTrue(found_hpidm, "Missing HP-IDM extension.") - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/client/test_frontends.py b/keystone/test/client/test_frontends.py deleted file mode 100644 index 636f6607..00000000 --- a/keystone/test/client/test_frontends.py +++ /dev/null @@ -1,38 +0,0 @@ -import unittest2 as unittest -from keystone.test.functional import common - - -class LegacyAuthenticationTest(common.FunctionalTestCase): - use_server = True - - def setUp(self, *args, **kwargs): - super(LegacyAuthenticationTest, self).setUp(*args, **kwargs) - - password = common.unique_str() - self.tenant = self.create_tenant().json['tenant'] - self.user = self.create_user(user_password=password, - tenant_id=self.tenant['id']).json['user'] - self.user['password'] = password - - self.services = {} - self.endpoint_templates = {} - for x in range(5): - self.services[x] = self.create_service().json['OS-KSADM:service'] - self.endpoint_templates[x] = self.create_endpoint_template( - name=self.services[x]['name'], \ - type=self.services[x]['type']).\ - json['OS-KSCATALOG:endpointTemplate'] - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_templates[x]['id']) - - def test_authenticate_legacy(self): - r = self.service_request(version='1.0', assert_status=204, headers={ - "X-Auth-User": self.user['name'], - "X-Auth-Key": self.user['password']}) - - self.assertIsNotNone(r.getheader('x-auth-token')) - for service in self.services.values(): - self.assertIsNotNone(r.getheader('x-' + service['name'])) - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/client/test_keystone_manage.py b/keystone/test/client/test_keystone_manage.py deleted file mode 100644 index d60e8a5c..00000000 --- a/keystone/test/client/test_keystone_manage.py +++ /dev/null @@ -1,54 +0,0 @@ -import os -import subprocess -import sys -import unittest2 as unittest - -import keystone.test.client as client_tests -from keystone.test import sampledata -from keystone import manage - -# Calculate root path so ewe call files in bin -possible_topdir = os.path.normpath(os.path.join(os.path.abspath(__file__), - os.pardir, - os.pardir, - os.pardir, - os.pardir)) - - -class TestKeystoneManage(unittest.TestCase): - """ - Tests for the keystone-manage client. - """ - - def test_check_can_call_keystone_manage(self): - """ - Test that we can call keystone-manage - """ - cmd = [ - os.path.join(possible_topdir, 'bin', 'keystone-manage'), - '--help', - ] - process = subprocess.Popen(cmd, stdout=subprocess.PIPE) - result = process.communicate()[0] - self.assertIn('usage', result.lower()) - - def test_keystone_manage_calls(self): - """ - Test that we can call keystone-manage and all sampledata calls work - """ - cmd = [ - os.path.join(possible_topdir, 'bin', 'keystone-manage'), - '-c', client_tests.TEST_CONFIG_FILE_NAME, - '--log-file', os.path.join(possible_topdir, 'bin', 'keystone.log'), - 'service', 'list' - ] - # This will init backends - manage.parse_args(cmd[1:]) - - # Loop through and try sampledata calls - sampledata_calls = sampledata.DEFAULT_FIXTURE - for call in sampledata_calls: - manage.process(*call) - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/client/test_middleware.py b/keystone/test/client/test_middleware.py deleted file mode 100644 index 543e0b70..00000000 --- a/keystone/test/client/test_middleware.py +++ /dev/null @@ -1,124 +0,0 @@ -import unittest2 as unittest -import uuid - -import keystone.common.exception -import keystone.backends.api as db_api -from keystone.test.functional import common -from keystone.test import client as client_tests - -# -# Auth Token -# -from keystone.middleware import auth_token - - -class TestAuthTokenMiddleware(common.MiddlewareTestCase): - """ - Tests for Keystone WSGI middleware: Auth Token - """ - - def setUp(self): - super(TestAuthTokenMiddleware, self).setUp(auth_token) - - -class TestAuthTokenMiddlewareWithNoAdminToken(common.MiddlewareTestCase): - """ - Tests for Keystone WSGI middleware: Auth Token - """ - - def setUp(self): - settings = {'delay_auth_decision': '0', - 'auth_host': client_tests.TEST_TARGET_SERVER_ADMIN_ADDRESS, - 'auth_port': client_tests.TEST_TARGET_SERVER_ADMIN_PORT, - 'auth_protocol': - client_tests.TEST_TARGET_SERVER_ADMIN_PROTOCOL, - 'auth_uri': ('%s://%s:%s/' % \ - (client_tests.TEST_TARGET_SERVER_SERVICE_PROTOCOL, - client_tests.TEST_TARGET_SERVER_SERVICE_ADDRESS, - client_tests.TEST_TARGET_SERVER_SERVICE_PORT)), - 'admin_user': self.admin_username, - 'admin_password': self.admin_password} - super(TestAuthTokenMiddlewareWithNoAdminToken, self).setUp(auth_token, - settings) - -# -# Glance -# -try: - from keystone.middleware import glance_auth_token -except ImportError as e: - print 'Could not load glance_auth_token: %s' % e - - -@unittest.skipUnless('glance_auth_token' in vars(), - "Glance Auth Token not imported") -class TestGlanceMiddleware(common.MiddlewareTestCase): - """ - Tests for Keystone WSGI middleware: Glance - """ - - def setUp(self): - super(TestGlanceMiddleware, self).setUp( - (auth_token, glance_auth_token)) - - -# -# Quantum -# -from keystone.middleware import quantum_auth_token - - -class TestQuantumMiddleware(common.MiddlewareTestCase): - """ - Tests for Keystone WSGI middleware: Glance - """ - - def setUp(self): - access = self.authenticate(self.admin_username, self.admin_password).\ - json['access'] - self.admin_token = access['token']['id'] - settings = {'delay_auth_decision': '0', - 'auth_host': client_tests.TEST_TARGET_SERVER_ADMIN_ADDRESS, - 'auth_port': client_tests.TEST_TARGET_SERVER_ADMIN_PORT, - 'auth_protocol': - client_tests.TEST_TARGET_SERVER_ADMIN_PROTOCOL, - 'auth_uri': ('%s://%s:%s/' % \ - (client_tests.TEST_TARGET_SERVER_SERVICE_PROTOCOL, - client_tests.TEST_TARGET_SERVER_SERVICE_ADDRESS, - client_tests.TEST_TARGET_SERVER_SERVICE_PORT)), - 'auth_version': '2.0', - 'admin_token': self.admin_token, - 'admin_user': self.admin_username, - 'admin_password': self.admin_password} - super(TestQuantumMiddleware, self).setUp(quantum_auth_token, settings) - - -# -# Swift -# -try: - from keystone.middleware import swift_auth -except ImportError as e: - print 'Could not load swift_auth: %s' % e - -#TODO(Ziad): find out how to disable swift logging -#@unittest.skipUnless('swift_auth' in vars(), -# "swift_auth not imported") -#class TestSwiftMiddleware(common.MiddlewareTestCase): -# """ -# Tests for Keystone WSGI middleware: Glance -# """ -# -# def setUp(self): -# settings = {'delay_auth_decision': '0', -# 'auth_host': '127.0.0.1', -# 'auth_port': '35357', -# 'auth_protocol': 'http', -# 'auth_uri': 'http://localhost:35357/', -# 'admin_token': self.admin_token, -# 'set log_facility': 'LOG_NULL'} -# super(TestSwiftMiddleware, self).setUp(swift_auth, settings) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/client/test_request_specs.py b/keystone/test/client/test_request_specs.py deleted file mode 100644 index 39118a07..00000000 --- a/keystone/test/client/test_request_specs.py +++ /dev/null @@ -1,124 +0,0 @@ -import unittest2 as unittest -from keystone.test.functional import common - - -class TestResponseHeaders(common.FunctionalTestCase): - """Tests API's response headers""" - use_server = True - - def test_vary_header_on_error(self): - """A Vary header should be provided to support caching responses.""" - r = self.admin_request(path='/tokens/not-a-valid-token', - assert_status=404) - self.assertIn('X-Auth-Token', r.getheader('Vary')) - - def test_vary_header_on_admin(self): - """A Vary header should be provided to support caching responses.""" - r = self.admin_request(path='/tokens/%s' % self.admin_token) - self.assertIn('X-Auth-Token', r.getheader('Vary')) - - def test_vary_header_on_service(self): - """A Vary header should be provided to support caching responses.""" - r = self.service_request(path='/tenants') - self.assertIn('X-Auth-Token', r.getheader('Vary')) - - def test_vary_header_on_legacy(self): - """A Vary header should be provided to support caching responses.""" - r = self.admin_request('1.1/tenants') - self.assertIn('X-Auth-Token', r.getheader('Vary')) - - def test_vary_header_on_static(self): - """A Vary header should not be provided on a static request.""" - r = self.service_request('2.0/') - self.assertEqual(None, r.getheader('Vary')) - - -class TestUrlHandling(common.FunctionalTestCase): - """Tests API's global URL handling behaviors""" - use_server = True - - def test_optional_trailing_slash(self): - """Same response returned regardless of a trailing slash in the url.""" - r1 = self.service_request(path='/') - r2 = self.service_request(path='') - self.assertEqual(r1.read(), r2.read()) - - -class TestContentTypes(common.FunctionalTestCase): - """Tests API's Content-Type handling""" - use_server = True - - def test_default_content_type(self): - """Service returns JSON without being asked to""" - r = self.service_request() - self.assertTrue('application/json' in r.getheader('Content-Type')) - - def test_xml_extension(self): - """Service responds to .xml URL extension""" - r = self.service_request(path='.xml') - self.assertTrue('application/xml' in r.getheader('Content-Type')) - - def test_json_extension(self): - """Service responds to .json URL extension""" - r = self.service_request(path='.json') - self.assertTrue('application/json' in r.getheader('Content-Type')) - - def test_xml_accept_header(self): - """Service responds to xml Accept header""" - r = self.service_request(headers={'Accept': 'application/xml'}) - self.assertTrue('application/xml' in r.getheader('Content-Type')) - - def test_json_accept_header(self): - """Service responds to json Accept header""" - r = self.service_request(headers={'Accept': 'application/json'}) - self.assertTrue('application/json' in r.getheader('Content-Type')) - - def test_versioned_xml_accept_header(self): - """Service responds to versioned xml Accept header""" - r = self.service_request(headers={ - 'Accept': 'application/vnd.openstack.identity-v2.0+xml'}) - self.assertTrue('application/xml' in r.getheader('Content-Type')) - - def test_versioned_json_accept_header(self): - """Service responds to versioned json Accept header""" - r = self.service_request(headers={ - 'Accept': 'application/vnd.openstack.identity-v2.0+json'}) - self.assertTrue('application/json' in r.getheader('Content-Type')) - - def test_xml_extension_overrides_conflicting_header(self): - """Service returns XML when Accept header conflicts with extension""" - r = self.service_request(path='.xml', - headers={'Accept': 'application/json'}) - - self.assertTrue('application/xml' in r.getheader('Content-Type')) - - def test_json_extension_overrides_conflicting_header(self): - """Service returns JSON when Accept header conflicts with extension""" - r = self.service_request(path='.json', - headers={'Accept': 'application/xml'}) - - self.assertTrue('application/json' in r.getheader('Content-Type')) - - def test_xml_content_type_on_404(self): - """Content-Type should be honored even on 404 errors (Issue #13)""" - r = self.service_request(path='/completely-invalid-path', - headers={'Accept': 'application/xml'}, - assert_status=404) - - # Commenting this assertion out, as it currently fails - self.assertTrue('application/xml' in r.getheader('Content-Type'), - 'application/xml not in %s' % r.getheader('Content-Type')) - - def test_json_content_type_on_404(self): - """Content-Type should be honored even on 404 errors (Issue #13)""" - r = self.service_request(path='/completely-invalid-path', - headers={'Accept': 'application/json'}, - assert_status=404) - - # Commenting this assertion out, as it currently fails - self.assertTrue('application/json' in r.getheader('Content-Type'), - 'application/json not in %s' % r.getheader('Content-Type')) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/client/test_static_files.py b/keystone/test/client/test_static_files.py deleted file mode 100644 index df3bc620..00000000 --- a/keystone/test/client/test_static_files.py +++ /dev/null @@ -1,94 +0,0 @@ -import unittest2 as unittest -from keystone.test.functional import common - - -class TestStaticFiles(common.ApiTestCase): - use_server = True - - def test_pdf_contract(self): - if not common.isSsl(): - #TODO(ziad): Caller hangs in SSL (but works with cURL) - r = self.service_request(path='/identitydevguide.pdf') - self.assertTrue('pdf' in r.getheader('Content-Type')) - - def test_wadl_contract(self): - r = self.service_request(path='/identity.wadl') - self.assertTrue('xml' in r.getheader('Content-Type')) - - def test_wadl_common(self): - r = self.service_request(path='/common.ent') - self.assertTrue('xml' in r.getheader('Content-Type')) - - def test_xsd_contract(self): - r = self.service_request(path='/xsd/api.xsd') - self.assertTrue('xml' in r.getheader('Content-Type')) - - def test_xsd_atom_contract(self): - r = self.service_request(path='/xsd/atom/atom.xsd') - self.assertTrue('xml' in r.getheader('Content-Type')) - - def test_xslt(self): - r = self.service_request(path='/xslt/schema.xslt') - self.assertTrue('xml' in r.getheader('Content-Type')) - - def test_js(self): - r = self.service_request(path='/js/shjs/sh_java.js') - self.assertTrue('javascript' in r.getheader('Content-Type')) - - def test_xml_sample(self): - r = self.service_request(path='/samples/auth.xml') - self.assertTrue('xml' in r.getheader('Content-Type')) - - def test_json_sample(self): - r = self.service_request(path='/samples/auth.json') - self.assertTrue('json' in r.getheader('Content-Type')) - - def test_stylesheet(self): - r = self.service_request(path='/style/shjs/sh_acid.css') - self.assertTrue('css' in r.getheader('Content-Type')) - - -class TestAdminStaticFiles(common.FunctionalTestCase): - use_server = True - - def test_pdf_contract(self): - if not common.isSsl(): - #TODO(ziad): Caller hangs in SSL (but works with cURL) - r = self.admin_request(path='/identityadminguide.pdf') - self.assertTrue('pdf' in r.getheader('Content-Type')) - - def test_wadl_contract(self): - r = self.admin_request(path='/identity-admin.wadl') - self.assertTrue('xml' in r.getheader('Content-Type')) - - def test_xsd_contract(self): - r = self.admin_request(path='/xsd/api.xsd') - self.assertTrue('xml' in r.getheader('Content-Type')) - - def test_xsd_atom_contract(self): - r = self.admin_request(path='/xsd/atom/atom.xsd') - self.assertTrue('xml' in r.getheader('Content-Type')) - - def test_xslt(self): - r = self.admin_request(path='/xslt/schema.xslt') - self.assertTrue('xml' in r.getheader('Content-Type')) - - def test_js(self): - r = self.admin_request(path='/js/shjs/sh_java.js') - self.assertTrue('javascript' in r.getheader('Content-Type')) - - def test_xml_sample(self): - r = self.admin_request(path='/samples/auth.xml') - self.assertTrue('xml' in r.getheader('Content-Type')) - - def test_json_sample(self): - r = self.admin_request(path='/samples/auth.json') - self.assertTrue('json' in r.getheader('Content-Type')) - - def test_stylesheet(self): - r = self.admin_request(path='/style/shjs/sh_acid.css') - self.assertTrue('css' in r.getheader('Content-Type')) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/etc/ldap.conf.template b/keystone/test/etc/ldap.conf.template deleted file mode 100644 index 385afc93..00000000 --- a/keystone/test/etc/ldap.conf.template +++ /dev/null @@ -1,60 +0,0 @@ -[DEFAULT] -verbose = False -debug = False -default_store = sqlite -log_file = %(test_dir)s/keystone.log -log_dir = %(test_dir)s -backends = keystone.backends.sqlalchemy,keystone.backends.ldap -extensions= osksadm, oskscatalog, hpidm -service-header-mappings = { - 'nova' : 'X-Server-Management-Url', - 'swift' : 'X-Storage-Url', - 'cdn' : 'X-CDN-Management-Url'} -service_host = 0.0.0.0 -service_port = %(service_port)s -service_ssl = False -admin_host = 0.0.0.0 -admin_port = %(admin_port)s -admin_ssl = False -keystone-admin-role = Admin -keystone-service-admin-role = KeystoneServiceAdmin -hash-password = True - -[keystone.backends.sqlalchemy] -sql_connection = sqlite:// -sql_idle_timeout = 30 -backend_entities = ['Endpoints', 'Credentials', 'EndpointTemplates', 'Token', 'Service'] - -[keystone.backends.ldap] -ldap_url = fake://memory -ldap_user = cn=Admin -ldap_password = password -backend_entities = ['Tenant', 'User', 'UserRoleAssociation', 'Role'] - -[pipeline:admin] -pipeline = - urlnormalizer - d5_compat - admin_api - -[pipeline:keystone-legacy-auth] -pipeline = - urlnormalizer - legacy_auth - d5_compat - service_api - -[app:service_api] -paste.app_factory = keystone.server:service_app_factory - -[app:admin_api] -paste.app_factory = keystone.server:admin_app_factory - -[filter:urlnormalizer] -paste.filter_factory = keystone.frontends.normalizer:filter_factory - -[filter:d5_compat] -paste.filter_factory = keystone.frontends.d5_compat:filter_factory - -[filter:legacy_auth] -paste.filter_factory = keystone.frontends.legacy_token_auth:filter_factory diff --git a/keystone/test/etc/memcache.conf.template b/keystone/test/etc/memcache.conf.template deleted file mode 100644 index 6cb434ac..00000000 --- a/keystone/test/etc/memcache.conf.template +++ /dev/null @@ -1,58 +0,0 @@ -[DEFAULT] -verbose = False -debug = False -default_store = sqlite -log_file = %(test_dir)s/keystone.log -log_dir = %(test_dir)s -backends = keystone.backends.sqlalchemy,keystone.backends.memcache -extensions= osksadm, oskscatalog, hpidm -service-header-mappings = { - 'nova' : 'X-Server-Management-Url', - 'swift' : 'X-Storage-Url', - 'cdn' : 'X-CDN-Management-Url'} -service_host = 0.0.0.0 -service_port = %(service_port)s -service_ssl = False -admin_host = 0.0.0.0 -admin_port = %(admin_port)s -admin_ssl = False -keystone-admin-role = Admin -keystone-service-admin-role = KeystoneServiceAdmin - -[keystone.backends.sqlalchemy] -sql_connection = sqlite:// -sql_idle_timeout = 30 -backend_entities = ['Endpoints', 'Credentials', 'EndpointTemplates', 'Tenant', 'User', 'UserRoleAssociation', 'Role', 'Service'] - -[keystone.backends.memcache] -memcache_hosts = 127.0.0.1:11211 -backend_entities = ['Token'] -cache_time = 86400 - -[pipeline:admin] -pipeline = - urlnormalizer - d5_compat - admin_api - -[pipeline:keystone-legacy-auth] -pipeline = - urlnormalizer - legacy_auth - d5_compat - service_api - -[app:service_api] -paste.app_factory = keystone.server:service_app_factory - -[app:admin_api] -paste.app_factory = keystone.server:admin_app_factory - -[filter:urlnormalizer] -paste.filter_factory = keystone.frontends.normalizer:filter_factory - -[filter:d5_compat] -paste.filter_factory = keystone.frontends.d5_compat:filter_factory - -[filter:legacy_auth] -paste.filter_factory = keystone.frontends.legacy_token_auth:filter_factory diff --git a/keystone/test/etc/sql.conf.template b/keystone/test/etc/sql.conf.template deleted file mode 100644 index c8b7b4b0..00000000 --- a/keystone/test/etc/sql.conf.template +++ /dev/null @@ -1,54 +0,0 @@ -[DEFAULT] -verbose = False -debug = False -default_store = sqlite -log_file = %(test_dir)s/keystone.log -log_dir = %(test_dir)s -backends = keystone.backends.sqlalchemy -extensions= osksadm, oskscatalog, hpidm -service-header-mappings = { - 'nova' : 'X-Server-Management-Url', - 'swift' : 'X-Storage-Url', - 'cdn' : 'X-CDN-Management-Url'} -service_host = 0.0.0.0 -service_port = %(service_port)s -service_ssl = False -admin_host = 0.0.0.0 -admin_port = %(admin_port)s -admin_ssl = False -keystone-admin-role = Admin -keystone-service-admin-role = KeystoneServiceAdmin -hash-password = True - -[keystone.backends.sqlalchemy] -sql_connection = sqlite:// -sql_idle_timeout = 30 -backend_entities = ['Endpoints', 'Credentials', 'EndpointTemplates', 'Tenant', 'User', 'UserRoleAssociation', 'Role', 'Token', 'Service'] - -[pipeline:admin] -pipeline = - urlnormalizer - d5_compat - admin_api - -[pipeline:keystone-legacy-auth] -pipeline = - urlnormalizer - legacy_auth - d5_compat - service_api - -[app:service_api] -paste.app_factory = keystone.server:service_app_factory - -[app:admin_api] -paste.app_factory = keystone.server:admin_app_factory - -[filter:urlnormalizer] -paste.filter_factory = keystone.frontends.normalizer:filter_factory - -[filter:d5_compat] -paste.filter_factory = keystone.frontends.d5_compat:filter_factory - -[filter:legacy_auth] -paste.filter_factory = keystone.frontends.legacy_token_auth:filter_factory diff --git a/keystone/test/etc/sql_no_hpidm.conf.template b/keystone/test/etc/sql_no_hpidm.conf.template deleted file mode 100644 index 2f5cefa4..00000000 --- a/keystone/test/etc/sql_no_hpidm.conf.template +++ /dev/null @@ -1,54 +0,0 @@ -[DEFAULT] -verbose = False -debug = False -default_store = sqlite -log_file = %(test_dir)s/keystone.log -log_dir = %(test_dir)s -backends = keystone.backends.sqlalchemy -extensions= osksadm, oskscatalog -service-header-mappings = { - 'nova' : 'X-Server-Management-Url', - 'swift' : 'X-Storage-Url', - 'cdn' : 'X-CDN-Management-Url'} -service_host = 0.0.0.0 -service_port = %(service_port)s -service_ssl = False -admin_host = 0.0.0.0 -admin_port = %(admin_port)s -admin_ssl = False -keystone-admin-role = Admin -keystone-service-admin-role = KeystoneServiceAdmin -hash-password = True - -[keystone.backends.sqlalchemy] -sql_connection = sqlite:// -sql_idle_timeout = 30 -backend_entities = ['Endpoints', 'Credentials', 'EndpointTemplates', 'Tenant', 'User', 'UserRoleAssociation', 'Role', 'Token', 'Service'] - -[pipeline:admin] -pipeline = - urlnormalizer - d5_compat - admin_api - -[pipeline:keystone-legacy-auth] -pipeline = - urlnormalizer - legacy_auth - d5_compat - service_api - -[app:service_api] -paste.app_factory = keystone.server:service_app_factory - -[app:admin_api] -paste.app_factory = keystone.server:admin_app_factory - -[filter:urlnormalizer] -paste.filter_factory = keystone.frontends.normalizer:filter_factory - -[filter:d5_compat] -paste.filter_factory = keystone.frontends.d5_compat:filter_factory - -[filter:legacy_auth] -paste.filter_factory = keystone.frontends.legacy_token_auth:filter_factory diff --git a/keystone/test/etc/ssl.conf.template b/keystone/test/etc/ssl.conf.template deleted file mode 100644 index 1889c7d6..00000000 --- a/keystone/test/etc/ssl.conf.template +++ /dev/null @@ -1,58 +0,0 @@ -[DEFAULT] -verbose = False -debug = False -default_store = sqlite -log_file = %(test_dir)s/keystone.log -log_dir = %(test_dir)s -backends = keystone.backends.sqlalchemy -extensions= osksadm, oskscatalog, hpidm -service-header-mappings = { - 'nova' : 'X-Server-Management-Url', - 'swift' : 'X-Storage-Url', - 'cdn' : 'X-CDN-Management-Url'} -service_host = 0.0.0.0 -service_port = %(service_port)s -service_ssl = True -admin_host = 0.0.0.0 -admin_port = %(admin_port)s -admin_ssl = True -keystone-admin-role = Admin -keystone-service-admin-role = KeystoneServiceAdmin -hash-password = True -certfile = %(base_dir)s/examples/ssl/certs/keystone.pem -keyfile = %(base_dir)s/examples/ssl/private/keystonekey.pem -ca_certs = %(base_dir)s/examples/ssl/certs/ca.pem -cert_required = True - -[keystone.backends.sqlalchemy] -sql_connection = sqlite:// -sql_idle_timeout = 30 -backend_entities = ['Endpoints', 'Credentials', 'EndpointTemplates', 'Tenant', 'User', 'UserRoleAssociation', 'Role', 'Token', 'Service'] - -[pipeline:admin] -pipeline = - urlnormalizer - d5_compat - admin_api - -[pipeline:keystone-legacy-auth] -pipeline = - urlnormalizer - legacy_auth - d5_compat - service_api - -[app:service_api] -paste.app_factory = keystone.server:service_app_factory - -[app:admin_api] -paste.app_factory = keystone.server:admin_app_factory - -[filter:urlnormalizer] -paste.filter_factory = keystone.frontends.normalizer:filter_factory - -[filter:d5_compat] -paste.filter_factory = keystone.frontends.d5_compat:filter_factory - -[filter:legacy_auth] -paste.filter_factory = keystone.frontends.legacy_token_auth:filter_factory diff --git a/keystone/test/functional/__init__.py b/keystone/test/functional/__init__.py deleted file mode 100644 index df704bbe..00000000 --- a/keystone/test/functional/__init__.py +++ /dev/null @@ -1,25 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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. - -""" Functional Tests - -Functional tests are tests that run calls through the core logic modues of -Keystone through to the backends. The backend components will eventually be -tested separately, but have not been stubbed out yet. - -They do not test http(s) calls or run a server on a TCP/IP port. -""" diff --git a/keystone/test/functional/common.py b/keystone/test/functional/common.py deleted file mode 100644 index 80a10819..00000000 --- a/keystone/test/functional/common.py +++ /dev/null @@ -1,1738 +0,0 @@ -import datetime -import httplib -import json -import logging -import os -import random -import unittest2 as unittest -import uuid -from webob import Request, Response -from xml.etree import ElementTree - -from keystone import server -import keystone.backends.api as db_api -from keystone.test import client as client_tests -from keystone import utils - -logger = logging.getLogger(__name__) - - -def isSsl(): - """ See if we are testing with SSL. If cert is non-empty, we are! """ - if 'cert_file' in os.environ: - return os.environ['cert_file'] - return None - - -class HttpTestCase(unittest.TestCase): - """Performs generic HTTP request testing. - - Defines a ``request`` method for use in test cases that makes - HTTP requests, and two new asserts: - - * assertResponseSuccessful - * assertResponseStatus - """ - - def request(self, host='127.0.0.1', protocol='http', port=80, method='GET', - path='/', headers=None, body=None, assert_status=None): - """Perform request and fetch httplib.HTTPResponse from the server""" - - # Initialize headers dictionary - headers = {} if not headers else headers - - logger.debug("Connecting to %s://%s:%s", protocol, host, port) - if protocol == 'https': - cert_file = isSsl() - connection = httplib.HTTPSConnection(host, port, - cert_file=cert_file, - timeout=20) - else: - connection = httplib.HTTPConnection(host, port, timeout=20) - - # Perform the request - connection.request(method, path, body, headers) - - # Retrieve the response so can go ahead and close the connection - response = connection.getresponse() - logger.debug("%s %s returned %s", method, path, response.status) - - response.body = response.read() - if response.status != httplib.OK: - logger.debug("Response Body:") - for line in response.body.split("\n"): - logger.debug(line) - - # Close the connection - connection.close() - - # Automatically assert HTTP status code - if assert_status: - self.assertResponseStatus(response, assert_status) - else: - self.assertResponseSuccessful(response) - - # Contains the response headers, body, etc - return response - - def assertResponseSuccessful(self, response): - """Asserts that a status code lies inside the 2xx range - - :param response: :py:class:`httplib.HTTPResponse` to be - verified to have a status code between 200 and 299. - - example:: - - >>> self.assertResponseSuccessful(response, 203) - """ - self.assertTrue(response.status >= 200 and response.status <= 299, - 'Status code %d is outside of the expected range (2xx)\n\n%s' % - (response.status, response.body)) - - def assertResponseStatus(self, response, assert_status): - """Asserts a specific status code on the response - - :param response: :py:class:`httplib.HTTPResponse` - :param assert_status: The specific ``status`` result expected - - example:: - - >>> self.assertResponseStatus(response, 203) - """ - self.assertEqual(response.status, assert_status, - 'Status code %s is not %s, as expected)\n\n%s' % - (response.status, assert_status, response.body)) - - -class RestfulTestCase(HttpTestCase): - """Performs restful HTTP request testing""" - - def restful_request(self, headers=None, as_json=None, as_xml=None, - **kwargs): - """Encodes and decodes (JSON & XML) HTTP requests and responses. - - Dynamically encodes json or xml as request body if one is provided. - - .. WARNING:: - - * Existing Content-Type header will be overwritten. - * If both as_json and as_xml are provided, as_xml is ignored. - * If either as_json or as_xml AND a body is provided, the body - is ignored. - - Dynamically returns 'as_json' or 'as_xml' attribute based on the - detected response type, and fails the current test case if - unsuccessful. - - response.as_json: standard python dictionary - - response.as_xml: as_etree.ElementTree - """ - - # Initialize headers dictionary - headers = {} if not headers else headers - - # Attempt to encode JSON and XML automatically, if requested - if as_json: - body = RestfulTestCase._encode_json(as_json) - headers['Content-Type'] = 'application/json' - elif as_xml: - body = as_xml - headers['Content-Type'] = 'application/xml' - - # Assume the client wants xml back if it didn't specify - if 'Accept' not in headers: - headers['Accept'] = 'application/xml' - elif 'body' in kwargs: - body = kwargs.pop('body') - else: - body = None - - # Perform the HTTP request/response - response = self.request(headers=headers, body=body, **kwargs) - - # Attempt to parse JSON and XML automatically, if detected - response = self._decode_response_body(response) - - # Contains the decoded response as_json/as_xml, etc - return response - - @staticmethod - def _encode_json(data): - """Returns a JSON-encoded string of the given python dictionary - - :param data: python object to be encoded into JSON - :returns: string of JSON encoded data - """ - return json.dumps(data) - - def _decode_response_body(self, response): - """Detects response body type, and attempts to decode it - - :param response: :py:class:`httplib.HTTPResponse` - :returns: response object with additions: - - If context type is application/json, the response will have an - additional attribute ``json`` that will have the decoded JSON - result (typically a dict) - - If context type is application/xml, the response will have an - additional attribute ``xml`` that will have the an ElementTree - result. - """ - if response.body is not None and response.body.strip(): - if 'application/json' in response.getheader('Content-Type', ''): - response.json = self._decode_json(response.body) - elif 'application/xml' in response.getheader('Content-Type', ''): - response.xml = self._decode_xml(response.body) - return response - - @staticmethod - def _decode_json(json_str): - """Returns a dict of the given JSON string""" - return json.loads(json_str) - - @staticmethod - def _decode_xml(xml_str): - """Returns an ElementTree of the given XML string""" - return ElementTree.XML(xml_str) - - -class ApiTestCase(RestfulTestCase): - """Abstracts REST verbs & resources of the service & admin API.""" - use_server = False - - admin_role_name = 'Admin' - service_admin_role_name = 'KeystoneServiceAdmin' - member_role_name = 'Member' - - # Same as KeystoneTest settings - admin_username = 'admin' - admin_password = 'secrete' - - service_token = None - admin_token = None - - service_api = None - admin_api = None - - """ - Dict of configuration options to pass to the API controller - """ - options = { - 'backends': "keystone.backends.sqlalchemy", - 'keystone.backends.sqlalchemy': { - # in-memory db - 'sql_connection': 'sqlite://', - 'verbose': False, - 'debug': False, - 'backend_entities': - "['UserRoleAssociation', 'Endpoints', 'Role', 'Tenant', " - "'Tenant', 'User', 'Credentials', 'EndpointTemplates', " - "'Token', 'Service']", - }, - 'extensions': 'osksadm, oskscatalog, hpidm', - 'keystone-admin-role': 'Admin', - 'keystone-service-admin-role': 'KeystoneServiceAdmin', - 'hash-password': 'True', - } - # Populate the CONF module with these values - utils.set_configuration(options) - - def fixture_create_role(self, **kwargs): - """ - Creates a role fixture. - - :params \*\*kwargs: Attributes of the role to create - """ - values = kwargs.copy() - role = db_api.ROLE.create(values) - logger.debug("Created role fixture %s (id=%s)", role.name, role.id) - return role - - def fixture_create_token(self, **kwargs): - """ - Creates a token fixture. - - :params \*\*kwargs: Attributes of the token to create - """ - values = kwargs.copy() - token = db_api.TOKEN.create(values) - logger.debug("Created token fixture %s", token.id) - return token - - def fixture_create_tenant(self, **kwargs): - """ - Creates a tenant fixture. - - :params \*\*kwargs: Attributes of the tenant to create - """ - values = kwargs.copy() - tenant = db_api.TENANT.create(values) - logger.debug("Created tenant fixture %s (id=%s)", tenant.name, - tenant.id) - return tenant - - def fixture_create_user(self, **kwargs): - """ - Creates a user fixture. If the user's tenant ID is set, and the tenant - does not exist in the database, the tenant is created. - - :params \*\*kwargs: Attributes of the user to create - """ - values = kwargs.copy() - tenant_name = values.get('tenant_name') - if tenant_name: - if not db_api.TENANT.get_by_name(tenant_name): - tenant = db_api.TENANT.create({'name': tenant_name, - 'enabled': True, - 'desc': tenant_name}) - values['tenant_id'] = tenant.id - user = db_api.USER.create(values) - logger.debug("Created user fixture %s (id=%s)", user.name, user.id) - return user - - def fixture_create_service(self, service_name=None, service_type=None, - service_description=None, **kwargs): - """ - Creates a service fixture. - - :params \*\*kwargs: Additional attributes of the service to create - """ - values = kwargs.copy() - service_name = optional_str(service_name) - if service_type is None: - service_type = ['compute', 'identity', 'image-service', - 'object-store', 'ext:extension-service' - ][random.randrange(5)] - service_description = optional_str(service_description) - values["name"] = service_name - values["type"] = service_type - values["description"] = service_description - - service = db_api.SERVICE.create(values) - logger.debug("Created service fixture %s (id=%s)", service.name, - service.id) - return service - - def setUp(self): - super(ApiTestCase, self).setUp() - if self.use_server: - return - - self.service_api = server.ServiceApi() - self.admin_api = server.AdminApi() - - # ADMIN ROLE - self.admin_role = self.fixture_create_role( - name=self.admin_role_name) - - # ADMIN - password = unique_str() - self.admin_user = self.fixture_create_user( - name="admin-user-%s" % uuid.uuid4().hex, enabled=True, - password=password) - self.admin_user['password'] = password - self.admin_password = password - self.admin_username = self.admin_user['name'] - - obj = {} - obj['role_id'] = self.admin_role['id'] - obj['user_id'] = self.admin_user['id'] - obj['tenant_id'] = None - result = db_api.USER.user_role_add(obj) - logger.debug("Created grant fixture %s", result.id) - - # SERVICE ADMIN ROLE - self.service_admin_role = self.fixture_create_role( - name=self.service_admin_role_name) - - # MEMBER ROLE - self.member_role = self.fixture_create_role( - name='Member') - - def tearDown(self): - super(ApiTestCase, self).tearDown() - # Explicitly release these to limit memory use. - self.service_api = self.admin_api = self.admin_role = None - self.admin_user = self.admin_password = self.admin_username = None - self.service_admin_role = self.member_role = None - - def request(self, host='127.0.0.1', protocol='http', port=80, method='GET', - path='/', headers=None, body=None, assert_status=None, - server=None): - """Overrides HttpTestCase and uses local calls""" - if self.use_server: - # Call a real server (bypass the override) - return super(ApiTestCase, self).request(host=host, port=port, - protocol=protocol, method=method, - path=path, headers=headers, body=body, - assert_status=assert_status) - - req = Request.blank(path) - req.method = method - req.headers = headers - if isinstance(body, unicode): - req.body = body.encode('utf-8') - else: - req.body = body - - res = req.get_response(server) - logger.debug("%s %s returned %s", req.method, req.path_qs, - res.status) - if res.status_int != httplib.OK: - logger.debug("Response Body:") - for line in res.body.split("\n"): - logger.debug(line) - - # Automatically assert HTTP status code - if assert_status: - self.assertEqual(res.status_int, assert_status, - 'Status code %s is not %s, as expected)\n\n%s' % - (res.status_int, assert_status, res.body)) - else: - self.assertTrue(299 >= res.status_int >= 200, - 'Status code %d is outside of the expected range (2xx)\n\n%s' % - (res.status_int, res.body)) - - # Contains the response headers, body, etc - return res - - def _decode_response_body(self, response): - """Override to support webob.Response. - """ - if self.use_server: - # Call a real server (bypass the override) - return super(ApiTestCase, self)._decode_response_body(response) - - if response.body is not None and response.body.strip(): - if 'application/json' in response.content_type: - response.json = self._decode_json(response.body) - elif 'application/xml' in response.content_type: - response.xml = self._decode_xml(response.body) - return response - - def assertResponseSuccessful(self, response): - """Asserts that a status code lies inside the 2xx range - - :param response: :py:class:`webob.Response` to be - verified to have a status code between 200 and 299. - - example:: - - >>> self.assertResponseSuccessful(response, 203) - """ - if self.use_server: - # Call a real server (bypass the override) - return super(ApiTestCase, self).assertResponseSuccessful(response) - - self.assertTrue(response.status_int >= 200 and - response.status_int <= 299, - 'Status code %d is outside of the expected range (2xx)\n\n%s' % - (response.status_int, response.body)) - - def assertResponseStatus(self, response, assert_status): - """Asserts a specific status code on the response - - :param response: :py:class:`webob.Response` - :param assert_status: The specific ``status`` result expected - - example:: - - >>> self.assertResponseStatus(response, 203) - """ - if self.use_server: - # Call a real server (bypass the override) - return super(ApiTestCase, self).assertResponseStatus(response, - assert_status) - - self.assertEqual(response.status_int, assert_status, - 'Status code %s is not %s, as expected)\n\n%s' % - (response.status_int, assert_status, response.body)) - - def service_request(self, version='2.0', path='', port=None, headers=None, - host=None, protocol=None, **kwargs): - """Returns a request to the service API""" - - # Initialize headers dictionary - headers = {} if not headers else headers - - if self.use_server: - path = ApiTestCase._version_path(version, path) - if port is None: - port = client_tests.TEST_TARGET_SERVER_SERVICE_PORT or 5000 - if host is None: - host = (client_tests.TEST_TARGET_SERVER_SERVICE_ADDRESS - or '127.0.0.1') - if protocol is None: - protocol = (client_tests.TEST_TARGET_SERVER_SERVICE_PROTOCOL - or 'http') - - if 'use_token' in kwargs: - headers['X-Auth-Token'] = kwargs.pop('use_token') - elif self.service_token: - headers['X-Auth-Token'] = self.service_token - elif self.admin_token: - headers['X-Auth-Token'] = self.admin_token - - return self.restful_request(host=host, protocol=protocol, port=port, - path=path, headers=headers, server=self.service_api, **kwargs) - - def admin_request(self, version='2.0', path='', port=None, headers=None, - host=None, protocol=None, **kwargs): - """Returns a request to the admin API""" - - # Initialize headers dictionary - headers = {} if not headers else headers - - if self.use_server: - path = ApiTestCase._version_path(version, path) - if port is None: - port = client_tests.TEST_TARGET_SERVER_ADMIN_PORT or 35357 - if host is None: - host = (client_tests.TEST_TARGET_SERVER_ADMIN_ADDRESS - or '127.0.0.1') - if protocol is None: - protocol = (client_tests.TEST_TARGET_SERVER_ADMIN_PROTOCOL - or 'http') - - if 'use_token' in kwargs: - headers['X-Auth-Token'] = kwargs.pop('use_token') - elif self.admin_token: - headers['X-Auth-Token'] = self.admin_token - - return self.restful_request(host=host, protocol=protocol, port=port, - path=path, headers=headers, server=self.admin_api, **kwargs) - - @staticmethod - def _version_path(version, path): - """Prepend the given path with the API version. - - An empty version results in no version being prepended.""" - if version: - return '/v' + str(version) + str(path) - else: - return str(path) - - def post_token(self, **kwargs): - """POST /tokens""" - #Setting service call as the default behavior.""" - if 'request_type' in kwargs and \ - kwargs.pop('request_type') == 'admin': - return self.admin_request(method='POST', - path='/tokens', **kwargs) - else: - return self.service_request(method='POST', - path='/tokens', **kwargs) - - def get_token(self, token_id, **kwargs): - """GET /tokens/{token_id}""" - return self.admin_request(method='GET', - path='/tokens/%s' % (token_id,), **kwargs) - - def get_token_belongsto(self, token_id, tenant_id, **kwargs): - """GET /tokens/{token_id}?belongsTo={tenant_id}""" - return self.admin_request(method='GET', - path='/tokens/%s?belongsTo=%s' % (token_id, tenant_id), **kwargs) - - def check_token(self, token_id, **kwargs): - """HEAD /tokens/{token_id}""" - return self.admin_request(method='HEAD', - path='/tokens/%s' % (token_id,), **kwargs) - - def check_token_belongs_to(self, token_id, tenant_id, **kwargs): - """HEAD /tokens/{token_id}?belongsTo={tenant_id}""" - return self.admin_request(method='HEAD', - path='/tokens/%s?belongsTo=%s' % (token_id, tenant_id), **kwargs) - - def delete_token(self, token_id, **kwargs): - """DELETE /tokens/{token_id}""" - return self.admin_request(method='DELETE', - path='/tokens/%s' % (token_id,), **kwargs) - - def post_tenant(self, **kwargs): - """POST /tenants""" - return self.admin_request(method='POST', path='/tenants', **kwargs) - - def get_tenants(self, **kwargs): - """GET /tenants""" - if 'request_type' in kwargs and \ - kwargs.pop('request_type') == 'service': - return self.service_request(method='GET', - path='/tenants', **kwargs) - else: - return self.admin_request(method='GET', path='/tenants', **kwargs) - - def get_tenant(self, tenant_id, **kwargs): - """GET /tenants/{tenant_id}""" - return self.admin_request(method='GET', - path='/tenants/%s' % (tenant_id,), **kwargs) - - def get_tenant_by_name(self, tenant_name, **kwargs): - """GET /tenants?name=tenant_name""" - return self.admin_request(method='GET', - path='/tenants?name=%s' % (tenant_name,), **kwargs) - - def post_tenant_for_update(self, tenant_id, **kwargs): - """GET /tenants/{tenant_id}""" - return self.admin_request(method='POST', - path='/tenants/%s' % (tenant_id,), **kwargs) - - def get_tenant_users(self, tenant_id, **kwargs): - """GET /tenants/{tenant_id}/users""" - return self.admin_request(method='GET', - path='/tenants/%s/users' % (tenant_id,), **kwargs) - - def get_tenant_users_by_role(self, tenant_id, role_id, **kwargs): - """GET /tenants/{tenant_id}/users?roleId={roleId}""" - return self.admin_request(method='GET', - path='/tenants/%s/users?roleId=%s' % (\ - tenant_id, role_id), **kwargs) - - def delete_tenant(self, tenant_id, **kwargs): - """DELETE /tenants/{tenant_id}""" - return self.admin_request(method='DELETE', - path='/tenants/%s' % (tenant_id,), **kwargs) - - def post_user(self, **kwargs): - """POST /users""" - return self.admin_request(method='POST', path='/users', **kwargs) - - def get_users(self, **kwargs): - """GET /users""" - return self.admin_request(method='GET', path='/users', **kwargs) - - def get_user(self, user_id, **kwargs): - """GET /users/{user_id}""" - return self.admin_request(method='GET', - path='/users/%s' % (user_id,), **kwargs) - - def query_user(self, user_name, **kwargs): - """GET /users?name={user_name}""" - return self.admin_request(method='GET', - path='/users?name=%s' % (user_name,), **kwargs) - - def post_user_for_update(self, user_id, **kwargs): - """POST /users/{user_id}""" - return self.admin_request(method='POST', - path='/users/%s' % (user_id,), **kwargs) - - def put_user_password(self, user_id, **kwargs): - """PUT /users/{user_id}/OS-KSADM/password""" - return self.admin_request(method='PUT', - path='/users/%s/OS-KSADM/password' % (user_id,), **kwargs) - - def put_user_tenant(self, user_id, **kwargs): - """PUT /users/{user_id}/OS-KSADM/tenant""" - return self.admin_request(method='PUT', - path='/users/%s/OS-KSADM/tenant' % (user_id,), **kwargs) - - def put_user_enabled(self, user_id, **kwargs): - """PUT /users/{user_id}/OS-KSADM/enabled""" - return self.admin_request(method='PUT', - path='/users/%s/OS-KSADM/enabled' % (user_id,), **kwargs) - - def delete_user(self, user_id, **kwargs): - """DELETE /users/{user_id}""" - return self.admin_request(method='DELETE', - path='/users/%s' % (user_id,), **kwargs) - - def get_user_roles(self, user_id, **kwargs): - """GET /users/{user_id}/roles""" - return self.admin_request(method='GET', - path='/users/%s/roles' % (user_id,), **kwargs) - - def put_user_role(self, user_id, role_id, tenant_id, **kwargs): - if tenant_id is None: - # PUT /users/{user_id}/roles/OS-KSADM/{role_id} - return self.admin_request(method='PUT', - path='/users/%s/roles/OS-KSADM/%s' % - (user_id, role_id), **kwargs) - else: - # PUT /tenants/{tenant_id}/users/{user_id}/ - # roles/OS-KSADM/{role_id} - return self.admin_request(method='PUT', - path='/tenants/%s/users/%s/roles/OS-KSADM/%s' % (tenant_id, - user_id, role_id,), **kwargs) - - def delete_user_role(self, user_id, role_id, tenant_id, **kwargs): - """DELETE /users/{user_id}/roles/{role_id}""" - if tenant_id is None: - return self.admin_request(method='DELETE', - path='/users/%s/roles/OS-KSADM/%s' - % (user_id, role_id), **kwargs) - else: - return self.admin_request(method='DELETE', - path='/tenants/%s/users/%s/roles/OS-KSADM/%s' % - (tenant_id, user_id, role_id), **kwargs) - - def post_role(self, **kwargs): - """POST /roles""" - return self.admin_request(method='POST', - path='/OS-KSADM/roles', **kwargs) - - def get_roles(self, **kwargs): - """GET /OS-KSADM/roles""" - return self.admin_request(method='GET', - path='/OS-KSADM/roles', **kwargs) - - def get_roles_by_service(self, service_id, **kwargs): - """GET /OS-KSADM/roles""" - return self.admin_request(method='GET', path=( - '/OS-KSADM/roles?serviceId=%s') - % (service_id), - **kwargs) - - def get_role(self, role_id, **kwargs): - """GET /roles/{role_id}""" - return self.admin_request(method='GET', - path='/OS-KSADM/roles/%s' % (role_id,), **kwargs) - - def get_role_by_name(self, role_name, **kwargs): - """GET /roles?name={role_name}""" - return self.admin_request(method='GET', - path='/OS-KSADM/roles?name=%s' % (role_name,), **kwargs) - - def delete_role(self, role_id, **kwargs): - """DELETE /roles/{role_id}""" - return self.admin_request(method='DELETE', - path='/OS-KSADM/roles/%s' % (role_id,), **kwargs) - - def get_endpoint_templates(self, **kwargs): - """GET /OS-KSCATALOG/endpointTemplates""" - return self.admin_request(method='GET', - path='/OS-KSCATALOG/endpointTemplates', - **kwargs) - - def get_endpoint_templates_by_service(self, service_id, **kwargs): - """GET /OS-KSCATALOG/endpointTemplates""" - return self.admin_request(method='GET', path=( - '/OS-KSCATALOG/endpointTemplates?serviceId=%s') - % (service_id), - **kwargs) - - def post_endpoint_template(self, **kwargs): - """POST /OS-KSCATALOG/endpointTemplates""" - return self.admin_request(method='POST', - path='/OS-KSCATALOG/endpointTemplates', - **kwargs) - - def put_endpoint_template(self, endpoint_template_id, **kwargs): - """PUT /OS-KSCATALOG/endpointTemplates/{endpoint_template_id}""" - return self.admin_request(method='PUT', - path='/OS-KSCATALOG/endpointTemplates/%s' - % (endpoint_template_id,), - **kwargs) - - def get_endpoint_template(self, endpoint_template_id, **kwargs): - """GET /OS-KSCATALOG/endpointTemplates/{endpoint_template_id}""" - return self.admin_request(method='GET', - path='/OS-KSCATALOG/endpointTemplates/%s' - % (endpoint_template_id,), - **kwargs) - - def delete_endpoint_template(self, endpoint_template_id, **kwargs): - """DELETE /OS-KSCATALOG/endpointTemplates/{endpoint_template_id}""" - return self.admin_request(method='DELETE', - path='/OS-KSCATALOG/endpointTemplates/%s' % - (endpoint_template_id,), - **kwargs) - - def get_tenant_endpoints(self, tenant_id, **kwargs): - """GET /tenants/{tenant_id}/OS-KSCATALOG/endpoints""" - return self.admin_request(method='GET', - path='/tenants/%s/OS-KSCATALOG/endpoints' % - (tenant_id,), - **kwargs) - - def post_tenant_endpoint(self, tenant_id, **kwargs): - """POST /tenants/{tenant_id}/OS-KSCATALOG/endpoints""" - return self.admin_request(method='POST', - path='/tenants/%s/OS-KSCATALOG/endpoints' % - (tenant_id,), **kwargs) - - def delete_tenant_endpoint(self, tenant_id, endpoint_id, **kwargs): - """DELETE /tenants/{tenant_id}/OS-KSCATALOG/endpoints/{endpoint_id}""" - return self.admin_request(method='DELETE', - path='/tenants/%s/OS-KSCATALOG/endpoints/%s' % - (tenant_id, endpoint_id,), - **kwargs) - - def get_token_endpoints(self, token_id, **kwargs): - """GET /tokens/{token_id}/endpoints""" - return self.admin_request(method='GET', - path='/tokens/%s/endpoints' % - (token_id,), - **kwargs) - - def post_service(self, **kwargs): - """POST /services""" - return self.admin_request(method='POST', - path='/OS-KSADM/services', **kwargs) - - def get_services(self, **kwargs): - """GET /services""" - return self.admin_request(method='GET', - path='/OS-KSADM/services', **kwargs) - - def get_service(self, service_id, **kwargs): - """GET /services/{service_id}""" - return self.admin_request(method='GET', - path='/OS-KSADM/services/%s' % (service_id,), **kwargs) - - def get_service_by_name(self, service_name, **kwargs): - """GET /services?name={service_name}""" - return self.admin_request(method='GET', - path='/OS-KSADM/services?name=%s' % (service_name,), **kwargs) - - def delete_service(self, service_id, **kwargs): - """DELETE /services/{service_id}""" - return self.admin_request(method='DELETE', - path='/OS-KSADM/services/%s' % (service_id,), **kwargs) - - def get_root(self, **kwargs): - """GET /""" - return self.service_request(method='GET', path='/', **kwargs) - - def get_extensions(self, **kwargs): - """GET /extensions""" - return self.service_request(method='GET', path='/extensions', **kwargs) - - def get_admin_guide(self, **kwargs): - """GET /identityadminguide.pdf""" - return self.service_request(method='GET', - path='/identityadminguide.pdf', **kwargs) - - def get_admin_wadl(self, **kwargs): - """GET /identity-admin.wadl""" - return self.service_request(method='GET', path='/identity-admin.wadl', - **kwargs) - - def get_common_ent(self, **kwargs): - """GET /common.ent""" - return self.service_request(method='GET', path='/common.ent', - **kwargs) - - def get_xsd(self, filename, **kwargs): - """GET /xsd/{xsd}""" - return self.service_request(method='GET', path='/xsd/%s' % (filename,), - **kwargs) - - def get_xsd_atom(self, filename, **kwargs): - """GET /xsd/atom/{xsd}""" - return self.service_request(method='GET', - path='/xsd/atom/%s' % (filename,), **kwargs) - - def get_xslt(self, filename, **kwargs): - """GET /xslt/{file:.*}""" - return self.service_request(method='GET', - path='/xslt/%s' % (filename,), **kwargs) - - def get_javascript(self, filename, **kwargs): - """GET /js/{file:.*}""" - return self.service_request(method='GET', path='/js/%s' % (filename,), - **kwargs) - - def get_style(self, filename, **kwargs): - """GET /style/{file:.*}""" - return self.service_request(method='GET', - path='/style/%s' % (filename,), **kwargs) - - def get_sample(self, filename, **kwargs): - """GET /samples/{file:.*}""" - return self.service_request(method='GET', - path='/samples/%s' % (filename,), **kwargs) - - def get_user_credentials(self, user_id, **kwargs): - """GET /users/{user_id}/OS-KSADM/credentials""" - return self.admin_request(method='GET', - path='/users/%s/OS-KSADM/credentials' % (user_id,), **kwargs) - - def get_user_credentials_by_type(self, - user_id, credentials_type, **kwargs): - """GET /users/{user_id}/OS-KSADM/credentials/{credentials_type}""" - return self.admin_request(method='GET', - path='/users/%s/OS-KSADM/credentials/%s'\ - % (user_id, credentials_type,), **kwargs) - - def post_credentials(self, user_id, **kwargs): - """POST /users/{user_id}/OS-KSADM/credentials""" - return self.admin_request(method='POST', - path='/users/%s/OS-KSADM/credentials' % (user_id,), **kwargs) - - def post_credentials_by_type(self, user_id, credentials_type, **kwargs): - """POST /users/{user_id}/OS-KSADM/credentials/{credentials_type}""" - return self.admin_request(method='POST', - path='/users/%s/OS-KSADM/credentials/%s' %\ - (user_id, credentials_type), **kwargs) - - def delete_user_credentials_by_type(self, user_id, - credentials_type, **kwargs): - """DELETE /users/{user_id}/OS-KSADM/credentials/{credentials_type}""" - return self.admin_request(method='DELETE', - path='/users/%s/OS-KSADM/credentials/%s' %\ - (user_id, credentials_type,), **kwargs) - - -def unique_str(): - """Generates and return a unique string""" - return str(uuid.uuid4()) - - -def unique_email(): - """Generates and return a unique email""" - return "%s@openstack.org" % unique_str() - - -def unique_url(): - """Generates and return a unique email""" - return "http://%s" % unique_str() - - -def optional_str(val): - """Automatically populates optional string fields""" - return val if val is not None else unique_str() - - -def optional_email(val): - """Automatically populates optional email fields""" - return val if val is not None else unique_email() - - -def optional_url(val): - """Automatically populates optional url fields""" - return val if val is not None else unique_url() - - -class FunctionalTestCase(ApiTestCase): - """Abstracts functional CRUD of the identity API""" - admin_user_id = None - - admin_token = None - service_token = None - expired_admin_token = None - disabled_admin_token = None - service_admin_token = None - - user = None - user_token = None - service_user = None - - tenant = None - tenant_user = None # user with default tenant - tenant_user_token = None - - disabled_tenant = None - disabled_user = None - - xmlns = 'http://docs.openstack.org/identity/api/v2.0' - xmlns_ksadm = 'http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0' - xmlns_kscatalog = "http://docs.openstack.org/identity/api/ext"\ - + "/OS-KSCATALOG/v1.0" - - def setUp(self): - """Prepare keystone for system tests""" - super(FunctionalTestCase, self).setUp() - - # Authenticate as admin user to establish admin_token - access = self.authenticate(self.admin_username, self.admin_password).\ - json['access'] - - self.admin_token = access['token']['id'] - self.admin_user_id = access['user']['id'] - - def fixture_create_service_admin(self): - if self.service_user: - return - # SERVICE ADMIN - password = unique_str() - self.service_user = self.fixture_create_user( - name="service-user-%s" % uuid.uuid4().hex, enabled=True, - password=password) - self.service_user['password'] = password - - self.service_admin_role = self.fetch_role_by_name( - self.service_admin_role_name, - assert_status=200).json['role'] - self.grant_global_role_to_user(self.service_user['id'], - self.service_admin_role['id'], - assert_status=201) - - self.service_user_token = self.authenticate(self.service_user['name'], - self.service_user['password']).\ - json['access']['token'] - self.service_admin_token = self.service_user_token['id'] - - def fixture_create_normal_tenant(self): - if self.tenant: - return - # TENANT - self.tenant = self.fixture_create_tenant( - name="tenant-%s" % uuid.uuid4().hex, enabled=True) - - def fixture_create_disabled_tenant(self): - if self.disabled_tenant: - return - # DISABLED TENANT - self.disabled_tenant = self.fixture_create_tenant( - name="disabled-tenant-%s" % uuid.uuid4().hex, enabled=False) - - def fixture_create_normal_user(self): - if self.user: - return - # USER - password = unique_str() - self.user = self.fixture_create_user( - name="user-%s" % uuid.uuid4().hex, enabled=True, - password=password) - self.user['password'] = password - - self.user_token = self.authenticate(self.user['name'], - self.user['password']).\ - json['access']['token'] - - def fixture_create_tenant_user(self): - if self.tenant_user: - return - self.fixture_create_tenant() - # USER with DEFAULT TENANT - password = unique_str() - self.tenant_user = self.fixture_create_user( - name="user_in_tenant-%s" % uuid.uuid4().hex, enabled=True, - tenant_id=self.tenant.id, password=password) - self.tenant_user['password'] = password - - self.tenant_user_token = self.authenticate(self.tenant_user['name'], - self.tenant_user['password'], - self.tenant.id).\ - json['access']['token'] - - def fixture_create_disabled_user_and_token(self): - if self.disabled_user: - return - self.fixture_create_normal_tenant() - # DISABLED USER - self.disabled_user = self.fixture_create_user( - name="disabled_user-%s" % uuid.uuid4().hex, enabled=False) - - # TOKEN for DISABLED user - token = self.fixture_create_token( - id="disabled-user-tenant-token-%s" % uuid.uuid4().hex, - user_id=self.disabled_user.id, - tenant_id=self.tenant.id, - expires=datetime.datetime.now() + datetime.timedelta(1)) - self.disabled_admin_token = token.id - - def fixture_create_expired_token(self): - if self.expired_admin_token: - return - self.fixture_create_normal_tenant() - # EXPIRED token (for enabled user) - token = self.fixture_create_token( - id="expired-admin-token-%s" % uuid.uuid4().hex, - user_id=self.admin_user_id, - tenant_id=self.tenant.id, - expires=datetime.datetime.now() - datetime.timedelta(1)) - self.expired_admin_token = token.id - - def authenticate(self, user_name=None, user_password=None, tenant_id=None, - **kwargs): - user_name = optional_str(user_name) - user_password = optional_str(user_password) - - data = { - "auth": { - "passwordCredentials": { - "username": user_name, - "password": user_password}}} - if tenant_id: - data["auth"]["tenantId"] = tenant_id - - return self.post_token(as_json=data, **kwargs) - - def authenticate_D5(self, user_name=None, user_password=None, - tenant_id=None, **kwargs): - user_name = optional_str(user_name) - user_password = optional_str(user_password) - - data = {"passwordCredentials": { - "username": user_name, - "password": user_password}} - if tenant_id: - data["passwordCredentials"]["tenantId"] = tenant_id - - return self.post_token(as_json=data, **kwargs) - - def authenticate_using_token(self, token, tenant_id=None, - **kwargs): - - data = { - "auth": { - "token": { - "id": token}}} - - if tenant_id: - data["auth"]["tenantId"] = tenant_id - - return self.post_token(as_json=data, **kwargs) - - def validate_token(self, token_id=None, tenant_id=None, **kwargs): - token_id = optional_str(token_id) - - if tenant_id: - # validate scoped token - return self.get_token_belongsto(token_id, tenant_id, **kwargs) - else: - # validate unscoped token - return self.get_token(token_id, **kwargs) - - def remove_token(self, token_id=None, **kwargs): - token_id = optional_str(token_id) - return self.delete_token(token_id, **kwargs) - - def create_tenant(self, tenant_name=None, tenant_description=None, - tenant_enabled=True, **kwargs): - """Creates a tenant for testing - - The tenant name and description are generated from UUIDs. - """ - tenant_name = optional_str(tenant_name) - tenant_description = optional_str(tenant_description) - - data = { - "tenant": { - "name": tenant_name, - "description": tenant_description, - "enabled": tenant_enabled}} - - return self.post_tenant(as_json=data, **kwargs) - - def list_tenants(self, **kwargs): - return self.get_tenants(**kwargs) - - def fetch_tenant(self, tenant_id=None, **kwargs): - tenant_id = optional_str(tenant_id) - return self.get_tenant(tenant_id, **kwargs) - - def fetch_tenant_by_name(self, tenant_name=None, **kwargs): - tenant_name = optional_str(tenant_name) - if tenant_name: - return self.get_tenant_by_name(tenant_name, **kwargs) - - def update_tenant(self, tenant_id=None, tenant_name=None, - tenant_description=None, tenant_enabled=True, **kwargs): - tenant_id = optional_str(tenant_id) - tenant_description = optional_str(tenant_description) - - data = {"tenant": {}} - - if tenant_name is not None: - data['tenant']['name'] = tenant_name - - data['tenant']['description'] = tenant_description - - if tenant_enabled is not None: - data['tenant']['enabled'] = tenant_enabled - - return self.post_tenant_for_update(tenant_id, as_json=data, **kwargs) - - def list_tenant_users(self, tenant_id, role_id=None, **kwargs): - tenant_id = optional_str(tenant_id) - if role_id: - return self.get_tenant_users_by_role(tenant_id, role_id, **kwargs) - else: - return self.get_tenant_users(tenant_id, **kwargs) - - def remove_tenant(self, tenant_id=None, **kwargs): - tenant_id = optional_str(tenant_id) - return self.delete_tenant(tenant_id, **kwargs) - - def create_user(self, user_name=None, user_password=None, user_email=None, - tenant_id=None, user_enabled=True, **kwargs): - """Creates a user for testing - - The user name is generated from UUIDs. - """ - user_name = optional_str(user_name) - user_password = optional_str(user_password) - user_email = optional_email(user_email) - - data = { - "user": { - "password": user_password, - "name": user_name, - "tenantId": tenant_id, - "email": user_email, - "enabled": user_enabled}} - - return self.post_user(as_json=data, **kwargs) - - def create_user_with_known_password(self, **kwargs): - """Manually injects the new user's password into the response data""" - - password = unique_str() - r = self.create_user(user_password=password, **kwargs) - r.json['user']['password'] = password - return r - - def list_users(self, **kwargs): - return self.get_users(**kwargs) - - def fetch_user(self, user_id=None, **kwargs): - user_id = optional_str(user_id) - return self.get_user(user_id, **kwargs) - - def fetch_user_by_name(self, user_name=None, **kwargs): - user_name = optional_str(user_name) - return self.query_user(user_name, **kwargs) - - def update_user(self, user_id=None, user_email=None, user_enabled=None, - user_name=None, **kwargs): - user_id = optional_str(user_id) - - data = {"user": {}} - - if user_email is not None: - data['user']['email'] = user_email - - if user_enabled is not None: - data['user']['enabled'] = user_enabled - if user_name is not None: - data['user']['name'] = user_name - return self.post_user_for_update(user_id, as_json=data, **kwargs) - - def update_user_password(self, user_id=None, user_password=None, **kwargs): - user_id = optional_str(user_id) - user_password = optional_str(user_password) - - data = {"user": {"password": user_password}} - return self.put_user_password(user_id, as_json=data, **kwargs) - - def update_user_tenant(self, user_id=None, tenant_id=None, **kwargs): - user_id = optional_str(user_id) - tenant_id = optional_str(tenant_id) - - data = {"user": {"tenantId": tenant_id}} - return self.put_user_tenant(user_id, as_json=data, **kwargs) - - def _enable_disable_user(self, user_id, user_enabled, **kwargs): - """Private function to enable and disable a user. - - Use enable_user() and disable_user() instead.""" - data = {"user": {"enabled": user_enabled}} - - return self.put_user_enabled(user_id, as_json=data, **kwargs) - - def enable_user(self, user_id=None, **kwargs): - user_id = optional_str(user_id) - return self._enable_disable_user(user_id, True, **kwargs) - - def disable_user(self, user_id=None, **kwargs): - user_id = optional_str(user_id) - return self._enable_disable_user(user_id, False, **kwargs) - - def remove_user(self, user_id=None, **kwargs): - user_id = optional_str(user_id) - return self.delete_user(user_id, **kwargs) - - def grant_role_to_user(self, user_id=None, role_id=None, tenant_id=None, - **kwargs): - user_id = optional_str(user_id) - role_id = optional_str(role_id) - tenant_id = optional_str(tenant_id) - return self.put_user_role(user_id, role_id, tenant_id, **kwargs) - - def grant_global_role_to_user(self, user_id=None, role_id=None, - **kwargs): - user_id = optional_str(user_id) - role_id = optional_str(role_id) - return self.put_user_role(user_id, role_id, None, **kwargs) - - def revoke_global_role_from_user(self, - user_id=None, role_id=None, **kwargs): - user_id = optional_str(user_id) - role_id = optional_str(role_id) - return self.delete_user_role(user_id, role_id, **kwargs) - - def revoke_role_from_user(self, - user_id=None, role_id=None, tenant_id=None, **kwargs): - user_id = optional_str(user_id) - role_id = optional_str(role_id) - tenant_id = optional_str(tenant_id) - return self.delete_user_role(user_id, tenant_id, **kwargs) - - def create_role(self, role_name=None, role_description=None, - service_id=None, service_name=None, **kwargs): - """Creates a role for testing - - The role name and description are generated from UUIDs. - """ - if service_name and not role_name: - role_name = "%s:%s" % (service_name, optional_str(role_name)) - else: - role_name = optional_str(role_name) - role_description = optional_str(role_description) - - data = { - "role": { - "name": role_name, - "description": role_description}} - - if service_id is not None: - data['role']['serviceId'] = service_id - - return self.post_role(as_json=data, **kwargs) - - def list_roles(self, service_id=None, **kwargs): - if service_id is None: - return self.get_roles(**kwargs) - else: - return self.get_roles_by_service(service_id, **kwargs) - - def fetch_role(self, role_id=None, **kwargs): - role_id = optional_str(role_id) - return self.get_role(role_id, **kwargs) - - def fetch_role_by_name(self, role_name=None, **kwargs): - role_name = optional_str(role_name) - return self.get_role_by_name(role_name, **kwargs) - - def remove_role(self, role_id=None, **kwargs): - role_id = optional_str(role_id) - return self.delete_role(role_id, **kwargs) - - def create_service(self, service_name=None, service_type=None, - service_description=None, **kwargs): - service_name = optional_str(service_name) - if service_type is None: - service_type = ['compute', 'identity', 'image-service', - 'object-store', 'ext:extension-service' - ][random.randrange(5)] - service_description = optional_str(service_description) - data = { - "OS-KSADM:service": { - "name": service_name, - "type": service_type, - "description": service_description}} - return self.post_service(as_json=data, **kwargs) - - def list_services(self, **kwargs): - return self.get_services(**kwargs) - - def fetch_service(self, service_id=None, **kwargs): - service_id = optional_str(service_id) - return self.get_service(service_id, **kwargs) - - def fetch_service_by_name(self, service_name=None, **kwargs): - service_name = optional_str(service_name) - return self.get_service_by_name(service_name, **kwargs) - - def remove_service(self, service_id=None, **kwargs): - service_id = optional_str(service_id) - self.delete_service(service_id, **kwargs) - - def create_endpoint_for_tenant(self, tenant_id=None, - endpoint_template_id=None, **kwargs): - tenant_id = optional_str(tenant_id) - endpoint_template_id = optional_str(endpoint_template_id) - - data = {"OS-KSCATALOG:endpointTemplate": {"id": endpoint_template_id}} - - return self.post_tenant_endpoint(tenant_id, as_json=data, **kwargs) - - def list_tenant_endpoints(self, tenant_id=None, **kwargs): - tenant_id = optional_str(tenant_id) - return self.get_tenant_endpoints(tenant_id, **kwargs) - - def remove_endpoint_from_tenant(self, tenant_id=None, endpoint_id=None, - **kwargs): - tenant_id = optional_str(tenant_id) - endpoint_id = optional_str(endpoint_id) - - """TODO: Should this be an 'endpoint_id' or 'endpoint_template_id'??""" - return self.delete_tenant_endpoint(tenant_id, endpoint_id, **kwargs) - - def remove_tenant_endpoint(self, tenant_id=None, endpoint_id=None, - **kwargs): - tenant_id = optional_str(tenant_id) - endpoint_id = optional_str(endpoint_id) - - """TODO: Should this be an 'endpoint_id' or 'endpoint_template_id'??""" - return self.delete_tenant_endpoint(tenant_id, endpoint_id, **kwargs) - - def list_endpoint_templates(self, service_id=None, **kwargs): - if service_id is None: - return self.get_endpoint_templates(**kwargs) - else: - return self.get_endpoint_templates_by_service(service_id, **kwargs) - - def create_endpoint_template(self, region=None, name=None, type=None, - public_url=None, admin_url=None, internal_url=None, enabled=True, - is_global=True, version_id=None, - version_list=None, version_info=None, **kwargs): - - region = optional_str(region) - name = optional_str(name) - type = optional_str(type) - public_url = optional_url(public_url) - admin_url = optional_url(admin_url) - internal_url = optional_url(internal_url) - version_id = optional_str(version_id)[:20] - version_list = optional_str(version_list) - version_info = optional_str(version_info) - - data = { - "OS-KSCATALOG:endpointTemplate": { - "region": region, - "name": name, - "type": type, - "publicURL": public_url, - "adminURL": admin_url, - "internalURL": internal_url, - "enabled": enabled, - "global": is_global, - "versionId": version_id, - "versionInfo": version_info, - "versionList": version_list}} - return self.post_endpoint_template(as_json=data, **kwargs) - - def remove_endpoint_template(self, endpoint_template_id=None, **kwargs): - endpoint_template_id = optional_str(endpoint_template_id) - return self.delete_endpoint_template(endpoint_template_id, **kwargs) - - def fetch_endpoint_template(self, endpoint_template_id, **kwargs): - endpoint_template_id = optional_str(endpoint_template_id) - return self.get_endpoint_template(endpoint_template_id, **kwargs) - - def update_endpoint_template(self, endpoint_template_id=None, region=None, - name=None, type=None, public_url=None, admin_url=None, - internal_url=None, enabled=None, is_global=None, - version_id=None, version_list=None, version_info=None, **kwargs): - - data = {"OS-KSCATALOG:endpointTemplate": {}} - - if region is not None: - data['OS-KSCATALOG:endpointTemplate']['region'] = region - - if name is not None: - data['OS-KSCATALOG:endpointTemplate']['name'] = name - - if type is not None: - data['OS-KSCATALOG:endpointTemplate']['type'] = type - - if public_url is not None: - data['OS-KSCATALOG:endpointTemplate']['publicURL'] = public_url - - if admin_url is not None: - data['OS-KSCATALOG:endpointTemplate']['adminURL'] = admin_url - - if internal_url is not None: - data['OS-KSCATALOG:endpointTemplate']['internalURL'] = internal_url - - if enabled is not None: - data['OS-KSCATALOG:endpointTemplate']['enabled'] = enabled - - if is_global is not None: - data['OS-KSCATALOG:endpointTemplate']['global'] = is_global - - if version_id is not None: - data['OS-KSCATALOG:endpointTemplate']['versionId'] = version_id - - if version_list is not None: - data['OS-KSCATALOG:endpointTemplate']['versionList'] = version_list - - if version_info is not None: - data['OS-KSCATALOG:endpointTemplate']['versionInfo'] = version_info - - return self.put_endpoint_template(endpoint_template_id, as_json=data, - **kwargs) - - def fetch_user_credentials(self, user_id=None, **kwargs): - user_id = optional_str(user_id) - return self.get_user_credentials(user_id, **kwargs) - - def fetch_password_credentials(self, user_id=None, **kwargs): - user_id = optional_str(user_id) - return self.get_user_credentials_by_type( - user_id, 'passwordCredentials', **kwargs) - - def create_password_credentials(self, user_id, user_name, - password=None, **kwargs): - user_id = optional_str(user_id) - password = optional_str(password) - data = { - "passwordCredentials": { - "username": user_name, - "password": password}} - return self.post_credentials(user_id, as_json=data, **kwargs) - - def update_password_credentials(self, user_id, user_name, - password=None, **kwargs): - user_id = optional_str(user_id) - password = optional_str(password) - data = { - "passwordCredentials": { - "username": user_name, - "password": password}} - return self.post_credentials_by_type( - user_id, 'passwordCredentials', as_json=data, **kwargs) - - def delete_password_credentials(self, user_id, **kwargs): - user_id = optional_str(user_id) - return self.delete_user_credentials_by_type( - user_id, 'passwordCredentials', **kwargs) - - def check_urls_for_regular_user(self, service_catalog): - self.assertIsNotNone(service_catalog) - for x in range(0, len(service_catalog)): - endpoints = service_catalog[x]['endpoints'] - for y in range(0, len(endpoints)): - endpoint = endpoints[y] - for key in endpoint: - #Checks whether adminURL is not present. - self.assertNotEquals(key, 'adminURL') - - def check_urls_for_regular_user_xml(self, service_catalog): - self.assertIsNotNone(service_catalog) - services = service_catalog.findall('{%s}service' % self.xmlns) - self.assertIsNotNone(services) - for service in services: - endpoints = service.findall('{%s}endpoint' % self.xmlns) - self.assertIsNotNone(endpoints) - for endpoint in endpoints: - #Checks whether adminURL is not present. - self.assertIsNone(endpoint.get('adminURL')) - self.assertIsNotNone(service_catalog) - - def check_urls_for_admin_user(self, service_catalog): - self.assertIsNotNone(service_catalog) - for x in range(0, len(service_catalog)): - endpoints = service_catalog[x]['endpoints'] - is_admin__url_present = None - for y in range(0, len(endpoints)): - endpoint = endpoints[y] - for key in endpoint: - if key == 'adminURL': - is_admin__url_present = True - self.assertTrue(is_admin__url_present, - "Admin API does not return admin URL") - - def check_urls_for_admin_user_xml(self, service_catalog): - self.assertIsNotNone(service_catalog) - services = service_catalog.findall('{%s}service' % self.xmlns) - self.assertIsNotNone(services) - is_admin_url_present = None - for service in services: - endpoints = service.findall('{%s}endpoint' % self.xmlns) - self.assertIsNotNone(endpoints) - for endpoint in endpoints: - if endpoint.get('adminURL'): - is_admin_url_present = True - self.assertTrue(is_admin_url_present, - "Admin API does not return admin URL") - - -class HeaderApp(object): - """ - Dummy WSGI app the returns HTTP headers in the body - - This is useful for making sure the headers we want - aer being passwed down to the downstream WSGI app. - """ - def __init__(self): - pass - - def __call__(self, env, start_response): - self.request = Request.blank('', environ=env) - body = '' - for key in env: - if key.startswith('HTTP_'): - body += '%s: %s\n' % (key, env[key]) - return Response(status="200 OK", - body=body)(env, start_response) - - -class BlankApp(object): - """ - Dummy WSGI app - does not do anything - """ - def __init__(self): - pass - - def __call__(self, env, start_response): - self.request = Request.blank('', environ=env) - return Response(status="200 OK", - body={})(env, start_response) - - -class MiddlewareTestCase(FunctionalTestCase): - """ - Base class to run tests for Keystone WSGI middleware. - """ - use_server = True - - def _setup_test_middleware(self): - test_middleware = None - if isinstance(self.middleware, tuple): - test_middleware = HeaderApp() - for filter in self.middleware: - test_middleware = \ - filter.filter_factory(self.settings)(test_middleware) - else: - test_middleware = \ - self.middleware.filter_factory(self.settings)(HeaderApp()) - return test_middleware - - def setUp(self, middleware, settings=None): - super(MiddlewareTestCase, self).setUp() - if settings is None: - settings = {'delay_auth_decision': '0', - 'auth_host': client_tests.TEST_TARGET_SERVER_ADMIN_ADDRESS, - 'auth_port': client_tests.TEST_TARGET_SERVER_ADMIN_PORT, - 'auth_protocol': - client_tests.TEST_TARGET_SERVER_ADMIN_PROTOCOL, - 'auth_uri': ('%s://%s:%s/' % \ - (client_tests.TEST_TARGET_SERVER_SERVICE_PROTOCOL, - client_tests.TEST_TARGET_SERVER_SERVICE_ADDRESS, - client_tests.TEST_TARGET_SERVER_SERVICE_PORT)), - 'admin_token': self.admin_token, - 'admin_user': self.admin_username, - 'admin_password': self.admin_password} - cert_file = isSsl() - if cert_file: - settings['certfile'] = cert_file - self.settings = settings - self.middleware = middleware - self.test_middleware = self._setup_test_middleware() - - name = unique_str() - r = self.create_tenant(tenant_name=name, assert_status=201) - self.tenant = r.json.get('tenant') - - user_name = unique_str() - password = unique_str() - r = self.create_user(user_name=user_name, - user_password=password, - tenant_id=self.tenant['id']) - self.tenant_user = r.json.get('user') - self.tenant_user['password'] = password - - access = self.authenticate(user_name, password).\ - json['access'] - self.tenant_user_token = access['token'] - - self.services = {} - self.endpoint_templates = {} - for x in range(0, 5): - self.services[x] = self.create_service().json['OS-KSADM:service'] - self.endpoint_templates[x] = self.create_endpoint_template( - name=self.services[x]['name'], \ - type=self.services[x]['type']).\ - json['OS-KSCATALOG:endpointTemplate'] - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_templates[x]['id']) - - @unittest.skipIf(isSsl() or 'HP-IDM_Disabled' in os.environ, - "Skipping SSL or HP-IDM tests") - def test_with_service_id(self): - if isSsl() or ('HP-IDM_Disabled' in os.environ): - # TODO(zns): why is this not skipping with the decorator?! - raise unittest.SkipTest("Skipping SSL or HP-IDM tests") - # create a service role so the scope token validation will succeed - role_resp = self.create_role(service_name=self.services[0]['name']) - role = role_resp.json['role'] - self.grant_role_to_user(self.tenant_user['id'], - role['id'], self.tenant['id']) - auth_resp = self.authenticate(self.tenant_user['name'], - self.tenant_user['password'], - self.tenant['id'], assert_status=200) - user_token = auth_resp.json['access']['token']['id'] - self.settings['service_ids'] = "%s" % self.services[0]['id'] - test_middleware = self._setup_test_middleware() - resp = Request.blank('/', - headers={'X-Auth-Token': user_token}) \ - .get_response(test_middleware) - self.assertEquals(resp.status_int, 200) - - # now give it a bogus service ID to make sure we get a 401 - self.settings['service_ids'] = "boguzz" - test_middleware = self._setup_test_middleware() - resp = Request.blank('/', - headers={'X-Auth-Token': user_token}) \ - .get_response(test_middleware) - self.assertEquals(resp.status_int, 401) - - @unittest.skipUnless(not isSsl() and 'HP-IDM_Disabled' in os.environ, - "Skipping since HP-IDM is enabled") - def test_with_service_id_with_hpidm_disabled(self): - # create a service role so the scope token validation will succeed - role_resp = self.create_role(service_name=self.services[0]['name']) - role = role_resp.json['role'] - self.grant_role_to_user(self.tenant_user['id'], - role['id'], self.tenant['id']) - auth_resp = self.authenticate(self.tenant_user['name'], - self.tenant_user['password'], - self.tenant['id'], assert_status=200) - user_token = auth_resp.json['access']['token']['id'] - self.settings['service_ids'] = "%s" % self.services[0]['id'] - test_middleware = self._setup_test_middleware() - resp = Request.blank('/', - headers={'X-Auth-Token': user_token}) \ - .get_response(test_middleware) - self.assertEquals(resp.status_int, 200) - - # now give it a bogus service ID to make sure it got ignored - self.settings['service_ids'] = "boguzz" - test_middleware = self._setup_test_middleware() - resp = Request.blank('/', - headers={'X-Auth-Token': user_token}) \ - .get_response(test_middleware) - self.assertEquals(resp.status_int, 200) - - def test_401_without_token(self): - resp = Request.blank('/').get_response(self.test_middleware) - self.assertEquals(resp.status_int, 401) - headers = resp.headers - self.assertTrue("WWW-Authenticate" in headers) - self.assertEquals(headers['WWW-Authenticate'], - "Keystone uri='%s://%s:%s/'" % \ - (client_tests.TEST_TARGET_SERVER_SERVICE_PROTOCOL, - client_tests.TEST_TARGET_SERVER_SERVICE_ADDRESS, - client_tests.TEST_TARGET_SERVER_SERVICE_PORT)) - - def test_401_bad_token(self): - resp = Request.blank('/', - headers={'X-Auth-Token': 'MADE_THIS_UP'}) \ - .get_response(self.test_middleware) - self.assertEquals(resp.status_int, 401) - - def test_200_good_token(self): - resp = Request.blank('/', - headers={'X-Auth-Token': self.tenant_user_token['id']}) \ - .get_response(self.test_middleware) - - self.assertEquals(resp.status_int, 200) - - headers = resp.body.split('\n') - - header = "HTTP_X_IDENTITY_STATUS: Confirmed" - self.assertTrue(header in headers, "Missing %s" % header) - - header = "HTTP_X_USER_ID: %s" % self.tenant_user['id'] - self.assertTrue(header in headers, "Missing %s" % header) - - header = "HTTP_X_USER_NAME: %s" % self.tenant_user['name'] - self.assertTrue(header in headers, "Missing %s" % header) - - header = "HTTP_X_TENANT_ID: %s" % self.tenant['id'] - self.assertTrue(header in headers, "Missing %s" % header) - - header = "HTTP_X_TENANT_NAME: %s" % self.tenant['name'] - self.assertTrue(header in headers, "Missing %s" % header) - - # These are here for legacy support and should be removed by F - header = "HTTP_X_TENANT: %s" % self.tenant['id'] - self.assertTrue(header in headers, "Missing %s" % header) - - header = "HTTP_X_USER: %s" % self.tenant_user['id'] - self.assertTrue(header in headers, "Missing %s" % header) diff --git a/keystone/test/functional/test_auth.py b/keystone/test/functional/test_auth.py deleted file mode 100644 index 661901fd..00000000 --- a/keystone/test/functional/test_auth.py +++ /dev/null @@ -1,494 +0,0 @@ -import unittest2 as unittest -from keystone.test.functional import common - - -class TestAdminAuthentication(common.FunctionalTestCase): - """Test admin-side user authentication""" - - def test_bootstrapped_admin_user(self): - """Bootstrap script should create an 'admin' user with 'Admin' role""" - # Authenticate as admin - unscoped = self.authenticate(self.admin_username, - self.admin_password).json['access'] - - # Assert we get back a token with an expiration date - self.assertTrue(unscoped['token']['id']) - self.assertTrue(unscoped['token']['expires']) - - # Make sure there's no default tenant going on - self.assertIsNone(unscoped['token'].get('tenant')) - self.assertIsNone(unscoped['user'].get('tenantId')) - - -class TestAdminAuthenticationNegative(common.FunctionalTestCase): - """Negative test admin-side user authentication""" - - def test_admin_user_scoping_to_tenant_with_role(self): - """A Keystone Admin SHOULD be able to retrieve a scoped token... - - But only if the Admin has some Role on the Tenant other than Admin. - - Formerly: - test_admin_user_trying_to_scope_to_tenant_with_established_role - """ - tenant = self.create_tenant().json['tenant'] - role = self.create_role().json['role'] - - self.grant_role_to_user(self.admin_user_id, role['id'], tenant['id']) - - # Try to authenticate for this tenant - access = self.post_token(as_json={ - 'auth': { - 'token': { - 'id': self.admin_token}, - 'tenantId': tenant['id']}}).json['access'] - - self.assertEqual(access['token']['tenant']['id'], tenant['id']) - - def test_admin_user_trying_to_scope_to_tenant(self): - """A Keystone Admin should NOT be able to retrieve a scoped token""" - tenant = self.create_tenant().json['tenant'] - - # Try (and fail) to authenticate for this tenant - self.post_token(as_json={ - 'auth': { - 'token': { - 'id': self.admin_token}, - 'tenantId': tenant['id']}}, assert_status=401) - - def test_service_token_as_admin_token(self): - """Admin actions should fail for mere service tokens""" - - # Admin create a user - password = common.unique_str() - user = self.create_user(user_password=password).json['user'] - user['password'] = password - - # Replace our admin_token with a mere service token - self.admin_token = self.authenticate(user['name'], user['password']).\ - json['access']['token']['id'] - - # Try creating another user using the wrong token - self.create_user(assert_status=401) - - -class TestServiceAuthentication(common.FunctionalTestCase): - """Test service-side user authentication""" - - def setUp(self): - super(TestServiceAuthentication, self).setUp() - - # Create a user - password = common.unique_str() - self.user = self.create_user(user_password=password).json['user'] - self.tenant = self.create_tenant().json['tenant'] - self.user['password'] = password - self.services = {} - self.endpoint_templates = {} - self.services = self.create_service().json['OS-KSADM:service'] - self.endpoint_templates = self.create_endpoint_template( - name=self.services['name'], \ - type=self.services['type']).\ - json['OS-KSCATALOG:endpointTemplate'] - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_templates['id']) - - def test_authenticate_twice(self): - """Authenticating twice in a row should not result in a new token - - The original token should not have expired and should be provided again - """ - first_token = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}}}).\ - json['access']['token']['id'] - - second_token = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}}}).\ - json['access']['token']['id'] - - self.assertEqual(first_token, second_token) - - def test_authenticate_twice_for_tenant(self): - """Authenticating twice in a row should not result in a new token - - The original token should not have expired and should be provided again - """ - # Additonal setUp - role = self.create_role().json['role'] - self.grant_role_to_user(self.user['id'], role['id'], self.tenant['id']) - - self.service_token = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}}}).\ - json['access']['token']['id'] - - tenant = self.service_request(method='GET', path='/tenants').\ - json['tenants'][0] - - first_token = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}, - 'tenantId': tenant['id']}}).json['access']['token']['id'] - - second_token = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}, - 'tenantId': tenant['id']}}).json['access']['token']['id'] - - # unscoped token should be different than the two scoped tokens - self.assertNotEqual(self.service_token, first_token) - self.assertEqual(first_token, second_token) - - def test_unscoped_user_auth(self): - """Admin should be able to validate a user's token""" - # Authenticate as user to get a token - self.service_token = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}}}).\ - json['access']['token']['id'] - - # In the real world, the service user would then pass his/her token - # to some service that depends on keystone, which would then need to - # use keystone to validate the provided token. - - # Admin independently validates the user token - r = self.get_token(self.service_token) - self.assertEqual(r.json['access']['token']['id'], self.service_token) - self.assertTrue(r.json['access']['token']['expires']) - self.assertEqual(r.json['access']['user']['id'], self.user['id']) - self.assertEqual(r.json['access']['user']['name'], - self.user['name']) - self.assertEqual(r.json['access']['user']['roles'], []) - - def test_user_auth_with_role_on_tenant(self): - # Additonal setUp - role = self.create_role().json['role'] - self.grant_role_to_user(self.user['id'], role['id'], self.tenant['id']) - - # Create an unscoped token - unscoped = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}}}).json['access'] - - # The token shouldn't be scoped to a tenant nor have roles just yet - self.assertIsNone(unscoped['token'].get('tenant')) - self.assertIsNotNone(unscoped.get('user')) - self.assertIsNotNone(unscoped['user'].get('roles')) - self.assertEqual(len(unscoped['user']['roles']), 0) - self.assertEqual(unscoped['user'].get('id'), self.user['id']) - self.assertEqual(unscoped['user'].get('name'), self.user['name']) - - # Request our tenant list as a service user - self.service_token = unscoped['token']['id'] - tenants = self.service_request(method='GET', path='/tenants').\ - json['tenants'] - self.service_token = None # Should become a service_request() param... - - # Our tenant should be the only tenant in the list - self.assertEqual(len(tenants), 1, tenants) - self.assertEqual(self.tenant['id'], tenants[0]['id']) - self.assertEqual(self.tenant['name'], tenants[0]['name']) - self.assertEqual(self.tenant['description'], tenants[0]['description']) - self.assertEqual(self.tenant['enabled'], tenants[0]['enabled']) - - # We can now get a token scoped to our tenant - scoped = self.post_token(as_json={ - 'auth': { - 'token': { - 'id': unscoped['token']['id']}, - 'tenantId': self.tenant['id']}}).json['access'] - - self.assertEqual(scoped['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(scoped['token']['tenant']['name'], - self.tenant['name']) - self.assertIn('tenants', scoped['token']) - self.assertEqual(scoped['token']['tenants'][0]['id'], - self.tenant['id']) - self.assertEqual(scoped['token']['tenants'][0]['name'], - self.tenant['name']) - self.assertEqual( - scoped['user']['roles'][0]['id'], role['id']) - self.assertEqual(scoped['user']['roles'][0]['name'], role['name']) - self.assertEqual(scoped['user']['roles'][0]['tenantId'], - self.tenant['id']) - - # And an admin should be able to validate that our new token is scoped - r = self.validate_token(scoped['token']['id'], self.tenant['id']) - access = r.json['access'] - - self.assertEqual(access['user']['id'], self.user['id']) - self.assertEqual(access['user']['name'], self.user['name']) - self.assertEqual(access['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(access['token']['tenant']['name'],\ - self.tenant['name']) - - def test_user_auth_with_role_on_tenant_xml(self): - # Additonal setUp - tenant = self.create_tenant().json['tenant'] - role = self.create_role().json['role'] - self.grant_role_to_user(self.user['id'], role['id'], tenant['id']) - - # Create an unscoped token - r = self.post_token(as_xml='<?xml version="1.0" encoding="UTF-8"?>' - '<auth xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"' - 'xmlns="http://docs.openstack.org/identity/api/v2.0">' - '<passwordCredentials username="%s" password="%s"/>' - '</auth>' % (self.user['name'], self.user['password'])) - - # The token shouldn't be scoped to a tenant nor have roles just yet - self.assertEqual(r.xml.tag, '{%s}access' % self.xmlns) - - token = r.xml.find('{%s}token' % self.xmlns) - self.assertIsNotNone(token) - self.assertIsNotNone(token.get('id')) - self.assertIsNotNone(token.get('expires')) - - user = r.xml.find('{%s}user' % self.xmlns) - self.assertIsNotNone(user) - self.assertEqual(user.get('id'), self.user['id']) - self.assertEqual(user.get('name'), self.user['name']) - self.assertIsNone(user.get('tenantId')) - - roles = user.find('{%s}roles' % self.xmlns) - self.assertIsNotNone(roles) - self.assertEqual(len(roles), 0) - - # Request our tenant list as a service user - self.service_token = token.get('id') - r = self.service_request(method='GET', path='/tenants', headers={ - 'Accept': 'application/xml'}) - - self.assertEqual(r.xml.tag, '{%s}tenants' % self.xmlns) - tenants = r.xml.findall('{%s}tenant' % self.xmlns) - - # Our tenant should be the only tenant in the list - self.assertEqual(len(tenants), 1, tenants) - self.assertEqual(tenant['id'], tenants[0].get('id')) - self.assertEqual(tenant['name'], tenants[0].get('name')) - self.assertEqual(str(tenant['enabled']).lower(), - tenants[0].get('enabled')) - description = tenants[0].find('{%s}description' % self.xmlns) - self.assertEqual(tenant['description'], description.text) - - # We can now get a token scoped to our tenant - r = self.post_token(as_xml='<?xml version="1.0" encoding="UTF-8"?>' - '<auth xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' - 'xmlns="http://docs.openstack.org/identity/api/v2.0" ' - 'tenantId="%s">' - '<passwordCredentials username="%s" password="%s"/>' - '</auth>' % (tenant['id'], self.user['name'], - self.user['password'])) - - self.assertEqual(r.xml.tag, '{%s}access' % self.xmlns) - - token = r.xml.find('{%s}token' % self.xmlns) - self.assertIsNotNone(token) - tenant_scope = token.find('{%s}tenant' % self.xmlns) - self.assertIsNotNone(tenant_scope) - self.assertEqual(tenant_scope.get('id'), tenant['id']) - self.assertEqual(tenant_scope.get('name'), tenant['name']) - - # And an admin should be able to validate that our new token is scoped - r = self.validate_token(token.get('id'), tenant['id'], headers={ - 'Accept': 'application/xml'}) - self.assertEqual(r.xml.tag, '{%s}access' % self.xmlns) - - token = r.xml.find('{%s}token' % self.xmlns) - self.assertIsNotNone(token) - tenant_scope = token.find('{%s}tenant' % self.xmlns) - self.assertIsNotNone(tenant_scope) - self.assertEqual(tenant_scope.get('id'), tenant['id']) - self.assertEqual(tenant_scope.get('name'), tenant['name']) - - user = r.xml.find('{%s}user' % self.xmlns) - self.assertIsNotNone(user) - self.assertEqual(user.get('id'), self.user['id']) - self.assertEqual(user.get('name'), self.user['name']) - self.assertIsNone(user.get('tenantId')) - - def test_scope_to_tenant_by_name(self): - # Additonal setUp - tenant = self.create_tenant().json['tenant'] - role = self.create_role().json['role'] - self.grant_role_to_user(self.user['id'], role['id'], tenant['id']) - - # Create an unscoped token - unscoped = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}}}).json['access'] - - # We can now get a token scoped to our tenant - scoped = self.post_token(as_json={ - 'auth': { - 'token': { - 'id': unscoped['token']['id']}, - 'tenantName': tenant['name']}}).json['access'] - - self.assertEqual(scoped['token']['tenant']['id'], tenant['id']) - self.assertEqual(scoped['token']['tenant']['name'], tenant['name']) - - # And an admin should be able to validate that our new token is scoped - r = self.validate_token(scoped['token']['id'], tenant['id']) - access = r.json['access'] - - self.assertEqual(access['user']['id'], self.user['id']) - self.assertEqual(access['user']['name'], self.user['name']) - self.assertEqual(access['token']['tenant']['id'], tenant['id']) - self.assertEqual(access['token']['tenant']['name'], tenant['name']) - - def test_scope_to_tenant_by_name_with_credentials(self): - # Additonal setUp - tenant = self.create_tenant().json['tenant'] - role = self.create_role().json['role'] - self.grant_role_to_user(self.user['id'], role['id'], tenant['id']) - - scoped = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}, - 'tenantName': tenant['name']}}).json['access'] - - self.assertEqual(scoped['token']['tenant']['id'], tenant['id']) - self.assertEqual(scoped['token']['tenant']['name'], tenant['name']) - - # And an admin should be able to validate that our new token is scoped - r = self.validate_token(scoped['token']['id'], tenant['id']) - access = r.json['access'] - - self.assertEqual(access['user']['id'], self.user['id']) - self.assertEqual(access['user']['name'], self.user['name']) - self.assertEqual(access['token']['tenant']['id'], tenant['id']) - self.assertEqual(access['token']['tenant']['name'], tenant['name']) - - def test_user_auth_against_nonexistent_tenant(self): - # Create an unscoped token - unscoped = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}}}).json['access'] - - # Invalid tenant id - self.post_token(assert_status=401, as_json={ - 'auth': { - 'token': {'id': unscoped['token']['id']}, - 'tenantId': common.unique_str()}}) - - # Invalid tenant name - self.post_token(assert_status=401, as_json={ - 'auth': { - 'token': {'id': unscoped['token']['id']}, - 'tenantName': common.unique_str()}}) - - # Invalid tenant id - self.post_token(assert_status=401, as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}, - 'tenantId': common.unique_str()}}) - - # Invalid tenant name - self.post_token(assert_status=401, as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}, - 'tenantName': common.unique_str()}}) - - def test_scope_to_tenant_by_bad_request(self): - # Additonal setUp - tenant = self.create_tenant().json['tenant'] - role = self.create_role().json['role'] - self.grant_role_to_user(self.user['id'], role['id'], tenant['id']) - - # Create an unscoped token - unscoped = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}}}).json['access'] - - # tenant Name & ID should never be provided together - self.post_token(as_json={ - 'auth': { - 'tokenId': unscoped['token']['id'], - 'tenantId': tenant['id'], - 'tenantName': tenant['name']}}, assert_status=400) - - def test_get_request_fails(self): - """GET /tokens should return a 404 (Github issue #5)""" - self.service_request(method='GET', path='/tokens', assert_status=404) - - def test_user_auth_with_malformed_request_body(self): - """Authenticating with unnexpected json returns a 400""" - # Authenticate as user to get a token - self.post_token(assert_status=400, as_json={ - 'this-is-completely-wrong': { - 'username': self.user['name'], - 'password': self.user['password']}}) - - def test_user_auth_with_wrong_name(self): - """Authenticating with an unknown username returns a 401""" - # Authenticate as user to get a token - self.post_token(assert_status=401, as_json={ - 'auth': {'passwordCredentials': { - 'username': 'this-is-completely-wrong', - 'password': self.user['password']}}}) - - def test_user_auth_with_no_name(self): - """Authenticating without a username returns a 400""" - # Authenticate as user to get a token - self.post_token(assert_status=400, as_json={ - 'auth': {'passwordCredentials': { - 'password': self.user['password']}}}) - - def test_user_auth_with_wrong_password(self): - """Authenticating with an invalid password returns a 401""" - # Authenticate as user to get a token - self.post_token(assert_status=401, as_json={ - 'auth': {'passwordCredentials': { - 'username': self.user['name'], - 'password': 'this-is-completely-wrong'}}}) - - def test_user_auth_with_no_password(self): - """Authenticating with an invalid password returns a 400""" - # Authenticate as user to get a token - self.post_token(assert_status=400, as_json={ - 'auth': {'passwordCredentials': { - 'username': self.user['name'], - 'password': None}}}) - - def test_user_auth_with_invalid_tenant(self): - """Authenticating with an invalid password returns a 401""" - # Authenticate as user to get a token - self.post_token(assert_status=401, as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password'], - }, - 'tenantId': 'this-is-completely-wrong'}}) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/functional/test_authentication.py b/keystone/test/functional/test_authentication.py deleted file mode 100644 index a029fbd7..00000000 --- a/keystone/test/functional/test_authentication.py +++ /dev/null @@ -1,352 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-2011 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 unittest2 as unittest - -from keystone.test.functional import common - - -class AuthenticationTest(common.FunctionalTestCase): - def setUp(self, *args, **kwargs): - super(AuthenticationTest, self).setUp(*args, **kwargs) - - password = common.unique_str() - self.tenant = self.create_tenant().json['tenant'] - self.user = self.create_user(user_password=password, - tenant_id=self.tenant['id']).json['user'] - self.user['password'] = password - - self.services = {} - self.endpoint_templates = {} - self.services = self.create_service().json['OS-KSADM:service'] - self.endpoint_templates = self.create_endpoint_template( - name=self.services['name'], \ - type=self.services['type']).\ - json['OS-KSCATALOG:endpointTemplate'] - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_templates['id']) - - def test_authenticate_for_a_tenant(self): - response = self.authenticate(self.user['name'], self.user['password'], - self.tenant['id'], assert_status=200) - - self.assertIsNotNone(response.json['access']['token']) - service_catalog = response.json['access']['serviceCatalog'] - self.assertIsNotNone(service_catalog) - self.check_urls_for_regular_user(service_catalog) - - def test_authenticate_for_a_tenant_xml(self): - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<auth xmlns="%s" tenantId="%s">' - '<passwordCredentials username="%s" password="%s" ' - '/> </auth>') % ( - self.xmlns, self.tenant['id'], - self.user['name'], self.user['password']) - response = self.post_token(as_xml=data, assert_status=200) - - self.assertEquals(response.xml.tag, '{%s}access' % self.xmlns) - service_catalog = response.xml.find('{%s}serviceCatalog' % self.xmlns) - self.check_urls_for_regular_user_xml(service_catalog) - - def test_authenticate_for_a_tenant_on_admin_api(self): - response = self.authenticate(self.user['name'], self.user['password'], - self.tenant['id'], assert_status=200, request_type='admin') - - self.assertIsNotNone(response.json['access']['token']) - self.assertIsNotNone(response.json['access']['serviceCatalog']) - service_catalog = response.json['access']['serviceCatalog'] - self.check_urls_for_regular_user(service_catalog) - - def test_authenticate_for_a_tenant_xml_on_admin_api(self): - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<auth xmlns="%s" tenantId="%s">' - '<passwordCredentials username="%s" password="%s" ' - '/> </auth>') % ( - self.xmlns, self.tenant['id'], - self.user['name'], self.user['password']) - response = self.post_token(as_xml=data, assert_status=200, - request_type='admin') - - self.assertEquals(response.xml.tag, '{%s}access' % self.xmlns) - service_catalog = response.xml.find('{%s}serviceCatalog' % self.xmlns) - self.check_urls_for_regular_user_xml(service_catalog) - - def test_authenticate_user_disabled(self): - self.disable_user(self.user['id']) - self.authenticate(self.user['name'], self.user['password'], - self.tenant['id'], assert_status=403) - - def test_authenticate_user_wrong(self): - data = { - "auth": { - "passwordCredentials": { - "username-field-completely-wrong": self.user['name'], - "password": self.user['password']}, - "tenantId": self.tenant['id']}} - self.post_token(as_json=data, assert_status=400) - - def test_authenticate_user_wrong_xml(self): - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<passwordCredentials ' - 'xmlns="http://docs.openstack.org/identity/api/v2.0" ' - 'usernamefieldcompletelywrong="%s" ' - 'password="%s" ' - 'tenantId="%s"/>') % ( - self.user['name'], self.user['password'], self.tenant['id']) - - self.post_token(as_xml=data, assert_status=400) - - -class AuthenticationUsingTokenTest(common.FunctionalTestCase): - def setUp(self, *args, **kwargs): - super(AuthenticationUsingTokenTest, self).setUp(*args, **kwargs) - password = common.unique_str() - self.tenant = self.create_tenant().json['tenant'] - self.user = self.create_user(user_password=password, - tenant_id=self.tenant['id']).json['user'] - self.user['password'] = password - - self.services = {} - self.endpoint_templates = {} - for x in range(0, 5): - self.services[x] = self.create_service().json['OS-KSADM:service'] - self.endpoint_templates[x] = self.create_endpoint_template( - name=self.services[x]['name'], \ - type=self.services[x]['type']).\ - json['OS-KSCATALOG:endpointTemplate'] - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_templates[x]['id']) - self.token = self.authenticate(self.user['name'], - self.user['password']).json['access']['token']['id'] - - def test_authenticate_for_a_tenant_using_token(self): - response = self.authenticate_using_token(self.token, - self.tenant['id'], assert_status=200) - - self.assertIsNotNone(response.json['access']['token']) - service_catalog = response.json['access']['serviceCatalog'] - self.assertIsNotNone(service_catalog) - self.check_urls_for_regular_user(service_catalog) - - def test_authenticate_for_a_tenant_xml(self): - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<auth xmlns="%s" tenantId="%s">' - '<token id="%s" ' - '/> </auth>') % ( - self.xmlns, self.tenant['id'], - self.token) - response = self.post_token(as_xml=data, assert_status=200) - - self.assertEquals(response.xml.tag, '{%s}access' % self.xmlns) - service_catalog = response.xml.find('{%s}serviceCatalog' % self.xmlns) - self.check_urls_for_regular_user_xml(service_catalog) - - def test_authenticate_for_a_tenant_on_admin_api(self): - response = self.authenticate_using_token(self.token, - self.tenant['id'], request_type='admin') - - self.assertIsNotNone(response.json['access']['token']) - self.assertIsNotNone(response.json['access']['serviceCatalog']) - service_catalog = response.json['access']['serviceCatalog'] - self.check_urls_for_regular_user(service_catalog) - - def test_authenticate_for_a_tenant_xml_on_admin_api(self): - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<auth xmlns="%s" tenantId="%s">' - '<token id="%s" ' - '/> </auth>') % ( - self.xmlns, self.tenant['id'], - self.token) - response = self.post_token(as_xml=data, assert_status=200, - request_type='admin') - - self.assertEquals(response.xml.tag, '{%s}access' % self.xmlns) - service_catalog = response.xml.find('{%s}serviceCatalog' % self.xmlns) - self.check_urls_for_regular_user_xml(service_catalog) - - -class UnScopedAuthenticationTest(common.FunctionalTestCase): - def setUp(self, *args, **kwargs): - super(UnScopedAuthenticationTest, self).setUp(*args, **kwargs) - - self.tenant = self.create_tenant().json['tenant'] - self.user = self.create_user_with_known_password( - tenant_id=self.tenant['id']).json['user'] - - self.services = {} - self.endpoint_templates = {} - for x in range(0, 5): - self.services[x] = self.create_service().json['OS-KSADM:service'] - self.endpoint_templates[x] = self.create_endpoint_template( - name=self.services[x]['name'], \ - type=self.services[x]['type']).\ - json['OS-KSCATALOG:endpointTemplate'] - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_templates[x]['id']) - - def test_authenticate(self): - response = self.authenticate(self.user['name'], self.user['password'],\ - assert_status=200) - - self.assertIsNotNone(response.json['access']['token']) - service_catalog = response.json['access'].get('serviceCatalog') - self.assertIsNotNone(service_catalog, response.json) - self.check_urls_for_regular_user(service_catalog) - - def test_authenticate_xml(self): - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<auth xmlns="%s" >' - '<passwordCredentials username="%s" password="%s" ' - '/> </auth>') % ( - self.xmlns, self.user['name'], - self.user['password']) - response = self.post_token(as_xml=data, assert_status=200) - - self.assertEquals(response.xml.tag, '{%s}access' % self.xmlns) - service_catalog = response.xml.find('{%s}serviceCatalog' % self.xmlns) - self.check_urls_for_regular_user_xml(service_catalog) - - def test_authenticate_on_admin_api(self): - response = self.authenticate(self.user['name'], self.user['password'], - assert_status=200, request_type='admin') - - self.assertIsNotNone(response.json['access'].get('token'), - response.json) - self.assertIsNotNone(response.json['access'].get('serviceCatalog'), - response.json) - service_catalog = response.json['access']['serviceCatalog'] - self.check_urls_for_regular_user(service_catalog) - - def test_authenticate_for_a_tenant_xml_on_admin_api(self): - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<auth xmlns="%s" tenantId="%s">' - '<passwordCredentials username="%s" password="%s" ' - '/> </auth>') % ( - self.xmlns, self.tenant['id'], - self.user['name'], self.user['password']) - response = self.post_token(as_xml=data, - assert_status=200, request_type='admin') - - self.assertEquals(response.xml.tag, '{%s}access' % self.xmlns) - service_catalog = response.xml.find('{%s}serviceCatalog' % self.xmlns) - self.check_urls_for_regular_user_xml(service_catalog) - - def test_authenticate_without_default_tenant(self): - # Create user with no default tenant set (but granted a role) - self.nodefaultuser = self.create_user_with_known_password()\ - .json['user'] - self.role = self.create_role().json['role'] - self.grant_role_to_user(self.nodefaultuser['id'], self.role['id'], - self.tenant['id']) - - response = self.authenticate(self.nodefaultuser['name'], - self.nodefaultuser['password'], - tenant_id=None, assert_status=200) - - self.assertIsNotNone(response.json['access']['token']) - self.assertNotIn('tenant', response.json['access']['token']) - - -class AdminUserAuthenticationTest(common.FunctionalTestCase): - def setUp(self, *args, **kwargs): - super(AdminUserAuthenticationTest, self).setUp(*args, **kwargs) - - password = common.unique_str() - self.tenant = self.create_tenant().json['tenant'] - self.user = self.create_user(user_password=password, - tenant_id=self.tenant['id']).json['user'] - self.role = self.get_role_by_name('Admin').json['role'] - self.grant_global_role_to_user(self.user['id'], self.role['id']) - self.user['password'] = password - - self.services = {} - self.endpoint_templates = {} - for x in range(0, 5): - self.services[x] = self.create_service().json['OS-KSADM:service'] - self.endpoint_templates[x] = self.create_endpoint_template( - name=self.services[x]['name'], \ - type=self.services[x]['type']).\ - json['OS-KSCATALOG:endpointTemplate'] - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_templates[x]['id']) - - def test_authenticate(self): - response = self.authenticate(self.user['name'], self.user['password'],\ - assert_status=200) - - self.assertIsNotNone(response.json['access']['token']) - service_catalog = response.json['access']['serviceCatalog'] - self.assertIsNotNone(service_catalog) - self.check_urls_for_admin_user(service_catalog) - - def test_authenticate_xml(self): - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<auth xmlns="%s" >' - '<passwordCredentials username="%s" password="%s" ' - '/> </auth>') % ( - self.xmlns, self.user['name'], - self.user['password']) - response = self.post_token(as_xml=data, assert_status=200) - - self.assertEquals(response.xml.tag, '{%s}access' % self.xmlns) - service_catalog = response.xml.find('{%s}serviceCatalog' % self.xmlns) - self.check_urls_for_admin_user_xml(service_catalog) - - def test_authenticate_for_a_tenant(self): - response = self.authenticate(self.user['name'], self.user['password'], - self.tenant['id'], assert_status=200) - - self.assertIsNotNone(response.json['access']['token']) - service_catalog = response.json['access']['serviceCatalog'] - self.assertIsNotNone(service_catalog) - self.check_urls_for_admin_user(service_catalog) - - def test_authenticate_for_a_tenant_xml(self): - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<auth xmlns="%s" tenantId="%s">' - '<passwordCredentials username="%s" password="%s" ' - '/> </auth>') % ( - self.xmlns, self.tenant['id'], - self.user['name'], self.user['password']) - response = self.post_token(as_xml=data, assert_status=200) - - self.assertEquals(response.xml.tag, '{%s}access' % self.xmlns) - service_catalog = response.xml.find('{%s}serviceCatalog' % self.xmlns) - self.check_urls_for_admin_user_xml(service_catalog) - - -class MultiTokenTest(common.FunctionalTestCase): - def setUp(self, *args, **kwargs): - super(MultiTokenTest, self).setUp(*args, **kwargs) - - self.tenants = {} - self.users = {} - for x in range(0, 2): - self.tenants[x] = self.create_tenant().json['tenant'] - - password = common.unique_str() - self.users[x] = self.create_user(user_password=password, - tenant_id=self.tenants[x]['id']).json['user'] - self.users[x]['password'] = password - - def test_unassigned_user(self): - self.authenticate(self.users[1]['name'], self.users[1]['password'], - self.tenants[0]['id'], assert_status=401) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/functional/test_credentials.py b/keystone/test/functional/test_credentials.py deleted file mode 100644 index 2db01579..00000000 --- a/keystone/test/functional/test_credentials.py +++ /dev/null @@ -1,231 +0,0 @@ -import unittest2 as unittest -from keystone.test.functional import common - - -class TestGetCredentials(common.FunctionalTestCase): - """Test Get credentials operations""" - - def setUp(self): - super(TestGetCredentials, self).setUp() - self.fixture_create_normal_user() - - def test_get_user_credentials(self): - password_credentials = self.fetch_user_credentials( - self.user['id']).json['credentials'][0]['passwordCredentials'] - self.assertEquals(password_credentials['username'], self.user['name']) - - def test_get_user_credentials_xml(self): - r = self.fetch_user_credentials(self.user['id'], - assert_status=200, headers={ - 'Accept': 'application/xml'}) - self.assertEquals(r.xml.tag, '{%s}credentials' % self.xmlns) - password_credentials =\ - r.xml.find('{%s}passwordCredentials' % self.xmlns) - self.assertEqual( - password_credentials.get('username'), self.user['name']) - - def test_get_user_credentials_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.fetch_user_credentials(self.user['id'], assert_status=403) - - def test_get_user_credentials_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.fetch_user_credentials(self.user['id'], assert_status=403) - - def test_get_user_credentials_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.fetch_user_credentials(self.user['name'], assert_status=403) - - def test_get_user_credentials_using_missing_token(self): - self.admin_token = '' - self.fetch_user_credentials(self.user['id'], assert_status=401) - - def test_get_user_credentials_using_invalid_token(self): - self.admin_token = common.unique_str() - self.fetch_user_credentials(self.user['id'], assert_status=401) - - -class TestGetPasswordCredentials(common.FunctionalTestCase): - """Test get password credentials operations""" - - def setUp(self): - super(TestGetPasswordCredentials, self).setUp() - self.fixture_create_normal_user() - - def test_get_user_credentials(self): - password_credentials = self.fetch_password_credentials( - self.user['id']).json['passwordCredentials'] - self.assertEquals(password_credentials['username'], self.user['name']) - - def test_get_user_credentials_xml(self): - r = self.fetch_password_credentials(self.user['id'], - assert_status=200, headers={ - 'Accept': 'application/xml'}) - password_credentials = r.xml - self.assertEqual( - password_credentials.get('username'), self.user['name']) - - def test_get_user_credentials_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.fetch_password_credentials(self.user['id'], assert_status=403) - - def test_get_user_credentials_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.fetch_password_credentials(self.user['id'], assert_status=403) - - def test_get_user_credentials_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.fetch_password_credentials(self.user['name'], assert_status=403) - - def test_get_user_credentials_using_missing_token(self): - self.admin_token = '' - self.fetch_password_credentials(self.user['id'], assert_status=401) - - def test_get_user_credentials_using_invalid_token(self): - self.admin_token = common.unique_str() - self.fetch_password_credentials(self.user['id'], assert_status=401) - - -class TestCreatePasswordCredentials(common.FunctionalTestCase): - """Test create password credentials operations""" - - def setUp(self): - super(TestCreatePasswordCredentials, self).setUp() - self.fixture_create_normal_user() - self.delete_user_credentials_by_type( - self.user['id'], 'passwordCredentials') - - def test_create_password_credentials(self): - self.create_password_credentials( - self.user['id'], self.user['name'], - assert_status=201) - - def test_create_password_credentials_using_empty_password(self): - self.create_password_credentials( - user_id=self.user['id'], user_name=self.user['name'], password='',\ - assert_status=400) - - def test_create_password_credentials_xml(self): - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<passwordCredentials xmlns="%s"' - ' username="%s" password="%s"/>') % ( - self.xmlns, self.user['name'], 'passw0rd') - self.post_credentials(self.user['id'], as_xml=data, assert_status=201) - - def test_create_password_credentials_xml_using_empty_password(self): - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<passwordCredentials xmlns="%s"' - ' username="%s" password="%s"/>') % ( - self.xmlns, self.user['name'], '') - self.post_credentials(self.user['id'], as_xml=data, assert_status=400) - - def test_create_password_credentials_twice(self): - self.create_password_credentials(self.user['id'], self.user['name'], - assert_status=201) - self.create_password_credentials(self.user['id'], self.user['name'], - assert_status=400) - - def test_create_password_credentials_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.create_password_credentials(self.user['id'], self.user['name'], - assert_status=403) - - def test_create_password_credentials_missing_token(self): - self.admin_token = '' - self.create_password_credentials(self.user['id'], self.user['name'], - assert_status=401) - - def test_create_password_credentials_invalid_token(self): - self.admin_token = common.unique_str() - self.create_password_credentials(self.user['id'], self.user['name'], - assert_status=401) - - -class TestUpdatePasswordCredentials(common.FunctionalTestCase): - """Test update password credentials operations""" - - def setUp(self): - super(TestUpdatePasswordCredentials, self).setUp() - self.fixture_create_normal_user() - - def test_update_password_credentials(self): - self.update_password_credentials(self.user['id'], self.user['name'], - assert_status=200) - - def test_update_password_credentials_xml(self): - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<passwordCredentials xmlns="%s"' - ' username="%s" password="%s"/>') % ( - self.xmlns, self.user['name'], 'passw0rd') - self.post_credentials_by_type(self.user['id'], 'passwordCredentials', - as_xml=data, assert_status=200) - - def test_update_password_credentials_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.update_password_credentials(self.user['id'], self.user['name'], - assert_status=403) - - def test_update_password_credentials_missing_token(self): - self.admin_token = '' - self.update_password_credentials(self.user['id'], self.user['name'], - assert_status=401) - - def test_update_password_credentials_invalid_token(self): - self.admin_token = common.unique_str() - self.update_password_credentials(self.user['id'], self.user['name'], - assert_status=401) - - -class TestDeletePasswordCredentials(common.FunctionalTestCase): - """Test delete password credentials operations""" - - def setUp(self): - super(TestDeletePasswordCredentials, self).setUp() - self.fixture_create_normal_user() - - def test_delete_password_credentials(self): - self.delete_password_credentials(self.user['id'], - assert_status=204) - - def test_delete_password_credentials_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.delete_password_credentials(self.user['id'], - assert_status=403) - - def test_delete_password_credentials_missing_token(self): - self.admin_token = '' - self.delete_password_credentials(self.user['id'], - assert_status=401) - - def test_delete_password_credentials_invalid_token(self): - self.admin_token = common.unique_str() - self.delete_password_credentials(self.user['id'], - assert_status=401) - - -class TestAuthentication(common.FunctionalTestCase): - """Test authentication after a password update.""" - def setUp(self): - super(TestAuthentication, self).setUp() - self.fixture_create_normal_user() - - def test_authentication_after_password_change(self): - self.authenticate(self.user['name'], self.user['password'], - assert_status=200) - password = common.unique_str() - self.update_password_credentials(self.user['id'], self.user['name'], - password=password, assert_status=200) - self.authenticate(self.user['name'], password, - assert_status=200) - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/functional/test_endpoints.py b/keystone/test/functional/test_endpoints.py deleted file mode 100644 index 9f7c989e..00000000 --- a/keystone/test/functional/test_endpoints.py +++ /dev/null @@ -1,740 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-2011 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 unittest2 as unittest -from keystone.test.functional import common - - -class EndpointTemplatesTest(common.FunctionalTestCase): - def setUp(self, *args, **kwargs): - super(EndpointTemplatesTest, self).setUp(*args, **kwargs) - - self.service = self.create_service().json['OS-KSADM:service'] - - self.endpoint_template = self.create_endpoint_template( - name=self.service['name'], \ - type=self.service['type']).\ - json['OS-KSCATALOG:endpointTemplate'] - - self.fixture_create_service_admin() - admin_token = self.admin_token - self.admin_token = self.service_admin_token - self.my_service = self.create_service().json['OS-KSADM:service'] - self.admin_token = admin_token - - -class CreateEndpointTemplatesTest(EndpointTemplatesTest): - def test_create_endpoint_template(self): - endpoint_template = self.create_endpoint_template( - name=self.service['name'], - type=self.service['type'], - assert_status=201).\ - json['OS-KSCATALOG:endpointTemplate'] - - self.assertIsNotNone(endpoint_template['id'], endpoint_template) - self.assertIsNotNone(endpoint_template['name'], endpoint_template) - self.assertIsNotNone(endpoint_template['type'], endpoint_template) - - def test_create_endpoint_template_with_empty_name(self): - self.create_endpoint_template( - name=self.service['name'], - type='', - assert_status=400) - - def test_create_endpoint_template_with_empty_type(self): - self.create_endpoint_template( - name='', - type=self.service['type'], - assert_status=400) - - def test_create_endpoint_template_xml(self): - region = common.unique_str() - public_url = common.unique_url() - admin_url = common.unique_url() - internal_url = common.unique_url() - enabled = True - is_global = True - - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<endpointTemplate xmlns="%s" region="%s" name="%s" ' - 'type="%s" publicURL="%s" adminURL="%s" ' - 'internalURL="%s" enabled="%s" global="%s"/>' - ) % (self.xmlns_kscatalog, region, self.service['name'], - self.service['type'], public_url, admin_url, internal_url, - enabled, is_global) - r = self.post_endpoint_template(as_xml=data, assert_status=201) - - self.assertEqual(r.xml.tag, - '{%s}endpointTemplate' % self.xmlns_kscatalog) - - self.assertIsNotNone(r.xml.get("id")) - self.assertEqual(r.xml.get("name"), self.service['name']) - self.assertEqual(r.xml.get("type"), self.service['type']) - self.assertEqual(r.xml.get("region"), region) - self.assertEqual(r.xml.get("publicURL"), public_url) - self.assertEqual(r.xml.get("adminURL"), admin_url) - self.assertEqual(r.xml.get("internalURL"), internal_url) - self.assertEqual(r.xml.get("enabled"), str(enabled).lower()) - self.assertEqual(r.xml.get("global"), str(is_global).lower()) - - def test_create_endpoint_template_xml_using_empty_type(self): - region = common.unique_str() - public_url = common.unique_url() - admin_url = common.unique_url() - internal_url = common.unique_url() - enabled = True - is_global = True - - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<endpointTemplate xmlns="%s" region="%s" name="%s" ' - 'type="%s" publicURL="%s" adminURL="%s" ' - 'internalURL="%s" enabled="%s" global="%s"/>' - ) % (self.xmlns_kscatalog, region, self.service['name'], - '', public_url, admin_url, internal_url, - enabled, is_global) - self.post_endpoint_template(as_xml=data, assert_status=400) - - def test_create_endpoint_template_xml_using_empty_name(self): - region = common.unique_str() - public_url = common.unique_url() - admin_url = common.unique_url() - internal_url = common.unique_url() - enabled = True - is_global = True - - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<endpointTemplate xmlns="%s" region="%s" name="%s" ' - 'type="%s" publicURL="%s" adminURL="%s" ' - 'internalURL="%s" enabled="%s" global="%s"/>' - ) % (self.xmlns_kscatalog, region, '', - self.service['type'], public_url, admin_url, internal_url, - enabled, is_global) - self.post_endpoint_template(as_xml=data, assert_status=400) - - def test_delete_endpoint_template_that_has_dependencies(self): - tenant = self.create_tenant().json['tenant'] - - self.create_endpoint_for_tenant(tenant['id'], - self.endpoint_template['id'], assert_status=201) - - self.remove_endpoint_template(self.endpoint_template['id'], - assert_status=204) - - def test_create_endpoint_template_using_service_admin_token(self): - self.admin_token = self.service_admin_token - endpoint_template = self.create_endpoint_template( - name=self.my_service['name'], - type=self.my_service['type'], - assert_status=201).\ - json['OS-KSCATALOG:endpointTemplate'] - - self.assertIsNotNone(endpoint_template['id']) - self.assertEqual(endpoint_template['name'], self.my_service['name']) - self.assertEqual(endpoint_template['type'], self.my_service['type']) - - -class GetEndpointTemplatesTest(EndpointTemplatesTest): - def test_get_endpoint_templates(self): - r = self.list_endpoint_templates(assert_status=200) - self.assertIsNotNone(r.json['OS-KSCATALOG:endpointTemplates']) - - def test_get_endpoint_templates_using_service_admin_token(self): - self.admin_token = self.service_admin_token - r = self.list_endpoint_templates(assert_status=200) - self.assertIsNotNone(r.json['OS-KSCATALOG:endpointTemplates']) - - def test_get_endpoint_templates_using_expired_auth_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.list_endpoint_templates(assert_status=403) - - def test_get_endpoint_templates_using_disabled_auth_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.list_endpoint_templates(assert_status=403) - - def test_get_endpoint_templates_using_missing_auth_token(self): - self.admin_token = '' - self.list_endpoint_templates(assert_status=401) - - def test_get_endpoint_templates_using_invalid_auth_token(self): - self.admin_token = common.unique_str() - self.list_endpoint_templates(assert_status=401) - - def test_get_endpoint_templates_xml(self): - r = self.get_endpoint_templates(assert_status=200, headers={ - 'Accept': 'application/xml'}) - self.assertEqual(r.xml.tag, - "{%s}endpointTemplates" % self.xmlns_kscatalog) - - def test_get_endpoint_templates_xml_expired_auth_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_endpoint_templates(assert_status=403, headers={ - 'Accept': 'application/xml'}) - - def test_get_endpoint_templates_xml_disabled_auth_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.get_endpoint_templates(assert_status=403, headers={ - 'Accept': 'application/xml'}) - - def test_get_endpoint_templates_xml_missing_auth_token(self): - self.admin_token = '' - self.get_endpoint_templates(assert_status=401, headers={ - 'Accept': 'application/xml'}) - - def test_get_endpoint_templates_xml_invalid_auth_token(self): - self.admin_token = common.unique_str() - self.get_endpoint_templates(assert_status=401, headers={ - 'Accept': 'application/xml'}) - - -class GetEndpointTemplatesByServiceTest(EndpointTemplatesTest): - def test_get_endpoint_templates(self): - r = self.list_endpoint_templates( - service_id=self.service['id'], assert_status=200) - self.assertIsNotNone(r.json['OS-KSCATALOG:endpointTemplates']) - self.assertEquals(len(r.json['OS-KSCATALOG:endpointTemplates']), 1) - self.assertEquals(r.json['OS-KSCATALOG:endpointTemplates'][0]['name'], - self.service['name']) - self.assertEquals(r.json['OS-KSCATALOG:endpointTemplates'][0]['type'], - self.service['type']) - - def test_get_endpoint_templates_using_service_admin_token(self): - self.admin_token = self.service_admin_token - r = self.list_endpoint_templates(service_id=self.service['id'], - assert_status=200) - self.assertIsNotNone(r.json['OS-KSCATALOG:endpointTemplates']) - self.assertEquals(len(r.json['OS-KSCATALOG:endpointTemplates']), 1) - self.assertEquals(r.json['OS-KSCATALOG:endpointTemplates'][0]['name'], - self.service['name']) - self.assertEquals( - r.json['OS-KSCATALOG:endpointTemplates'][0]['type'], - self.service['type']) - - def test_get_endpoint_templates_using_expired_auth_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.list_endpoint_templates( - service_id=self.service['id'], assert_status=403) - - def test_get_endpoint_templates_using_disabled_auth_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.list_endpoint_templates( - service_id=self.service['id'], assert_status=403) - - def test_get_endpoint_templates_using_missing_auth_token(self): - self.admin_token = '' - self.list_endpoint_templates(service_id=self.service['id'], - assert_status=401) - - def test_get_endpoint_templates_using_invalid_auth_token(self): - self.admin_token = common.unique_str() - self.list_endpoint_templates(service_id=self.service['id'], - assert_status=401) - - def test_get_endpoint_templates_xml(self): - r = self.get_endpoint_templates_by_service( - service_id=self.service['id'], - assert_status=200, headers={'Accept': 'application/xml'}) - self.assertEqual(r.xml.tag, - "{%s}endpointTemplates" % self.xmlns_kscatalog) - endpoint_template = r.xml.find( - '{%s}endpointTemplate' % self.xmlns_kscatalog) - self.assertIsNotNone(endpoint_template) - self.assertEqual(endpoint_template.get('name'), self.service['name']) - self.assertEqual(endpoint_template.get('type'), self.service['type']) - - def test_get_endpoint_templates_xml_expired_auth_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_endpoint_templates_by_service( - service_id=self.service['id'], assert_status=403, headers={ - 'Accept': 'application/xml'}) - - def test_get_endpoint_templates_xml_disabled_auth_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.get_endpoint_templates_by_service( - service_id=self.service['id'], assert_status=403, headers={ - 'Accept': 'application/xml'}) - - def test_get_endpoint_templates_xml_missing_auth_token(self): - self.admin_token = '' - self.get_endpoint_templates_by_service( - service_id=self.service['id'], assert_status=401, headers={ - 'Accept': 'application/xml'}) - - def test_get_endpoint_templates_xml_invalid_auth_token(self): - self.admin_token = common.unique_str() - self.get_endpoint_templates_by_service( - service_id=self.service['id'], assert_status=401, headers={ - 'Accept': 'application/xml'}) - - -class GetEndpointTemplateTest(EndpointTemplatesTest): - def test_get_endpoint(self): - r = self.fetch_endpoint_template(self.endpoint_template['id']) - self.assertIsNotNone(r.json['OS-KSCATALOG:endpointTemplate']) - -# def test_get_endpoint_using_service_admin_token(self): -# self.admin_token = service_admin_token -# r = self.fetch_endpoint_template(self.endpoint_template['id']) -# self.assertIsNotNone(r.json['endpointTemplate']) - - def test_get_endpoint_using_expired_auth_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.fetch_endpoint_template(self.endpoint_template['id'], - assert_status=403) - - def test_get_endpoint_using_disabled_auth_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.fetch_endpoint_template(self.endpoint_template['id'], - assert_status=403) - - def test_get_endpoint_using_missing_auth_token(self): - self.admin_token = '' - self.fetch_endpoint_template(self.endpoint_template['id'], - assert_status=401) - - def test_get_endpoint_using_invalid_auth_token(self): - self.admin_token = common.unique_str() - self.fetch_endpoint_template(self.endpoint_template['id'], - assert_status=401) - - def test_get_endpoint_xml(self): - r = self.get_endpoint_template(self.endpoint_template['id'], - headers={'Accept': 'application/xml'}, assert_status=200) - - self.assertEqual(r.xml.tag, - "{%s}endpointTemplate" % self.xmlns_kscatalog) - - def test_non_existent_get_endpoint(self): - self.fetch_endpoint_template('99999999', - assert_status=404) - - -class UpdateEndpointTemplateTest(EndpointTemplatesTest): - def test_update_endpoint(self): - self.update_endpoint_template(self.endpoint_template['id'], - name=self.service['name'], type=self.service['type'], - assert_status=201) -# self.assertIsNotNone(r.json['endpointTemplate'].get('enabled'), r.json) - - def test_update_endpoint_with_empty_name(self): - self.update_endpoint_template(self.endpoint_template['id'], - name='', type=self.service['type'], - assert_status=400) - - def test_update_endpoint_with_empty_type(self): - self.update_endpoint_template(self.endpoint_template['id'], - name=self.service['name'], type='', - assert_status=400) - - def test_update_endpoint_xml(self): - region = common.unique_str() - public_url = common.unique_url() - admin_url = common.unique_url() - internal_url = common.unique_url() - enabled = True - is_global = True - - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<endpointTemplate ' - 'xmlns="%s" ' - 'region="%s" name="%s" type="%s"' - ' publicURL="%s" adminURL="%s"' - ' internalURL="%s" enabled="%s" global="%s"/>') % ( - self.xmlns_kscatalog, region, - self.service['name'], self.service['type'], - public_url, admin_url, internal_url, - enabled, is_global) - r = self.put_endpoint_template(self.endpoint_template['id'], - as_xml=data, assert_status=201, headers={ - 'Accept': 'application/xml'}) - - self.assertEqual(r.xml.tag, - '{%s}endpointTemplate' % self.xmlns_kscatalog) - - self.assertIsNotNone(r.xml.get("id")) - self.assertEqual(r.xml.get("name"), self.service['name']) - self.assertEqual(r.xml.get("type"), self.service['type']) - self.assertEqual(r.xml.get("region"), region) - self.assertEqual(r.xml.get("publicURL"), public_url) - self.assertEqual(r.xml.get("adminURL"), admin_url) - self.assertEqual(r.xml.get("internalURL"), internal_url) - self.assertEqual(r.xml.get("enabled"), str(enabled).lower()) - self.assertEqual(r.xml.get("global"), str(is_global).lower()) - -# def test_update_endpoint_using_service_admin_token(self): -# self.admin_token = service_admin_token -# region = common.unique_str() -# public_url = common.unique_url() -# admin_url = common.unique_url() -# internal_url = common.unique_url() -# enabled = True -# is_global = True -# -# r = self.update_endpoint_template(self.endpoint_template['id'], -# region, self.service['id'], public_url, admin_url, internal_url, -# enabled, is_global, assert_status=201) -# -# endpoint_template = r.json.get('endpointTemplate') -# -# self.assertIsNotNone(endpoint_template.get("id"), r.json) -# self.assertEqual(endpoint_template.get("serviceId"), -# self.service['id']) -# self.assertEqual(endpoint_template.get("region"), region) -# self.assertEqual(endpoint_template.get("publicURL"), public_url) -# self.assertEqual(endpoint_template.get("adminURL"), admin_url) -# self.assertEqual(endpoint_template.get("internalURL"), internal_url) -# self.assertEqual(endpoint_template.get("enabled"), -# str(enabled).lower()) -# self.assertEqual(endpoint_template.get("global"), -# str(is_global).lower()) - -# def test_update_endpoint_xml_using_service_admin_token(self): -# self.admin_token = service_admin_token -# -# self.test_update_endpoint_xml() - - def test_update_endpoint_template_with_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.update_endpoint_template(self.endpoint_template['id'], - assert_status=403) - - def test_update_endpoint_template_with_missing_token(self): - self.admin_token = '' - self.update_endpoint_template(self.endpoint_template['id'], - assert_status=401) - - def test_update_endpoint_template_with_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.update_endpoint_template(self.endpoint_template['id'], - assert_status=403) - - def test_update_endpoint_template_with_invalid_token(self): - self.admin_token = common.unique_str() - self.update_endpoint_template(self.endpoint_template['id'], - assert_status=401) - - def test_update_invalid_endpoint_template(self): - self.update_endpoint_template(assert_status=404) - - -class CreateEndpointsTest(EndpointTemplatesTest): - - def setUp(self, *args, **kwargs): - super(CreateEndpointsTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - - def test_endpoint_create_json_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_template['id'], assert_status=403) - - def test_endpoint_create_json_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_template['id'], assert_status=403) - - def test_endpoint_create_json_using_missing_token(self): - self.admin_token = '' - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_template['id'], assert_status=401) - - def test_endpoint_create_json_using_invalid_token(self): - self.admin_token = common.unique_str() - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_template['id'], assert_status=401) - - def test_endpoint_create_json(self): - endpoint = self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_template['id'], assert_status=201).json['endpoint'] - self.assertEqual(str(endpoint["name"]), str(self.service['name'])) - self.assertEqual(str(endpoint["type"]), str(self.service['type'])) - self.assertEqual(endpoint["region"], self.endpoint_template["region"]) - self.assertEqual(endpoint["publicURL"], - self.endpoint_template["publicURL"]) - self.assertEqual(endpoint["adminURL"], - self.endpoint_template["adminURL"]) - self.assertEqual(endpoint["internalURL"], - self.endpoint_template["internalURL"]) - -# def test_endpoint_create_using_service_admin_token(self): -# self.admin_token = service_admin_token -# self.create_endpoint_template(assert_status=201) - - def test_endpoint_create_xml(self): - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<endpointTemplate ' - 'xmlns="%s" id="%s">' - '</endpointTemplate>') % (self.xmlns_kscatalog, - self.endpoint_template["id"]) - r = self.post_tenant_endpoint(self.tenant['id'], - as_xml=data, assert_status=201, - headers={'Accept': 'application/xml'}) - - self.assertEqual(r.xml.tag, - '{%s}endpoint' % self.xmlns) - - self.assertIsNotNone(r.xml.get("id")) - self.assertEqual(r.xml.get("name"), self.service['name']) - self.assertEqual(r.xml.get("type"), self.service['type']) - self.assertEqual(r.xml.get("region"), self.endpoint_template["region"]) - self.assertEqual(r.xml.get("publicURL"), - self.endpoint_template["publicURL"]) - self.assertEqual(r.xml.get("adminURL"), - self.endpoint_template["adminURL"]) - self.assertEqual(r.xml.get("internalURL"), - self.endpoint_template["internalURL"]) - - def test_endpoint_create_xml_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<endpointTemplate ' - 'xmlns="%s" ' - 'region="%s" name="%s"' - ' type="%s" publicURL="%s" adminURL="%s" ' - 'internalURL="%s" enabled="%s" global="%s"/>') % ( - self.xmlns_kscatalog, - common.unique_str(), - self.service['name'], - self.service['type'], common.unique_url(), - common.unique_url(), common.unique_url(), True, True) - self.post_endpoint_template(as_xml=data, assert_status=403, headers={ - 'Accept': 'application/xml'}) - - def test_endpoint_create_xml_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<endpointTemplate ' - 'xmlns="%s" ' - 'region="%s" name="%s" type="%s" publicURL="%s" adminURL="%s" ' - 'internalURL="%s" enabled="%s" global="%s"/>') % ( - self.xmlns_kscatalog, common.unique_str(), - self.service['name'], - self.service['type'], common.unique_url(), - common.unique_url(), common.unique_url(), True, True) - self.post_endpoint_template(as_xml=data, assert_status=403, headers={ - 'Accept': 'application/xml'}) - - def test_endpoint_create_xml_using_missing_token(self): - self.admin_token = '' - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<endpointTemplate ' - 'xmlns="%s" ' - 'region="%s" name="%s" type="%s" publicURL="%s" adminURL="%s" ' - 'internalURL="%s" enabled="%s" global="%s"/>') % ( - self.xmlns_kscatalog, - common.unique_str(), - self.service['name'], self.service['type'], - common.unique_url(), - common.unique_url(), common.unique_url(), True, True) - self.post_endpoint_template(as_xml=data, assert_status=401, headers={ - 'Accept': 'application/xml'}) - - def test_endpoint_create_xml_using_invalid_token(self): - self.admin_token = common.unique_str() - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<endpointTemplate ' - 'xmlns="%s" ' - 'region="%s" name="%s" type="%s" publicURL="%s" adminURL="%s" ' - 'internalURL="%s" enabled="%s" global="%s"/>') % ( - self.xmlns_kscatalog, common.unique_str(), - self.service['name'], - self.service['type'], common.unique_url(), - common.unique_url(), common.unique_url(), True, True) - self.post_endpoint_template(as_xml=data, assert_status=401, headers={ - 'Accept': 'application/xml'}) - - -class GetEndpointsTest(EndpointTemplatesTest): - def setUp(self, *args, **kwargs): - super(GetEndpointsTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - - def test_get_tenant_endpoint_xml(self): - self.get_tenant_endpoints(self.tenant['id'], assert_status=200, - headers={"Accept": "application/xml"}) - - def test_get_tenant_endpoint_xml_using_expired_auth_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_tenant_endpoints(self.tenant['id'], assert_status=403, - headers={"Accept": "application/xml"}) - - def test_get_tenant_endpoint_xml_using_disabled_auth_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.get_tenant_endpoints(self.tenant['id'], assert_status=403, - headers={"Accept": "application/xml"}) - - def test_get_tenant_endpoint_xml_using_missing_auth_token(self): - self.admin_token = '' - self.get_tenant_endpoints(self.tenant['id'], assert_status=401, - headers={"Accept": "application/xml"}) - - def test_get_tenant_endpoint_xml_using_invalid_auth_token(self): - self.admin_token = common.unique_str() - self.get_tenant_endpoints(self.tenant['id'], assert_status=401, - headers={"Accept": "application/xml"}) - - def test_get_tenant_endpoint_json(self): - r = self.get_tenant_endpoints(self.tenant['id'], assert_status=200) - self.assertIsNotNone(r.json.get('endpoints'), r.json) - - def test_get_tenant_endpoint_json_using_expired_auth_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_tenant_endpoints(self.tenant['id'], assert_status=403) - - def test_get_endpoint_json_using_disabled_auth_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.get_tenant_endpoints(self.tenant['id'], assert_status=403) - - def test_get_endpoint_json_using_missing_auth_token(self): - self.admin_token = '' - self.get_tenant_endpoints(self.tenant['id'], assert_status=401) - - def test_get_endpoint_json_using_invalid_auth_token(self): - self.admin_token = common.unique_str() - self.get_tenant_endpoints(self.tenant['id'], assert_status=401) - - -class DeleteEndpointsTest(EndpointTemplatesTest): - def setUp(self, *args, **kwargs): - super(DeleteEndpointsTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_template['id']) - - def test_delete_endpoint(self): - self.delete_tenant_endpoint(self.tenant['id'], - self.endpoint_template['id'], assert_status=204) - - def test_delete_endpoint_using_expired_auth_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.delete_tenant_endpoint(self.tenant['id'], - self.endpoint_template['id'], assert_status=403) - - def test_delete_endpoint_using_disabled_auth_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.delete_tenant_endpoint(self.tenant['id'], - self.endpoint_template['id'], assert_status=403) - - def test_delete_endpoint_using_missing_auth_token(self): - self.admin_token = '' - self.delete_tenant_endpoint(self.tenant['id'], - self.endpoint_template['id'], assert_status=401) - - def test_delete_endpoint_using_invalid_auth_token(self): - self.admin_token = common.unique_str() - self.delete_tenant_endpoint(self.tenant['id'], - self.endpoint_template['id'], assert_status=401) - - -class GetEndpointsForTokenTest(EndpointTemplatesTest): - def setUp(self, *args, **kwargs): - super(GetEndpointsForTokenTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - self.fixture_create_tenant_user() - - self.services = {} - self.endpoint_templates = {} - for x in range(0, 5): - self.services[x] = self.create_service().json['OS-KSADM:service'] - self.endpoint_templates[x] = self.create_endpoint_template( - name=self.services[x]['name'], \ - type=self.services[x]['type']).\ - json['OS-KSCATALOG:endpointTemplate'] - self.create_endpoint_for_tenant(self.tenant['id'], - self.endpoint_templates[x]['id']) - - def test_get_token_endpoints_xml(self): - self.get_token_endpoints(self.tenant_user_token['id'], - assert_status=200, - headers={"Accept": "application/xml"}) - - def test_get_token_endpoints_xml_using_expired_auth_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_token_endpoints(self.tenant_user_token['id'], - assert_status=403, - headers={"Accept": "application/xml"}) - - def test_get_token_endpoints_xml_using_disabled_auth_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.get_token_endpoints(self.tenant_user_token['id'], - assert_status=403, - headers={"Accept": "application/xml"}) - - def test_get_token_endpoints_xml_using_missing_auth_token(self): - self.admin_token = '' - self.get_token_endpoints(self.tenant_user_token['id'], - assert_status=401, - headers={"Accept": "application/xml"}) - - def test_get_token_endpoints_xml_using_invalid_auth_token(self): - self.admin_token = common.unique_str() - self.get_token_endpoints(self.tenant_user_token['id'], - assert_status=401, - headers={"Accept": "application/xml"}) - - def test_get_token_endpoints_json(self): - r = self.get_token_endpoints(self.tenant_user_token['id'], - assert_status=200) - self.assertIsNotNone(r.json.get('endpoints'), r.json) - - def test_get_token_endpoints_json_using_expired_auth_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_token_endpoints(self.tenant_user_token['id'], - assert_status=403) - - def test_get_token_endpoints_json_using_disabled_auth_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.get_token_endpoints(self.tenant_user_token['id'], - assert_status=403) - - def test_get_token_endpoints_json_using_missing_auth_token(self): - self.admin_token = '' - self.get_token_endpoints(self.tenant_user_token['id'], - assert_status=401) - - def test_get_token_endpoints_json_using_invalid_auth_token(self): - self.admin_token = common.unique_str() - self.get_token_endpoints(self.tenant_user_token['id'], - assert_status=401) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/functional/test_extensions.py b/keystone/test/functional/test_extensions.py deleted file mode 100644 index ba090bda..00000000 --- a/keystone/test/functional/test_extensions.py +++ /dev/null @@ -1,258 +0,0 @@ -import unittest2 as unittest -from keystone.test.functional import common - - -class TestHPIDMTokensExtension(common.FunctionalTestCase): - """Test HP-IDM token validation extension""" - - def setUp(self): - super(TestHPIDMTokensExtension, self).setUp() - password = common.unique_str() - self.user = self.create_user(user_password=password).json['user'] - self.user['password'] = password - self.tenant = self.create_tenant().json['tenant'] - self.service = self.create_service().json['OS-KSADM:service'] - r = self.create_role(service_name=self.service['name']) - self.role = r.json['role'] - self.another_service = self.create_service().json['OS-KSADM:service'] - self.service_with_no_users = self.create_service().\ - json['OS-KSADM:service'] - ar = self.create_role(service_name=self.another_service['name']) - self.another_role = ar.json['role'] - rnu = self.create_role(service_name=self.service_with_no_users['name']) - self.role_with_no_users = rnu.json['role'] - rns = self.create_role() - self.role_with_no_service = rns.json['role'] - self.grant_role_to_user(self.user['id'], - self.role['id'], self.tenant['id']) - self.grant_role_to_user(self.user['id'], - self.role_with_no_service['id'], - self.tenant['id']) - self.grant_role_to_user(self.user['id'], - self.another_role['id'], self.tenant['id']) - self.global_role = self.create_role().json['role'] - # crete a global role - self.put_user_role(self.user['id'], self.global_role['id'], None) - - def get_token_belongsto(self, token_id, tenant_id, service_ids, **kwargs): - """GET /tokens/{token_id}?belongsTo={tenant_id} - [&HP-IDM-serviceId={service_ids}]""" - serviceId_qs = "" - if service_ids: - serviceId_qs = "&HP-IDM-serviceId=%s" % (service_ids) - return self.admin_request(method='GET', - path='/tokens/%s?belongsTo=%s%s' % (token_id, tenant_id, - serviceId_qs), **kwargs) - - def check_token_belongs_to(self, token_id, tenant_id, service_ids, - **kwargs): - """HEAD /tokens/{token_id}?belongsTo={tenant_id} - [&HP-IDM-serviceId={service_ids}]""" - serviceId_qs = "" - if service_ids: - serviceId_qs = "&HP-IDM-serviceId=%s" % (service_ids) - return self.admin_request(method='HEAD', - path='/tokens/%s?belongsTo=%s%s' % (token_id, tenant_id, - serviceId_qs), **kwargs) - - @unittest.skipIf(common.isSsl(), - "Skipping SSL tests") - def test_token_validation_with_serviceId(self): - scoped = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}, - 'tenantName': self.tenant['name']}}).json['access'] - - self.assertEqual(scoped['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(scoped['token']['tenant']['name'], - self.tenant['name']) - # And an admin should be able to validate that our new token is scoped - r = self.get_token_belongsto(token_id=scoped['token']['id'], - tenant_id=self.tenant['id'], service_ids=self.service['id']) - access = r.json['access'] - - self.assertEqual(access['user']['id'], self.user['id']) - self.assertEqual(access['user']['name'], self.user['name']) - self.assertEqual(access['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(access['token']['tenant']['name'], - self.tenant['name']) - - # make sure only the service roles are returned - self.assertIsNotNone(access['user'].get('roles')) - self.assertEqual(len(access['user']['roles']), 1) - self.assertEqual(access['user']['roles'][0]['name'], - self.role['name']) - - # make sure check token also works - self.check_token_belongs_to(token_id=scoped['token']['id'], - tenant_id=self.tenant['id'], service_ids=self.service['id'], - assert_status=200) - - @unittest.skipIf(common.isSsl(), - "Skipping SSL tests") - def test_token_validation_with_all_serviceId(self): - scoped = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}, - 'tenantName': self.tenant['name']}}).json['access'] - - self.assertEqual(scoped['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(scoped['token']['tenant']['name'], - self.tenant['name']) - # And an admin should be able to validate that our new token is scoped - service_ids = "%s,%s" % \ - (self.service['id'], self.another_service['id']) - r = self.get_token_belongsto(token_id=scoped['token']['id'], - tenant_id=self.tenant['id'], service_ids=service_ids) - access = r.json['access'] - - self.assertEqual(access['user']['id'], self.user['id']) - self.assertEqual(access['user']['name'], self.user['name']) - self.assertEqual(access['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(access['token']['tenant']['name'], - self.tenant['name']) - - # make sure only the service roles are returned - self.assertIsNotNone(access['user'].get('roles')) - self.assertEqual(len(access['user']['roles']), 2) - role_names = map(lambda x: x['name'], access['user']['roles']) - self.assertTrue(self.role['name'] in role_names) - self.assertTrue(self.another_role['name'] in role_names) - - @unittest.skipIf(common.isSsl(), - "Skipping SSL tests") - def test_token_validation_with_no_user_service(self): - scoped = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}, - 'tenantName': self.tenant['name']}}).json['access'] - - self.assertEqual(scoped['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(scoped['token']['tenant']['name'], - self.tenant['name']) - # And an admin should be able to validate that our new token is scoped - service_ids = "%s,%s,%s" % (self.service['id'], - self.another_service['id'], - self.service_with_no_users['id']) - r = self.get_token_belongsto(token_id=scoped['token']['id'], - tenant_id=self.tenant['id'], service_ids=service_ids) - access = r.json['access'] - - self.assertEqual(access['user']['id'], self.user['id']) - self.assertEqual(access['user']['name'], self.user['name']) - self.assertEqual(access['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(access['token']['tenant']['name'], - self.tenant['name']) - - # make sure only the service roles are returned, excluding the one - # with no users - self.assertIsNotNone(access['user'].get('roles')) - self.assertEqual(len(access['user']['roles']), 2) - role_names = map(lambda x: x['name'], access['user']['roles']) - self.assertTrue(self.role['name'] in role_names) - self.assertTrue(self.another_role['name'] in role_names) - - # make sure check token also works - self.check_token_belongs_to(token_id=scoped['token']['id'], - tenant_id=self.tenant['id'], service_ids=service_ids, - assert_status=200) - - @unittest.skipIf(common.isSsl(), - "Skipping SSL tests") - def test_token_validation_without_serviceId(self): - scoped = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}, - 'tenantName': self.tenant['name']}}).json['access'] - - self.assertEqual(scoped['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(scoped['token']['tenant']['name'], - self.tenant['name']) - # And an admin should be able to validate that our new token is scoped - r = self.get_token_belongsto(token_id=scoped['token']['id'], - tenant_id=self.tenant['id'], service_ids=None) - access = r.json['access'] - - self.assertEqual(access['user']['id'], self.user['id']) - self.assertEqual(access['user']['name'], self.user['name']) - self.assertEqual(access['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(access['token']['tenant']['name'], - self.tenant['name']) - - # make sure all the roles are returned - self.assertIsNotNone(access['user'].get('roles')) - self.assertEqual(len(access['user']['roles']), 4) - role_names = map(lambda x: x['name'], access['user']['roles']) - self.assertTrue(self.role['name'] in role_names) - self.assertTrue(self.another_role['name'] in role_names) - self.assertTrue(self.global_role['name'] in role_names) - self.assertTrue(self.role_with_no_service['name'] in role_names) - - @unittest.skipIf(common.isSsl(), - "Skipping SSL tests") - def test_token_validation_with_global_service_id(self): - scoped = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}, - 'tenantName': self.tenant['name']}}).json['access'] - - self.assertEqual(scoped['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(scoped['token']['tenant']['name'], - self.tenant['name']) - service_ids = "%s,%s,global" % (self.service['id'], - self.another_service['id']) - r = self.get_token_belongsto(token_id=scoped['token']['id'], - tenant_id=self.tenant['id'], service_ids=service_ids) - access = r.json['access'] - - self.assertEqual(access['user']['id'], self.user['id']) - self.assertEqual(access['user']['name'], self.user['name']) - self.assertEqual(access['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(access['token']['tenant']['name'], - self.tenant['name']) - - # make sure only the service roles are returned - self.assertIsNotNone(access['user'].get('roles')) - self.assertEqual(len(access['user']['roles']), 3) - role_names = map(lambda x: x['name'], access['user']['roles']) - self.assertTrue(self.role['name'] in role_names) - self.assertTrue(self.another_role['name'] in role_names) - self.assertTrue(self.global_role['name'] in role_names) - - @unittest.skipIf(common.isSsl(), - "Skipping SSL tests") - def test_token_validation_with_bogus_service_id(self): - scoped = self.post_token(as_json={ - 'auth': { - 'passwordCredentials': { - 'username': self.user['name'], - 'password': self.user['password']}, - 'tenantName': self.tenant['name']}}).json['access'] - - self.assertEqual(scoped['token']['tenant']['id'], self.tenant['id']) - self.assertEqual(scoped['token']['tenant']['name'], - self.tenant['name']) - service_ids = "%s,%s,boguzzz" % (self.service['id'], - self.another_service['id']) - self.get_token_belongsto(token_id=scoped['token']['id'], - tenant_id=self.tenant['id'], service_ids=service_ids, - assert_status=401) - - # make sure check token also works - self.check_token_belongs_to(token_id=scoped['token']['id'], - tenant_id=self.tenant['id'], service_ids=service_ids, - assert_status=401) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/functional/test_issue_85.py b/keystone/test/functional/test_issue_85.py deleted file mode 100644 index a44d147c..00000000 --- a/keystone/test/functional/test_issue_85.py +++ /dev/null @@ -1,43 +0,0 @@ -import unittest2 as unittest -from keystone.test.functional import common - - -class TestIssue85(common.FunctionalTestCase): - """Illustrates github issue #85""" - - def test_disabling_tenant_disables_token(self): - """Disabling a tenant should invalidate previously-issued tokens""" - # Create a user for a specific tenant - tenant = self.create_tenant().json['tenant'] - password = common.unique_str() - user = self.create_user(user_password=password, - tenant_id=tenant['id']).json['user'] - user['password'] = password - - # Authenticate as user to get a token *for a specific tenant* - user_token = self.authenticate(user['name'], user['password'], - tenant['id']).json['access']['token']['id'] - - # Validate and check that token belongs to tenant - print self.get_token(user_token).body - tenant_id = self.get_token(user_token).\ - json['access']['token']['tenant']['id'] - self.assertEqual(tenant_id, tenant['id']) - - # Disable tenant - r = self.admin_request(method='POST', - path='/tenants/%s' % tenant['id'], - as_json={ - 'tenant': { - 'name': tenant['name'], - 'description': 'description', - 'enabled': False}}) - self.assertEquals(r.json['tenant']['enabled'], False) - - # Assert that token belonging to disabled tenant is invalid - r = self.admin_request(path='/tokens/%s?belongsTo=%s' % - (user_token, tenant['id']), assert_status=403) - self.assertTrue(r.json['tenantDisabled'], 'Tenant is disabled') - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/functional/test_roles.py b/keystone/test/functional/test_roles.py deleted file mode 100644 index 0444e738..00000000 --- a/keystone/test/functional/test_roles.py +++ /dev/null @@ -1,559 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-2011 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 unittest2 as unittest -from keystone.test.functional import common - - -class RolesTest(common.FunctionalTestCase): - expected_roles = ['Admin', 'Member', 'KeystoneServiceAdmin'] - - def setUp(self, *args, **kwargs): - super(RolesTest, self).setUp(*args, **kwargs) - - def tearDown(self, *args, **kwargs): - super(RolesTest, self).tearDown(*args, **kwargs) - - -class CreateRolesTest(RolesTest): - def test_create_role(self): - self.create_role(assert_status=201) - - def test_create_role_using_blank_name(self): - self.create_role(role_name='', assert_status=400) - - def test_create_role_using_service_token(self): - user = self.create_user_with_known_password().json['user'] - self.admin_token = self.authenticate(user['name'], - user['password']).json['access']['token']['id'] - self.create_role(assert_status=401) - - def test_create_roles_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.create_role(assert_status=403) - - def test_create_roles_using_missing_token(self): - self.admin_token = '' - self.create_role(assert_status=401) - - def test_create_roles_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.create_role(assert_status=403) - - def test_create_roles_using_invalid_token(self): - self.admin_token = common.unique_str() - self.create_role(assert_status=401) - - def test_create_role_mapped_to_a_service(self): - service = self.create_service().json['OS-KSADM:service'] - role_name = service['name'] + ':' + common.unique_str() - role = self.create_role(role_name=role_name, - service_id=service['id']).json['role'] - self.assertEqual(role_name, role['name']) - self.assertEqual(service['id'], role['serviceId']) - - def test_create_role_mapped_to_a_service_xml(self): - service = self.create_service().json['OS-KSADM:service'] - name = service['name'] + ':' + common.unique_str() - description = common.unique_str() - - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<role xmlns="%s" name="%s" description="%s" serviceId="%s"/>') % ( - self.xmlns, name, description, service['id']) - id = self.post_role(assert_status=201, as_xml=data).xml.get('id') - self.assertIsNotNone(id) - - role = self.fetch_role(id, assert_status=200).json['role'] - self.assertEqual(role['name'], name) - self.assertEqual(role['description'], description) - self.assertEqual(role['serviceId'], service['id']) - - def test_create__service_role_using_incorrect_role_name(self): - """ test_create_role_mapped_to_a_service_using_incorrect_role_name """ - self.create_role(common.unique_str(), service_id=common.unique_str(), - assert_status=400) - - def test_create_role_using_empty_name_xml(self): - name = '' - description = common.unique_str() - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<role xmlns="%s" name="%s" description="%s" />') % ( - self.xmlns, name, description) - self.post_role(assert_status=400, as_xml=data) - - -class DeleteRoleTest(RolesTest): - def setUp(self, *args, **kwargs): - super(DeleteRoleTest, self).setUp(*args, **kwargs) - - self.role = self.create_role().json['role'] - - def test_delete_roles_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.delete_role(self.role['id'], assert_status=403) - - def test_delete_roles_using_missing_token(self): - self.admin_token = '' - self.delete_role(self.role['id'], assert_status=401) - - def test_delete_roles_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.delete_role(self.role['id'], assert_status=403) - - def test_delete_roles_using_invalid_token(self): - self.admin_token = common.unique_str() - self.delete_role(self.role['id'], assert_status=401) - - def test_create_and_delete_role_that_has_references(self): - tenant = self.create_tenant().json['tenant'] - user = self.create_user(tenant_id=tenant['id']).json['user'] - self.grant_role_to_user(user['id'], self.role['id'], tenant['id']) - self.remove_role(self.role['id'], assert_status=204) - - def test_create_role_mapped_to_a_service(self): - service = self.create_service().json['OS-KSADM:service'] - role_name = service['name'] + ':' + common.unique_str() - role = self.create_role(role_name=role_name, - service_id=service['id']).json['role'] - self.assertEqual(service['id'], role['serviceId']) - - def test_create_role_mapped_to_a_service_xml(self): - service = self.create_service().json['OS-KSADM:service'] - name = service['name'] + ':' + common.unique_str() - description = common.unique_str() - - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<role xmlns="%s" name="%s" description="%s" serviceId="%s"/>') % ( - self.xmlns, name, description, service['id']) - role_id = self.post_role(assert_status=201, as_xml=data).xml.get('id') - - role = self.fetch_role(role_id, assert_status=200).json['role'] - self.assertEqual(role['id'], role_id) - self.assertEqual(role['serviceId'], service['id']) - - def test_create_service_role_using_incorrect_role_name(self): - """ Formerly: - test_create_role_mapped_to_a_service_using_incorrect_role_name""" - self.create_role(common.unique_str(), service_id=common.unique_str(), - assert_status=400) - - -class GetRolesByServiceTest(common.FunctionalTestCase): - def setUp(self, *args, **kwargs): - super(GetRolesByServiceTest, self).setUp(*args, **kwargs) - service = self.create_service().json['OS-KSADM:service'] - role_name = service['name'] + ':' + common.unique_str() - role = self.create_role(role_name=role_name, - service_id=service['id']).json['role'] - self.service_id = service['id'] - - def tearDown(self, *args, **kwargs): - super(GetRolesByServiceTest, self).tearDown(*args, **kwargs) - - def test_get_roles(self): - r = self.list_roles(assert_status=200, service_id=self.service_id) - self.assertTrue(len(r.json['roles'])) - - def test_get_roles_xml(self): - r = self.get_roles_by_service(assert_status=200, headers={ - 'Accept': 'application/xml'}, service_id=self.service_id,) - self.assertEquals(r.xml.tag, '{%s}roles' % self.xmlns) - roles = r.xml.findall('{%s}role' % self.xmlns) - - for role in roles: - self.assertIsNotNone(role.get('id')) - - def test_get_roles_exp_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_roles_by_service( - service_id=self.service_id, assert_status=403) - - def test_get_roles_exp_token_xml(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_roles_by_service( - service_id=self.service_id, assert_status=403, headers={ - 'Accept': 'application/xml'}) - - -class GetRolesTest(RolesTest): - def test_get_roles(self): - r = self.list_roles(assert_status=200) - self.assertTrue(len(r.json['roles'])) - - def test_get_roles_xml(self): - r = self.get_roles(assert_status=200, headers={ - 'Accept': 'application/xml'}) - self.assertEquals(r.xml.tag, '{%s}roles' % self.xmlns) - roles = r.xml.findall('{%s}role' % self.xmlns) - - for role in roles: - self.assertIsNotNone(role.get('id')) - - def test_get_roles_exp_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_roles(assert_status=403) - - def test_get_roles_exp_token_xml(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_roles(assert_status=403, headers={ - 'Accept': 'application/xml'}) - - -class GetRoleTest(RolesTest): - def setUp(self, *args, **kwargs): - super(GetRoleTest, self).setUp(*args, **kwargs) - self.role = self.create_role().json['role'] - - def test_get_role(self): - role = self.fetch_role(self.role['id'], assert_status=200).json['role'] - self.assertEqual(role['id'], self.role['id']) - self.assertEqual(role['name'], self.role['name']) - self.assertEqual(role['description'], self.role['description']) - self.assertEqual(role.get('serviceId'), self.role.get('serviceId')) - - def test_get_role_xml(self): - r = self.get_role(self.role['id'], assert_status=200, headers={ - 'Accept': 'application/xml'}) - self.assertEqual(r.xml.tag, '{%s}role' % self.xmlns) - self.assertEqual(r.xml.get('id'), self.role['id']) - self.assertEqual(r.xml.get('name'), self.role['name']) - self.assertEqual(r.xml.get('description'), self.role['description']) - self.assertEqual(r.xml.get('serviceId'), self.role.get('serviceId')) - - def test_get_role_bad(self): - self.fetch_role(common.unique_str(), assert_status=404) - - def test_get_role_xml_bad(self): - self.get_role(common.unique_str(), assert_status=404, headers={ - 'Accept': 'application/xml'}) - - def test_get_role_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.fetch_role(self.role['id'], assert_status=403) - - def test_get_role_xml_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_role(self.role['id'], assert_status=403, headers={ - 'Accept': 'application/xml'}) - - def test_get_role_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.fetch_role(self.role['id'], assert_status=403) - - def test_get_role_xml_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.get_role(self.role['id'], assert_status=403, headers={ - 'Accept': 'application/xml'}) - - def test_get_role_using_missing_token(self): - self.admin_token = '' - self.fetch_role(self.role['id'], assert_status=401) - - def test_get_role_xml_using_missing_token(self): - self.admin_token = '' - self.get_role(self.role['id'], assert_status=401, headers={ - 'Accept': 'application/xml'}) - - def test_get_role_using_invalid_token(self): - self.admin_token = common.unique_str() - self.fetch_role(self.role['id'], assert_status=401) - - def test_get_role_xml_using_invalid_token(self): - self.admin_token = common.unique_str() - self.get_role(self.role['id'], assert_status=401, headers={ - 'Accept': 'application/xml'}) - - -class GetRoleByNameTest(RolesTest): - def setUp(self, *args, **kwargs): - super(GetRoleByNameTest, self).setUp(*args, **kwargs) - - self.role = self.create_role().json['role'] - - def test_get_role(self): - role = self.fetch_role_by_name( - self.role['name'], assert_status=200).json['role'] - self.assertEqual(role['id'], self.role['id']) - self.assertEqual(role['name'], self.role['name']) - self.assertEqual(role['description'], self.role['description']) - self.assertEqual(role.get('serviceId'), self.role.get('serviceId')) - - def test_get_role_xml(self): - r = self.get_role_by_name(self.role['name'], - assert_status=200, headers={ - 'Accept': 'application/xml'}) - self.assertEqual(r.xml.tag, '{%s}role' % self.xmlns) - self.assertEqual(r.xml.get('id'), self.role['id']) - self.assertEqual(r.xml.get('name'), self.role['name']) - self.assertEqual(r.xml.get('description'), self.role['description']) - self.assertEqual(r.xml.get('serviceId'), self.role.get('serviceId')) - - def test_get_role_bad(self): - self.fetch_role_by_name(common.unique_str(), assert_status=404) - - def test_get_role_xml_bad(self): - self.get_role_by_name(common.unique_str(), assert_status=404, headers={ - 'Accept': 'application/xml'}) - - def test_get_role_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.fetch_role_by_name(self.role['name'], assert_status=403) - - def test_get_role_xml_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_role_by_name(self.role['name'], assert_status=403, headers={ - 'Accept': 'application/xml'}) - - def test_get_role_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.fetch_role_by_name(self.role['name'], assert_status=403) - - def test_get_role_xml_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.get_role_by_name(self.role['name'], assert_status=403, headers={ - 'Accept': 'application/xml'}) - - def test_get_role_using_missing_token(self): - self.admin_token = '' - self.fetch_role_by_name(self.role['name'], assert_status=401) - - def test_get_role_xml_using_missing_token(self): - self.admin_token = '' - self.get_role_by_name(self.role['name'], assert_status=401, headers={ - 'Accept': 'application/xml'}) - - def test_get_role_using_invalid_token(self): - self.admin_token = common.unique_str() - self.fetch_role_by_name(self.role['name'], assert_status=401) - - def test_get_role_xml_using_invalid_token(self): - self.admin_token = common.unique_str() - self.get_role_by_name(self.role['name'], assert_status=401, headers={ - 'Accept': 'application/xml'}) - - -class CreateRoleAssignmentTest(RolesTest): - def setUp(self, *args, **kwargs): - super(CreateRoleAssignmentTest, self).setUp(*args, **kwargs) - - self.fixture_create_normal_tenant() - self.fixture_create_tenant_user() - - self.role = self.create_role().json['role'] - - def test_grant_role(self): - self.grant_role_to_user(self.tenant_user['id'], self.role['id'], - self.tenant['id'], assert_status=201) - -# def test_grant_role_json_using_service_admin_token(self): -# tenant = self.create_tenant().json['tenant'] -# service_admin = self.create_user_with_known_password().json['user'] -# self.grant_role_to_user(service_admin['id'], 'KeystoneServiceAdmin', -# tenant['id'], assert_status=201) -# -# service_admin_token_id = self.authenticate(service_admin['name'], -# service_admin['password']).json['auth']['token']['id'] -# -# user = self.create_user().json['user'] -# -# role = self.create_role().json['role'] -# self.admin_token = service_admin_token_id -# self.grant_role_to_user(user['id'], role['id'], tenant['id'], -# assert_status=201) - - def test_grant_role_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.grant_role_to_user(self.tenant_user['id'], self.role['id'], - self.tenant['id'], assert_status=403) - - def test_grant_role_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.grant_role_to_user(self.tenant_user['id'], self.role['id'], - self.tenant['id'], assert_status=403) - - def test_grant_role_using_missing_token(self): - self.admin_token = '' - self.grant_role_to_user(self.tenant_user['id'], self.role['id'], - self.tenant['id'], assert_status=401) - - def test_grant_role_using_invalid_token(self): - self.admin_token = common.unique_str() - self.grant_role_to_user(self.tenant_user['id'], self.role['id'], - self.tenant['id'], assert_status=401) - - def test_grant_global_role_json(self): - self.grant_global_role_to_user( - self.tenant_user['id'], self.role['id'], assert_status=201) - - -class GetRoleAssignmentsTest(RolesTest): - def setUp(self, *args, **kwargs): - super(GetRoleAssignmentsTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - self.fixture_create_tenant_user() - - self.role = self.create_role().json['role'] - self.grant_role_to_user(self.tenant_user['id'], self.role['id'], - self.tenant['id']) - - def test_get_role_assignments(self): - r = self.get_user_roles(self.tenant_user['id'], assert_status=200) - self.assertIsNotNone(r.json['roles']) - - def test_get_roler_assignments_xml(self): - r = self.get_user_roles(self.tenant_user['id'], assert_status=200, - headers={'Accept': 'application/xml'}) - self.assertEqual(r.xml.tag, "{%s}roles" % self.xmlns) - - def test_get_role_assignments_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_user_roles(self.tenant_user['id'], assert_status=403) - - def test_get_role_assignments_xml_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_user_roles(self.tenant_user['id'], assert_status=403, - headers={'Accept': 'application/xml'}) - - def test_get_role_assignments_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.get_user_roles(self.tenant_user['id'], assert_status=403) - - def test_get_role_assignments_xml_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.get_user_roles(self.tenant_user['id'], assert_status=403, - headers={'Accept': 'application/xml'}) - - def test_get_role_assignments_using_missing_token(self): - self.admin_token = '' - self.get_user_roles(self.tenant_user['id'], assert_status=401) - - def test_get_role_assignments_xml_using_missing_token(self): - self.admin_token = '' - self.get_user_roles(self.tenant_user['id'], assert_status=401, - headers={'Accept': 'application/xml'}) - - def test_get_role_assignments_json_using_invalid_token(self): - self.admin_token = common.unique_str() - self.get_user_roles(self.tenant_user['id'], assert_status=401) - - def test_get_role_assignments_xml_using_invalid_token(self): - self.admin_token = common.unique_str() - self.get_user_roles(self.tenant_user['id'], assert_status=401, - headers={'Accept': 'application/xml'}) - - -class DeleteRoleAssignmentsTest(RolesTest): - def setUp(self, *args, **kwargs): - super(DeleteRoleAssignmentsTest, self).setUp(*args, **kwargs) - - self.fixture_create_normal_tenant() - self.fixture_create_tenant_user() - - self.role = self.create_role().json['role'] - self.grant_role_to_user(self.tenant_user['id'], self.role['id'], - self.tenant['id']) - self.roles = self.get_user_roles(self.tenant_user['id']).\ - json['roles'] - - def test_delete_role_assignment(self): - self.delete_user_role(self.tenant_user['id'], self.role['id'], - self.tenant['id'], assert_status=204) - - def test_delete_role_assignment_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.delete_user_role(self.tenant_user['id'], self.role['id'], - self.tenant['id'], assert_status=403) - - def test_delete_role_assignment_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.delete_user_role(self.tenant_user['id'], self.role['id'], - self.tenant['id'], assert_status=403) - - def test_delete_role_assignment_using_missing_token(self): - self.admin_token = '' - self.delete_user_role(self.tenant_user['id'], self.role['id'], - self.tenant['id'], assert_status=401) - - def test_delete_role_assignment_using_invalid_token(self): - self.admin_token = common.unique_str() - self.delete_user_role(self.tenant_user['id'], self.role['id'], - self.tenant['id'], assert_status=401) - - -class DeleteGlobalRoleAssignmentsTest(RolesTest): - def setUp(self, *args, **kwargs): - super(DeleteGlobalRoleAssignmentsTest, self).setUp(*args, **kwargs) - - self.fixture_create_normal_tenant() - self.fixture_create_tenant_user() - - self.role = self.create_role().json['role'] - self.grant_global_role_to_user(self.tenant_user['id'], self.role['id']) - self.roles = self.get_user_roles(self.tenant_user['id']).\ - json['roles'] - - def test_delete_role_assignment(self): - self.delete_user_role(self.tenant_user['id'], self.role['id'], - None, assert_status=204) - - def test_delete_role_assignment_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.delete_user_role(self.tenant_user['id'], self.role['id'], - None, assert_status=403) - - def test_delete_role_assignment_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.delete_user_role(self.tenant_user['id'], self.role['id'], - None, assert_status=403) - - def test_delete_role_assignment_using_missing_token(self): - self.admin_token = '' - self.delete_user_role(self.tenant_user['id'], self.role['id'], - None, assert_status=401) - - def test_delete_role_assignment_using_invalid_token(self): - self.admin_token = common.unique_str() - self.delete_user_role(self.tenant_user['id'], self.role['id'], - None, assert_status=401) - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/functional/test_services.py b/keystone/test/functional/test_services.py deleted file mode 100644 index 276a5c19..00000000 --- a/keystone/test/functional/test_services.py +++ /dev/null @@ -1,294 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-2011 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 unittest2 as unittest -import uuid - -from keystone.test.functional import common - - -class ServicesTest(common.FunctionalTestCase): - def setUp(self, *args, **kwargs): - super(ServicesTest, self).setUp(*args, **kwargs) - - service = self.create_service( - service_name="service-%s" % uuid.uuid4().hex, - service_type='identity', - service_description='Sample service', - assert_status=201).json['OS-KSADM:service'] - - def tearDown(self, *args, **kwargs): - super(ServicesTest, self).tearDown(*args, **kwargs) - - -class GetServicesTest(ServicesTest): - def test_get_services_using_keystone_admin_token_json(self): - services = self.list_services(assert_status=200).\ - json['OS-KSADM:services'] - - self.assertTrue(len(services)) - - def test_get_services_using_keystone_admin_token_xml(self): - r = self.list_services(assert_status=200, headers={ - 'Accept': 'application/xml'}) - - self.assertEqual(r.xml.tag, "{%s}services" % self.xmlns_ksadm) - services = r.xml.findall("{%s}service" % self.xmlns_ksadm) - self.assertTrue(len(services)) - - def test_get_services_using_service_admin_token(self): - self.fixture_create_service_admin() - self.admin_token = self.service_admin_token - services = self.list_services(assert_status=200).\ - json['OS-KSADM:services'] - - self.assertTrue(len(services)) - - def test_get_services_using_service_admin_token_xml(self): - self.fixture_create_service_admin() - self.admin_token = self.service_admin_token - r = self.get_services(assert_status=200, headers={ - 'Accept': 'application/xml'}) - - self.assertEqual(r.xml.tag, "{%s}services" % self.xmlns_ksadm) - services = r.xml.findall("{%s}service" % self.xmlns_ksadm) - self.assertTrue(len(services)) - - def test_get_services_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.list_services(assert_status=403) - - def test_get_services_using_missing_token(self): - self.admin_token = '' - self.list_services(assert_status=401) - - def test_get_services_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.list_services(assert_status=403) - - def test_get_services_using_invalid_token(self): - self.admin_token = common.unique_str() - self.list_services(assert_status=401) - - -class GetServiceTest(ServicesTest): - def setUp(self, *args, **kwargs): - super(ServicesTest, self).setUp(*args, **kwargs) - - self.service = self.create_service().json['OS-KSADM:service'] - - def test_service_get_json(self): - service = self.fetch_service(service_id=self.service['id'], - assert_status=200).json['OS-KSADM:service'] - - self.assertIsNotNone(service['id']) - self.assertIsNotNone(service['description']) - - def test_service_get_xml(self): - service = self.fetch_service(service_id=self.service['id'], - assert_status=200, headers={'Accept': 'application/xml'}).xml - - self.assertEqual(service.tag, '{%s}service' % self.xmlns_ksadm) - self.assertIsNotNone(service.get('id')) - self.assertIsNotNone(service.get('description')) - - def test_get_service_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.fetch_service(service_id=self.service['id'], assert_status=403) - - def test_get_service_using_missing_token(self): - self.admin_token = '' - self.fetch_service(service_id=self.service['id'], assert_status=401) - - def test_get_service_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.fetch_service(service_id=self.service['id'], assert_status=403) - - def test_get_service_using_invalid_token(self): - self.admin_token = common.unique_str() - self.fetch_service(service_id=self.service['id'], assert_status=401) - - -class GetServiceByNameTest(ServicesTest): - def setUp(self, *args, **kwargs): - super(GetServiceByNameTest, self).setUp(*args, **kwargs) - self.service = self.create_service().json['OS-KSADM:service'] - - def test_service_get_json(self): - service = self.fetch_service_by_name(service_name=self.service['name'], - assert_status=200).json['OS-KSADM:service'] - - self.assertIsNotNone(service['id']) - self.assertIsNotNone(service['name']) - self.assertIsNotNone(service['description']) - - def test_service_get_xml(self): - service = self.fetch_service_by_name(service_name=self.service['name'], - assert_status=200, headers={'Accept': 'application/xml'}).xml - - self.assertEqual(service.tag, '{%s}service' % self.xmlns_ksadm) - self.assertIsNotNone(service.get('id')) - self.assertIsNotNone(service.get('name')) - self.assertIsNotNone(service.get('description')) - - def test_get_service_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.fetch_service_by_name( - service_name=self.service['name'], assert_status=403) - - def test_get_service_using_missing_token(self): - self.admin_token = '' - self.fetch_service_by_name( - service_name=self.service['name'], assert_status=401) - - def test_get_service_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.fetch_service_by_name( - service_name=self.service['name'], assert_status=403) - - def test_get_service_using_invalid_token(self): - self.admin_token = common.unique_str() - self.fetch_service_by_name( - service_name=self.service['name'], assert_status=401) - - -class CreateServiceTest(ServicesTest): - def test_service_create_json(self): - name = common.unique_str() - type = common.unique_str() - description = common.unique_str() - - service = self.create_service(service_name=name, service_type=type, - service_description=description, - assert_status=201).json['OS-KSADM:service'] - - self.assertIsNotNone(service.get('id')) - self.assertEqual(name, service.get('name')) - self.assertEqual(type, service.get('type')) - self.assertEqual(description, service.get('description')) - - def test_service_create_xml(self): - name = common.unique_str() - type = common.unique_str() - description = common.unique_str() - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<service xmlns="%s" name="%s" type="%s" description="%s"/>') % ( - self.xmlns_ksadm, name, type, description) - r = self.post_service(as_xml=data, assert_status=201) - self.assertEqual(r.xml.tag, "{%s}service" % self.xmlns_ksadm) - self.assertIsNotNone(r.xml.get('id')) - self.assertEqual(name, r.xml.get('name')) - self.assertEqual(type, r.xml.get('type')) - self.assertEqual(description, r.xml.get('description')) - - def test_service_create_duplicate_json(self): - service_name = common.unique_str() - self.create_service(service_name=service_name, assert_status=201) - self.create_service(service_name=service_name, assert_status=409) - - def test_service_create_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.create_service(assert_status=403) - - def test_service_create_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.create_service(assert_status=403) - - def test_service_create_json_using_missing_token(self): - self.admin_token = '' - self.create_service(assert_status=401) - - def test_service_create_json_using_invalid_token(self): - self.admin_token = common.unique_str() - self.create_service(assert_status=401) - - def test_service_create_json_missing_name(self): - self.create_service(service_name='', assert_status=400) - - def test_service_create_json_missing_type(self): - self.create_service(service_type='', assert_status=400) - - def test_service_create_xml_using_missing_name(self): - name = '' - type = common.unique_str() - description = common.unique_str() - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<service xmlns="%s" name="%s" type="%s" description="%s"/>') % ( - self.xmlns_ksadm, name, type, description) - self.post_service(as_xml=data, assert_status=400) - - def test_service_create_xml_using_empty_type(self): - name = common.unique_str() - type = '' - description = common.unique_str() - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<service xmlns="%s" name="%s" type="%s" description="%s"/>') % ( - self.xmlns_ksadm, name, type, description) - self.post_service(as_xml=data, assert_status=400) - - -class DeleteServiceTest(ServicesTest): - def setUp(self, *args, **kwargs): - super(DeleteServiceTest, self).setUp(*args, **kwargs) - - self.service = self.create_service().json['OS-KSADM:service'] - - def test_service_delete(self): - self.remove_service(self.service['id'], assert_status=204) - self.get_service(self.service['id'], assert_status=404) - - def test_delete_service_with_dependencies(self): - role_id = self.service['name'] + ':' + common.unique_str() - role = self.create_role(role_id, service_id=self.service['id'], - assert_status=201).json['role'] - - tenant = self.create_tenant().json['tenant'] - user = self.create_user(tenant_id=tenant['id']).json['user'] - - self.grant_role_to_user(user['id'], role['id'], tenant['id']) - self.create_endpoint_template(name=self.service['name'], - type=self.service['type']) - self.remove_service(self.service['id'], assert_status=204) - - def test_service_delete_json_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.remove_service(self.service['id'], assert_status=403) - - def test_service_delete_json_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.remove_service(self.service['id'], assert_status=403) - - def test_service_delete_json_using_missing_token(self): - self.admin_token = '' - self.remove_service(self.service['id'], assert_status=401) - - def test_service_delete_json_using_invalid_token(self): - self.admin_token = common.unique_str() - self.remove_service(self.service['id'], assert_status=401) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/functional/test_static_files.py b/keystone/test/functional/test_static_files.py deleted file mode 100644 index 75b445f6..00000000 --- a/keystone/test/functional/test_static_files.py +++ /dev/null @@ -1,90 +0,0 @@ -import unittest2 as unittest -from keystone.test.functional import common - - -class TestStaticFiles(common.ApiTestCase): - def test_pdf_contract(self): - if not common.isSsl(): - #TODO(ziad): Caller hangs in SSL (but works with cURL) - r = self.service_request(path='/identitydevguide.pdf') - self.assertResponseSuccessful(r) - - def test_wadl_contract(self): - r = self.service_request(path='/identity.wadl') - self.assertResponseSuccessful(r) - - def test_wadl_common(self): - r = self.service_request(path='/common.ent') - self.assertResponseSuccessful(r) - - def test_xsd_contract(self): - r = self.service_request(path='/xsd/api.xsd') - self.assertResponseSuccessful(r) - - def test_xsd_atom_contract(self): - r = self.service_request(path='/xsd/atom/atom.xsd') - self.assertResponseSuccessful(r) - - def test_xslt(self): - r = self.service_request(path='/xslt/schema.xslt') - self.assertResponseSuccessful(r) - - def test_js(self): - r = self.service_request(path='/js/shjs/sh_java.js') - self.assertResponseSuccessful(r) - - def test_xml_sample(self): - r = self.service_request(path='/samples/auth.xml') - self.assertResponseSuccessful(r) - - def test_json_sample(self): - r = self.service_request(path='/samples/auth.json') - self.assertResponseSuccessful(r) - - def test_stylesheet(self): - r = self.service_request(path='/style/shjs/sh_acid.css') - self.assertResponseSuccessful(r) - - -class TestAdminStaticFiles(common.FunctionalTestCase): - def test_pdf_contract(self): - if not common.isSsl(): - #TODO(ziad): Caller hangs in SSL (but works with cURL) - r = self.admin_request(path='/identityadminguide.pdf') - self.assertResponseSuccessful(r) - - def test_wadl_contract(self): - r = self.admin_request(path='/identity-admin.wadl') - self.assertResponseSuccessful(r) - - def test_xsd_contract(self): - r = self.admin_request(path='/xsd/api.xsd') - self.assertResponseSuccessful(r) - - def test_xsd_atom_contract(self): - r = self.admin_request(path='/xsd/atom/atom.xsd') - self.assertResponseSuccessful(r) - - def test_xslt(self): - r = self.admin_request(path='/xslt/schema.xslt') - self.assertResponseSuccessful(r) - - def test_js(self): - r = self.admin_request(path='/js/shjs/sh_java.js') - self.assertResponseSuccessful(r) - - def test_xml_sample(self): - r = self.admin_request(path='/samples/auth.xml') - self.assertResponseSuccessful(r) - - def test_json_sample(self): - r = self.admin_request(path='/samples/auth.json') - self.assertResponseSuccessful(r) - - def test_stylesheet(self): - r = self.admin_request(path='/style/shjs/sh_acid.css') - self.assertResponseSuccessful(r) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/functional/test_tenants.py b/keystone/test/functional/test_tenants.py deleted file mode 100644 index 708edbab..00000000 --- a/keystone/test/functional/test_tenants.py +++ /dev/null @@ -1,598 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-2011 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 unittest2 as unittest -from keystone.test.functional import common - - -class TenantTest(common.FunctionalTestCase): - def _assertValidTenant(self, tenant): - self.assertIsNotNone(tenant.get('id')) - self.assertIsNotNone(tenant.get('name')) - self.assertIsNotNone(tenant.get('enabled')) - self.assertRaises(ValueError, int, tenant.get('id')) - self.assertTrue(0 < len(tenant.get('id')) < 256, - "ID must be between 1 and 255 characters long") - self.assertFalse('/' in tenant.get('id'), - "ID cannot contain / character") - - def _assertValidJsonTenant(self, tenant): - self._assertValidTenant(tenant) - # TODO(dolph): this is still a valid assertion in some cases - # self.assertIsNotNone(tenant.get('description')) - self.assertIn(tenant.get('enabled'), [True, False], tenant) - - def _assertValidXmlTenant(self, xml): - self.assertEquals(xml.tag, '{%s}tenant' % self.xmlns) - self._assertValidTenant(xml) - # TODO(zns): this is still a valid assertion in some cases - # description = xml.find('{%s}description' % self.xmlns) - # self.assertIsNotNone(description) - self.assertIn(xml.get('enabled'), ['true', 'false']) - return xml - - def assertValidJsonTenantResponse(self, response): - tenant = response.json.get('tenant') - self._assertValidJsonTenant(tenant) - return tenant - - def assertValidXmlTenantResponse(self, response): - return self._assertValidXmlTenant(response.xml) - - def _assertValidTenantList(self, tenants): - pass - - def _assertValidXmlTenantList(self, xml): - self.assertEquals(xml.tag, '{%s}tenants' % self.xmlns) - tenants = xml.findall('{%s}tenant' % self.xmlns) - - self._assertValidTenantList(tenants) - for tenant in tenants: - self._assertValidXmlTenant(tenant) - return tenants - - def _assertValidJsonTenantList(self, tenants): - self._assertValidTenantList(tenants) - for tenant in tenants: - self._assertValidJsonTenant(tenant) - return tenants - - def assertValidXmlTenantListResponse(self, response): - return self._assertValidXmlTenantList(response.xml) - - def assertValidJsonTenantListResponse(self, response): - tenants = response.json.get('tenants') - self.assertIsNotNone(tenants) - return self._assertValidJsonTenantList(tenants) - - def setUp(self, *args, **kwargs): - super(TenantTest, self).setUp(*args, **kwargs) - - def tearDown(self, *args, **kwargs): - super(TenantTest, self).tearDown(*args, **kwargs) - - -class CreateTenantTest(TenantTest): - def test_create_tenant(self): - name = common.unique_str() - description = common.unique_str() - response = self.create_tenant(tenant_name=name, - tenant_description=description, assert_status=201) - tenant = self.assertValidJsonTenantResponse(response) - self.assertEqual(name, tenant.get('name')) - self.assertEqual(description, tenant.get('description')) - - def test_create_tenant_blank_name(self): - self.create_tenant(tenant_name='', assert_status=400) - - def test_create_tenant_xml(self): - name = common.unique_str() - description = common.unique_str() - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<tenant xmlns="http://docs.openstack.org/identity/api/v2.0" ' - 'enabled="true" name="%s"> ' - '<description>%s</description> ' - '</tenant>') % (name, description) - response = self.post_tenant(as_xml=data, assert_status=201, headers={ - 'Accept': 'application/xml'}) - tenant = self.assertValidXmlTenantResponse(response) - self.assertEqual(name, tenant.get('name')) - self.assertEqual(description, - tenant.find('{%s}description' % self.xmlns).text) - - def test_create_tenant_again(self): - tenant = self.create_tenant().json['tenant'] - self.create_tenant(tenant_name=tenant['name'], assert_status=409) - - def test_create_tenant_again_xml(self): - tenant = self.create_tenant().json['tenant'] - - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<tenant xmlns="http://docs.openstack.org/identity/api/v2.0" ' - 'enabled="true" id="%s"> ' - '<description>A description...</description> ' - '</tenant>') % (tenant['name'],) - - self.create_tenant(tenant_name=tenant['name'], as_xml=data, - assert_status=409) - - def test_create_tenant_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.create_tenant(assert_status=403) - - def test_create_tenant_expired_token_xml(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - data = '<?xml version="1.0" encoding="UTF-8"?> \ - <tenant xmlns="http://docs.openstack.org/identity/api/v2.0" \ - enabled="true" name="%s"> \ - <description>A description...</description> \ - </tenant>' % (common.unique_str()) - - self.post_tenant(as_xml=data, assert_status=403) - - def test_create_tenant_missing_token(self): - self.admin_token = '' - self.create_tenant(assert_status=401) - - def test_create_tenant_missing_token_xml(self): - self.admin_token = '' - data = '<?xml version="1.0" encoding="UTF-8"?> \ - <tenant xmlns="http://docs.openstack.org/identity/api/v2.0" \ - enabled="true" name="%s"> \ - <description>A description...</description> \ - </tenant>' % (common.unique_str()) - - self.post_tenant(as_xml=data, assert_status=401) - - def test_create_tenant_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.create_tenant(assert_status=403) - - def test_create_tenant_disabled_token_xml(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - data = '<?xml version="1.0" encoding="UTF-8"?> \ - <tenant xmlns="http://docs.openstack.org/identity/api/v2.0" \ - enabled="true" name="%s"> \ - <description>A description...</description> \ - </tenant>' % (common.unique_str()) - - self.post_tenant(as_xml=data, assert_status=403) - - def test_create_tenant_invalid_token(self): - self.admin_token = common.unique_str() - self.create_tenant(assert_status=401) - - def test_create_tenant_invalid_token_xml(self): - self.admin_token = common.unique_str() - data = '<?xml version="1.0" encoding="UTF-8"?> \ - <tenant xmlns="http://docs.openstack.org/identity/api/v2.0" \ - enabled="true" name="%s"> \ - <description>A description...</description> \ - </tenant>' % (common.unique_str()) - - self.post_tenant(as_xml=data, assert_status=401) - - def test_create_tenant_missing_name_xml(self): - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<tenant xmlns="http://docs.openstack.org/identity/api/v2.0" ' - 'enabled="true" name="%s"> ' - '<description>A description...</description> ' - '</tenant>') % ('',) - self.post_tenant(as_xml=data, assert_status=400, headers={ - 'Accept': 'application/xml'}) - - -class GetTenantsTest(TenantTest): - def test_get_tenants_using_admin_token(self): - response = self.list_tenants(assert_status=200) - self.assertValidJsonTenantListResponse(response) - - def test_get_tenants_using_admin_token_xml(self): - response = self.get_tenants(assert_status=200, headers={ - 'Accept': 'application/xml'}) - self.assertValidXmlTenantListResponse(response) - - def test_get_tenants_using_admin_token_xml_on_service_api(self): - response = self.create_tenant() - tenant = self.assertValidJsonTenantResponse(response) - role = self.create_role().json['role'] - user = self.create_user_with_known_password(tenant_id=tenant['id']).\ - json['user'] - self.grant_role_to_user(user_id=user['id'], - role_id=role['id'], tenant_id=tenant['id']) - - # find the admin role - admin_role = self.get_role_by_name('Admin').json['role'] - - # grant global admin to user - self.grant_global_role_to_user(user_id=user['id'], - role_id=admin_role['id']) - - # authenticate as our new admin - self.service_token = self.authenticate(user['name'], - user['password']).json['access']['token']['id'] - - # make a service call with our admin token - response = self.get_tenants(assert_status=200, headers={ - 'Accept': 'application/xml'}, request_type='service') - tenants = self.assertValidXmlTenantListResponse(response) - self.assertEquals(len(tenants), 1) - self.assertIn(tenant['id'], [t.get('id') for t in tenants]) - - def test_get_tenants_using_user_token(self): - response = self.create_tenant() - tenant = self.assertValidJsonTenantResponse(response) - user = self.create_user_with_known_password(tenant_id=tenant['id']).\ - json['user'] - token = self.authenticate(user['name'], user['password'], - tenant['id']).json['access']['token'] - tmp = self.service_token - self.service_token = token['id'] - response = self.service_request(method='GET', path='/tenants', - assert_status=200) - self.service_token = tmp - tenants = self.assertValidJsonTenantListResponse(response) - self.assertTrue(len(tenants) == 1) - self.assertIn(tenant['id'], [t['id'] for t in tenants]) - - def test_get_tenants_using_user_token_xml(self): - response = self.create_tenant() - tenant = self.assertValidJsonTenantResponse(response) - user = self.create_user_with_known_password(tenant_id=tenant['id']).\ - json['user'] - token = self.authenticate(user['name'], user['password'], - tenant['id']).json['access']['token'] - tmp = self.service_token - self.service_token = token['id'] - response = self.service_request(method='GET', path='/tenants', - assert_status=200, headers={'Accept': 'application/xml'}) - self.service_token = tmp - tenants = self.assertValidXmlTenantListResponse(response) - self.assertIn(tenant['id'], [t.get('id') for t in tenants]) - - def test_get_tenants_exp_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.list_tenants(assert_status=403) - - def test_get_tenants_exp_token_xml(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.get_tenants(assert_status=403, headers={ - 'Accept': 'application/xml'}) - - def test_get_tenants_blank(self): - # Create user with no default tenant set - self.nodefaultuser = self.create_user_with_known_password()\ - .json['user'] - response = self.authenticate(self.nodefaultuser['name'], - self.nodefaultuser['password'], - tenant_id=None, assert_status=200) - token_id = response.json['access']['token']['id'] - response = self.get_tenants(request_type='service', use_token=token_id) - self.assertTrue(len(response.json['tenants']) == 0, - "No tenants should be returned") - - def test_get_tenants_default_and_role(self): - """ Validates that the GET /tenants call returns tenants that are - assigned to the user by role AND by default tenant setting """ - self.fixture_create_normal_tenant() - self.fixture_create_tenant_user() - other_tenant = self.create_tenant().json['tenant'] - role = self.create_role().json['role'] - self.grant_role_to_user(self.tenant_user['id'], role['id'], - other_tenant['id']) - - response = self.authenticate(self.tenant_user['name'], - self.tenant_user['password'], - tenant_id=None, assert_status=200) - token_id = response.json['access']['token']['id'] - response = self.get_tenants(request_type='service', use_token=token_id) - tenants = response.json['tenants'] - second_tenant = [tenant for tenant in tenants - if tenant['id'] == other_tenant['id']] - self.assertTrue(len(second_tenant) > 0, - "Tenants with roles assigned should be returned") - - default_tenant = [tenant for tenant in tenants - if tenant['id'] == self.tenant['id']] - self.assertTrue(len(default_tenant) > 0, - "Default tenant should be returned") - - def test_get_tenants_does_not_return_default(self): - self.fixture_create_normal_tenant() - self.fixture_create_tenant_user() - other_tenant = self.create_tenant().json['tenant'] - role = self.create_role().json['role'] - self.grant_role_to_user(self.tenant_user['id'], role['id'], - other_tenant['id']) - - # Authenticate scoped to the non-default tenant - response = self.authenticate(self.tenant_user['name'], - self.tenant_user['password'], - tenant_id=other_tenant['id'], - assert_status=200) - token_id = response.json['access']['token']['id'] - response = self.get_tenants(request_type='service', use_token=token_id) - tenants = response.json['tenants'] - second_tenant = [tenant for tenant in tenants - if tenant['id'] == other_tenant['id']] - self.assertTrue(len(second_tenant) > 0, - "Tenants with roles assigned should be returned") - - default_tenant = [tenant for tenant in tenants - if tenant['id'] == self.tenant['id']] - self.assertTrue(len(default_tenant) == 0, - "Default tenant should not be returned") - - -class GetTenantTest(TenantTest): - def setUp(self, *args, **kwargs): - super(GetTenantTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - - def test_get_tenant(self): - response = self.fetch_tenant(self.tenant['id'], assert_status=200) - tenant = self.assertValidJsonTenantResponse(response) - self.assertEquals(self.tenant['id'], tenant['id']) - self.assertEquals(self.tenant['name'], tenant['name']) - self.assertFalse('description' in tenant) - self.assertEquals(self.tenant['enabled'], tenant['enabled']) - - def test_get_tenant_xml(self): - response = self.fetch_tenant(self.tenant['id'], assert_status=200, - headers={"Accept": "application/xml"}) - tenant = self.assertValidXmlTenantResponse(response) - self.assertEquals(self.tenant['id'], tenant.get('id')) - self.assertEquals(self.tenant['name'], tenant.get('name')) - self.assertEquals(str(self.tenant['enabled']).lower(), - tenant.get('enabled')) - - def test_get_tenant_not_found(self): - self.fetch_tenant(assert_status=404) - - def test_get_tenant_not_found_xml(self): - self.get_tenant(common.unique_str(), assert_status=404, headers={ - 'Accept': 'application/xml'}) - - -class GetTenantUsersTest(TenantTest): - def setUp(self, *args, **kwargs): - super(GetTenantUsersTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - self.fixture_create_normal_user() - - role = self.create_role().json['role'] - self.grant_role_to_user(self.user['id'], role['id'], self.tenant['id']) - - def test_list_tenant_users(self): - user = self.list_tenant_users(self.tenant['id'], - assert_status=200).json['users'][0] - self.assertEquals(user['name'], self.user['name']) - - def test_list_tenant_users_xml(self): - response = self.list_tenant_users(self.tenant['id'], - assert_status=200, headers={ - "Accept": "application/xml"}) - self.assertEquals(response.xml.tag, '{%s}users' % self.xmlns) - users = response.xml.findall('{%s}user' % self.xmlns) - for user in users: - self.assertEqual(user.get('name'), self.user['name']) - - def test_list_tenant_users_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.list_tenant_users(self.tenant['id'], assert_status=403) - - def test_list_tenant_users_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.list_tenant_users(self.tenant['id'], assert_status=403) - - def test_list_tenant_users_missing_token(self): - self.admin_token = '' - self.list_tenant_users(self.tenant['id'], assert_status=401) - - def test_list_tenant_users_invalid_token(self): - self.admin_token = common.unique_str() - self.list_tenant_users(self.tenant['id'], assert_status=401) - - -class GetTenantUsersByRoleTest(TenantTest): - def setUp(self, *args, **kwargs): - super(GetTenantUsersByRoleTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - self.fixture_create_normal_user() - - self.role = self.create_role().json['role'] - self.grant_role_to_user(self.user['id'], - self.role['id'], self.tenant['id']) - - def test_list_tenant_users(self): - user = self.list_tenant_users(self.tenant['id'], - self.role['id'], assert_status=200).json['users'][0] - self.assertEquals(user['name'], self.user['name']) - - def test_list_tenant_users_xml(self): - response = self.list_tenant_users(self.tenant['id'], - self.role['id'], assert_status=200, headers={ - "Accept": "application/xml"}) - self.assertEquals(response.xml.tag, '{%s}users' % self.xmlns) - users = response.xml.findall('{%s}user' % self.xmlns) - for user in users: - self.assertEqual(user.get('name'), self.user['name']) - - def test_list_tenant_users_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.list_tenant_users(self.tenant['id'], - self.role['id'], assert_status=403) - - def test_list_tenant_users_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.list_tenant_users(self.tenant['id'], - self.role['id'], assert_status=403) - - def test_list_tenant_users_missing_token(self): - self.admin_token = '' - self.list_tenant_users(self.tenant['id'], - self.role['id'], assert_status=401) - - def test_list_tenant_users_invalid_token(self): - self.admin_token = common.unique_str() - self.list_tenant_users(self.tenant['id'], - self.role['id'], assert_status=401) - - -class GetTenantByNameTest(TenantTest): - def setUp(self, *args, **kwargs): - super(GetTenantByNameTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - - def test_get_tenant(self): - response = self.fetch_tenant_by_name(self.tenant['name'], - assert_status=200) - tenant = self.assertValidJsonTenantResponse(response) - self.assertEquals(self.tenant['id'], tenant['id']) - self.assertEquals(self.tenant['name'], tenant['name']) - self.assertEquals(self.tenant['enabled'], tenant['enabled']) - - def test_get_tenant_data(self): - tenant = self.fixture_create_tenant(name=common.unique_str(), - description=common.unique_str(), - enabled=True) - response = self.fetch_tenant_by_name(tenant['name'], assert_status=200) - returned = self.assertValidJsonTenantResponse(response) - self.assertEquals(returned['id'], tenant['id']) - self.assertEquals(returned['name'], tenant['name']) - self.assertEquals(returned['description'], tenant['description']) - self.assertEquals(returned['enabled'], tenant['enabled']) - - def test_get_tenant_xml(self): - response = self.fetch_tenant_by_name( - self.tenant['name'], assert_status=200, headers={ - "Accept": "application/xml"}) - tenant = self.assertValidXmlTenantResponse(response) - - self.assertEquals(self.tenant['id'], tenant.get('id')) - self.assertEquals(self.tenant['name'], tenant.get('name')) - self.assertEquals(str(self.tenant['enabled']).lower(), - tenant.get('enabled')) - - def test_get_tenant_not_found(self): - self.fetch_tenant_by_name(assert_status=404) - - def test_get_tenant_not_found_xml(self): - self.fetch_tenant_by_name( - common.unique_str(), assert_status=404, headers={ - 'Accept': 'application/xml'}) - - -class UpdateTenantTest(TenantTest): - def setUp(self, *args, **kwargs): - super(UpdateTenantTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - - def test_update_tenant(self): - new_tenant_name = common.unique_str() - new_description = common.unique_str() - response = self.update_tenant(self.tenant['id'], - tenant_name=new_tenant_name, tenant_enabled=False, - tenant_description=new_description, assert_status=200) - updated_tenant = self.assertValidJsonTenantResponse(response) - self.assertEqual(updated_tenant['name'], new_tenant_name) - self.assertEqual(updated_tenant['description'], new_description) - self.assertEqual(updated_tenant['enabled'], False) - - def test_update_tenant_xml(self): - new_tenant_name = common.unique_str() - new_description = common.unique_str() - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<tenant xmlns="http://docs.openstack.org/identity/api/v2.0" ' - 'name="%s" ' - 'enabled="false"> ' - '<description>%s</description> ' - '</tenant>') % (new_tenant_name, new_description,) - response = self.post_tenant_for_update( - self.tenant['id'], as_xml=data, assert_status=200) - updated = self.assertValidXmlTenantResponse(response) - - self.assertEqual(updated.get('id'), self.tenant['id']) - self.assertEqual(updated.get('name'), new_tenant_name) - description = updated.find("{%s}description" % self.xmlns) - self.assertEqual(description.text, new_description) - self.assertEqual(updated.get('enabled'), 'false') - - def test_update_tenant_bad(self): - data = '{"tenant": { "description_bad": "A NEW description...",\ - "enabled":true }}' - self.post_tenant_for_update( - self.tenant['id'], as_json=data, assert_status=400) - - def test_update_tenant_bad_xml(self): - data = '<?xml version="1.0" encoding="UTF-8"?> \ - <tenant xmlns="http://docs.openstack.org/identity/api/v2.0" \ - enabled="true"> \ - <description_bad>A NEW description...</description> \ - </tenant>' - self.post_tenant_for_update( - self.tenant['id'], as_xml=data, assert_status=400) - - def test_update_tenant_not_found(self): - self.update_tenant(assert_status=404) - - def test_update_tenant_not_found_xml(self): - data = ('<?xml version="1.0" encoding="UTF-8"?>' - '<tenant xmlns="http://docs.openstack.org/identity/api/v2.0" ' - 'enabled="true"> ' - '<description>A NEW description...</description> ' - '</tenant>') - self.post_tenant_for_update( - common.unique_str(), as_xml=data, assert_status=404) - - -class DeleteTenantTest(TenantTest): - def setUp(self, *args, **kwargs): - super(DeleteTenantTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - - def test_delete_tenant(self): - self.remove_tenant(self.tenant['id'], assert_status=204) - self.get_tenant(self.tenant['id'], assert_status=404) - self.update_tenant(self.tenant['id'], assert_status=404) - - def test_delete_tenant_xml(self): - self.delete_tenant(self.tenant['id'], assert_status=204, headers={ - 'Accept': 'application/xml'}) - self.get_tenant(self.tenant['id'], assert_status=404) - self.update_tenant(self.tenant['id'], assert_status=404) - - def test_delete_tenant_not_found(self): - self.remove_tenant(common.unique_str(), assert_status=404) - - def test_delete_tenant_not_found_xml(self): - self.delete_tenant(common.unique_str(), assert_status=404, headers={ - 'Accept': 'application/xml'}) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/functional/test_token.py b/keystone/test/functional/test_token.py deleted file mode 100644 index 54a41ce0..00000000 --- a/keystone/test/functional/test_token.py +++ /dev/null @@ -1,207 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-2011 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. - -# pylint: disable=W0613 - -import unittest2 as unittest - -from keystone.backends import api -from keystone.logic.types import fault -from keystone.logic import service -from keystone.test.functional import common - - -class ValidateToken(common.FunctionalTestCase): - def setUp(self, *args, **kwargs): - super(ValidateToken, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - - self.user = self.create_user_with_known_password( - tenant_id=self.tenant['id']).json['user'] - self.role = self.create_role().json['role'] - self.rolegrant = self.grant_role_to_user(self.user['id'], - self.role['id'], self.tenant['id']) - self.token = self.authenticate(self.user['name'], - self.user['password'], self.tenant['id']).json['access']['token'] - - def test_validate_token_true(self): - r = self.get_token_belongsto(self.token['id'], self.tenant['id'], - assert_status=200) - self.assertIsNotNone(r.json['access']['user']["roles"]) - self.assertEqual(r.json['access']['user']["roles"][0]['id'], - self.role['id']) - self.assertEqual(r.json['access']['user']["roles"][0]['name'], - self.role['name']) - self.assertIsNotNone(r.json['access']['user']['id'], self.user['id']) - self.assertIsNotNone(r.json['access']['user']['name'], - self.user['name']) - self.assertIn('tenants', r.json['access']['token']) - - def test_validate_token_true_using_service_token(self): - self.fixture_create_service_admin() - self.admin_token = self.service_admin_token - r = self.get_token_belongsto(self.token['id'], self.tenant['id'], - assert_status=200) - - self.assertIsNotNone(r.json['access']['user']["roles"]) - self.assertEqual(r.json['access']['user']["roles"][0]['id'], - self.role['id']) - self.assertEqual(r.json['access']['user']["roles"][0]['name'], - self.role['name']) - - def test_validate_token_true_xml(self): - r = self.get_token_belongsto(self.token['id'], self.tenant['id'], - assert_status=200, headers={'Accept': 'application/xml'}) - - self.assertEqual(r.xml.tag, '{%s}access' % self.xmlns) - - user = r.xml.find('{%s}user' % self.xmlns) - self.assertIsNotNone(user) - self.assertEqual(self.user['id'], user.get('id')) - self.assertEqual(self.user['name'], user.get('name')) - - roles = user.find('{%s}roles' % self.xmlns) - self.assertIsNotNone(roles) - - role = roles.find('{%s}role' % self.xmlns) - self.assertIsNotNone(role) - self.assertEqual(self.role['id'], role.get("id")) - self.assertEqual(self.role['name'], role.get("name")) - - def test_validate_token_expired(self): - self.get_token(self.expired_admin_token, assert_status=404) - - def test_validate_token_expired_xml(self): - self.get_token(self.expired_admin_token, assert_status=404, headers={ - 'Accept': 'application/xml'}) - - def test_validate_token_invalid(self): - self.get_token(common.unique_str(), assert_status=404) - - def test_validate_token_invalid_xml(self): - self.get_token(common.unique_str(), assert_status=404, headers={ - 'Accept': 'application/xml'}) - - -class CheckToken(common.FunctionalTestCase): - def setUp(self, *args, **kwargs): - super(CheckToken, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - self.fixture_create_tenant_user() - - self.token = self.authenticate(self.tenant_user['name'], - self.tenant_user['password'], - self.tenant['id']).json['access']['token'] - - def test_validate_token_true(self): - self.check_token_belongs_to(self.token['id'], self.tenant['id'], - assert_status=200) - - def test_validate_token_true_using_service_token(self): - self.fixture_create_service_admin() - self.admin_token = self.service_admin_token - self.check_token_belongs_to(self.token['id'], self.tenant['id'], - assert_status=200) - - def test_validate_token_expired(self): - self.fixture_create_expired_token() - self.check_token(self.expired_admin_token, assert_status=404) - - def test_validate_token_expired_xml(self): - self.fixture_create_expired_token() - self.check_token(self.expired_admin_token, assert_status=404, headers={ - 'Accept': 'application/xml'}) - - def test_validate_token_invalid(self): - self.check_token(common.unique_str(), assert_status=404) - - -# pylint: disable=E1101,E1120 -class TokenEndpointTest(unittest.TestCase): - def _noop_validate_admin_token(self, admin_token): - pass - - class FakeDtoken(object): - expires = 'now' - tenant_id = 1 - id = 2 - - def _fake_token_get(self, token_id): - return self.FakeDtoken() - - def _fake_missing_token_get(self, token_id): - return None - - class FakeEndpoint(object): - service = 'foo' - - def _fake_tenant_get_all_endpoints(self, tenant_id): - return [self.FakeEndpoint()] - - def _fake_exploding_tenant_get_all_endpoints(self, tenant_id): - raise Exception("boom") - - def setUp(self): - self.stubout = stubout.StubOutForTesting() - - self.identity = service.IdentityService() - # The downside of python "private" methods ... you - # have to do stuff like this to stub them out. - self.stubout.SmartSet(self.identity, - "_IdentityService__validate_admin_token", - self._noop_validate_admin_token) - - def tearDown(self): - self.stubout.SmartUnsetAll() - self.stubout.UnsetAll() - - def test_endpoints_from_good_token(self): - """Happy Day scenario.""" - self.stubout.SmartSet(api.TOKEN, - 'get', self._fake_token_get) - - self.stubout.SmartSet(api.BaseTenantAPI, - 'get_all_endpoints', - self._fake_tenant_get_all_endpoints) - - auth_data = self.identity.get_endpoints_for_token("admin token", - "token id") - self.assertEquals(auth_data.base_urls[0].service, 'foo') - self.assertEquals(len(auth_data.base_urls), 1) - - def test_endpoints_from_bad_token(self): - self.stubout.SmartSet(api.TOKEN, - 'get', self._fake_missing_token_get) - - self.assertRaises(fault.ItemNotFoundFault, - self.identity.get_endpoints_for_token, - "admin token", "token id") - - def test_bad_endpoints(self): - self.stubout.SmartSet(api.TOKEN, - 'get', self._fake_token_get) - - self.stubout.SmartSet(api.TENANT, - 'get_all_endpoints', - self._fake_exploding_tenant_get_all_endpoints) - - endpoints = self.identity.get_endpoints_for_token("admin token", - "token id") - self.assertEquals(endpoints.base_urls, []) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/functional/test_users.py b/keystone/test/functional/test_users.py deleted file mode 100644 index 7027986c..00000000 --- a/keystone/test/functional/test_users.py +++ /dev/null @@ -1,406 +0,0 @@ -# -# 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 unittest2 as unittest -from keystone.test.functional import common - - -class UserTest(common.FunctionalTestCase): - def setUp(self, *args, **kwargs): - super(UserTest, self).setUp(*args, **kwargs) - - -class CreateUserTest(UserTest): - def setUp(self, *args, **kwargs): - super(CreateUserTest, self).setUp(*args, **kwargs) - - def test_create_user_with_tenant(self): - tenant = self.create_tenant().json['tenant'] - self.user = self.create_user(tenant_id=tenant['id'], - assert_status=201) - - def test_user_with_no_tenant(self): - self.create_user(assert_status=201) - - def test_create_user_disabled_tenant(self): - tenant = self.create_tenant(tenant_enabled=False).json['tenant'] - self.create_user(tenant_id=tenant['id'], assert_status=403) - - def test_create_user_again(self): - user_name = common.unique_str() - self.create_user(user_name) - self.create_user(user_name, assert_status=409) - - def test_create_users_with_duplicate_emails(self): - email = common.unique_email() - self.create_user(user_email=email) - self.create_user(user_email=email, assert_status=409) - - def test_create_user_with_empty_password(self): - self.create_user(user_password='', assert_status=400) - - def test_create_user_with_empty_username(self): - self.create_user(user_name='', assert_status=400) - - def test_create_user_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.create_user(assert_status=403) - - def test_create_user_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.create_user(assert_status=403) - - def test_create_user_missing_token(self): - self.admin_token = '' - self.create_user(assert_status=401) - - def test_create_user_invalid_token(self): - self.admin_token = common.unique_str() - self.create_user(assert_status=401) - - def test_create_user_xml_missing_name(self): - data = ('<?xml version="1.0" encoding="UTF-8"?> ' - '<user xmlns="http://docs.openstack.org/identity/api/v2.0" ' - 'enabled="true" email="john.smith@example.org" ' - 'name="" id="u1000"/>') - self.post_user(as_xml=data, assert_status=400) - - -class GetUserTest(UserTest): - def setUp(self, *args, **kwargs): - super(GetUserTest, self).setUp(*args, **kwargs) - - self.user = self.create_user().json['user'] - - def test_get_user(self): - self.fetch_user(self.user['id']) - - def test_query_user(self): - self.fetch_user_by_name(self.user['name']) - - def test_get_user_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.fetch_user(self.user['id'], assert_status=403) - - def test_query_user_using_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.fetch_user_by_name(self.user['name'], assert_status=403) - - def test_get_user_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.fetch_user(self.user['id'], assert_status=403) - - def test_query_user_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.fetch_user_by_name(self.user['name'], assert_status=403) - - def test_get_user_using_missing_token(self): - self.admin_token = '' - self.fetch_user(self.user['id'], assert_status=401) - - def test_query_user_using_missing_token(self): - self.admin_token = '' - self.fetch_user_by_name(self.user['name'], assert_status=401) - - def test_get_user_using_invalid_token(self): - self.admin_token = common.unique_str() - self.fetch_user(self.user['id'], assert_status=401) - - def test_query_user_using_invalid_token(self): - self.admin_token = common.unique_str() - self.fetch_user_by_name(self.user['name'], assert_status=401) - - def test_get_disabled_user(self): - self.disable_user(self.user['id']) - user = self.fetch_user(self.user['id']).json['user'] - self.assertFalse(user['enabled']) - - def test_query_disabled_user(self): - self.disable_user(self.user['id']) - self.fetch_user_by_name(self.user['name']) - - -class DeleteUserTest(UserTest): - def setUp(self, *args, **kwargs): - super(DeleteUserTest, self).setUp(*args, **kwargs) - - self.user = self.create_user().json['user'] - - def test_user_delete(self): - self.remove_user(self.user['id'], assert_status=204) - - def test_user_delete_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.remove_user(self.user['id'], assert_status=403) - - def test_user_delete_missing_token(self): - self.admin_token = '' - self.remove_user(self.user['id'], assert_status=401) - - def test_user_delete_invalid_token(self): - self.admin_token = common.unique_str() - self.remove_user(self.user['id'], assert_status=401) - - -class GetAllUsersTest(UserTest): - def setUp(self, *args, **kwargs): - super(GetAllUsersTest, self).setUp(*args, **kwargs) - - for _x in range(0, 3): - self.create_user() - - def test_list_users(self): - self.list_users(assert_status=200) - - def test_list_users_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.list_users(assert_status=403) - - def test_list_users_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.list_users(assert_status=403) - - def test_list_users_missing_token(self): - self.admin_token = '' - self.list_users(assert_status=401) - - def test_list_users_invalid_token(self): - self.admin_token = common.unique_str() - self.list_users(assert_status=401) - - -class UpdateUserTest(UserTest): - def setUp(self, *args, **kwargs): - super(UpdateUserTest, self).setUp(*args, **kwargs) - - self.user = self.create_user().json['user'] - - def test_update_user_email(self): - new_user_email = common.unique_email() - self.update_user(self.user['id'], user_name=self.user['name'], - user_email=new_user_email) - r = self.fetch_user(self.user['id']) - self.assertTrue(r.json['user']['email'], new_user_email) - - def test_update_user_name(self): - new_user_name = common.unique_str() - new_user_email = common.unique_email() - self.update_user(self.user['id'], user_name=new_user_name, - user_email=new_user_email) - r = self.fetch_user(self.user['id']) - self.assertTrue(r.json['user']['name'], new_user_name) - - def test_enable_disable_user(self): - self.assertFalse(self.disable_user(self.user['id']).\ - json['user']['enabled']) - self.assertFalse(self.fetch_user(self.user['id']).\ - json['user']['enabled']) - self.assertTrue(self.enable_user(self.user['id']).\ - json['user']['enabled']) - self.assertTrue(self.fetch_user(self.user['id']).\ - json['user']['enabled']) - - def test_update_user_bad_request(self): - data = '{"user_bad": { "bad": "%s"}}' % (common.unique_email(),) - self.post_user_for_update( - self.user['id'], assert_status=400, body=data, headers={ - "Content-Type": "application/json"}) - - def test_update_user_expired_token(self): - self.fixture_create_expired_token() - self.fixture_create_normal_user() - - self.admin_token = self.expired_admin_token - self.update_user(self.user['id'], assert_status=403) - - def test_update_user_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.update_user(self.user['id'], user_email=common.unique_email(), - assert_status=403) - - def test_update_user_invalid_token(self): - self.admin_token = common.unique_str() - self.update_user(self.user['id'], assert_status=401) - - def test_update_user_missing_token(self): - self.admin_token = '' - self.update_user(self.user['id'], assert_status=401) - - -class TestUpdateConflict(UserTest): - def setUp(self, *args, **kwargs): - super(TestUpdateConflict, self).setUp(*args, **kwargs) - - self.users = {} - for x in range(0, 2): - self.users[x] = self.create_user().json['user'] - - def test_update_user_email_conflict(self): - """Replace the second user's email with that of the first""" - self.update_user(user_id=self.users[1]['id'], - user_name=self.users[1]['name'], - user_email=self.users[0]['email'], assert_status=409) - - -class SetPasswordTest(UserTest): - def setUp(self, *args, **kwargs): - super(SetPasswordTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_user() - - def test_update_user_password(self): - new_password = common.unique_str() - r = self.update_user_password(self.user['id'], new_password) - self.assertEqual(r.json['user']['password'], new_password) - - def test_update_disabled_users_password(self): - self.disable_user(self.user['id']) - - new_password = common.unique_str() - r = self.update_user_password(self.user['id'], new_password) - self.assertEqual(r.json['user']['password'], new_password) - - def test_user_password_bad_request(self): - data = '{"user_bad": { "password": "p@ssword"}}' - self.put_user_password(self.user['id'], body=data, assert_status=400, - headers={ - "Content-Type": "application/json"}) - - def test_user_password_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.update_user_password(self.user['id'], assert_status=403) - - def test_user_password_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.update_user_password(self.user['id'], assert_status=403) - - def test_user_password_invalid_token(self): - self.admin_token = common.unique_str() - self.update_user_password(self.user['id'], assert_status=401) - - def test_user_password_missing_token(self): - self.admin_token = '' - self.update_user_password(self.user['id'], assert_status=401) - - -class SetEnabledTest(UserTest): - def setUp(self, *args, **kwargs): - super(SetEnabledTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_user() - - def test_user_enabled_bad_request(self): - data = '{"user_bad": { "enabled": true}}' - self.put_user_enabled(self.user['id'], body=data, assert_status=400, - headers={ - "Content-Type": "application/json"}) - - def test_user_enabled_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.disable_user(self.user['id'], assert_status=403) - - def test_user_enabled_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.disable_user(self.user['id'], assert_status=403) - - -class TenantUpdateTest(UserTest): - def setUp(self, *args, **kwargs): - super(TenantUpdateTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_user() - self.fixture_create_normal_tenant() - - def test_update_user_tenant(self): - r = self.update_user_tenant(self.user['id'], self.tenant['id']) - self.assertEqual(r.json['user']['tenantId'], self.tenant['id']) - - def test_update_user_tenant_using_invalid_tenant(self): - self.update_user_tenant(self.user['id'], assert_status=404) - - def test_update_user_tenant_using_disabled_tenant(self): - disabled_tenant = self.create_tenant( - tenant_enabled=False).json['tenant'] - self.assertIsNotNone(disabled_tenant['id']) - self.update_user_tenant(self.user['id'], disabled_tenant['id'], - assert_status=403) - - def test_update_user_tenant_using_missing_token(self): - self.admin_token = '' - self.update_user_tenant(self.user['id'], self.tenant['id'], - assert_status=401) - - def test_update_user_tenant_using_invalid_token(self): - self.admin_token = common.unique_str() - self.update_user_tenant(self.user['id'], self.tenant['id'], - assert_status=401) - - def test_update_user_tenant_using_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.update_user_tenant(self.user['id'], self.tenant['id'], - assert_status=403) - - def test_update_user_tenant_using_exp_admin_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.update_user_tenant(self.user['id'], self.tenant['id'], - assert_status=403) - - -class AddUserTest(UserTest): - def setUp(self, *args, **kwargs): - super(AddUserTest, self).setUp(*args, **kwargs) - self.fixture_create_normal_tenant() - - def test_add_user_tenant(self): - self.create_user(tenant_id=self.tenant['id'], assert_status=201) - - def test_add_user_tenant_expired_token(self): - self.fixture_create_expired_token() - self.admin_token = self.expired_admin_token - self.create_user(assert_status=403) - - def test_add_user_tenant_disabled_token(self): - self.fixture_create_disabled_user_and_token() - self.admin_token = self.disabled_admin_token - self.create_user(assert_status=403) - - def test_add_user_tenant_invalid_token(self): - self.admin_token = common.unique_str() - self.create_user(assert_status=401) - - def test_add_user_tenant_missing_token(self): - self.admin_token = '' - self.create_user(assert_status=401) - - def test_add_user_tenant_disabled_tenant(self): - self.tenant = self.create_tenant(tenant_enabled=False).json['tenant'] - self.create_user(tenant_id=self.tenant['id'], assert_status=403) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/sampledata.py b/keystone/test/sampledata.py deleted file mode 100644 index 410e291d..00000000 --- a/keystone/test/sampledata.py +++ /dev/null @@ -1,137 +0,0 @@ -import keystone.manage - -DEFAULT_FIXTURE = [ -# Tenants - ('tenant', 'add', 'customer-x'), - ('tenant', 'add', 'ANOTHER:TENANT'), - ('tenant', 'add', 'project-y'), - ('tenant', 'disable', 'project-y'), - ('tenant', 'add', 'coffee-tea'), -# Add some services for the service roles - ('service', 'add', 'coffee', - 'coffee service', 'coffee service'), - ('service', 'add', 'tea', - 'tea house', 'tea house'), -# Users - ('user', 'add', 'joeuser', 'secrete', 'customer-x'), - ('user', 'add', 'pete', 'secrete', 'coffee-tea'), - ('user', 'add', 'joeadmin', 'secrete', 'customer-x'), - ('user', 'add', 'admin', 'secrete'), - ('user', 'add', 'serviceadmin', 'secrete', 'customer-x'), - ('user', 'add', 'disabled', 'secrete', 'customer-x'), - ('user', 'add', 'nodefaulttenant', 'secrete'), - ('user', 'disable', 'disabled'), -# Roles - ('role', 'add', 'Admin'), - ('role', 'add', 'KeystoneServiceAdmin'), - ('role', 'grant', 'Admin', 'admin'), - ('role', 'grant', 'KeystoneServiceAdmin', 'serviceadmin'), - ('role', 'grant', 'Admin', 'joeadmin', 'customer-x'), - ('role', 'grant', 'Admin', 'joeadmin', 'ANOTHER:TENANT'), - ('role', 'grant', 'Admin', 'nodefaulttenant', 'customer-x'), - ('role', 'add', 'Member'), - ('role', 'grant', 'Member', 'joeuser', 'customer-x'), - ('role', 'add', 'barista', 'coffee'), - ('role', 'add', 'barista', 'tea'), - ('role', 'grant', 'barista', 'pete'), - ('role', 'add', 'barista-global'), - ('role', 'grant', 'barista-global', 'pete'), -# Add Services - #1 Service Name:exampleservice Type:example type - ('service', 'add', 'exampleservice', - 'example type', 'example description'), - #2 Service Name:swift Type:object-store - ('service', 'add', 'swift', - 'object-store', 'Swift-compatible service'), - #3 Service Name:cdn Type:object-store - ('service', 'add', 'cdn', - 'object-store', 'Swift-compatible service'), - #4 Service Name:nova Type:compute - ('service', 'add', 'nova', - 'compute', 'OpenStack Compute Service'), - #5 Service Name:nova_compat Type:Compute - ('service', 'add', 'nova_compat', - 'compute', 'OpenStack Compute Service'), - #6 Service Name:glance Type:image - ('service', 'add', 'glance', - 'image', 'OpenStack Image Service'), - #7 Service Name:keystone Type:identity - ('service', 'add', 'identity', - 'identity', 'OpenStack Identity Service'), -# Keeping for compatibility for a while till dashboard catches up - ('endpointTemplates', 'add', 'RegionOne', 'swift', - 'http://swift.publicinternets.com/v1/AUTH_%tenant_id%', - 'http://swift.admin-nets.local:8080/', - 'http://127.0.0.1:8080/v1/AUTH_%tenant_id%', '1', '0'), - ('endpointTemplates', 'add', 'RegionOne', 'nova_compat', - 'http://nova.publicinternets.com/v1.0/', - 'http://127.0.0.1:8774/v1.0', 'http://localhost:8774/v1.0', '1', '0'), - ('endpointTemplates', 'add', 'RegionOne', 'nova', - 'http://nova.publicinternets.com/v1.1/', 'http://127.0.0.1:8774/v1.1', - 'http://localhost:8774/v1.1', '1', '0'), - ('endpointTemplates', 'add', 'RegionOne', 'glance', - 'http://glance.publicinternets.com/v1.1/', - 'http://nova.admin-nets.local/v1.1/', - 'http://127.0.0.1:9292/v1.1/', '1', '0'), - ('endpointTemplates', 'add', 'RegionOne', 'cdn', - 'http://cdn.publicinternets.com/v1.1/%tenant_id%', - 'http://cdn.admin-nets.local/v1.1/%tenant_id%', - 'http://127.0.0.1:7777/v1.1/%tenant_id%', '1', - '0', '1.1', 'http://127.0.0.1:7777/', 'http://127.0.0.1:7777/v1.1'), -# endpointTemplates - ('endpointTemplates', 'add', 'RegionOne', 'swift', - 'http://swift.publicinternets.com/v1/AUTH_%tenant_id%', - 'http://swift.admin-nets.local:8080/', - 'http://127.0.0.1:8080/v1/AUTH_%tenant_id%', '1', '0'), - ('endpointTemplates', 'add', 'RegionOne', 'nova', - 'http://nova.publicinternets.com/v1.0/', 'http://127.0.0.1:8774/v1.0', - 'http://localhost:8774/v1.0', '1', '0'), - ('endpointTemplates', 'add', 'RegionOne', 'nova_compat', - 'http://nova.publicinternets.com/v1.1/', 'http://127.0.0.1:8774/v1.1', - 'http://localhost:8774/v1.1', '1', '0'), - ('endpointTemplates', 'add', 'RegionOne', 'glance', - 'http://glance.publicinternets.com/v1.1/', - 'http://nova.admin-nets.local/v1.1/', - 'http://127.0.0.1:9292/v1.1/', '1', '0'), - ('endpointTemplates', 'add', 'RegionOne', 'cdn', - 'http://cdn.publicinternets.com/v1.1/%tenant_id%', - 'http://cdn.admin-nets.local/v1.1/%tenant_id%', - 'http://127.0.0.1:7777/v1.1/%tenant_id%', '1', '0'), -# Global endpointTemplate - ('endpointTemplates', 'add', 'RegionOne', 'identity', - 'http://keystone.publicinternets.com/v2.0', - 'http://127.0.0.1:35357/v2.0', 'http://127.0.0.1:5000/v2.0', '1', '1'), -# Tokens - ('token', 'add', '887665443383838', 'joeuser', 'customer-x', - '2012-02-05T00:00'), - ('token', 'add', '999888777666', 'admin', None, - '2015-02-05T00:00'), - ('token', 'add', '111222333444', 'serviceadmin', None, - '2015-02-05T00:00'), - ('token', 'add', '000999', 'admin', 'customer-x', '2010-02-05T00:00'), - ('token', 'add', '999888777', 'disabled', 'customer-x', - '2015-02-05T00:00'), -# Tenant endpointsGlobal endpoint not added - ('endpoint', 'add', 'customer-x', '1'), - ('endpoint', 'add', 'customer-x', '2'), - ('endpoint', 'add', 'customer-x', '3'), - ('endpoint', 'add', 'customer-x', '4'), - ('endpoint', 'add', 'customer-x', '5'), -# Add Credentials - ('credentials', 'add', 'admin', 'EC2', 'admin:admin', 'admin', - 'customer-x'), -] - - -def load_fixture(fixture=DEFAULT_FIXTURE, args=None): - keystone.manage.parse_args(args) - for cmd in fixture: - keystone.manage.process(*cmd) - - -def main(): - load_fixture() - - -if __name__ == '__main__': - main() diff --git a/keystone/test/unit/__init__.py b/keystone/test/unit/__init__.py deleted file mode 100644 index e69de29b..00000000 --- a/keystone/test/unit/__init__.py +++ /dev/null diff --git a/keystone/test/unit/base.py b/keystone/test/unit/base.py deleted file mode 100644 index 0ad0c327..00000000 --- a/keystone/test/unit/base.py +++ /dev/null @@ -1,385 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2011 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. - -"""Base test case classes for the unit tests""" - -import datetime -import functools -import httplib -import logging -from lxml import etree, objectify -import pprint -import os -import sys -import tempfile -import unittest2 as unittest -import webob - -from keystone import config -from keystone import server -import keystone.backends.sqlalchemy as db -import keystone.backends.api as db_api -from keystone import backends - -logger = logging.getLogger('test.unit.base') - -CONF = config.CONF - - -class ServiceAPITest(unittest.TestCase): - """ - Base test case class for any unit test that tests the main service API. - """ - def __init__(self, *args, **kwargs): - super(ServiceAPITest, self).__init__(*args, **kwargs) - # The `api` attribute for this base class is the `server.KeystoneAPI` - # controller. - self.api_class = server.ServiceApi - # Set of dicts of tenant attributes we start each test case with - self.tenant_fixtures = [ - {'name': 'tenant1', - 'enabled': True, - 'desc': 'tenant1'}] - # Attributes of the Service used for the roles. - self.service_attrs = { - 'id': "0", - 'name': 'test_service', - 'type': 'test', - 'desc': 'test service', - 'owner_id': "0"} - # Set of Role fixtures to create for each test - self.role_fixtures = [ - {'id': "0", - 'name': 'regular_role', - 'desc': 'regular role', - 'service_id': self.service_attrs['id']}, - {'id': "1", - 'name': 'Admin', - 'desc': 'Admin role', - 'service_id': self.service_attrs['id']}, - ] - # Attributes of the user the test creates for each test case that - # will authenticate against the API. The `auth_user` attribute - # will contain the created user with the following attributes. - self.user_attrs = { - 'auth_user': - {'name': 'auth_user', - 'password': 'auth_pass', - 'email': 'auth_user@example.com', - 'enabled': True, - 'tenant_name': 'tenant1', - 'roles': [self.role_fixtures[0]], - }, - 'admin_user': - {'name': 'admin_user', - 'password': 'admin_pass', - 'email': 'admin_user@example.com', - 'enabled': True, - 'tenant_name': 'tenant1', - 'roles': [self.role_fixtures[1]], - }} - # Special attribute that is the identifier of the token we use in - # authenticating. Makes it easy to test the authentication process. - self.auth_token_id = 'SPECIALAUTHTOKEN' - # The special attribute for identifying the admin token. - self.admin_token_id = 'SPECIALADMINTOKEN' - # Content-type of requests. Generally, you don't need to manually - # change this. Instead, :see test.unit.decorators - self.content_type = 'json' - # Version of the API to test - self.api_version = '2.0' - # Save the original configuration - self.conf_save = CONF - # Set the desired conf options - conf_text = """ -[DEFAULT] -backends = keystone.backends.sqlalchemy -keystone_admin_role = Admin -keystone_service_admin_role = KeystoneServiceAdmin -hash_password = True -verbose = False -debug = False - -[keystone.backends.sqlalchemy] -sql_connection = sqlite:// -backend_entities = ['UserRoleAssociation', - 'Endpoints', 'Role', 'Tenant', 'User', - 'Credentials', 'EndpointTemplates', 'Token', 'Service'] -""" - self.update_CONF(conf_text) - - @staticmethod - def update_CONF(txt): - """ - Resets the CONF file, and reads in the passed configuration text. - """ - CONF.reset() - fd, tmpname = tempfile.mkstemp() - os.close(fd) - with file(tmpname, "w") as fconf: - fconf.write(txt) - CONF(config_files=[tmpname]) - os.remove(tmpname) - - def setUp(self): - self.api = self.api_class() - - dt = datetime - self.expires = dt.datetime.utcnow() + dt.timedelta(days=1) - self.clear_all_data() - - # Create all our base tenants - for tenant in self.tenant_fixtures: - self.fixture_create_tenant(**tenant) - - # Create the test service - self.fixture_create_service(**self.service_attrs) - - # Create all our roles - for role in self.role_fixtures: - self.fixture_create_role(**role) - - # Create the user we will authenticate with - auth_user_attrs = self.user_attrs.get("auth_user") - admin_user_attrs = self.user_attrs.get("admin_user") - self.auth_user = self.fixture_create_user(**auth_user_attrs) - self.admin_user = self.fixture_create_user(**admin_user_attrs) - self.auth_token = self.fixture_create_token( - id=self.auth_token_id, - user_id=self.auth_user['id'], - tenant_id=self.auth_user['tenant_id'], - expires=self.expires, - ) - self.admin_token = self.fixture_create_token( - id=self.admin_token_id, - user_id=self.admin_user['id'], - tenant_id=self.admin_user['tenant_id'], - expires=self.expires, - ) - - self.add_verify_status_helpers() - - def tearDown(self): - self.clear_all_data() - setattr(self, 'req', None) - setattr(self, 'res', None) - - def clear_all_data(self): - """ - Purges the database of all data - """ - db.unregister_models() - logger.debug("Cleared all data from database") - reload(db) - backends.configure_backends() - - def fixture_create_credentials(self, **kwargs): - """ - Creates a tenant fixture. - - :params \*\*kwargs: Attributes of the tenant to create - """ - values = kwargs.copy() - user = db_api.USER.get_by_name(values['user_name']) - if user: - values['user_id'] = user.id - credentials = db_api.CREDENTIALS.create(values) - logger.debug("Created credentials fixture %s", - credentials['user_id']) - return credentials - - def fixture_create_tenant(self, **kwargs): - """ - Creates a tenant fixture. - - :params \*\*kwargs: Attributes of the tenant to create - """ - values = kwargs.copy() - tenant = db_api.TENANT.create(values) - logger.debug("Created tenant fixture %s", values['name']) - return tenant - - def fixture_create_service(self, **kwargs): - """ - Creates a service fixture. - - :params \*\*kwargs: Attributes of the service to create - """ - values = kwargs.copy() - service = db_api.SERVICE.create(values) - logger.debug("Created service fixture %s", values['name']) - return service - - def fixture_create_user(self, **kwargs): - """ - Creates a user fixture. If the user's tenant ID is set, and the tenant - does not exist in the database, the tenant is created. - - :params \*\*kwargs: Attributes of the user to create - """ - values = kwargs.copy() - roles = values.pop("roles", []) - tenant_name = values.get('tenant_name') - if tenant_name: - if not db_api.TENANT.get_by_name(tenant_name): - tenant = db_api.TENANT.create({'name': tenant_name, - 'enabled': True, - 'desc': tenant_name}) - values['tenant_id'] = tenant.id - user = db_api.USER.create(values) - logger.debug("Created user fixture %s", user.id) - for role in roles: - user_role = db_api.USER.user_role_add( - {'user_id': user['id'], 'tenant_id': user['tenant_id'], - 'role_id': role['id']}) - logger.debug("Created user-role association %s", user_role.id) - return user - - def fixture_create_role(self, **kwargs): - """ - Creates a role fixture. - - :params \*\*kwargs: Attributes of the role to create - """ - values = kwargs.copy() - role = db_api.ROLE.create(values) - logger.debug("Created role fixture %s", role.id) - return role - - def fixture_create_token(self, **kwargs): - """ - Creates a token fixture. - - :params \*\*kwargs: Attributes of the token to create - """ - values = kwargs.copy() - token = db_api.TOKEN.create(values) - logger.debug("Created token fixture %s", values['id']) - return token - - def get_request(self, method, url, headers=None): - """ - Sets the `req` attribute to a `webob.Request` object that - is constructed with the supplied method and url. Supplied - headers are added to appropriate Content-type headers. - """ - headers = headers or {} - self.req = webob.Request.blank(url) - self.req.method = method - self.req.headers = headers - if 'content-type' not in headers: - ct = 'application/%s' % self.content_type - self.req.headers['content-type'] = ct - self.req.headers['accept'] = ct - return self.req - - def get_response(self): - """ - Sets the appropriate headers for the `req` attribute for - the current content type, then calls `req.get_response()` and - sets the `res` attribute to the returned `webob.Response` object - """ - self.res = self.req.get_response(self.api) - logger.debug("%s %s returned %s", self.req.method, self.req.path_qs, - self.res.status) - if self.res.status_int != httplib.OK: - logger.debug("Response Body:") - for line in self.res.body.split("\n"): - logger.debug(line) - return self.res - - def verify_status(self, status_code): - """ - Simple convenience wrapper for validating a response's status - code. - """ - if not getattr(self, 'res'): - raise RuntimeError("Called verify_status() before calling " - "get_response()!") - - self.assertEqual(status_code, self.res.status_int, - "Incorrect status code %d. Expected %d" % - (self.res.status_int, status_code)) - - def add_verify_status_helpers(self): - """ - Adds some convenience helpers using partials... - """ - self.status_ok = functools.partial(self.verify_status, - httplib.OK) - self.status_not_found = functools.partial(self.verify_status, - httplib.NOT_FOUND) - self.status_unauthorized = functools.partial(self.verify_status, - httplib.UNAUTHORIZED) - self.status_bad_request = functools.partial(self.verify_status, - httplib.BAD_REQUEST) - - def assert_dict_equal(self, expected, got): - """ - Compares two dicts for equality and prints the dictionaries - nicely formatted for easy comparison if there is a failure. - """ - self.assertEqual(expected, got, "Mappings are not equal.\n" - "Got:\n%s\nExpected:\n%s" % - (pprint.pformat(got), - pprint.pformat(expected))) - - def assert_xml_strings_equal(self, expected, got): - """ - Compares two XML strings for equality by parsing them both - into DOMs. Prints the DOMs nicely formatted for easy comparison - if there is a failure. - """ - # This is a nice little trick... objectify.fromstring() returns - # a DOM different from etree.fromstring(). The objectify version - # removes any different whitespacing... - got = objectify.fromstring(got) - expected = objectify.fromstring(expected) - self.assertEqual(etree.tostring(expected), - etree.tostring(got), "DOMs are not equal.\n" - "Got:\n%s\nExpected:\n%s" % - (etree.tostring(got, pretty_print=True), - etree.tostring(expected, pretty_print=True))) - - -class AdminAPITest(ServiceAPITest): - """ - Base test case class for any unit test that tests the admin API. The - """ - def __init__(self, *args, **kwargs): - super(AdminAPITest, self).__init__(*args, **kwargs) - # The `api` attribute for this base class is the - # `server.KeystoneAdminAPI` controller. - self.api_class = server.AdminApi - # Set of dicts of tenant attributes we start each test case with - self.tenant_fixtures = [ - {'id': 'tenant1', - 'name': 'tenant1', - 'enabled': True, - 'desc': 'tenant1'}, - {'id': 'tenant2', - 'name': 'tenant2', - 'enabled': True, - 'desc': 'tenant2'}] - - # Attributes of the user the test creates for each test case that - # will authenticate against the API. - self.auth_user_attrs = {'id': 'admin_user', - 'password': 'admin_pass', - 'email': 'admin_user@example.com', - 'enabled': True, - 'tenant_id': 'tenant2', - 'roles': []} diff --git a/keystone/test/unit/decorators.py b/keystone/test/unit/decorators.py deleted file mode 100644 index 17a7d432..00000000 --- a/keystone/test/unit/decorators.py +++ /dev/null @@ -1,49 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2011 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. - -"""Decorators useful in unit tests""" - -import functools - - -def content_type(func, content_type='json'): - """ - Decorator for a test case method that sets the test case's - content_type to 'json' or 'xml' and resets it afterwards to - the original setting. This also asserts that if there is a - value for the test object's `res` attribute, that the content-type - header of the response is correct. - """ - @functools.wraps(func) - def wrapped(*a, **kwargs): - test_obj = a[0] - orig_content_type = test_obj.content_type - try: - test_obj.content_type = content_type - func(*a, **kwargs) - if getattr(test_obj, 'res'): - expected = 'application/%s' % content_type - got = test_obj.res.headers['content-type'].split(';')[0] - test_obj.assertEqual(expected, got, - "Bad content type: %s. Expected: %s" % - (got, expected)) - finally: - test_obj.content_type = orig_content_type - return wrapped - - -jsonify = functools.partial(content_type, content_type='json') -xmlify = functools.partial(content_type, content_type='xml') diff --git a/keystone/test/unit/test_auth.py b/keystone/test/unit/test_auth.py deleted file mode 100644 index 458873e7..00000000 --- a/keystone/test/unit/test_auth.py +++ /dev/null @@ -1,187 +0,0 @@ -import json -import unittest2 as unittest -import keystone.logic.types.auth as auth -import keystone.logic.types.fault as fault - - -class TestAuth(unittest.TestCase): - '''Unit tests for auth.py.''' - - pwd_xml = '<?xml version="1.0" encoding="UTF-8"?>\ - <auth xmlns="http://docs.openstack.org/identity/api/v2.0">\ - <passwordCredentials \ - xmlns="http://docs.openstack.org/identity/api/v2.0" \ - password="secret" username="disabled" \ - /></auth>' - - def test_pwd_cred_marshall(self): - creds = auth.AuthWithPasswordCredentials.from_xml(self.pwd_xml) - self.assertEqual(creds.password, "secret") - self.assertEqual(creds.username, "disabled") - - def test_pwd_creds_from_json(self): - data = json.dumps({"auth": - {"passwordCredentials": - {"username": "foo", "password": "bar"}}}) - creds = auth.AuthWithPasswordCredentials.from_json(data) - self.assertEqual(creds.username, "foo") - self.assertEqual(creds.password, "bar") - self.assertIsNone(creds.tenant_id) - self.assertIsNone(creds.tenant_name) - - def test_pwd_creds_with_tenant_name_from_json(self): - data = json.dumps({"auth": - {"tenantName": "blaa", - "passwordCredentials": - {"username": "foo", "password": "bar"}}}) - creds = auth.AuthWithPasswordCredentials.from_json(data) - self.assertEqual(creds.username, "foo") - self.assertEqual(creds.password, "bar") - self.assertIsNone(creds.tenant_id) - self.assertEqual(creds.tenant_name, "blaa") - - def test_pwd_creds_with_tenant_id_from_json(self): - data = json.dumps({"auth": - {"tenantId": "blaa", - "passwordCredentials": - {"username": "foo", "password": "bar"}}}) - creds = auth.AuthWithPasswordCredentials.from_json(data) - self.assertEqual(creds.username, "foo") - self.assertEqual(creds.password, "bar") - self.assertEqual(creds.tenant_id, "blaa") - self.assertIsNone(creds.tenant_name) - - def test_pwd_not_both_tenant_from_json(self): - data = json.dumps({"auth": {"tenantId": "blaa", "tenantName": "aalb"}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "not both", - auth.AuthWithPasswordCredentials.from_json, - data) - - def test_pwd_invalid_from_json(self): - self.assertRaisesRegexp(fault.BadRequestFault, - "Cannot parse", - auth.AuthWithPasswordCredentials.from_json, - "") - - def test_pwd_no_auth_from_json(self): - data = json.dumps({"foo": "bar"}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Expecting auth", - auth.AuthWithPasswordCredentials.from_json, - data) - - def test_pwd_no_creds_from_json(self): - data = json.dumps({"auth": {}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Expecting passwordCredentials", - auth.AuthWithPasswordCredentials.from_json, - data) - - def test_pwd_invalid_attribute_from_json(self): - data = json.dumps({"auth": {"foo": "bar"}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Invalid", - auth.AuthWithPasswordCredentials.from_json, - data) - - def test_pwd_no_username_from_json(self): - data = json.dumps({"auth": {"passwordCredentials": {}}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Expecting passwordCredentials:username", - auth.AuthWithPasswordCredentials.from_json, - data) - - def test_pwd_no_password_from_json(self): - data = json.dumps({"auth": {"passwordCredentials": - {"username": "foo"}}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Expecting passwordCredentials:password", - auth.AuthWithPasswordCredentials.from_json, - data) - - def test_pwd_invalid_creds_attribute_from_json(self): - data = json.dumps({"auth": {"passwordCredentials": {"bar": "foo"}}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Invalid", - auth.AuthWithPasswordCredentials.from_json, - data) - - def test_token_creds_from_json(self): - data = json.dumps({"auth": {"token": {"id": "1"}}}) - creds = auth.AuthWithUnscopedToken.from_json(data) - self.assertEqual(creds.token_id, "1") - self.assertIsNone(creds.tenant_id) - self.assertIsNone(creds.tenant_name) - - def test_token_creds_with_tenant_name_from_json(self): - data = json.dumps({"auth": - {"tenantName": "blaa", - "token": {"id": "1"}}}) - creds = auth.AuthWithUnscopedToken.from_json(data) - self.assertEqual(creds.token_id, "1") - self.assertIsNone(creds.tenant_id) - self.assertEqual(creds.tenant_name, "blaa") - - def test_token_creds_with_tenant_id_from_json(self): - data = json.dumps({"auth": - {"tenantId": "blaa", - "token": {"id": "1"}}}) - creds = auth.AuthWithUnscopedToken.from_json(data) - self.assertEqual(creds.token_id, "1") - self.assertEqual(creds.tenant_id, "blaa") - self.assertIsNone(creds.tenant_name) - - def test_token_not_both_tenant_from_json(self): - data = json.dumps({"auth": - {"tenantId": "blaa", - "tenantName": "aalb", - "token": {"id": "1"}}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "not both", - auth.AuthWithUnscopedToken.from_json, - data) - - def test_token_invalid_from_json(self): - self.assertRaisesRegexp(fault.BadRequestFault, - "Cannot parse", - auth.AuthWithUnscopedToken.from_json, - "") - - def test_token_no_auth_from_json(self): - data = json.dumps({"foo": "bar"}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Expecting auth", - auth.AuthWithUnscopedToken.from_json, - data) - - def test_token_no_creds_from_json(self): - data = json.dumps({"auth": {}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Expecting token", - auth.AuthWithUnscopedToken.from_json, - data) - - def test_token_invalid_attribute_from_json(self): - data = json.dumps({"auth": {"foo": "bar"}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Invalid", - auth.AuthWithUnscopedToken.from_json, - data) - - def test_token_no_id_from_json(self): - data = json.dumps({"auth": {"token": {}}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Expecting token:id", - auth.AuthWithUnscopedToken.from_json, - data) - - def test_token_invalid_token_attribute_from_json(self): - data = json.dumps({"auth": {"token": {"bar": "foo"}}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Invalid", - auth.AuthWithUnscopedToken.from_json, - data) - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_authn_ec2.py b/keystone/test/unit/test_authn_ec2.py deleted file mode 100644 index e8cdd995..00000000 --- a/keystone/test/unit/test_authn_ec2.py +++ /dev/null @@ -1,303 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2011 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 json -import logging -import unittest2 as unittest - -import base -from keystone.test.unit.decorators import jsonify -from keystone.logic import signer -from keystone.logic.types import auth - -LOGGER = logging.getLogger(__name__) - - -class EC2AuthnMethods(base.ServiceAPITest): - - @jsonify - def test_valid_authn_ec2_success_json(self): - """Tests correct syntax with {"auth":} wrapper and extension """ - url = "/tokens" - access = "xpd285.access" - secret = "345fgi.secret" - kwargs = { - "user_name": self.auth_user['name'], - "tenant_id": self.auth_user['tenant_id'], - "type": "EC2", - "key": access, - "secret": secret, - } - self.fixture_create_credentials(**kwargs) - req = self.get_request('POST', url) - params = { - "SignatureVersion": "2", - "one_param": "5", - "two_params": "happy", - } - credentials = { - "access": access, - "verb": "GET", - "params": params, - "host": "some.host.com:8773", - "path": "services/Cloud", - "signature": None, - } - sign = signer.Signer(secret) - obj_creds = auth.Ec2Credentials(**credentials) - credentials['signature'] = sign.generate(obj_creds) - body = { - "auth": { - "OS-KSEC2:ec2Credentials": credentials, - } - } - req.body = json.dumps(body) - self.get_response() - - expected = { - u'access': { - u'token': { - u'id': self.auth_token_id, - u'expires': self.expires.strftime("%Y-%m-%dT%H:%M:%S.%f")}, - u'user': { - u'id': unicode(self.auth_user['id']), - u'name': self.auth_user['name'], - u'roles': [{u'description': u'regular role', - u'id': u'0', - u'name': u'regular_role'}]}}} - self.assert_dict_equal(expected, json.loads(self.res.body)) - self.status_ok() - - @jsonify - def test_authn_ec2_success_json(self): - """Tests syntax with {"auth":} wrapper """ - self._auth_to_url(url="/ec2tokens") - - @jsonify - def test_authn_ec2_success_json_contract(self): - """Tests syntax with {"auth":} wrapper """ - self._auth_to_url(url="/tokens") - - def _auth_to_url(self, url): - """ - Test that plain ec2 credentials returns a 200 OK - """ - access = "xpd285.access" - secret = "345fgi.secret" - kwargs = { - "user_name": self.auth_user['name'], - "tenant_id": self.auth_user['tenant_id'], - "type": "EC2", - "key": access, - "secret": secret, - } - self.fixture_create_credentials(**kwargs) - req = self.get_request('POST', url) - params = { - "SignatureVersion": "2", - "one_param": "5", - "two_params": "happy", - } - credentials = { - "access": access, - "verb": "GET", - "params": params, - "host": "some.host.com:8773", - "path": "services/Cloud", - "signature": None, - } - sign = signer.Signer(secret) - obj_creds = auth.Ec2Credentials(**credentials) - credentials['signature'] = sign.generate(obj_creds) - body = { - "auth": { - "ec2Credentials": credentials, - } - } - req.body = json.dumps(body) - self.get_response() - - expected = { - u'access': { - u'token': { - u'id': self.auth_token_id, - u'expires': self.expires.strftime("%Y-%m-%dT%H:%M:%S.%f")}, - u'user': { - u'id': unicode(self.auth_user['id']), - u'name': self.auth_user['name'], - u'roles': [{u'description': u'regular role', - u'id': u'0', - u'name': u'regular_role'}]}}} - self.assert_dict_equal(expected, json.loads(self.res.body)) - self.status_ok() - - @jsonify - def test_old_authn_ec2_success_json(self): - """Tests old syntax without {"auth":} wrapper """ - self._old_auth_to_url(url="/ec2tokens") - - @jsonify - def test_old_authn_ec2_success_json_contract(self): - """Tests old syntax without {"auth":} wrapper """ - self._old_auth_to_url(url="/tokens") - - def _old_auth_to_url(self, url): - """ - Test that old ec2 credentials returns a 200 OK - """ - access = "xpd285.access" - secret = "345fgi.secret" - kwargs = { - "user_name": self.auth_user['name'], - "tenant_id": self.auth_user['tenant_id'], - "type": "EC2", - "key": access, - "secret": secret, - } - self.fixture_create_credentials(**kwargs) - req = self.get_request('POST', url) - params = { - "SignatureVersion": "2", - "one_param": "5", - "two_params": "happy", - } - credentials = { - "access": access, - "verb": "GET", - "params": params, - "host": "some.host.com:8773", - "path": "services/Cloud", - "signature": None, - } - sign = signer.Signer(secret) - obj_creds = auth.Ec2Credentials(**credentials) - credentials['signature'] = sign.generate(obj_creds) - body = { - "ec2Credentials": credentials, - } - req.body = json.dumps(body) - self.get_response() - - expected = { - u'access': { - u'token': { - u'id': self.auth_token_id, - u'expires': self.expires.strftime("%Y-%m-%dT%H:%M:%S.%f")}, - u'user': { - u'id': unicode(self.auth_user['id']), - u'name': self.auth_user['name'], - u'roles': [{u'description': u'regular role', - u'id': u'0', - u'name': u'regular_role'}]}}} - self.assert_dict_equal(expected, json.loads(self.res.body)) - self.status_ok() - - @jsonify - def test_authn_ec2_success_json_bad_user(self): - """ - Test that bad credentials returns a 401 - """ - access = "xpd285.access" - secret = "345fgi.secret" - url = "/ec2tokens" - req = self.get_request('POST', url) - params = { - "SignatureVersion": "2", - "one_param": "5", - "two_params": "happy", - } - credentials = { - "access": access, - "verb": "GET", - "params": params, - "host": "some.host.com:8773", - "path": "services/Cloud", - "signature": None, - } - sign = signer.Signer(secret) - obj_creds = auth.Ec2Credentials(**credentials) - credentials['signature'] = sign.generate(obj_creds) - body = { - "ec2Credentials": credentials, - } - req.body = json.dumps(body) - self.get_response() - - expected = { - u'unauthorized': { - u'code': u'401', - u'message': u'No credentials found for %s' % access, - } - } - self.assert_dict_equal(expected, json.loads(self.res.body)) - self.assertEqual(self.res.status_int, 401) - - @jsonify - def test_authn_ec2_success_json_bad_tenant(self): - """ - Test that bad credentials returns a 401 - """ - # Create dummy tenant (or adding creds will fail) - self.fixture_create_tenant(id='bad', name='bad') - access = "xpd285.access" - secret = "345fgi.secret" - kwargs = { - "user_name": self.auth_user['name'], - "tenant_id": 'bad', - "type": "EC2", - "key": access, - "secret": secret, - } - self.fixture_create_credentials(**kwargs) - # Delete the 'bad' tenant, orphaning the creds - self.get_request('DELETE', '/tenants/bad') - - url = "/ec2tokens" - req = self.get_request('POST', url) - params = { - "SignatureVersion": "2", - "one_param": "5", - "two_params": "happy", - } - credentials = { - "access": access, - "verb": "GET", - "params": params, - "host": "some.host.com:8773", - "path": "services/Cloud", - "signature": None, - } - sign = signer.Signer(secret) - obj_creds = auth.Ec2Credentials(**credentials) - credentials['signature'] = sign.generate(obj_creds) - body = { - "ec2Credentials": credentials, - } - req.body = json.dumps(body) - self.get_response() - - expected = { - u'unauthorized': { - u'code': u'401', - u'message': u'Unauthorized on this tenant', - } - } - self.assert_dict_equal(expected, json.loads(self.res.body)) - self.assertEqual(self.res.status_int, 401) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_authn_password.py b/keystone/test/unit/test_authn_password.py deleted file mode 100644 index 947dfc60..00000000 --- a/keystone/test/unit/test_authn_password.py +++ /dev/null @@ -1,65 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2011 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 json -import logging -import unittest2 as unittest - -import base -from keystone.test.unit.decorators import jsonify -from keystone.logic.types import auth - -LOGGER = logging.getLogger(__name__) - - -class PasswordAuthnMethods(base.ServiceAPITest): - - @jsonify - def test_authn_password_success_json(self): - """ - Test that good password credentials returns a 200 OK - """ - url = "/tokens" - req = self.get_request('POST', url) - credentials = { - "username": self.auth_user['name'], - "password": "auth_pass", - } - body = {"auth": { - "passwordCredentials": credentials, - "tenantId": self.auth_user['tenant_id'], - } - } - req.body = json.dumps(body) - self.get_response() - - expected = { - u'access': { - u'token': { - u'id': self.auth_token_id, - u'expires': self.expires.strftime("%Y-%m-%dT%H:%M:%S.%f")}, - u'user': { - u'id': unicode(self.auth_user['id']), - u'name': self.auth_user['name'], - u'roles': [{u'description': u'regular role', u'id': u'0', - u'name': u'regular_role'}]}}} - - self.assert_dict_equal(expected, json.loads(self.res.body)) - self.status_ok() - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_authn_s3.py b/keystone/test/unit/test_authn_s3.py deleted file mode 100644 index 2cb3a358..00000000 --- a/keystone/test/unit/test_authn_s3.py +++ /dev/null @@ -1,245 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2011 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 json -import logging -import unittest2 as unittest - -import base -from keystone.test.unit.decorators import jsonify -from keystone.logic import signer -from keystone.logic.types import auth - -LOGGER = logging.getLogger('test.unit.test_s3_authn') - - -class S3AuthnMethods(base.ServiceAPITest): - - @jsonify - def test_valid_authn_s3_success_json(self): - """Tests correct syntax with {"auth":} wrapper and extension """ - url = "/tokens" - access = "xpd285.access" - secret = "345fgi.secret" - kwargs = { - "user_name": self.auth_user['name'], - "tenant_id": self.auth_user['tenant_id'], - "type": "EC2", - "key": access, - "secret": secret, - } - self.fixture_create_credentials(**kwargs) - req = self.get_request('POST', url) - params = { - "x-amz-acl": "public-read-write", - "x-amz-server-side-encryption": "AES256", - } - credentials = { - "access": access, - "verb": "PUT", - "path": "/test.txt", - "expire": 0, - "content_type": "text/plain", - "content_md5": "1234567890abcdef", - "xheaders": params, - "signature": None, - } - sign = signer.Signer(secret) - obj_creds = auth.S3Credentials(**credentials) - credentials['signature'] = sign.generate(obj_creds, s3=True) - body = { - "auth": { - "OS-KSS3:s3Credentials": credentials, - } - } - req.body = json.dumps(body) - self.get_response() - - expected = { - u'access': { - u'token': { - u'id': self.auth_token_id, - u'expires': self.expires.strftime("%Y-%m-%dT%H:%M:%S.%f")}, - u'user': { - u'id': unicode(self.auth_user['id']), - u'name': self.auth_user['name'], - u'roles': [{u'description': u'regular role', - u'id': u'0', - u'name': u'regular_role'}]}}} - self.assert_dict_equal(expected, json.loads(self.res.body)) - self.status_ok() - - @jsonify - def test_authn_s3_success_json(self): - """Tests correct syntax with {"auth":} wrapper """ - self._auth_to_url(url="/tokens") - - def _auth_to_url(self, url): - """ - Test that good s3 credentials returns a 200 OK - """ - access = "xpd285.access" - secret = "345fgi.secret" - kwargs = { - "user_name": self.auth_user['name'], - "tenant_id": self.auth_user['tenant_id'], - "type": "EC2", - "key": access, - "secret": secret, - } - self.fixture_create_credentials(**kwargs) - req = self.get_request('POST', url) - params = { - "x-amz-acl": "public-read-write", - "x-amz-server-side-encryption": "AES256", - } - credentials = { - "access": access, - "verb": "PUT", - "path": "/test.txt", - "expire": 0, - "content_type": "text/plain", - "content_md5": "1234567890abcdef", - "xheaders": params, - "signature": None, - } - sign = signer.Signer(secret) - obj_creds = auth.S3Credentials(**credentials) - credentials['signature'] = sign.generate(obj_creds, s3=True) - body = { - "auth": { - "OS-KSS3:s3Credentials": credentials, - } - } - req.body = json.dumps(body) - self.get_response() - - expected = { - u'access': { - u'token': { - u'id': self.auth_token_id, - u'expires': self.expires.strftime("%Y-%m-%dT%H:%M:%S.%f")}, - u'user': { - u'id': unicode(self.auth_user['id']), - u'name': self.auth_user['name'], - u'roles': [{u'description': u'regular role', - u'id': u'0', - u'name': u'regular_role'}]}}} - self.assert_dict_equal(expected, json.loads(self.res.body)) - self.status_ok() - - @jsonify - def test_authn_s3_success_json_bad_user(self): - """ - Test that bad credentials returns a 401 - """ - access = "xpd285.access" - secret = "345fgi.secret" - url = "/tokens" - req = self.get_request('POST', url) - params = { - "x-amz-acl": "public-read-write", - "x-amz-server-side-encryption": "AES256", - } - credentials = { - "access": access, - "verb": "PUT", - "path": "/test.txt", - "expire": 0, - "content_type": "text/plain", - "content_md5": "1234567890abcdef", - "xheaders": params, - "signature": None, - } - sign = signer.Signer(secret) - obj_creds = auth.S3Credentials(**credentials) - credentials['signature'] = sign.generate(obj_creds, s3=True) - body = { - "auth": { - "OS-KSS3:s3Credentials": credentials, - } - } - req.body = json.dumps(body) - self.get_response() - - expected = { - u'unauthorized': { - u'code': u'401', - u'message': u'No credentials found for %s' % access, - } - } - self.assert_dict_equal(expected, json.loads(self.res.body)) - self.assertEqual(self.res.status_int, 401) - - @jsonify - def test_authn_s3_success_json_bad_tenant(self): - """ - Test that bad credentials returns a 401 - """ - # Create dummy tenant (or adding creds will fail) - self.fixture_create_tenant(id='bad', name='bad') - access = "xpd285.access" - secret = "345fgi.secret" - kwargs = { - "user_name": self.auth_user['name'], - "tenant_id": 'bad', - "type": "EC2", - "key": access, - "secret": secret, - } - self.fixture_create_credentials(**kwargs) - # Delete the 'bad' tenant, orphaning the creds - self.get_request('DELETE', '/tenants/bad') - - url = "/tokens" - req = self.get_request('POST', url) - params = { - "x-amz-acl": "public-read-write", - "x-amz-server-side-encryption": "AES256", - } - credentials = { - "access": access, - "verb": "PUT", - "path": "/test.txt", - "expire": 0, - "content_type": "text/plain", - "content_md5": "1234567890abcdef", - "xheaders": params, - "signature": None, - } - sign = signer.Signer(secret) - obj_creds = auth.S3Credentials(**credentials) - credentials['signature'] = sign.generate(obj_creds, s3=True) - body = { - "auth": { - "OS-KSS3:s3Credentials": credentials, - } - } - req.body = json.dumps(body) - self.get_response() - - expected = { - u'unauthorized': { - u'code': u'401', - u'message': u'Unauthorized on this tenant', - } - } - self.assert_dict_equal(expected, json.loads(self.res.body)) - self.assertEqual(self.res.status_int, 401) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_backends.py b/keystone/test/unit/test_backends.py deleted file mode 100644 index c1b9de14..00000000 --- a/keystone/test/unit/test_backends.py +++ /dev/null @@ -1,199 +0,0 @@ -# Copyright (c) 2011 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 os -import unittest2 as unittest -import uuid - -from keystone import backends -from keystone import config -from keystone.cfg import OptGroup, NoSuchOptError -import keystone.backends.api as api -import keystone.backends.models as legacy_backend_models -import keystone.backends.sqlalchemy as db -from keystone import models -from keystone.test import KeystoneTest -from keystone import utils - -CONF = config.CONF - - -class BackendTestCase(unittest.TestCase): - """ - Base class to run tests for Keystone backends (and backend configs) - """ - def __init__(self, *args, **kwargs): - super(BackendTestCase, self).__init__(*args, **kwargs) - self.base_template = "sql.conf.template" - self.ldap_template = "ldap.conf.template" - self.current_template = self.base_template - - def setUp(self): - self.update_CONF(self.current_template) - db.unregister_models() - reload(db) - backends.configure_backends() - super(BackendTestCase, self).setUp() - - def tearDown(self): - self.current_template = self.base_template - - def update_CONF(self, template): - """ - Resets the CONF file, and reads in the passed configuration text. - """ - kt = KeystoneTest() - kt.config_name = template - kt.construct_temp_conf_file() - fname = kt.conf_fp.name - # Provide a hook for customizing the config if needed. - self.modify_conf(fname) - # Create the configuration - CONF.reset() - CONF(config_files=[fname]) - - def modify_conf(self, fname): - pass - - def tearDown(self): - db.unregister_models() - reload(db) - - def test_registration(self): - self.assertIsNotNone(backends.api.CREDENTIALS) - self.assertIsNotNone(backends.api.ENDPOINT_TEMPLATE) - self.assertIsNotNone(backends.api.ROLE) - self.assertIsNotNone(backends.api.SERVICE) - self.assertIsNotNone(backends.api.TENANT) - self.assertIsNotNone(backends.api.TOKEN) - self.assertIsNotNone(backends.api.USER) - - def test_basic_tenant_create(self): - tenant = models.Tenant(name="Tee One", description="This is T1", - enabled=True) - - original_tenant = tenant.copy() - new_tenant = api.TENANT.create(tenant) - self.assertIsInstance(new_tenant, models.Tenant) - for k, v in original_tenant.items(): - if k not in ['id'] and k in new_tenant: - self.assertEquals(new_tenant[k], v) - - def test_tenant_create_with_id(self): - tenant = models.Tenant(id="T2%s" % uuid.uuid4().hex, name="Tee Two", - description="This is T2", enabled=True) - - original_tenant = tenant.to_dict() - new_tenant = api.TENANT.create(tenant) - self.assertIsInstance(new_tenant, models.Tenant) - for k, v in original_tenant.items(): - if k in new_tenant: - self.assertEquals(new_tenant[k], v, - "'%s' did not match" % k) - self.assertEqual(original_tenant['tenant'], tenant, - "Backend modified provided tenant") - - def test_tenant_update(self): - id = "T3%s" % uuid.uuid4().hex - tenant = models.Tenant(id=id, name="Tee Three", - description="This is T3", enabled=True) - new_tenant = api.TENANT.create(tenant) - new_tenant.enabled = False - new_tenant.description = "This is UPDATED T3" - api.TENANT.update(id, new_tenant) - updated_tenant = api.TENANT.get(id) - self.assertEqual(new_tenant, updated_tenant) - - def test_endpointtemplate_create(self): - service = models.Service(name="glance", type="image-service") - service = api.SERVICE.create(service) - - global_ept = models.EndpointTemplate( - region="north", - name="global", - type=service.type, - is_global=True, - public_URL="http://global.public") - global_ept = api.ENDPOINT_TEMPLATE.create(global_ept) - self.assertIsNotNone(global_ept.id) - - ept = models.EndpointTemplate( - region="north", - name="floating", - type=service.type, - is_global=False, - public_URL="http://floating.public/%tenant_id%/") - ept = api.ENDPOINT_TEMPLATE.create(ept) - self.assertIsNotNone(ept.id) - - def test_endpoint_list(self): - self.test_endpointtemplate_create() - self.test_basic_tenant_create() - tenant = api.TENANT.get_by_name("Tee One") - - templates = api.ENDPOINT_TEMPLATE.get_all() - for template in templates: - if not template.is_global: - endpoint = legacy_backend_models.Endpoints() - endpoint.tenant_id = tenant.id - endpoint.endpoint_template_id = template.id - api.ENDPOINT_TEMPLATE.endpoint_add(endpoint) - - global_endpoints = api.TENANT.get_all_endpoints(None) - self.assertGreater(len(global_endpoints), 0) - - tenant_endpoints = api.TENANT.get_all_endpoints(tenant.id) - self.assertGreater(len(tenant_endpoints), 0) - - -class LDAPBackendTestCase(BackendTestCase): - def setUp(self): - self.current_template = self.ldap_template - super(LDAPBackendTestCase, self).setUp() - - -class SQLiteBackendTestCase(BackendTestCase): - """ Tests SQLite backend using actual file (not in memory) - - Since we have a code path that is specific to in-memory databases, we need - to test for when we have a real file behind the ORM - """ - def setUp(self): - self.current_template = self.base_template - self.database_name = os.path.abspath("%s.test.db" % \ - uuid.uuid4().hex) - super(SQLiteBackendTestCase, self).setUp() - - def modify_conf(self, fname): - # Need to override the connection - conn = "sqlite:///%s" % self.database_name - out = [] - with file(fname, "r") as conf_file: - for ln in conf_file: - if ln.rstrip() == "sql_connection = sqlite://": - out.append("sql_connection = %s" % conn) - else: - out.append(ln.rstrip()) - with file(fname, "w") as conf_file: - conf_file.write("\n".join(out)) - - def tearDown(self): - super(SQLiteBackendTestCase, self).tearDown() - if os.path.exists(self.database_name): - os.unlink(self.database_name) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_buffout.py b/keystone/test/unit/test_buffout.py deleted file mode 100644 index 715960e0..00000000 --- a/keystone/test/unit/test_buffout.py +++ /dev/null @@ -1,91 +0,0 @@ -import unittest2 as unittest -import sys - -from keystone.tools import buffout - - -class TestStdoutIdentity(unittest.TestCase): - """Tests buffout's manipulation of the stdout pointer""" - def test_stdout(self): - stdout = sys.stdout - ob = buffout.OutputBuffer() - self.assertTrue(sys.stdout is stdout, - "sys.stdout was replaced") - ob.start() - self.assertTrue(sys.stdout is not stdout, - "sys.stdout not replaced") - ob.stop() - self.assertTrue(sys.stdout is stdout, - "sys.stdout not restored") - - -class TestOutputBufferContents(unittest.TestCase): - """Tests the contents of the buffer""" - def test_read_contents(self): - with buffout.OutputBuffer() as ob: - print 'foobar' - print 'wompwomp' - output = ob.read() - self.assertEquals(len(output), 16, output) - self.assertIn('foobar', output) - self.assertIn('ompwom', output) - - def test_read_lines(self): - with buffout.OutputBuffer() as ob: - print 'foobar' - print 'wompwomp' - lines = ob.read_lines() - self.assertTrue(isinstance(lines, list)) - self.assertEqual(len(lines), 2) - self.assertIn('foobar', lines) - self.assertIn('wompwomp', lines) - - def test_additional_output(self): - with buffout.OutputBuffer() as ob: - print 'foobar' - lines = ob.read_lines() - self.assertEqual(len(lines), 1) - print 'wompwomp' - lines = ob.read_lines() - self.assertEqual(len(lines), 2) - - def test_clear(self): - with buffout.OutputBuffer() as ob: - print 'foobar' - ob.clear() - print 'wompwomp' - output = ob.read() - self.assertNotIn('foobar', output) - self.assertIn('ompwom', output) - - def test_buffer_preservation(self): - ob = buffout.OutputBuffer() - ob.start() - - print 'foobar' - print 'wompwomp' - - ob.stop() - - output = ob.read() - self.assertIn('foobar', output) - self.assertIn('ompwom', output) - - def test_buffer_contents(self): - ob = buffout.OutputBuffer() - ob.start() - - print 'foobar' - print 'wompwomp' - - ob.stop() - - self.assertEqual('foobar\nwompwomp\n', unicode(ob)) - self.assertEqual('foobar\nwompwomp\n', str(ob)) - - def test_exception_raising(self): - def raise_value_error(): - with buffout.OutputBuffer(): - raise ValueError() - - self.assertRaises(ValueError, raise_value_error) diff --git a/keystone/test/unit/test_cfg.py b/keystone/test/unit/test_cfg.py deleted file mode 100644 index 9dc07727..00000000 --- a/keystone/test/unit/test_cfg.py +++ /dev/null @@ -1,826 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 Red Hat, Inc. -# -# 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 os -import sys -import StringIO -import tempfile -import unittest - -import stubout - -from keystone.cfg import * - - -class BaseTestCase(unittest.TestCase): - - def setUp(self): - self.conf = ConfigOpts(prog='test', - version='1.0', - usage='%prog FOO BAR', - default_config_files=[]) - self.tempfiles = [] - self.stubs = stubout.StubOutForTesting() - - def tearDown(self): - self.remove_tempfiles() - self.stubs.UnsetAll() - - def create_tempfiles(self, files): - for (basename, contents) in files: - (fd, path) = tempfile.mkstemp(prefix=basename) - self.tempfiles.append(path) - try: - os.write(fd, contents) - finally: - os.close(fd) - return self.tempfiles[-len(files):] - - def remove_tempfiles(self): - for p in self.tempfiles: - os.remove(p) - - -class LeftoversTestCase(BaseTestCase): - - def test_leftovers(self): - self.conf.register_cli_opt(StrOpt('foo')) - self.conf.register_cli_opt(StrOpt('bar')) - - leftovers = self.conf(['those', '--foo', 'this', - 'thems', '--bar', 'that', 'these']) - - self.assertEquals(leftovers, ['those', 'thems', 'these']) - - -class FindConfigFilesTestCase(BaseTestCase): - - def test_find_config_files(self): - config_files = \ - [os.path.expanduser('~/.blaa/blaa.conf'), '/etc/foo.conf'] - - self.stubs.Set(os.path, 'exists', lambda p: p in config_files) - - self.assertEquals(find_config_files(project='blaa', prog='foo'), - config_files) - - -class CliOptsTestCase(BaseTestCase): - - def _do_cli_test(self, opt_class, default, cli_args, value): - self.conf.register_cli_opt(opt_class('foo', default=default)) - - self.conf(cli_args) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, value) - - def test_str_default(self): - self._do_cli_test(StrOpt, None, [], None) - - def test_str_arg(self): - self._do_cli_test(StrOpt, None, ['--foo', 'bar'], 'bar') - - def test_bool_default(self): - self._do_cli_test(BoolOpt, False, [], False) - - def test_bool_arg(self): - self._do_cli_test(BoolOpt, None, ['--foo'], True) - - def test_bool_arg_inverse(self): - self._do_cli_test(BoolOpt, None, ['--foo', '--nofoo'], False) - - def test_int_default(self): - self._do_cli_test(IntOpt, 10, [], 10) - - def test_int_arg(self): - self._do_cli_test(IntOpt, None, ['--foo=20'], 20) - - def test_float_default(self): - self._do_cli_test(FloatOpt, 1.0, [], 1.0) - - def test_float_arg(self): - self._do_cli_test(FloatOpt, None, ['--foo', '2.0'], 2.0) - - def test_list_default(self): - self._do_cli_test(ListOpt, ['bar'], [], ['bar']) - - def test_list_arg(self): - self._do_cli_test(ListOpt, None, - ['--foo', 'blaa,bar'], ['blaa', 'bar']) - - def test_multistr_default(self): - self._do_cli_test(MultiStrOpt, ['bar'], [], ['bar']) - - def test_multistr_arg(self): - self._do_cli_test(MultiStrOpt, None, - ['--foo', 'blaa', '--foo', 'bar'], ['blaa', 'bar']) - - def test_help(self): - self.stubs.Set(sys, 'stdout', StringIO.StringIO()) - self.assertRaises(SystemExit, self.conf, ['--help']) - self.assertTrue('FOO BAR' in sys.stdout.getvalue()) - self.assertTrue('--version' in sys.stdout.getvalue()) - self.assertTrue('--help' in sys.stdout.getvalue()) - self.assertTrue('--config-file=PATH' in sys.stdout.getvalue()) - - def test_version(self): - self.stubs.Set(sys, 'stdout', StringIO.StringIO()) - self.assertRaises(SystemExit, self.conf, ['--version']) - self.assertTrue('1.0' in sys.stdout.getvalue()) - - def test_config_file(self): - paths = self.create_tempfiles([('1.conf', '[DEFAULT]'), - ('2.conf', '[DEFAULT]')]) - - self.conf(['--config-file', paths[0], '--config-file', paths[1]]) - - self.assertEquals(self.conf.config_file, paths) - - -class ConfigFileOptsTestCase(BaseTestCase): - - def test_str_default(self): - self.conf.register_opt(StrOpt('foo', default='bar')) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 'bar') - - def test_str_value(self): - self.conf.register_opt(StrOpt('foo')) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'foo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 'bar') - - def test_str_value_file_override(self): - self.conf.register_cli_opt(StrOpt('foo')) - - paths = self.create_tempfiles([('1.conf', - '[DEFAULT]\n' - 'foo = baar\n'), - ('2.conf', - '[DEFAULT]\n' - 'foo = baaar\n')]) - - self.conf(['--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 'baaar') - - def test_str_value_arg_override(self): - self.conf.register_cli_opt(StrOpt('foo')) - - paths = self.create_tempfiles([('1.conf', - '[DEFAULT]\n' - 'foo = baar\n'), - ('2.conf', - '[DEFAULT]\n' - 'foo = baaar\n')]) - - self.conf(['--foo', 'bar', - '--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 'bar') - - def test_int_default(self): - self.conf.register_opt(IntOpt('foo', default=666)) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 666) - - def test_int_value(self): - self.conf.register_opt(IntOpt('foo')) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'foo = 666\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 666) - - def test_int_value_file_override(self): - self.conf.register_cli_opt(IntOpt('foo')) - - paths = self.create_tempfiles([('1.conf', - '[DEFAULT]\n' - 'foo = 66\n'), - ('2.conf', - '[DEFAULT]\n' - 'foo = 666\n')]) - - self.conf(['--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 666) - - def test_int_value_arg_override(self): - self.conf.register_cli_opt(IntOpt('foo')) - - paths = self.create_tempfiles([('1.conf', - '[DEFAULT]\n' - 'foo = 66\n'), - ('2.conf', - '[DEFAULT]\n' - 'foo = 666\n')]) - - self.conf(['--foo', '6', - '--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 6) - - def test_float_default(self): - self.conf.register_opt(FloatOpt('foo', default=6.66)) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 6.66) - - def test_float_value(self): - self.conf.register_opt(FloatOpt('foo')) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'foo = 6.66\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 6.66) - - def test_float_value_arg_override(self): - self.conf.register_cli_opt(FloatOpt('foo')) - - paths = self.create_tempfiles([('1.conf', - '[DEFAULT]\n' - 'foo = 6.6\n'), - ('2.conf', - '[DEFAULT]\n' - 'foo = 6.66\n')]) - - self.conf(['--foo', '6', - '--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 6.0) - - def test_float_value_file_override(self): - self.conf.register_cli_opt(FloatOpt('foo')) - - paths = self.create_tempfiles([('1.conf', - '[DEFAULT]\n' - 'foo = 6.6\n'), - ('2.conf', - '[DEFAULT]\n' - 'foo = 6.66\n')]) - - self.conf(['--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, 6.66) - - def test_list_default(self): - self.conf.register_opt(ListOpt('foo', default=['bar'])) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, ['bar']) - - def test_list_value(self): - self.conf.register_opt(ListOpt('foo')) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'foo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, ['bar']) - - def test_list_value_override(self): - self.conf.register_cli_opt(ListOpt('foo')) - - paths = self.create_tempfiles([('1.conf', - '[DEFAULT]\n' - 'foo = bar,bar\n'), - ('2.conf', - '[DEFAULT]\n' - 'foo = b,a,r\n')]) - - self.conf(['--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, ['b', 'a', 'r']) - - def test_multistr_default(self): - self.conf.register_opt(MultiStrOpt('foo', default=['bar'])) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, ['bar']) - - def test_multistr_value(self): - self.conf.register_opt(MultiStrOpt('foo')) - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'foo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, ['bar']) - - def test_multistr_values_append(self): - self.conf.register_cli_opt(ListOpt('foo')) - - paths = self.create_tempfiles([('1.conf', - '[DEFAULT]\n' - 'foo = bar\n'), - ('2.conf', - '[DEFAULT]\n' - 'foo = bar\n')]) - - self.conf(['--foo', 'bar', - '--config-file', paths[0], - '--config-file', paths[1]]) - - self.assertTrue(hasattr(self.conf, 'foo')) - - # FIXME(markmc): values spread across the CLI and multiple - # config files should be appended - # self.assertEquals(self.conf.foo, ['bar', 'bar', 'bar']) - - -class OptGroupsTestCase(BaseTestCase): - - def test_arg_group(self): - blaa_group = OptGroup('blaa') - self.conf.register_group(blaa_group) - self.conf.register_cli_opt(StrOpt('foo'), group=blaa_group) - - self.conf(['--blaa-foo', 'bar']) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEquals(self.conf.blaa.foo, 'bar') - - def test_arg_group_by_name(self): - self.conf.register_group(OptGroup('blaa')) - self.conf.register_cli_opt(StrOpt('foo'), group='blaa') - - self.conf(['--blaa-foo', 'bar']) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEquals(self.conf.blaa.foo, 'bar') - - def test_arg_group_with_default(self): - self.conf.register_group(OptGroup('blaa')) - self.conf.register_cli_opt(StrOpt('foo', default='bar'), group='blaa') - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEquals(self.conf.blaa.foo, 'bar') - - def test_arg_group_in_config_file(self): - self.conf.register_group(OptGroup('blaa')) - self.conf.register_opt(StrOpt('foo'), group='blaa') - - paths = self.create_tempfiles([('test.conf', - '[blaa]\n' - 'foo = bar\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEquals(self.conf.blaa.foo, 'bar') - - -class TemplateSubstitutionTestCase(BaseTestCase): - - def _prep_test_str_sub(self, foo_default=None, bar_default=None): - self.conf.register_cli_opt(StrOpt('foo', default=foo_default)) - self.conf.register_cli_opt(StrOpt('bar', default=bar_default)) - - def _assert_str_sub(self): - self.assertTrue(hasattr(self.conf, 'bar')) - self.assertEquals(self.conf.bar, 'blaa') - - def test_str_sub_default_from_default(self): - self._prep_test_str_sub(foo_default='blaa', bar_default='$foo') - - self.conf([]) - - self._assert_str_sub() - - def test_str_sub_default_from_arg(self): - self._prep_test_str_sub(bar_default='$foo') - - self.conf(['--foo', 'blaa']) - - self._assert_str_sub() - - def test_str_sub_default_from_config_file(self): - self._prep_test_str_sub(bar_default='$foo') - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'foo = blaa\n')]) - - self.conf(['--config-file', paths[0]]) - - self._assert_str_sub() - - def test_str_sub_arg_from_default(self): - self._prep_test_str_sub(foo_default='blaa') - - self.conf(['--bar', '$foo']) - - self._assert_str_sub() - - def test_str_sub_arg_from_arg(self): - self._prep_test_str_sub() - - self.conf(['--foo', 'blaa', '--bar', '$foo']) - - self._assert_str_sub() - - def test_str_sub_arg_from_config_file(self): - self._prep_test_str_sub() - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'foo = blaa\n')]) - - self.conf(['--config-file', paths[0], '--bar=$foo']) - - self._assert_str_sub() - - def test_str_sub_config_file_from_default(self): - self._prep_test_str_sub(foo_default='blaa') - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'bar = $foo\n')]) - - self.conf(['--config-file', paths[0]]) - - self._assert_str_sub() - - def test_str_sub_config_file_from_arg(self): - self._prep_test_str_sub() - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'bar = $foo\n')]) - - self.conf(['--config-file', paths[0], '--foo=blaa']) - - self._assert_str_sub() - - def test_str_sub_config_file_from_config_file(self): - self._prep_test_str_sub() - - paths = self.create_tempfiles([('test.conf', - '[DEFAULT]\n' - 'bar = $foo\n' - 'foo = blaa\n')]) - - self.conf(['--config-file', paths[0]]) - - self._assert_str_sub() - - def test_str_sub_group_from_default(self): - self.conf.register_cli_opt(StrOpt('foo', default='blaa')) - self.conf.register_group(OptGroup('ba')) - self.conf.register_cli_opt(StrOpt('r', default='$foo'), group='ba') - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'ba')) - self.assertTrue(hasattr(self.conf.ba, 'r')) - self.assertEquals(self.conf.ba.r, 'blaa') - - -class ReparseTestCase(BaseTestCase): - - def test_reparse(self): - self.conf.register_group(OptGroup('blaa')) - self.conf.register_cli_opt(StrOpt('foo', default='r'), group='blaa') - - paths = self.create_tempfiles([('test.conf', - '[blaa]\n' - 'foo = b\n')]) - - self.conf(['--config-file', paths[0]]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEquals(self.conf.blaa.foo, 'b') - - self.conf(['--blaa-foo', 'a']) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEquals(self.conf.blaa.foo, 'a') - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertTrue(hasattr(self.conf.blaa, 'foo')) - self.assertEquals(self.conf.blaa.foo, 'r') - - -class OverridesTestCase(BaseTestCase): - - def test_no_default_override(self): - self.conf.register_opt(StrOpt('foo')) - self.conf([]) - self.assertEquals(self.conf.foo, None) - self.conf.set_default('foo', 'bar') - self.assertEquals(self.conf.foo, 'bar') - - def test_default_override(self): - self.conf.register_opt(StrOpt('foo', default='foo')) - self.conf([]) - self.assertEquals(self.conf.foo, 'foo') - self.conf.set_default('foo', 'bar') - self.assertEquals(self.conf.foo, 'bar') - self.conf.set_default('foo', None) - self.assertEquals(self.conf.foo, 'foo') - - def test_override(self): - self.conf.register_opt(StrOpt('foo')) - self.conf.set_override('foo', 'bar') - self.conf([]) - self.assertEquals(self.conf.foo, 'bar') - - def test_group_no_default_override(self): - self.conf.register_group(OptGroup('blaa')) - self.conf.register_opt(StrOpt('foo'), group='blaa') - self.conf([]) - self.assertEquals(self.conf.blaa.foo, None) - self.conf.set_default('foo', 'bar', group='blaa') - self.assertEquals(self.conf.blaa.foo, 'bar') - - def test_default_override_II(self): - self.conf.register_group(OptGroup('blaa')) - self.conf.register_opt(StrOpt('foo', default='foo'), group='blaa') - self.conf([]) - self.assertEquals(self.conf.blaa.foo, 'foo') - self.conf.set_default('foo', 'bar', group='blaa') - self.assertEquals(self.conf.blaa.foo, 'bar') - self.conf.set_default('foo', None, group='blaa') - self.assertEquals(self.conf.blaa.foo, 'foo') - - def test_override_II(self): - self.conf.register_group(OptGroup('blaa')) - self.conf.register_opt(StrOpt('foo'), group='blaa') - self.conf.set_override('foo', 'bar', group='blaa') - self.conf([]) - self.assertEquals(self.conf.blaa.foo, 'bar') - - -class SadPathTestCase(BaseTestCase): - - def test_unknown_attr(self): - self.conf([]) - self.assertFalse(hasattr(self.conf, 'foo')) - self.assertRaises(NoSuchOptError, getattr, self.conf, 'foo') - - def test_unknown_group_attr(self): - self.conf.register_group(OptGroup('blaa')) - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'blaa')) - self.assertFalse(hasattr(self.conf.blaa, 'foo')) - self.assertRaises(NoSuchOptError, getattr, self.conf.blaa, 'foo') - - def test_ok_duplicate(self): - opt = StrOpt('foo') - self.conf.register_cli_opt(opt) - self.conf.register_cli_opt(opt) - - self.conf([]) - - self.assertTrue(hasattr(self.conf, 'foo')) - self.assertEquals(self.conf.foo, None) - - def test_error_duplicate(self): - self.conf.register_cli_opt(StrOpt('foo')) - self.assertRaises(DuplicateOptError, - self.conf.register_cli_opt, StrOpt('foo')) - - def test_error_duplicate_with_different_dest(self): - self.conf.register_cli_opt(StrOpt('foo', dest='f')) - self.assertRaises(DuplicateOptError, - self.conf.register_cli_opt, StrOpt('foo')) - - def test_error_duplicate_short(self): - self.conf.register_cli_opt(StrOpt('foo', short='f')) - self.assertRaises(DuplicateOptError, - self.conf.register_cli_opt, StrOpt('bar', short='f')) - - def test_no_such_group(self): - self.assertRaises(NoSuchGroupError, self.conf.register_cli_opt, - StrOpt('foo'), group='blaa') - - def test_already_parsed(self): - self.conf([]) - - self.assertRaises(ArgsAlreadyParsedError, - self.conf.register_cli_opt, StrOpt('foo')) - - def test_bad_cli_arg(self): - self.stubs.Set(sys, 'stderr', StringIO.StringIO()) - - self.assertRaises(SystemExit, self.conf, ['--foo']) - - self.assertTrue('error' in sys.stderr.getvalue()) - self.assertTrue('--foo' in sys.stderr.getvalue()) - - def _do_test_bad_cli_value(self, opt_class): - self.conf.register_cli_opt(opt_class('foo')) - - self.stubs.Set(sys, 'stderr', StringIO.StringIO()) - - self.assertRaises(SystemExit, self.conf, ['--foo', 'bar']) - - self.assertTrue('foo' in sys.stderr.getvalue()) - self.assertTrue('bar' in sys.stderr.getvalue()) - - def test_bad_int_arg(self): - self._do_test_bad_cli_value(IntOpt) - - def test_bad_float_arg(self): - self._do_test_bad_cli_value(FloatOpt) - - def test_conf_file_not_found(self): - paths = self.create_tempfiles([('test.conf', 'foo')]) - - self.assertRaises(ConfigFileParseError, - self.conf, ['--config-file', paths[0]]) - - def _do_test_conf_file_bad_value(self, opt_class): - self.conf.register_opt(opt_class('foo')) - - def test_conf_file_bad_bool(self): - self._do_test_conf_file_bad_value(BoolOpt) - - def test_conf_file_bad_int(self): - self._do_test_conf_file_bad_value(IntOpt) - - def test_conf_file_bad_float(self): - self._do_test_conf_file_bad_value(FloatOpt) - - def test_str_sub_from_group(self): - self.conf.register_group(OptGroup('f')) - self.conf.register_cli_opt(StrOpt('oo', default='blaa'), group='f') - self.conf.register_cli_opt(StrOpt('bar', default='$f.oo')) - - self.conf([]) - - self.assertFalse(hasattr(self.conf, 'bar')) - self.assertRaises(TemplateSubstitutionError, getattr, self.conf, 'bar') - - def test_set_default_unknown_attr(self): - self.conf([]) - self.assertRaises(NoSuchOptError, self.conf.set_default, 'foo', 'bar') - - def test_set_default_unknown_group(self): - self.conf([]) - self.assertRaises(NoSuchGroupError, - self.conf.set_default, 'foo', 'bar', group='blaa') - - def test_set_override_unknown_attr(self): - self.conf([]) - self.assertRaises(NoSuchOptError, self.conf.set_override, 'foo', 'bar') - - def test_set_override_unknown_group(self): - self.conf([]) - self.assertRaises(NoSuchGroupError, - self.conf.set_override, 'foo', 'bar', group='blaa') - - -class OptDumpingTestCase(BaseTestCase): - - class FakeLogger: - - def __init__(self, test_case, expected_lvl): - self.test_case = test_case - self.expected_lvl = expected_lvl - self.logged = [] - - def log(self, lvl, fmt, *args): - self.test_case.assertEquals(lvl, self.expected_lvl) - self.logged.append(fmt % args) - - def test_log_opt_values(self): - self.conf.register_cli_opt(StrOpt('foo')) - self.conf.register_group(OptGroup('blaa')) - self.conf.register_cli_opt(StrOpt('bar'), 'blaa') - - self.conf(['--foo', 'this', '--blaa-bar', 'that']) - - logger = self.FakeLogger(self, 666) - - self.conf.log_opt_values(logger, 666) - - self.assertEquals(logger.logged, [ - "*" * 80, - "Configuration options gathered from:", - "command line args: ['--foo', 'this', '--blaa-bar', 'that']", - "config files: []", - "=" * 80, - "config_file = []", - "foo = this", - "blaa.bar = that", - "*" * 80, - ]) - - -class CommonOptsTestCase(BaseTestCase): - - def setUp(self): - super(CommonOptsTestCase, self).setUp() - self.conf = CommonConfigOpts() - - def test_debug_verbose(self): - self.conf(['--debug', '--verbose']) - - self.assertEquals(self.conf.debug, True) - self.assertEquals(self.conf.verbose, True) - - def test_logging_opts(self): - self.conf([]) - - self.assertTrue(self.conf.log_config is None) - self.assertTrue(self.conf.log_file is None) - self.assertTrue(self.conf.log_dir is None) - - self.assertEquals(self.conf.log_format, - CommonConfigOpts.DEFAULT_LOG_FORMAT) - self.assertEquals(self.conf.log_date_format, - CommonConfigOpts.DEFAULT_LOG_DATE_FORMAT) - - self.assertEquals(self.conf.use_syslog, False) - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_commands.py b/keystone/test/unit/test_commands.py deleted file mode 100644 index cb8e86df..00000000 --- a/keystone/test/unit/test_commands.py +++ /dev/null @@ -1,1241 +0,0 @@ -import argparse -import datetime -import logging -import unittest2 as unittest -import uuid - -from keystone import backends -import keystone.backends.sqlalchemy as db -from keystone.manage2 import base -from keystone.manage2 import common -from keystone.manage2.commands import create_credential -from keystone.manage2.commands import create_endpoint_template -from keystone.manage2.commands import create_role -from keystone.manage2.commands import create_service -from keystone.manage2.commands import create_tenant -from keystone.manage2.commands import create_token -from keystone.manage2.commands import create_user -from keystone.manage2.commands import delete_credential -from keystone.manage2.commands import delete_endpoint_template -from keystone.manage2.commands import delete_role -from keystone.manage2.commands import delete_service -from keystone.manage2.commands import delete_tenant -from keystone.manage2.commands import delete_token -from keystone.manage2.commands import delete_user -from keystone.manage2.commands import grant_role -from keystone.manage2.commands import list_credentials -from keystone.manage2.commands import list_endpoint_templates -from keystone.manage2.commands import list_endpoints -from keystone.manage2.commands import list_role_grants -from keystone.manage2.commands import list_roles -from keystone.manage2.commands import list_services -from keystone.manage2.commands import list_tenants -from keystone.manage2.commands import list_tokens -from keystone.manage2.commands import list_users -from keystone.manage2.commands import map_endpoint -from keystone.manage2.commands import revoke_role -from keystone.manage2.commands import unmap_endpoint -from keystone.manage2.commands import update_credential -from keystone.manage2.commands import update_endpoint_template -from keystone.manage2.commands import update_role -from keystone.manage2.commands import update_service -from keystone.manage2.commands import update_tenant -from keystone.manage2.commands import update_token -from keystone.manage2.commands import update_user -from keystone.manage2.commands import version -from keystone.tools import buffout -from keystone import utils - - -LOGGER = logging.getLogger(__name__) - -OPTIONS = { - "keystone-service-admin-role": "KeystoneServiceAdmin", - "keystone-admin-role": "KeystoneAdmin", - "hash-password": "False", - 'backends': 'keystone.backends.sqlalchemy', - 'keystone.backends.sqlalchemy': { - "sql_connection": "sqlite://", - "backend_entities": "['UserRoleAssociation', " - "'Endpoints', 'Role', 'Tenant', 'User', " - "'Credentials', 'EndpointTemplates', 'Token', " - "'Service']", - "sql_idle_timeout": "30"}} -# Configure the CONF module to match -utils.set_configuration(OPTIONS) - - -class CommandTestCase(unittest.TestCase): - """Buffers stdout to test keystone-manage commands""" - - def run_cmd(self, module, args=None, use_managers=True): - """Runs the Command in the given module using the provided args""" - args = args if args is not None else [] - - managers = self.managers if use_managers else None - - cmd = module.Command(managers=managers) - parsed_args = cmd.parser.parse_args(args) - return cmd.run(parsed_args) - - def setUp(self): - self.managers = common.init_managers() - - # buffer stdout so we can assert what's printed - self.ob = buffout.OutputBuffer() - self.ob.start() - - def tearDown(self): - self.ob.stop() - - self.clear_all_data() - - def clear_all_data(self): - """ - Purges the database of all data - """ - db.unregister_models() - reload(db) - backends.configure_backends() - - def assertTableContainsRow(self, table, row): - """Assumes that row[0] is a unique identifier for the row""" - # ensure we're comparing str to str - row = [str(col) for col in row] - - # ensure the data we're looking for is *somewhere* in the table - self.assertIn(row[0], table) - - # find the matching row - matching_row = [r for r in table.split("\n") if row[0] in r][0] - matching_row = [c for c in matching_row.split() if c.strip() != '|'] - - self.assertEquals(row, matching_row) - - def _create_user(self): - # TODO(dolph): these ob.clear()'s need to be cleaned up - self.ob.clear() - self.run_cmd(create_user, [ - '--name', uuid.uuid4().hex, - '--password', uuid.uuid4().hex]) - obj_id = self.ob.read_lines()[0] - self.ob.clear() - return obj_id - - def _create_tenant(self): - self.ob.clear() - self.run_cmd(create_tenant, [ - '--name', uuid.uuid4().hex]) - obj_id = self.ob.read_lines()[0] - self.ob.clear() - return obj_id - - def _create_token(self, user_id): - self.ob.clear() - self.run_cmd(create_token, [ - '--user-id', user_id]) - obj_id = self.ob.read_lines()[0] - self.ob.clear() - return obj_id - - def _create_credential(self, user_id): - self.ob.clear() - cred_type = uuid.uuid4().hex - key = uuid.uuid4().hex - secret = uuid.uuid4().hex - self.run_cmd(create_credential, [ - '--user-id', user_id, - '--type', cred_type, - '--key', key, - '--secret', secret]) - obj_id = self.ob.read_lines()[0] - self.ob.clear() - return obj_id - - def _create_service(self): - self.ob.clear() - self.run_cmd(create_service, [ - '--name', uuid.uuid4().hex, - '--type', uuid.uuid4().hex]) - obj_id = self.ob.read_lines()[0] - self.ob.clear() - return obj_id - - def _create_role(self): - self.ob.clear() - self.run_cmd(create_role, [ - '--name', uuid.uuid4().hex]) - obj_id = self.ob.read_lines()[0] - self.ob.clear() - return obj_id - - def _create_endpoint_template(self, service_id): - self.ob.clear() - self.run_cmd(create_endpoint_template, [ - '--region', uuid.uuid4().hex, - '--service-id', service_id, - '--public-url', 'http://%s' % (uuid.uuid4().hex), - '--admin-url', 'http://%s' % (uuid.uuid4().hex), - '--internal-url', 'http://%s' % (uuid.uuid4().hex)]) - obj_id = self.ob.read_lines()[0] - self.ob.clear() - return obj_id - - def _map_endpoint(self, endpoint_template_id, tenant_id): - self.ob.clear() - self.run_cmd(map_endpoint, [ - '--endpoint-template-id', endpoint_template_id, - '--tenant-id', tenant_id]) - self.ob.clear() - - -class TestCommon(unittest.TestCase): - def test_enable_disable(self): - """$ manage [command] --enable --disable""" - args = argparse.Namespace(enable=True, disable=True) - with self.assertRaises(SystemExit): - cmd = base.BaseCommand() - cmd.true_or_false(args, 'enable', 'disable') - - "Unable to apply both: --enable and --disable" - - def test_enable(self): - """$ manage [command] --enable""" - args = argparse.Namespace(enable=True, disable=False) - cmd = base.BaseCommand() - self.assertTrue(cmd.true_or_false(args, 'enable', 'disable')) - - def test_disable(self): - """$ manage [command] --disable""" - args = argparse.Namespace(enable=False, disable=True) - cmd = base.BaseCommand() - self.assertFalse(cmd.true_or_false(args, 'enable', 'disable')) - - def test_no_args(self): - """$ manage [command]""" - args = argparse.Namespace(enable=False, disable=False) - cmd = base.BaseCommand() - self.assertFalse(cmd.true_or_false(args, 'enable', 'disable')) - - -class TestVersionCommand(CommandTestCase): - """Tests for ./bin/keystone-manage version""" - API_VERSION = '2.0 beta' - IMPLEMENTATION_VERSION = '2012.1-dev' - DATABASE_VERSION = 'not under version control' - - def test_api_version(self): - v = version.Command.get_api_version() - self.assertEqual(v, self.API_VERSION) - - def test_implementation_version(self): - v = version.Command.get_implementation_version() - self.assertEqual(v, self.IMPLEMENTATION_VERSION) - - def test_all_version_responses(self): - self.run_cmd(version, [], use_managers=False) - lines = self.ob.read_lines() - self.assertEqual(len(lines), 3, lines) - self.assertIn(self.API_VERSION, lines[0]) - self.assertIn(self.IMPLEMENTATION_VERSION, lines[1]) - self.assertIn(self.DATABASE_VERSION, lines[2]) - - def test_api_version_arg(self): - self.run_cmd(version, ['--api'], use_managers=False) - lines = self.ob.read_lines() - self.assertEqual(len(lines), 1, lines) - self.assertIn(self.API_VERSION, lines[0]) - - def test_implementation_version_arg(self): - self.run_cmd(version, ['--implementation'], use_managers=False) - lines = self.ob.read_lines() - self.assertEqual(len(lines), 1, lines) - self.assertIn(self.IMPLEMENTATION_VERSION, lines[0]) - - def test_database_version_arg(self): - self.run_cmd(version, ['--database'], use_managers=False) - lines = self.ob.read_lines() - self.assertEqual(len(lines), 1, lines) - self.assertIn(self.DATABASE_VERSION, lines[0]) - - -class TestCreateUserCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(create_user) - - def test_create_user_min_fields(self): - name = uuid.uuid4().hex - password = uuid.uuid4().hex - self.run_cmd(create_user, [ - '--name', name, - '--password', password]) - user_id = self.ob.read_lines()[0] - self.assertEqual(len(user_id), 32) - - self.ob.clear() - - self.run_cmd(list_users) - output = self.ob.read() - self.assertIn(user_id, output) - self.assertIn(name, output) - self.assertNotIn(password, output) - - def test_create_user_all_fields(self): - user_id = uuid.uuid4().hex - name = uuid.uuid4().hex - password = uuid.uuid4().hex - email = uuid.uuid4().hex - self.run_cmd(create_user, [ - '--id', user_id, - '--name', name, - '--password', password, - '--email', email, - '--disable']) - output = self.ob.read_lines() - self.assertEquals(user_id, output[0]) - - self.ob.clear() - - self.run_cmd(list_users) - self.assertTableContainsRow(self.ob.read(), [user_id, name, email, - str(None), str(False)]) - - -class TestListUsersCommand(CommandTestCase): - def test_no_args(self): - self.run_cmd(list_users) - lines = self.ob.read_lines() - row = [col.strip() for col in lines[1].split('|') if col.strip()] - self.assertEquals('ID', row[0]) - self.assertEquals('Name', row[1]) - self.assertEquals('Email', row[2]) - self.assertEquals('Default Tenant ID', row[3]) - self.assertEquals('Enabled', row[4]) - - -class TestUpdateUserCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(update_user) - - def test_invalid_id(self): - with self.assertRaises(KeyError): - self.run_cmd(update_user, [ - '--where-id', uuid.uuid4().hex]) - - def test_create_update_list(self): - user_id = uuid.uuid4().hex - self.run_cmd(create_user, [ - '--id', user_id, - '--name', uuid.uuid4().hex, - '--password', uuid.uuid4().hex, - '--email', uuid.uuid4().hex]) - - name = uuid.uuid4().hex - password = uuid.uuid4().hex - email = uuid.uuid4().hex - self.run_cmd(update_user, [ - '--where-id', user_id, - '--name', name, - '--password', password, - '--email', email, - '--disable']) - - self.ob.clear() - - self.run_cmd(list_users) - output = self.ob.read() - self.assertIn(user_id, output) - - lines = self.ob.read_lines() - row = [row for row in lines if user_id in row][0] - row = [col for col in row.split() if col.strip() != '|'] - self.assertEquals(user_id, row[0]) - self.assertEquals(name, row[1]) - self.assertEquals(email, row[2]) - self.assertEquals(str(None), row[3]) - self.assertEquals(str(False), row[4]) - - -class TestDeleteUserCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(delete_user) - - def test_invalid_id(self): - with self.assertRaises(KeyError): - self.run_cmd(delete_user, [ - '--where-id', uuid.uuid4().hex]) - - def test_create_delete_list(self): - user_id = uuid.uuid4().hex - self.run_cmd(create_user, [ - '--id', user_id, - '--name', uuid.uuid4().hex, - '--password', uuid.uuid4().hex, - '--email', uuid.uuid4().hex]) - - self.run_cmd(delete_user, [ - '--where-id', user_id]) - - self.ob.clear() - - self.run_cmd(list_users) - self.assertNotIn(user_id, self.ob.read()) - - -class TestCreateTenantCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(create_tenant) - - def test_create_enabled_tenant(self): - name = uuid.uuid4().hex - self.run_cmd(create_tenant, [ - '--name', name]) - tenant_id = self.ob.read_lines()[0] - self.assertEqual(len(tenant_id), 32) - - self.ob.clear() - - self.run_cmd(list_tenants) - output = self.ob.read() - self.assertIn(tenant_id, output) - self.assertIn(name, output) - - def test_create_disabled_tenant(self): - name = uuid.uuid4().hex - self.run_cmd(create_tenant, [ - '--name', name, - '--disabled']) - tenant_id = self.ob.read_lines()[0] - self.assertEqual(len(tenant_id), 32) - - self.ob.clear() - - self.run_cmd(list_tenants) - output = self.ob.read() - self.assertIn(tenant_id, output) - - lines = self.ob.read_lines() - row = [row for row in lines if tenant_id in row][0] - row = [col for col in row.split() if col.strip() != '|'] - self.assertEquals(tenant_id, row[0]) - self.assertEquals(name, row[1]) - self.assertEquals(str(False), row[2]) - - -class TestListTenantsCommand(CommandTestCase): - def test_no_args(self): - self.run_cmd(list_tenants) - lines = self.ob.read_lines() - row = [col.strip() for col in lines[1].split('|') if col.strip()] - self.assertEquals('ID', row[0]) - self.assertEquals('Name', row[1]) - self.assertEquals('Enabled', row[2]) - - -class TestUpdateTenantCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(update_tenant) - - def test_invalid_id(self): - with self.assertRaises(KeyError): - self.run_cmd(update_tenant, [ - '--where-id', uuid.uuid4().hex]) - - def test_create_update_list(self): - tenant_id = uuid.uuid4().hex - self.run_cmd(create_tenant, [ - '--id', tenant_id, - '--name', uuid.uuid4().hex]) - - name = uuid.uuid4().hex - self.run_cmd(update_tenant, [ - '--where-id', tenant_id, - '--name', name, - '--disable']) - - self.ob.clear() - - self.run_cmd(list_tenants) - output = self.ob.read() - self.assertIn(tenant_id, output) - - lines = self.ob.read_lines() - row = [row for row in lines if tenant_id in row][0] - row = [col for col in row.split() if col.strip() != '|'] - self.assertEquals(tenant_id, row[0]) - self.assertEquals(name, row[1]) - self.assertEquals(str(False), row[2]) - - -class TestDeleteTenantCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(delete_tenant) - - def test_invalid_id(self): - with self.assertRaises(KeyError): - self.run_cmd(delete_tenant, [ - '--where-id', uuid.uuid4().hex]) - - def test_create_delete_list(self): - tenant_id = uuid.uuid4().hex - self.run_cmd(create_tenant, [ - '--id', tenant_id, - '--name', uuid.uuid4().hex]) - - self.run_cmd(delete_tenant, [ - '--where-id', tenant_id]) - - self.ob.clear() - - self.run_cmd(list_tenants) - self.assertNotIn(tenant_id, self.ob.read()) - - -class TestCreateRoleCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(create_role) - - def test_create_role(self): - name = uuid.uuid4().hex - self.run_cmd(create_role, [ - '--name', name]) - role_id = self.ob.read_lines()[0] - self.assertTrue(int(role_id)) - - self.ob.clear() - - self.run_cmd(list_roles) - output = self.ob.read() - self.assertIn(role_id, output) - self.assertIn(name, output) - - def test_create_role_with_description(self): - name = uuid.uuid4().hex - description = uuid.uuid4().hex - self.run_cmd(create_role, [ - '--name', name, - '--description', description]) - role_id = self.ob.read_lines()[0] - self.assertTrue(int(role_id)) - - self.ob.clear() - - self.run_cmd(list_roles) - output = self.ob.read() - self.assertIn(role_id, output) - - lines = self.ob.read_lines() - row = [row for row in lines if role_id in row][0] - row = [col for col in row.split() if col.strip() != '|'] - self.assertEquals(role_id, row[0]) - self.assertEquals(name, row[1]) - self.assertEquals(str(None), row[2]) - self.assertEquals(description, row[3]) - - def test_create_role_owned_by_service(self): - self.run_cmd(create_service, [ - '--name', uuid.uuid4().hex, - '--type', uuid.uuid4().hex]) - service_id = self.ob.read_lines()[0] - - name = uuid.uuid4().hex - self.run_cmd(create_role, [ - '--name', name, - '--service-id', service_id]) - role_id = self.ob.read_lines()[0] - - self.ob.clear() - - self.run_cmd(list_roles) - output = self.ob.read() - self.assertIn(role_id, output) - - lines = self.ob.read_lines() - row = [row for row in lines if role_id in row][0] - row = [col for col in row.split() if col.strip() != '|'] - self.assertEquals(role_id, row[0]) - self.assertEquals(name, row[1]) - self.assertEquals(service_id, row[2]) - self.assertEquals(str(None), row[3]) - - -class TestUpdateRoleCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(update_role) - - def test_update_role(self): - old_name = uuid.uuid4().hex - old_description = uuid.uuid4().hex - old_service_id = self._create_service() - self.run_cmd(create_role, [ - '--name', old_name, - '--description', old_description, - '--service-id', old_service_id]) - role_id = self.ob.read_lines()[0] - - # update the role - name = uuid.uuid4().hex - description = uuid.uuid4().hex - service_id = self._create_service() - self.run_cmd(update_role, [ - '--where-id', role_id, - '--name', name, - '--description', description, - '--service-id', service_id]) - - self.ob.clear() - - self.run_cmd(list_roles) - self.assertTableContainsRow(self.ob.read(), [role_id, name, - service_id, description]) - - -class TestListRolesCommand(CommandTestCase): - def test_no_args(self): - self.run_cmd(list_roles) - lines = self.ob.read_lines() - row = [col.strip() for col in lines[1].split('|') if col.strip()] - self.assertEquals('ID', row[0]) - self.assertEquals('Name', row[1]) - self.assertEquals('Service ID', row[2]) - self.assertEquals('Description', row[3]) - - -class TestDeleteRoleCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(delete_role) - - def test_invalid_id(self): - with self.assertRaises(KeyError): - self.run_cmd(delete_role, [ - '--where-id', uuid.uuid4().hex]) - - def test_delete_role(self): - role_id = self._create_role() - - # delete it - self.run_cmd(delete_role, [ - '--where-id', role_id]) - - self.ob.clear() - - # ensure it's not returned - self.run_cmd(list_roles) - output = self.ob.read() - self.assertNotIn(role_id, output) - - -class TestListRoleGrants(CommandTestCase): - def test_no_args(self): - self.run_cmd(list_role_grants) - lines = self.ob.read_lines() - row = [col.strip() for col in lines[1].split('|') if col.strip()] - self.assertEquals(['Role ID', 'User ID', 'Tenant ID', 'Global'], row) - - -class TestGrantRoleCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(grant_role) - - def test_invalid_ids(self): - with self.assertRaises(KeyError): - self.run_cmd(grant_role, [ - '--user-id', uuid.uuid4().hex, - '--role-id', uuid.uuid4().hex]) - - def test_grant_global_role(self): - user_id = self._create_user() - role_id = self._create_role() - - self.run_cmd(grant_role, [ - '--user-id', user_id, - '--role-id', role_id]) - - # granting again should fail - # TODO(dolph): this should be an IntegrityError - with self.assertRaises(KeyError): - self.run_cmd(grant_role, [ - '--user-id', user_id, - '--role-id', role_id]) - - self.ob.clear() - - self.run_cmd(list_role_grants, [ - '--where-user-id', user_id, - '--where-role-id', role_id, - '--where-global']) - self.assertTableContainsRow(self.ob.read(), [role_id, user_id, - str(None), str(True)]) - - def test_grant_tenant_role(self): - user_id = self._create_user() - role_id = self._create_role() - tenant_id = self._create_tenant() - - self.run_cmd(grant_role, [ - '--user-id', user_id, - '--role-id', role_id, - '--tenant-id', tenant_id]) - - # granting again should fail - # TODO(dolph): this should be an IntegrityError - with self.assertRaises(KeyError): - self.run_cmd(grant_role, [ - '--user-id', user_id, - '--role-id', role_id, - '--tenant-id', tenant_id]) - - self.ob.clear() - - self.run_cmd(list_role_grants, [ - '--where-user-id', user_id, - '--where-role-id', role_id, - '--where-tenant-id', tenant_id]) - self.assertTableContainsRow(self.ob.read(), [role_id, user_id, - tenant_id, str(False)]) - - -class TestRevokeRoleCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(revoke_role) - - def test_revoke_global_role(self): - user_id = self._create_user() - role_id = self._create_role() - - self.run_cmd(grant_role, [ - '--user-id', user_id, - '--role-id', role_id]) - - self.run_cmd(revoke_role, [ - '--user-id', user_id, - '--role-id', role_id]) - - self.ob.clear() - - self.run_cmd(list_role_grants, [ - '--where-user-id', user_id, - '--where-role-id', role_id, - '--where-global']) - with self.assertRaises(AssertionError): - self.assertTableContainsRow(self.ob.read(), [role_id, user_id, - str(None), str(True)]) - - def test_revoke_tenant_role(self): - user_id = self._create_user() - role_id = self._create_role() - tenant_id = self._create_tenant() - - self.run_cmd(grant_role, [ - '--user-id', user_id, - '--role-id', role_id, - '--tenant-id', tenant_id]) - - self.run_cmd(revoke_role, [ - '--user-id', user_id, - '--role-id', role_id, - '--tenant-id', tenant_id]) - - self.ob.clear() - - self.run_cmd(list_role_grants, [ - '--where-user-id', user_id, - '--where-role-id', role_id, - '--where-tenant-id', tenant_id]) - with self.assertRaises(AssertionError): - self.assertTableContainsRow(self.ob.read(), [role_id, user_id, - tenant_id, str(False)]) - - -class TestCreateServiceCommand(CommandTestCase): - def test_create_service(self): - name = uuid.uuid4().hex - service_type = uuid.uuid4().hex - description = uuid.uuid4().hex - owner_id = self._create_user() - self.run_cmd(create_service, [ - '--name', name, - '--type', service_type, - '--description', description, - '--owner-id', owner_id]) - service_id = self.ob.read_lines()[0] - self.assertTrue(int(service_id)) - - self.ob.clear() - - self.run_cmd(list_services) - output = self.ob.read() - self.assertIn(service_id, output) - - lines = self.ob.read_lines() - row = [row for row in lines if service_id in row][0] - row = [col for col in row.split() if col.strip() != '|'] - self.assertEquals(service_id, row[0]) - self.assertEquals(name, row[1]) - self.assertEquals(service_type, row[2]) - self.assertEquals(owner_id, row[3]) - self.assertEquals(description, row[4]) - - -class TestUpdateServiceCommand(CommandTestCase): - def test_update_service(self): - old_name = uuid.uuid4().hex - old_type = uuid.uuid4().hex - old_description = uuid.uuid4().hex - old_owner_id = self._create_user() - self.run_cmd(create_service, [ - '--name', old_name, - '--type', old_type, - '--description', old_description, - '--owner-id', old_owner_id]) - service_id = self.ob.read_lines()[0] - - # update the service - name = uuid.uuid4().hex - service_type = uuid.uuid4().hex - description = uuid.uuid4().hex - owner_id = self._create_user() - self.run_cmd(update_service, [ - '--where-id', service_id, - '--name', name, - '--type', service_type, - '--description', description, - '--owner-id', owner_id]) - - self.ob.clear() - - self.run_cmd(list_services) - output = self.ob.read() - self.assertIn(service_id, output) - - lines = self.ob.read_lines() - row = [row for row in lines if service_id in row][0] - row = [col for col in row.split() if col.strip() != '|'] - self.assertEquals(service_id, row[0]) - self.assertEquals(name, row[1]) - self.assertEquals(service_type, row[2]) - self.assertEquals(owner_id, row[3]) - self.assertEquals(description, row[4]) - - -class TestListServicesCommand(CommandTestCase): - def test_no_args(self): - self.run_cmd(list_services) - lines = self.ob.read_lines() - row = [col.strip() for col in lines[1].split('|') if col.strip()] - self.assertEquals('ID', row[0]) - self.assertEquals('Name', row[1]) - self.assertEquals('Type', row[2]) - self.assertEquals('Owner ID', row[3]) - self.assertEquals('Description', row[4]) - - -class TestDeleteServiceCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(delete_service) - - def test_invalid_id(self): - with self.assertRaises(KeyError): - self.run_cmd(delete_service, [ - '--where-id', uuid.uuid4().hex]) - - def test_delete_service(self): - service_id = self._create_service() - - self.run_cmd(delete_service, [ - '--where-id', service_id]) - - self.ob.clear() - - self.run_cmd(list_services) - output = self.ob.read() - self.assertNotIn(service_id, output) - - -class TestCreateTokenCommand(CommandTestCase): - """Creates tokens and validates their attributes. - - This class has a known potential race condition, due to the expected - token expiration being 24 hours after token creation. If the - 'create_token' command runs immediately before the minute rolls over, - and the test class produces a timestamp for the subsequent minute, the - test will fail. - - """ - - @staticmethod - def _get_tomorrow_str(): - return (datetime.datetime.utcnow() + - datetime.timedelta(days=1)).strftime('%Y-%m-%dT%H:%M') - - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(create_token) - - def test_create_unscoped_token(self): - user_id = self._create_user() - self.run_cmd(create_token, [ - '--user-id', user_id]) - tomorrow = TestCreateTokenCommand._get_tomorrow_str() - token_id = self.ob.read_lines()[0] - self.assertEqual(len(token_id), 32) - - self.ob.clear() - - self.run_cmd(list_tokens) - self.assertTableContainsRow(self.ob.read(), [token_id, user_id, - str(None), tomorrow]) - - def test_create_scoped_token(self): - user_id = self._create_user() - tenant_id = self._create_tenant() - self.run_cmd(create_token, [ - '--user-id', user_id, - '--tenant-id', tenant_id]) - tomorrow = TestCreateTokenCommand._get_tomorrow_str() - token_id = self.ob.read_lines()[0] - self.assertEqual(len(token_id), 32) - - self.ob.clear() - - self.run_cmd(list_tokens) - self.assertTableContainsRow(self.ob.read(), [token_id, user_id, - tenant_id, tomorrow]) - - def test_create_expired_token(self): - user_id = self._create_user() - expiration = '1999-12-31T23:59' - self.run_cmd(create_token, [ - '--user-id', user_id, - '--expires', expiration]) - token_id = self.ob.read_lines()[0] - self.assertEqual(len(token_id), 32) - - self.ob.clear() - - self.run_cmd(list_tokens) - self.assertTableContainsRow(self.ob.read(), [token_id, user_id, - str(None), expiration]) - - def test_create_specific_token_id(self): - token_id = uuid.uuid4().hex - user_id = self._create_user() - self.run_cmd(create_token, [ - '--id', token_id, - '--user-id', user_id]) - tomorrow = TestCreateTokenCommand._get_tomorrow_str() - self.assertEqual(token_id, self.ob.read_lines()[0]) - - self.ob.clear() - - self.run_cmd(list_tokens) - self.assertTableContainsRow(self.ob.read(), [token_id, user_id, - str(None), tomorrow]) - - -class TestUpdateTokenCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(update_token) - - def test_update_token(self): - token_id = self._create_token(self._create_user()) - - user_id = self._create_user() - tenant_id = self._create_tenant() - expiration = '1999-12-31T23:59' - self.run_cmd(update_token, [ - '--where-id', token_id, - '--user-id', user_id, - '--tenant-id', tenant_id, - '--expires', expiration]) - - self.ob.clear() - - self.run_cmd(list_tokens) - self.assertTableContainsRow(self.ob.read(), [token_id, user_id, - tenant_id, expiration]) - - -class TestListTokensCommand(CommandTestCase): - def test_no_args(self): - self.run_cmd(list_tokens) - lines = self.ob.read_lines() - row = [col.strip() for col in lines[1].split('|') if col.strip()] - self.assertEquals(['ID', 'User ID', 'Tenant ID', 'Expiration'], - row) - - -class TestDeleteTokenCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(delete_token) - - def test_delete_token(self): - token_id = self._create_token(self._create_user()) - - self.run_cmd(delete_token, [ - '--where-id', token_id]) - - self.ob.clear() - - # ensure it's not returned - self.run_cmd(list_tokens) - output = self.ob.read() - self.assertNotIn(token_id, output) - - -class TestCreateCredentialCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(create_credential) - - def test_create_credential(self): - user_id = self._create_user() - cred_type = uuid.uuid4().hex - key = uuid.uuid4().hex - secret = uuid.uuid4().hex - self.run_cmd(create_credential, [ - '--user-id', user_id, - '--type', cred_type, - '--key', key, - '--secret', secret]) - credential_id = self.ob.read_lines()[0] - self.assertTrue(int(credential_id)) - - self.ob.clear() - - self.run_cmd(list_credentials) - self.assertTableContainsRow(self.ob.read(), [credential_id, - user_id, str(None), cred_type, key, secret]) - - def test_create_credential_with_tenant(self): - user_id = self._create_user() - tenant_id = self._create_tenant() - cred_type = uuid.uuid4().hex - key = uuid.uuid4().hex - secret = uuid.uuid4().hex - self.run_cmd(create_credential, [ - '--user-id', user_id, - '--tenant-id', tenant_id, - '--type', cred_type, - '--key', key, - '--secret', secret]) - credential_id = self.ob.read_lines()[0] - self.assertTrue(int(credential_id)) - - self.ob.clear() - - self.run_cmd(list_credentials) - self.assertTableContainsRow(self.ob.read(), [credential_id, - user_id, tenant_id, cred_type, key, secret]) - - -class TestUpdateCredentialCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(update_credential) - - def test_update_credential(self): - credential_id = self._create_credential(self._create_user()) - - user_id = self._create_user() - tenant_id = self._create_tenant() - cred_type = uuid.uuid4().hex - key = uuid.uuid4().hex - secret = uuid.uuid4().hex - self.run_cmd(update_credential, [ - '--where-id', credential_id, - '--user-id', user_id, - '--tenant-id', tenant_id, - '--type', cred_type, - '--key', key, - '--secret', secret]) - - self.ob.clear() - - self.run_cmd(list_credentials) - self.assertTableContainsRow(self.ob.read(), [credential_id, - user_id, tenant_id, cred_type, key, secret]) - - -class TestListCredentialsCommand(CommandTestCase): - def test_no_args(self): - self.run_cmd(list_credentials) - lines = self.ob.read_lines() - row = [col.strip() for col in lines[1].split('|') if col.strip()] - self.assertEquals(['ID', 'User ID', 'Tenant ID', 'Type', 'Key', - 'Secret'], row) - - -class TestDeleteCredentialCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(delete_credential) - - def test_delete_credential(self): - credential_id = self._create_credential(self._create_user()) - - self.run_cmd(delete_credential, [ - '--where-id', credential_id]) - - self.ob.clear() - - # ensure it's not returned - self.run_cmd(list_credentials) - output = self.ob.read() - self.assertNotIn(credential_id, output) - - -class TestCreateEndpointTemplateCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(create_endpoint_template) - - def test_create_global_endpoint_template(self): - region = uuid.uuid4().hex - service_id = self._create_service() - public_url = 'http://%s' % (uuid.uuid4().hex) - admin_url = 'http://%s' % (uuid.uuid4().hex) - internal_url = 'http://%s' % (uuid.uuid4().hex) - self.run_cmd(create_endpoint_template, [ - '--region', region, - '--service-id', service_id, - '--public-url', public_url, - '--admin-url', admin_url, - '--internal-url', internal_url, - '--global']) - endpoint_template_id = self.ob.read_lines()[0] - self.assertTrue(int(endpoint_template_id)) - - self.ob.clear() - - self.run_cmd(list_endpoint_templates) - self.assertTableContainsRow(self.ob.read(), [endpoint_template_id, - service_id, region, str(True), str(True), public_url, admin_url, - internal_url]) - - -class TestUpdateEndpointTemplateCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(update_endpoint_template) - - def test_update_endpoint_template(self): - endpoint_template_id = self._create_endpoint_template( - self._create_service()) - - region = uuid.uuid4().hex - service_id = self._create_service() - public_url = 'http://%s' % (uuid.uuid4().hex) - admin_url = 'http://%s' % (uuid.uuid4().hex) - internal_url = 'http://%s' % (uuid.uuid4().hex) - - self.run_cmd(update_endpoint_template, [ - '--where-id', endpoint_template_id, - '--region', region, - '--service-id', service_id, - '--public-url', public_url, - '--admin-url', admin_url, - '--internal-url', internal_url, - '--global', - '--disable']) - - self.ob.clear() - - self.run_cmd(list_endpoint_templates) - self.assertTableContainsRow(self.ob.read(), [endpoint_template_id, - service_id, region, str(False), str(True), public_url, admin_url, - internal_url]) - - -class TestListEndpointTemplatesCommand(CommandTestCase): - def test_no_args(self): - self.run_cmd(list_endpoint_templates) - lines = self.ob.read_lines() - row = [col.strip() for col in lines[1].split('|') if col.strip()] - self.assertEquals(['ID', 'Service ID', 'Region', 'Enabled', 'Global', - 'Public URL', 'Admin URL', 'Internal URL'], row) - - -class TestDeleteEndpointTemplateCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(delete_endpoint_template) - - def test_delete_endpoint_template(self): - endpoint_template_id = self._create_endpoint_template( - self._create_service()) - - self.run_cmd(delete_endpoint_template, [ - '--where-id', endpoint_template_id]) - - self.ob.clear() - - # ensure it's not returned - self.run_cmd(list_endpoint_templates) - output = self.ob.read() - self.assertNotIn(endpoint_template_id, output) - - -class TestCreateEndpointCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(map_endpoint) - - def test_create_global_endpoint(self): - tenant_id = self._create_tenant() - endpoint_template_id = self._create_endpoint_template( - self._create_service()) - self.run_cmd(map_endpoint, [ - '--endpoint-template-id', endpoint_template_id, - '--tenant-id', tenant_id]) - - self.ob.clear() - - self.run_cmd(list_endpoints) - self.assertTableContainsRow(self.ob.read(), [endpoint_template_id, - tenant_id]) - - -class TestListEndpointsCommand(CommandTestCase): - def test_no_args(self): - self.run_cmd(list_endpoints) - lines = self.ob.read_lines() - row = [col.strip() for col in lines[1].split('|') if col.strip()] - self.assertEquals(['Endpoint Template ID', 'Tenant ID'], row) - - -class TestDeleteEndpointCommand(CommandTestCase): - def test_no_args(self): - with self.assertRaises(SystemExit): - self.run_cmd(unmap_endpoint) - - def test_delete_endpoint(self): - tenant_id = self._create_tenant() - endpoint_template_id = self._create_endpoint_template( - self._create_service()) - self._map_endpoint(endpoint_template_id, tenant_id) - - self.run_cmd(unmap_endpoint, [ - '--tenant-id', tenant_id, - '--endpoint-template-id', endpoint_template_id]) - - self.ob.clear() - - # ensure it's not returned - self.run_cmd(list_endpoints) - output = self.ob.read() - self.assertNotIn(" | ".join([endpoint_template_id, tenant_id]), output) diff --git a/keystone/test/unit/test_commands_v1.py b/keystone/test/unit/test_commands_v1.py deleted file mode 100644 index abd722f6..00000000 --- a/keystone/test/unit/test_commands_v1.py +++ /dev/null @@ -1,77 +0,0 @@ -import datetime -import unittest2 as unittest - -from keystone import backends -import keystone.backends.sqlalchemy as db -import keystone.backends.api as db_api -import keystone.manage.api as manage_api -from keystone import utils - - -class TestCommandsV1(unittest.TestCase): - """Tests for keystone-manage version 1 commands""" - - def __init__(self, *args, **kwargs): - super(TestCommandsV1, self).__init__(*args, **kwargs) - self.options = { - 'backends': 'keystone.backends.sqlalchemy', - 'keystone.backends.sqlalchemy': { - # in-memory db - 'sql_connection': 'sqlite://', - 'backend_entities': - "['UserRoleAssociation', 'Endpoints', 'Role', 'Tenant', " - "'Tenant', 'User', 'Credentials', 'EndpointTemplates', " - "'Token', 'Service']", - }, - } - # Need to populate the CONF module with these options - utils.set_configuration(self.options) - - def setUp(self): - self.clear_all_data() - manage_api.add_tenant('Test tenant') - self.user = manage_api.add_user('Test user', 'Test password', - 'Test tenant') - - def tearDown(self): - self.clear_all_data() - - @staticmethod - def clear_all_data(): - """ - Purges the database of all data - """ - db.unregister_models() - reload(db) - backends.configure_backends() - - def test_service_list(self): - result = manage_api.list_services() - self.assertEqual(result, []) - - def test_add_service(self): - data = { - 'name': 'Test name', - 'type': 'Test type', - 'desc': 'Test description', - 'owner_id': self.user.id, - } - manage_api.add_service(**data) - result = manage_api.list_services() - self.assertEqual(result, [['1', data['name'], data['type'], - data['owner_id'], data['desc']]]) - - def test_add_token(self): - data = { - 'token': 'Test token', - 'user': 'Test user', - 'tenant': 'Test tenant', - 'expires': '20120104T18:30', - } - manage_api.add_token(**data) - result = manage_api.list_tokens() - user = db_api.USER.get_by_name(data['user']) - tenant = db_api.TENANT.get_by_name(data['tenant']) - self.assertEqual(result, [[data['token'], user['id'], - datetime.datetime(2012, 1, 4, 18, 30), - tenant['id']]]) diff --git a/keystone/test/unit/test_config.py b/keystone/test/unit/test_config.py deleted file mode 100644 index b1827055..00000000 --- a/keystone/test/unit/test_config.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) 2011 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 ast -import unittest2 as unittest - -from keystone import config -from keystone import utils - -CONF = config.CONF - - -class ConfigTestCase(unittest.TestCase): - """ - Base class to test keystone/config.py - """ - def __init__(self, *args, **kwargs): - super(ConfigTestCase, self).__init__(*args, **kwargs) - - def setUp(self): - pass - - def tearDown(self): - pass - - def test_old_config_syntax(self): - options = { - 'verbose': True, - 'debug': False, - 'backends': "keystone.backends.sqlalchemy", - 'keystone.backends.sqlalchemy': { - # in-memory db - 'sql_connection': 'sqlite://', - 'backend_entities': - "['UserRoleAssociation', 'Endpoints', 'Role', 'Tenant', " - "'Tenant', 'User', 'Credentials', 'EndpointTemplates', " - "'Token', 'Service']", - }, - 'extensions': 'osksadm, oskscatalog, hpidm', - 'keystone-admin-role': 'Admin', - 'keystone-service-admin-role': 'KeystoneServiceAdmin', - 'hash-password': 'True', - } - utils.set_configuration(options) - self.assertTrue(CONF.verbose) - self.assertFalse(CONF.debug) - self.assertIn('hpidm', [ext.strip() for ext in CONF.extensions]) - self.assertIn('keystone.backends.sqlalchemy', CONF.backends) - self.assertTrue(CONF.hash_password) - self.assertEquals(CONF['keystone.backends.sqlalchemy'].sql_connection, - 'sqlite://') - self.assertIsInstance(ast.literal_eval( - CONF['keystone.backends.sqlalchemy'].backend_entities), - list) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_controller_version.py b/keystone/test/unit/test_controller_version.py deleted file mode 100644 index eccf629d..00000000 --- a/keystone/test/unit/test_controller_version.py +++ /dev/null @@ -1,139 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2011 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 json -import logging -from lxml import etree -import unittest2 as unittest -from webob import Request - -from keystone.controllers.version import VersionController - -LOGGER = logging.getLogger(__name__) - - -class TestVersionController(unittest.TestCase): - def setUp(self): - self.controller = VersionController() - - def _default_version(self, file=None): - """ Verify default response for versions is JSON """ - if file is None: - file = 'admin/version' - req = Request.blank('/') - req.environ = {} - response = self.controller.get_version_info(req, file=file) - self.assertEqual(response.content_type, 'application/json') - data = json.loads(response.body) - self.assertIsNotNone(data) - - def _json_version(self, file=None): - """ Verify JSON response for versions - - Checks that JSON is returned when Accept is set to application/json. - Also checks that verions and version exist and that - values are as expected - - """ - if file is None: - file = 'admin/version' - req = Request.blank('/') - req.headers['Accept'] = 'application/json' - req.environ = {} - response = self.controller.get_version_info(req, file=file) - self.assertEqual(response.content_type, 'application/json') - data = json.loads(response.body) - self.assertIn("versions", data) - versions = data['versions'] - self.assertIn("values", versions) - values = versions['values'] - self.assertIsInstance(values, list) - for version in values: - for item in version: - self.assertIn(item, ["id", "status", "updated", "links", - "media-types"]) - - def _xml_version(self, file=None): - """ Verify XML response for versions - - Checks that XML is returned when Accept is set to application/xml. - Also checks that verions and version tags exist and that - attributes are as expected - - """ - if file is None: - file = 'admin/version' - req = Request.blank('/') - req.headers['Accept'] = 'application/xml' - req.environ = {} - response = self.controller.get_version_info(req, file=file) - self.assertEqual(response.content_type, 'application/xml') - data = etree.fromstring(response.body) - self.assertEqual(data.tag, - '{http://docs.openstack.org/common/api/v2.0}versions') - for version in data: - self.assertEqual(version.tag, - '{http://docs.openstack.org/common/api/v2.0}version') - for attribute in version.attrib: - self.assertIn(attribute, ["id", "status", "updated", "links", - "media-types"]) - - def _atom_version(self, file=None): - """ Verify ATOM response for versions - - Checks that ATOM XML is returned when Accept is set to - aapplication/atom+xml. - Also checks that verions and version tags exist and that - attributes are as expected - - """ - if file is None: - file = 'admin/version' - req = Request.blank('/') - req.headers['Accept'] = 'application/atom+xml' - req.environ = {} - response = self.controller.get_version_info(req, file=file) - self.assertEqual(response.content_type, 'application/atom+xml') - data = etree.fromstring(response.body) - self.assertEqual(data.tag, - '{http://www.w3.org/2005/Atom}feed') - - def test_default_version_admin(self): - self._default_version(file='admin/version') - - def test_default_version_service(self): - self._default_version(file='service/version') - - def test_xml_version_admin(self): - self._xml_version(file='admin/version') - - def test_xml_version_service(self): - self._xml_version(file='service/version') - - def test_json_version_admin(self): - self._json_version(file='admin/version') - - def test_json_version_service(self): - self._json_version(file='service/version') - - def test_atom_version_admin(self): - self._atom_version(file='admin/version') - - def test_atom_version_service(self): - self._atom_version(file='service/version') - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_d5_compat.py b/keystone/test/unit/test_d5_compat.py deleted file mode 100644 index 40d0e997..00000000 --- a/keystone/test/unit/test_d5_compat.py +++ /dev/null @@ -1,174 +0,0 @@ -import json -import unittest2 as unittest -from keystone.frontends import d5_compat -import keystone.logic.types.fault as fault - - -class TestD5Auth(unittest.TestCase): - """Test to make sure Keystone honors the 'unofficial' D5 API contract. - - The main differences were: - - POST /v2.0/tokens without the "auth" wrapper - - POST /v2.0/tokens with tenantId in the passwordCredentials object - (instead of the auth wrapper) - - Response for validate token was wrapped in "auth" - - TODO(zns): deprecate this once we move to the next version of the API - """ - - pwd_xml = '<?xml version="1.0" encoding="UTF-8"?>\ - <passwordCredentials\ - xmlns="http://docs.openstack.org/identity/api/v2.0" \ - password="secret" username="disabled" \ - />' - - def test_pwd_cred_marshall(self): - creds = d5_compat.D5AuthWithPasswordCredentials.from_xml(self.pwd_xml) - self.assertEqual(creds.password, "secret") - self.assertEqual(creds.username, "disabled") - - def test_pwd_creds_from_json(self): - data = json.dumps({"passwordCredentials": - {"username": "foo", "password": "bar"}}) - creds = d5_compat.D5AuthWithPasswordCredentials.from_json(data) - self.assertEqual(creds.username, "foo") - self.assertEqual(creds.password, "bar") - self.assertIsNone(creds.tenant_id) - self.assertIsNone(creds.tenant_name) - - def test_pwd_creds_with_tenant_name_from_json(self): - data = json.dumps({"passwordCredentials": - {"tenantName": "blaa", "username": "foo", - "password": "bar"}}) - creds = d5_compat.D5AuthWithPasswordCredentials.from_json(data) - self.assertEqual(creds.username, "foo") - self.assertEqual(creds.password, "bar") - self.assertIsNone(creds.tenant_id) - self.assertEqual(creds.tenant_name, "blaa") - - def test_pwd_creds_with_tenant_id_from_json(self): - data = json.dumps({"passwordCredentials": - {"tenantId": "blaa", "username": "foo", - "password": "bar"}}) - creds = d5_compat.D5AuthWithPasswordCredentials.from_json(data) - self.assertEqual(creds.username, "foo") - self.assertEqual(creds.password, "bar") - self.assertEqual(creds.tenant_id, "blaa") - self.assertIsNone(creds.tenant_name) - - def test_pwd_not_both_tenant_from_json(self): - data = json.dumps({"tenantId": "blaa", "tenantName": "aalb"}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Expecting passwordCredentials", - d5_compat.D5AuthWithPasswordCredentials.from_json, - data) - - def test_pwd_no_creds_from_json(self): - data = json.dumps({"auth": {}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Expecting passwordCredentials", - d5_compat.D5AuthWithPasswordCredentials.from_json, - data) - - def test_pwd_invalid_attribute_from_json(self): - data = json.dumps({"passwordCredentials": {"foo": "bar"}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Invalid", - d5_compat.D5AuthWithPasswordCredentials.from_json, - data) - - def test_pwd_no_username_from_json(self): - data = json.dumps({"passwordCredentials": {}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Expecting passwordCredentials:username", - d5_compat.D5AuthWithPasswordCredentials.from_json, - data) - - def test_pwd_no_password_from_json(self): - data = json.dumps({"passwordCredentials": - {"username": "foo"}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Expecting passwordCredentials:password", - d5_compat.D5AuthWithPasswordCredentials.from_json, - data) - - def test_pwd_invalid_creds_attribute_from_json(self): - data = json.dumps({"passwordCredentials": {"bar": "foo"}}) - self.assertRaisesRegexp(fault.BadRequestFault, - "Invalid", - d5_compat.D5AuthWithPasswordCredentials.from_json, - data) - - def test_json_pwd_creds_from_D5(self): - D5_data = json.dumps({"passwordCredentials": - {"username": "foo", "password": "bar"}}) - diablo_data = json.dumps({"auth": {"passwordCredentials": - {"username": "foo", "password": "bar"}}}) - creds = d5_compat.D5AuthWithPasswordCredentials.from_json(D5_data) - diablo = creds.to_json() - self.assertEquals(diablo, diablo_data) - - def test_json_authdata_from_D5(self): - pass - - def test_json_validatedata_from_D5(self): - diablo_data = { - "access": { - "token": { - "expires": "2011-12-07T21:31:49.215675", - "id": "92c8962a-7e9b-40d1-83eb-a2f3b6eb45c3" - }, - "user": { - "id": "3", - "name": "admin", - "roles": [ - { - "id": "1", - "name": "Admin" - } - ], - "username": "admin" - } - } - } - D5_data = {"auth": { - "token": { - "expires": "2011-12-07T21:31:49.215675", - "id": "92c8962a-7e9b-40d1-83eb-a2f3b6eb45c3" - }, - "user": { - "roleRefs": [ - { - "id": "1", - "roleId": "Admin" - } - ], - "username": "admin" - } - } - } - creds = d5_compat.D5ValidateData.from_json(json.dumps(diablo_data)) - D5 = json.loads(creds.to_json()) - self.assertEquals(diablo_data['access'], D5['access'], - "D5 compat response must contain Diablo format") - self.assertEquals(D5_data['auth'], D5['auth'], - "D5 compat response must contain D5 format") - - def test_no_catalog_in_response(self): - minimal_response = { - "access": { - "token": { - "expires": "2011-12-07T21:31:49.215675", - "id": "92c8962a-7e9b-40d1-83eb-a2f3b6eb45c3" - }, - "user": { - "id": "3", - "name": "admin", - } - } - } - d5 = d5_compat.D5toDiabloAuthData(init_json=minimal_response) - self.assertTrue(d5.to_json()) - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_extensions.py b/keystone/test/unit/test_extensions.py deleted file mode 100644 index 2a72d570..00000000 --- a/keystone/test/unit/test_extensions.py +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright (c) 2011 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 xml.etree import ElementTree -import json -import unittest2 as unittest - -from keystone import config -from keystone.contrib.extensions import CONFIG_EXTENSION_PROPERTY -from keystone.contrib.extensions.admin import EXTENSION_ADMIN_PREFIX -from keystone.logic.extension_reader import ExtensionsReader -from keystone.logic.extension_reader import get_supported_extensions - -CONF = config.CONF - - -class TestExtensionReader(unittest.TestCase): - """Unit tests for ExtensionsReader.These - tests check whether the returned extensions vary - when they are configured differently.""" - - def setUp(self): - self.original_extensions = CONF.extensions - CONF.set_override(CONFIG_EXTENSION_PROPERTY, ["osksadm"]) - self.extensions_reader = ExtensionsReader(EXTENSION_ADMIN_PREFIX) - - def tearDown(self): - CONF.set_override(CONFIG_EXTENSION_PROPERTY, self.original_extensions) - - def test_extensions_reader_getsupportedoptions(self): - self.assertIn('osksadm', get_supported_extensions()) - - def test_extensions_with_only_osksadm_json(self): - r = self.extensions_reader.get_extensions().to_json() - content = json.loads(r) - self.assertIsNotNone(content['extensions']) - self.assertIsNotNone(content['extensions']['values']) - found_osksadm = False - found_oskscatalog = False - for value in content['extensions']['values']: - if value['extension']['alias'] == 'OS-KSADM': - found_osksadm = True - if value['extension']['alias'] == 'OS-KSCATALOG': - found_oskscatalog = True - self.assertTrue(found_osksadm, - "Missing OS-KSADM extension.") - self.assertFalse(found_oskscatalog, - "Non configured OS-KSCATALOG extension returned.") - - def test_extensions_with_only_osksadm_xml(self): - r = self.extensions_reader.get_extensions().to_xml() - content = ElementTree.XML(r) - extensions = content.findall( - "{http://docs.openstack.org/common/api/v1.0}extension") - found_osksadm = False - found_oskscatalog = False - for extension in extensions: - if extension.get("alias") == 'OS-KSADM': - found_osksadm = True - if extension.get("alias") == 'OS-KSCATALOG': - found_oskscatalog = True - self.assertTrue(found_osksadm, - "Missing OS-KSADM extension.") - self.assertFalse(found_oskscatalog, - "Non configured OS-KSCATALOG extension returned.") - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_logic_auth.py b/keystone/test/unit/test_logic_auth.py deleted file mode 100644 index 3b61debf..00000000 --- a/keystone/test/unit/test_logic_auth.py +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright (c) 2011 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 json -import datetime -from lxml import etree -import unittest2 as unittest - -import base -from keystone.logic.types import auth as logic_auth -from keystone import models -from keystone.test import utils as test_utils - - -class LogicTypesAuthTestCase(base.ServiceAPITest): - """ - Base class to test keystone/logic/types/auth.py - """ - def __init__(self, *args, **kwargs): - super(LogicTypesAuthTestCase, self).__init__(*args, **kwargs) - - self.user = models.User(id='u1', name='john', username='john') - self.role = models.Role(id=1, name='Admin') - self.user.rolegrants = models.Roles([self.role], links=None) - self.token = models.Token(id='abc123T', user_id=self.user.id, - expires=datetime.date(2000, 1, 31)) - self.tenant = models.Tenant(id='ten8', name='The Tenant') - self.token.tenant = self.tenant - self.base_urls = [models.EndpointTemplate( - id="1", - internal_url="http://127.0.0.1/v1/%tenant_id%", - public_url="http://internet.com/v1/%tenant_id%", - admin_url="http://private.net/v1/", - version_id="v1", - version_url="http://127.0.0.1/v1/", - version_info="http://127.0.0.1/", - region="RegionOne", - service_id="0" - ), - models.EndpointTemplate( - id="2", - internal_url="http://127.0.0.1/v1/%tenant_id%", - public_url="http://internet.com/v1/%tenant_id%", - service_id="0" - )] - self.url_types = ["internal", "public", "admin"] - - def test_AuthData_json_serialization(self): - auth = logic_auth.AuthData(self.token, self.user) - data = json.loads(auth.to_json()) - expected = { - 'access': { - 'token': { - 'expires': '2000-01-31', - 'tenants': [{ - 'id': 'ten8', - 'name': 'The Tenant' - }], - 'id': 'abc123T', - 'tenant': { - 'id': 'ten8', - 'name': 'The Tenant' - } - }, - 'user': { - 'id': 'u1', - 'roles': [{ - 'name': 'Admin', - 'id': '1' - }], - 'name': 'john' - } - } - } - self.assertDictEqual(data, expected) - - def test_AuthData_xml_serialization(self): - auth = logic_auth.AuthData(self.token, self.user) - xml_str = auth.to_xml() - expected = ('<access xmlns=' - '"http://docs.openstack.org/identity/api/v2.0"><token expires=' - '"2000-01-31" id="abc123T"><tenant name="The Tenant" ' - 'id="ten8"/></token><user name="john" id="u1"><roles ' - 'xmlns="http://docs.openstack.org/identity/api/v2.0"><role ' - 'xmlns="http://docs.openstack.org/identity/api/v2.0" id="1" ' - 'name="Admin"/></roles></user></access>') - self.assertTrue(test_utils.XMLTools.xmlEqual(xml_str, expected)) - - def test_AuthData_json_catalog(self): - auth = logic_auth.AuthData(self.token, self.user, self.base_urls) - data = json.loads(auth.to_json()) - self.assertIn("access", data) - self.assertIn("serviceCatalog", data['access']) - catalog = data['access']['serviceCatalog'] - self.assertTrue(len(catalog) > 0) - endpoints = catalog[0]['endpoints'] - self.assertTrue(len(endpoints) > 1) - endpoint = endpoints[0] - self.assertIn("publicURL", endpoint) - self.assertIn("versionId", endpoint) - self.assertIn("tenantId", endpoint) - - endpoint = endpoints[1] - self.assertNotIn("versionId", endpoint) - - def test_AuthData_xml_catalog(self): - auth = logic_auth.AuthData(self.token, self.user, self.base_urls) - xml_str = auth.to_xml() - dom = etree.fromstring(xml_str) - xmlns = "http://docs.openstack.org/identity/api/v2.0" - catalog = dom.find("{%s}serviceCatalog" % xmlns) - service = catalog.find("{%s}service" % xmlns) - endpoint = service.find("{%s}endpoint" % xmlns) - self.assertIsNotNone("publicURL", endpoint.attrib) - self.assertIn("versionId", endpoint.attrib) - self.assertIn("tenantId", endpoint.attrib) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_migrations.py b/keystone/test/unit/test_migrations.py deleted file mode 100644 index 9284386a..00000000 --- a/keystone/test/unit/test_migrations.py +++ /dev/null @@ -1,136 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010-2011 OpenStack, LLC -# All Rights Reserved. -# -# 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. - -""" -Tests for database migrations. The test case runs a series of test cases to -ensure that migrations work properly both upgrading and downgrading, and that -no data loss occurs if possible. -""" - -import os -import unittest2 as unittest -import urlparse - -from migrate.versioning.repository import Repository -from sqlalchemy import * -from sqlalchemy.pool import NullPool - -import keystone.backends.sqlalchemy.migration as migration_api -from keystone.logic.types import fault - - -class TestMigrations(unittest.TestCase): - - """Test sqlalchemy-migrate migrations""" - - TEST_DATABASES = {'sqlite': 'sqlite:///migration.db'} - - REPOSITORY_PATH = os.path.abspath(os.path.join(os.path.abspath(__file__), - os.pardir, os.pardir, os.pardir, 'backends', - 'sqlalchemy', 'migrate_repo')) - REPOSITORY = Repository(REPOSITORY_PATH) - - def __init__(self, *args, **kwargs): - super(TestMigrations, self).__init__(*args, **kwargs) - - def setUp(self): - # Load test databases - self.engines = {} - for key, value in TestMigrations.TEST_DATABASES.items(): - self.engines[key] = create_engine(value, poolclass=NullPool) - - # We start each test case with a completely blank slate. - self._reset_databases() - - def tearDown(self): - # We destroy the test data store between each test case, - # and recreate it, which ensures that we have no side-effects - # from the tests - self._reset_databases() - - def _reset_databases(self): - for key, engine in self.engines.items(): - conn_string = TestMigrations.TEST_DATABASES[key] - conn_pieces = urlparse.urlparse(conn_string) - if conn_string.startswith('sqlite'): - # We can just delete the SQLite database, which is - # the easiest and cleanest solution - db_path = conn_pieces.path.strip('/') - if os.path.exists(db_path): - os.unlink(db_path) - # No need to recreate the SQLite DB. SQLite will - # create it for us if it's not there... - elif conn_string.startswith('mysql'): - # We can execute the MySQL client to destroy and re-create - # the MYSQL database, which is easier and less error-prone - # than using SQLAlchemy to do this via MetaData...trust me. - database = conn_pieces.path.strip('/') - loc_pieces = conn_pieces.netloc.split('@') - host = loc_pieces[1] - auth_pieces = loc_pieces[0].split(':') - user = auth_pieces[0] - password = "" - if len(auth_pieces) > 1: - if auth_pieces[1].strip(): - password = "-p%s" % auth_pieces[1] - sql = ("drop database if exists %(database)s; " - "create database %(database)s;") % locals() - cmd = ("mysql -u%(user)s %(password)s -h%(host)s " - "-e\"%(sql)s\"") % locals() - exitcode, out, err = execute(cmd) - self.assertEqual(0, exitcode) - - def test_walk_versions(self): - """ - Walks all version scripts for each tested database, ensuring - that there are no errors in the version scripts for each engine - """ - for key, engine in self.engines.items(): - self._walk_versions(TestMigrations.TEST_DATABASES[key]) - - def _walk_versions(self, sql_connection): - # Determine latest version script from the repo, then - # upgrade from 1 through to the latest, with no data - # in the databases. This just checks that the schema itself - # upgrades successfully. - - # Assert we are not under version control... - self.assertRaises(fault.DatabaseMigrationError, - migration_api.db_version, - sql_connection) - # Place the database under version control - print migration_api.version_control(sql_connection) - - cur_version = migration_api.db_version(sql_connection) - self.assertEqual(0, cur_version) - - for version in xrange(1, TestMigrations.REPOSITORY.latest + 1): - migration_api.upgrade(sql_connection, version) - cur_version = migration_api.db_version(sql_connection) - self.assertEqual(cur_version, version) - - # Now walk it back down to 0 from the latest, testing - # the downgrade paths. - for version in reversed( - xrange(0, TestMigrations.REPOSITORY.latest)): - migration_api.downgrade(sql_connection, version) - cur_version = migration_api.db_version(sql_connection) - self.assertEqual(cur_version, version) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_models.py b/keystone/test/unit/test_models.py deleted file mode 100644 index dd096aab..00000000 --- a/keystone/test/unit/test_models.py +++ /dev/null @@ -1,131 +0,0 @@ -import json -import unittest2 as unittest - -from keystone.models import AttrDict, Resource -from keystone.test import utils as testutils - - -class TestModels(unittest.TestCase): - '''Unit tests for keystone/models.py.''' - - def test_attrdict_class(self): - ad = AttrDict() - ad.id = 1 - self.assertEqual(ad.id, 1) - ad._sa_instance_state = AttrDict() - self.assertIsInstance(ad._sa_instance_state, AttrDict) - - def test_resource(self): - resource = Resource() - self.assertEquals(str(resource.__class__), - "<class 'keystone.models.Resource'>", - "Resource should be of instance " - "class keystone.models.Resource but instead " - "was '%s'" % str(resource.__class__)) - self.assertIsInstance(resource, dict, "") - resource._sa_instance_state = AttrDict() - self.assertIsInstance(resource._sa_instance_state, AttrDict) - - def test_resource_respresentation(self): - resource = Resource(id=1, name="John") - self.assertIn(resource.__repr__(), [ - "<Resource(id=1, name='John')>", - "<Resource(name='John', id=1)>"]) - self.assertIn(resource.__str__(), [ - "{'resource': {'name': 'John', 'id': 1}}", - "{'resource': {'id': 1, 'name': 'John'}}"]) - self.assertEqual(resource['resource']['id'], 1) - - def test_resource_static_properties(self): - resource = Resource(id=1, name="the resource", blank=None) - self.assertEquals(resource.id, 1) - self.assertEquals(resource.name, "the resource") - self.assertRaises(AttributeError, getattr, resource, - 'some_bad_property') - - def test_resource_keys(self): - resource = Resource(id=1, name="the resource", blank=None) - self.assertEquals(resource.id, 1) - self.assertEquals(resource['id'], 1) - - self.assertTrue('id' in resource) - self.assertTrue(hasattr(resource, 'id')) - - resource['dynamic'] = '1' - self.assertEquals(resource['dynamic'], '1') - - self.assertTrue('dynamic' in resource) - - def test_resource_dynamic_properties(self): - resource = Resource(id=1, name="the resource", blank=None) - resource["dynamic"] = "test" - self.assertEquals(resource["dynamic"], "test") - self.assertEquals(resource["name"], "the resource") - - def test_resource_json_serialization(self): - resource = Resource(id=1, name="the resource", blank=None) - json_str = resource.to_json() - d1 = json.loads(json_str) - d2 = json.loads('{"resource": {"name": "the resource", "id": 1}}') - self.assertDictEqual(d1, d2) - - def test_resource_json_serialization_mapping(self): - resource = Resource(id=1, name="the resource", rolegrant_id=12) - json_str = resource.to_json(hints={"maps": {"refId": "rolegrant_id", - }}) - d1 = json.loads(json_str) - d2 = {"resource": {"name": "the resource", "id": 1, "refId": 12}} - self.assertDictEqual(d1, d2) - - def test_resource_json_serialization_types(self): - resource = Resource(id=1, name="the resource", bool=True, int=5) - json_str = resource.to_json(hints={"types": - [("bool", bool), ("int", int)]}) - d1 = json.loads(json_str) - d2 = {"resource": {"name": "the resource", "id": 1, "bool": True, - "int": 5}} - self.assertDictEqual(d1, d2) - - def test_resource_xml_serialization(self): - resource = Resource(id=1, name="the resource", blank=None) - xml_str = resource.to_xml() - self.assertTrue(testutils.XMLTools.xmlEqual(xml_str, - '<resource id="1" name="the resource"/>')) - - def test_resource_xml_serialization_mapping(self): - resource = Resource(id=1, name="the resource", rolegrant_id=12) - xml_str = resource.to_xml(hints={"maps": {"refId": "rolegrant_id", }}) - self.assertTrue(testutils.XMLTools.xmlEqual(xml_str, - '<resource id="1" name="the resource" refId="12"/>')) - - def test_resource_xml_deserialization(self): - resource = Resource.from_xml('<Resource blank="" id="1" \ - name="the resource"/>', - hints={ - "contract_attributes": ['id', 'name'], - "types": [("id", int)]}) - self.assertIsInstance(resource, Resource) - self.assertEquals(resource.id, 1) - self.assertEquals(resource.name, "the resource") - - def test_resource_json_deserialization(self): - resource = Resource.from_json('{"resource": {"name": "the resource", \ - "id": 1}}', - hints={ - "contract_attributes": ['id', 'name'], - "types": [("id", int)]}) - self.assertIsInstance(resource, Resource) - self.assertEquals(resource.id, 1) - self.assertEquals(resource.name, "the resource") - - def test_resource_inspection(self): - resource = Resource(id=1, name="the resource", blank=None) - self.assertFalse(resource.inspect()) - - def test_resource_validation(self): - resource = Resource(id=1, name="the resource", blank=None) - self.assertTrue(resource.validate()) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_models_endpoint.py b/keystone/test/unit/test_models_endpoint.py deleted file mode 100644 index 1f216010..00000000 --- a/keystone/test/unit/test_models_endpoint.py +++ /dev/null @@ -1,78 +0,0 @@ -import json -import unittest2 as unittest - -from keystone.models import Endpoint -from keystone.test import utils as testutils - - -class TestModelsEndpoint(unittest.TestCase): - '''Unit tests for keystone/models.py:Endpoint class.''' - - def test_endpoint(self): - endpoint = Endpoint() - self.assertEquals(str(endpoint.__class__), - "<class 'keystone.models.Endpoint'>", - "endpoint should be of instance " - "class keystone.models.Endpoint but instead " - "was '%s'" % str(endpoint.__class__)) - self.assertIsInstance(endpoint, dict, "") - - def test_endpoint_static_properties(self): - endpoint = Endpoint(id=1, name="the endpoint", enabled=True, - blank=None) - self.assertEquals(endpoint.id, 1) - self.assertEquals(endpoint.name, "the endpoint") - self.assertTrue(endpoint.enabled) - self.assertRaises(AttributeError, getattr, endpoint, - 'some_bad_property') - - def test_endpoint_properties(self): - endpoint = Endpoint(id=2, name="the endpoint", blank=None) - endpoint["dynamic"] = "test" - self.assertEquals(endpoint["dynamic"], "test") - - def test_endpoint_json_serialization(self): - endpoint = Endpoint(id=3, name="the endpoint", blank=None) - endpoint["dynamic"] = "test" - json_str = endpoint.to_json() - d1 = json.loads(json_str) - d2 = json.loads('{"endpoint": {"name": "the endpoint", \ - "id": 3, "dynamic": "test"}}') - self.assertDictEqual(d1, d2) - - def test_endpoint_xml_serialization(self): - endpoint = Endpoint(id=4, name="the endpoint", blank=None) - xml_str = endpoint.to_xml() - self.assertTrue(testutils.XMLTools.xmlEqual(xml_str, - '<endpoint name="the endpoint" id="4"/>')) - - def test_endpoint_json_deserialization(self): - endpoint = Endpoint.from_json('{"name": "the endpoint", "id": 5}', - hints={"contract_attributes": ['id', 'name']}) - self.assertIsInstance(endpoint, Endpoint) - self.assertEquals(endpoint.id, 5) - self.assertEquals(endpoint.name, "the endpoint") - - def test_endpoint_json_deserialization_rootless(self): - endpoint = Endpoint.from_json('{"endpoint": {"name": "the endpoint", \ - "id": 6}}', - hints={"contract_attributes": ['id', 'name']}) - self.assertIsInstance(endpoint, Endpoint) - self.assertEquals(endpoint.id, 6) - self.assertEquals(endpoint.name, "the endpoint") - - def test_endpoint_xml_deserialization(self): - endpoint = Endpoint(id=7, name="the endpoint", blank=None) - self.assertIsInstance(endpoint, Endpoint) - - def test_endpoint_inspection(self): - endpoint = Endpoint(id=8, name="the endpoint", blank=None) - self.assertFalse(endpoint.inspect()) - - def test_endpoint_validation(self): - endpoint = Endpoint(id=9, name="the endpoint", blank=None) - self.assertTrue(endpoint.validate()) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_models_endpoint_template.py b/keystone/test/unit/test_models_endpoint_template.py deleted file mode 100644 index 14841085..00000000 --- a/keystone/test/unit/test_models_endpoint_template.py +++ /dev/null @@ -1,79 +0,0 @@ -import json -import unittest2 as unittest - -from keystone.models import EndpointTemplate -from keystone.test import utils as testutils - - -class TestModelsEndpointTemplate(unittest.TestCase): - '''Unit tests for keystone/models.py:EndpointTemplate class.''' - - def test_endpointtemplate(self): - endpointtemplate = EndpointTemplate() - self.assertEquals(str(endpointtemplate.__class__), - "<class 'keystone.models.EndpointTemplate'>", - "endpointtemplate should be of instance " - "class keystone.models.EndpointTemplate but instead " - "was '%s'" % str(endpointtemplate.__class__)) - self.assertIsInstance(endpointtemplate, dict, "") - - def test_endpointtemplate_static_properties(self): - endpointtemplate = EndpointTemplate(id=1, name="the endpointtemplate", - enabled=True, blank=None) - self.assertEquals(endpointtemplate.id, 1) - self.assertEquals(endpointtemplate.name, "the endpointtemplate") - self.assertTrue(endpointtemplate.enabled) - self.assertEquals(endpointtemplate.admin_url, None) - self.assertRaises(AttributeError, getattr, endpointtemplate, - 'some_bad_property') - - def test_endpointtemplate_properties(self): - endpointtemplate = EndpointTemplate(id=1, name="the endpointtemplate", - blank=None) - endpointtemplate["dynamic"] = "test" - self.assertEquals(endpointtemplate["dynamic"], "test") - - def test_endpointtemplate_json_serialization(self): - endpointtemplate = EndpointTemplate(id=1, name="the endpointtemplate", - blank=None) - endpointtemplate["dynamic"] = "test" - json_str = endpointtemplate.to_json() - d1 = json.loads(json_str) - d2 = json.loads('{"endpointtemplate": {"name": "the endpointtemplate",\ - "id": 1, "dynamic": "test"}}') - self.assertDictEqual(d1, d2) - - def test_endpointtemplate_xml_serialization(self): - endpointtemplate = EndpointTemplate(id=1, name="the endpointtemplate", - blank=None) - xml_str = endpointtemplate.to_xml() - self.assertTrue(testutils.XMLTools.xmlEqual(xml_str, - '<endpointtemplate \ - name="the endpointtemplate" id="1"/>')) - - def test_endpointtemplate_json_deserialization(self): - endpointtemplate = EndpointTemplate.from_json('{"name": \ - "the endpointtemplate", "id": 1}', - hints={"contract_attributes": ['id', 'name']}) - self.assertIsInstance(endpointtemplate, EndpointTemplate) - self.assertEquals(endpointtemplate.id, 1) - self.assertEquals(endpointtemplate.name, "the endpointtemplate") - - def test_endpointtemplate_xml_deserialization(self): - endpointtemplate = EndpointTemplate(id=1, name="the endpointtemplate", - blank=None) - self.assertIsInstance(endpointtemplate, EndpointTemplate) - - def test_endpointtemplate_inspection(self): - endpointtemplate = EndpointTemplate(id=1, name="the endpointtemplate", - blank=None) - self.assertFalse(endpointtemplate.inspect()) - - def test_endpointtemplate_validation(self): - endpointtemplate = EndpointTemplate(id=1, name="the endpointtemplate", - blank=None) - self.assertTrue(endpointtemplate.validate()) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_models_role.py b/keystone/test/unit/test_models_role.py deleted file mode 100644 index 9c254484..00000000 --- a/keystone/test/unit/test_models_role.py +++ /dev/null @@ -1,115 +0,0 @@ -import json -import unittest2 as unittest - -from keystone.models import Role, UserRoleAssociation -from keystone.test import utils as testutils - - -class TestModelsRole(unittest.TestCase): - '''Unit tests for keystone/models.py:Role class.''' - - def test_role(self): - role = Role() - self.assertEquals(str(role.__class__), - "<class 'keystone.models.Role'>", - "role should be of instance " - "class keystone.models.Role but instead " - "was '%s'" % str(role.__class__)) - self.assertIsInstance(role, dict, "") - - def test_role_static_properties(self): - role = Role(id=1, name="the role", service_id=1, blank=None) - self.assertEquals(role.id, "1") - self.assertEquals(role.name, "the role") - self.assertEquals(role.service_id, "1") - self.assertEquals(role.description, None) - self.assertRaises(AttributeError, getattr, role, - 'some_bad_property') - - def test_role_properties(self): - role = Role(id=1, name="the role", blank=None) - role["dynamic"] = "test" - self.assertEquals(role["dynamic"], "test") - - def test_role_json_serialization(self): - role = Role(id=1, name="the role", blank=None) - role["dynamic"] = "test" - json_str = role.to_json() - d1 = json.loads(json_str) - d2 = {"role": {"name": "the role", "id": "1", "dynamic": "test"}} - self.assertDictEqual(d1, d2) - - def test_role_json_serialization_mapped(self): - role = Role(id=1, name="the role", - service_id="s1", - tenant_id="t1") - json_str = role.to_json() - d1 = json.loads(json_str) - d2 = {"role": {"name": "the role", "id": "1", "serviceId": "s1", - "tenantId": "t1"}} - self.assertDictEqual(d1, d2) - - def test_role_xml_serialization(self): - role = Role(id=1, name="the role", blank=None) - xml_str = role.to_xml() - expected = ('<role xmlns="http://docs.openstack.org/identity/api/v2.0"' - ' id="1" name="the role"/>') - self.assertTrue(testutils.XMLTools.xmlEqual(xml_str, expected), - msg='%s != %s' % (xml_str, expected)) - - def test_role_xml_serialization_mapping(self): - role = Role(id=1, name="the role", - service_id="s1", - tenant_id="t1") - xml_str = role.to_xml() - expected = ('<role xmlns="http://docs.openstack.org/identity/api/v2.0"' - ' id="1" name="the role" serviceId="s1" tenantId="t1"/>') - self.assertTrue(testutils.XMLTools.xmlEqual(xml_str, expected), - msg='%s != %s' % (xml_str, - expected)) - self.assertEquals(role.service_id, "s1") - - def test_role_json_deserialization(self): - role = Role.from_json('{"name": "the role", "id": "1"}', - hints={"contract_attributes": ['id', 'name']}) - self.assertIsInstance(role, Role) - self.assertEquals(role.id, "1") - self.assertEquals(role.name, "the role") - - role = Role.from_json('{"role":{"name": "r1", "serviceId": "s1"}}') - self.assertEquals(role.service_id, "s1") - - def test_role_json_deserialization_types(self): - role = Role.from_json('{"name": "the role", "id": 1}') - self.assertIsInstance(role, Role) - self.assertEquals(role.id, "1", - "'id' should always be returned as a string") - self.assertEquals(role.name, "the role") - - def test_role_xml_deserialization(self): - role = Role(id=1, name="the role", blank=None) - self.assertIsInstance(role, Role) - - def test_role_inspection(self): - role = Role(id=1, name="the role", blank=None) - self.assertFalse(role.inspect()) - - def test_role_validation(self): - role = Role(id=1, name="the role", blank=None) - self.assertTrue(role.validate()) - - # - # UserRoleAssociation - # - def test_userroleassociation_json_serialization_mapped(self): - ura = UserRoleAssociation(id=1, role_id=1, user_id="u1", - tenant_id="t1") - json_str = ura.to_json() - d1 = json.loads(json_str) - d2 = {"role": {"id": 1, "roleId": 1, "userId": "u1", - "tenantId": "t1"}} - self.assertDictEqual(d1, d2) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_models_service.py b/keystone/test/unit/test_models_service.py deleted file mode 100644 index a7a73478..00000000 --- a/keystone/test/unit/test_models_service.py +++ /dev/null @@ -1,90 +0,0 @@ -import json -import unittest2 as unittest - -from keystone.logic.types import fault -from keystone.models import Service -from keystone.test import utils as testutils - - -class TestModelsService(unittest.TestCase): - '''Unit tests for keystone/models.py:Service class.''' - - def test_service(self): - service = Service() - self.assertEquals(str(service.__class__), - "<class 'keystone.models.Service'>", - "service should be of instance " - "class keystone.models.Service but instead " - "was '%s'" % str(service.__class__)) - self.assertIsInstance(service, dict, "") - - def test_service_static_properties(self): - service = Service(id=1, name="the service", type="compute", blank=None) - self.assertEquals(service.id, "1") - self.assertEquals(service.name, "the service") - self.assertRaises(AttributeError, getattr, service, - 'some_bad_property') - - def test_service_properties(self): - service = Service(id=1, name="the service", type="compute", blank=None) - service["dynamic"] = "test" - self.assertEquals(service["dynamic"], "test") - - def test_service_json_serialization(self): - service = Service(id=1, name="the service", type="compute", blank=None) - service["dynamic"] = "test" - json_str = service.to_json() - d1 = json.loads(json_str) - d2 = json.loads('{"OS-KSADM:service": {"name": "the service", \ - "id": "1", "dynamic": "test", "type": "compute"}}') - self.assertDictEqual(d1, d2) - - def test_service_xml_serialization(self): - service = Service(id=1, name="the service", type="compute", blank=None) - xml_str = service.to_xml() - self.assertTrue(testutils.XMLTools.xmlEqual(xml_str, - '<service \ - xmlns="http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0" \ - id="1" name="the service" type="compute"/>')) - - def test_service_json_deserialization(self): - service = Service.from_json('{"name": "the service", "id": 1,\ - "type": "compute"}', - hints={ - "contract_attributes": ['id', 'name'], - "types": [("id", int)]}) - self.assertIsInstance(service, Service) - self.assertEquals(service.id, 1) - self.assertEquals(service.name, "the service") - - def test_service_xml_deserialization(self): - service = Service(id=1, name="the service", blank=None) - self.assertIsInstance(service, Service) - - def test_service_inspection(self): - service = Service(id=1, name="the service", type="compute") - self.assertFalse(service.inspect()) - - def test_service_validation_from_json(self): - self.assertRaises(fault.BadRequestFault, Service.from_json, - '{"name": "", "id": 1}') - self.assertRaises(fault.BadRequestFault, Service.from_json, - '{"type": None, "id": 1}') - - def test_service_validation_from_xml(self): - self.assertRaises(fault.BadRequestFault, Service.from_xml, - '<service \ - xmlns="http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0" \ - id="1" name="the service" type=""/>') - self.assertRaises(fault.BadRequestFault, Service.from_xml, - '<service \ - xmlns="http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0" \ - id="1" name="" type="compute"/>') - - def test_service_validation(self): - service = Service(id=1, name="the service", type="compute") - self.assertTrue(service.validate()) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_models_services.py b/keystone/test/unit/test_models_services.py deleted file mode 100644 index 3ceb2385..00000000 --- a/keystone/test/unit/test_models_services.py +++ /dev/null @@ -1,47 +0,0 @@ -import json -import unittest2 as unittest - -from keystone.logic.types import fault -from keystone.models import Service, Services -from keystone.test import utils as testutils - - -class TestModelsServices(unittest.TestCase): - '''Unit tests for keystone/models.py:Service class.''' - - def test_services(self): - services = Services(None, None) - self.assertEquals(str(services.__class__), - "<class 'keystone.models.Services'>", - "services should be of instance " - "class keystone.models.Services but instead " - "was '%s'" % str(services.__class__)) - - def test_xml_serialization(self): - service_list = [Service(name="keystone", type="identity"), - Service(name="nova", type="compute"), - Service(name="glance", type="image-service")] - services = Services(service_list, {}) - xml_str = services.to_xml() - self.assertTrue(testutils.XMLTools.xmlEqual(xml_str, - '<services xmlns="http://docs.openstack.org/identity/api/ext/\ -OS-KSADM/v1.0"><service xmlns="http://docs.openstack.org/identity/api/ext/\ -OS-KSADM/v1.0" type="identity" name="keystone"/><service xmlns="http://\ -docs.openstack.org/identity/api/ext/OS-KSADM/v1.0" type="compute" name="nova"\ -/><service xmlns="http://docs.openstack.org/identity/api/ext/OS-KSADM/v1.0" \ -type="image-service" name="glance"/></services>')) - - def test_json_serialization(self): - service_list = [Service(name="keystone", type="identity"), - Service(name="nova", type="compute"), - Service(name="glance", type="image-service")] - services = Services(service_list, {}) - json_str = services.to_json() - self.assertEqual(json_str, - '{"OS-KSADM:services": [{"type": "identity", "name": \ -"keystone"}, {"type": "compute", "name": "nova"}, {"type": "image-service", \ -"name": "glance"}], "OS-KSADM:services_links": []}') - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_models_tenant.py b/keystone/test/unit/test_models_tenant.py deleted file mode 100644 index 9f8e7fa0..00000000 --- a/keystone/test/unit/test_models_tenant.py +++ /dev/null @@ -1,167 +0,0 @@ -import json -import unittest2 as unittest - -from keystone.models import Tenant -from keystone.test import utils as testutils - - -class TestModelsTenant(unittest.TestCase): - '''Unit tests for keystone/models.py:Tenant class.''' - - def test_tenant(self): - tenant = Tenant() - self.assertEquals(str(tenant.__class__), - "<class 'keystone.models.Tenant'>", - "tenant should be of instance " - "class keystone.models.Tenant but instead " - "was '%s'" % str(tenant.__class__)) - self.assertIsInstance(tenant, dict, "") - - def test_tenant_static_properties(self): - tenant = Tenant(id=1, name="the tenant", enabled=True, blank=None) - self.assertEquals(tenant.id, "1") - self.assertEquals(tenant.name, "the tenant") - self.assertTrue(tenant.enabled) - self.assertEquals(tenant.description, None) - self.assertRaises(AttributeError, getattr, tenant, 'some_bad_property') - - def test_tenant_properties(self): - tenant = Tenant(id=2, name="the tenant", blank=None) - tenant["dynamic"] = "test" - self.assertEquals(tenant["dynamic"], "test") - - def test_tenant_initialization(self): - tenant = Tenant(id=3, name="the tenant", enabled=True, blank=None) - self.assertTrue(tenant.enabled) - - tenant = Tenant(id=35, name="the tenant", enabled=0, blank=None) - self.assertEquals(tenant.enabled, False) - - json_str = tenant.to_json() - d1 = json.loads(json_str) - self.assertIn('tenant', d1) - self.assertIn('enabled', d1['tenant']) - self.assertEquals(d1['tenant']['enabled'], False) - - tenant = Tenant(id=36, name="the tenant", enabled=False, blank=None) - self.assertEquals(tenant.enabled, False) - - def test_tenant_json_serialization(self): - tenant = Tenant(id=3, name="the tenant", enabled=True, blank=None) - tenant["dynamic"] = "test" - json_str = tenant.to_json() - - d1 = json.loads(json_str) - d2 = {"tenant": {"name": "the tenant", "id": "3", "enabled": True, - "dynamic": "test"}} - self.assertDictEqual(d1, d2) - - def test_tenant_xml_serialization(self): - tenant = Tenant(id=4, name="the tenant", description="X", blank=None) - xml_str = tenant.to_xml() - self.assertTrue(testutils.XMLTools.xmlEqual(xml_str, - '<tenant \ - xmlns="http://docs.openstack.org/identity/api/v2.0" \ - id="4" name="the tenant">\ - <description>X</description></tenant>')) - - def test_resource_json_serialization_mapping(self): - tenant = Tenant(id=1, name="the tenant", rolegrant_id=12) - json_str = tenant.to_json(hints={"maps": {"refId": "rolegrant_id", }}) - d1 = json.loads(json_str) - d2 = {"tenant": {"name": "the tenant", "id": "1", "refId": 12}} - self.assertDictEqual(d1, d2) - - def test_tenant_json_serialization_types(self): - tenant = Tenant(id=1, name="the tenant", bool=True, int=5) - json_str = tenant.to_json(hints={"types": - [("bool", bool), ("int", int)]}) - d1 = json.loads(json_str) - d2 = {"tenant": {"name": "the tenant", "id": "1", "bool": True, - "int": 5}} - self.assertDictEqual(d1, d2) - - def test_tenant_xml_serialization_mapping(self): - tenant = Tenant(id=1, name="the resource", rolegrant_id=12) - xml_str = tenant.to_xml(hints={"maps": {"refId": "rolegrant_id", }}) - self.assertTrue(testutils.XMLTools.xmlEqual(xml_str, - '<tenant xmlns="http://docs.openstack.org/identity/api/v2.0"\ - id="1" name="the resource" refId="12"></tenant>')) - - def test_tenant_json_deserialization(self): - tenant = Tenant.from_json('{"tenant": {"name": "the tenant",\ - "id": 5, "extra": "some data"}}', - hints={"contract_attributes": ['id', 'name']}) - self.assertIsInstance(tenant, Tenant) - self.assertEquals(tenant.id, 5) - self.assertEquals(tenant.name, "the tenant") - - def test_tenant_xml_deserialization(self): - tenant = Tenant.from_xml('<tenant \ - xmlns="http://docs.openstack.org/identity/api/v2.0" \ - enabled="true" id="6" name="the tenant">\ - <description>qwerty text</description></tenant>', - hints={ - "contract_attributes": ['id', 'name'], - "types": [("id", int), - ("description", str)]}) - self.assertIsInstance(tenant, Tenant) - self.assertEquals(tenant.id, 6) - self.assertEquals(tenant.name, "the tenant") - self.assertEquals(tenant.description, "qwerty text") - - def test_tenant_xml_deserialization_hintless(self): - tenant = Tenant.from_xml('<tenant \ - xmlns="http://docs.openstack.org/identity/api/v2.0" \ - enabled="none" id="7" name="the tenant">\ - <description>qwerty text</description></tenant>') - self.assertIsInstance(tenant, Tenant) - self.assertEquals(tenant.id, "7") - self.assertEquals(tenant.name, "the tenant") - self.assertEquals(tenant.description, "qwerty text") - - def test_tenant_inspection(self): - tenant = Tenant(id=8, name="the tenant", blank=None) - self.assertFalse(tenant.inspect()) - - def test_tenant_validation(self): - tenant = Tenant(id=9, name="the tenant", blank=None) - self.assertTrue(tenant.validate()) - - def test_tenant_description_values(self): - tenant = Tenant(id=10, name="the tenant") - self.assertIsNone(tenant.description, - "Uninitialized description should be None") - xml = tenant.to_dom() - desc = xml.find("{http://docs.openstack.org/identity/api/v2.0}" - "description") - self.assertIsNone(desc, - "Uninitialized description should not exist in xml") - - tenant = Tenant(id=10, name="the tenant", description=None) - self.assertIsNone(tenant.description, - "Description initialized to None should be None") - xml = tenant.to_dom() - desc = xml.find("{http://docs.openstack.org/identity/api/v2.0}" - "description") - self.assertIsNone(desc, - "Uninitialized description should not exist in xml") - - tenant = Tenant(id=10, name="the tenant", description='') - self.assertEquals(tenant.description, '', - 'Description initialized to empty string should be empty string') - xml = tenant.to_dom() - desc = xml.find("description") - self.assertEquals(desc.text, '', - "Blank Description should show as blank tag in xml") - - tenant = Tenant(id=10, name="the tenant", description=None) - xml = tenant.to_xml(hints={"tags": ["description"]}) - xml = tenant.to_dom() - desc = xml.find("description") - self.assertEquals(desc, None, - "'None' Description should show as empty tag in xml") - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_models_token.py b/keystone/test/unit/test_models_token.py deleted file mode 100644 index 06affd9c..00000000 --- a/keystone/test/unit/test_models_token.py +++ /dev/null @@ -1,70 +0,0 @@ -import json -from lxml import etree -import unittest2 as unittest - -from keystone.models import Token -from keystone.test import utils as testutils - - -class TestModelsToken(unittest.TestCase): - '''Unit tests for keystone/models.py:Token class.''' - - def test_token(self): - token = Token() - self.assertEquals(str(token.__class__), - "<class 'keystone.models.Token'>", - "token should be of instance " - "class keystone.models.Token but instead " - "was '%s'" % str(token.__class__)) - self.assertIsInstance(token, dict, "") - - def test_token_static_properties(self): - token = Token(id=1, name="the token", enabled=True, blank=None) - self.assertEquals(token.id, 1) - self.assertEquals(token.name, "the token") - self.assertTrue(token.enabled) - self.assertRaises(AttributeError, getattr, token, - 'some_bad_property') - - def test_token_properties(self): - token = Token(id=1, name="the token", blank=None) - token["dynamic"] = "test" - self.assertEquals(token["dynamic"], "test") - - def test_token_json_serialization(self): - token = Token(id=1, name="the token", blank=None) - token["dynamic"] = "test" - json_str = token.to_json() - d1 = json.loads(json_str) - d2 = json.loads('{"token": {"name": "the token", \ - "id": 1, "dynamic": "test"}}') - self.assertDictEqual(d1, d2) - - def test_token_xml_serialization(self): - token = Token(id=1, name="the token", blank=None) - xml_str = token.to_xml() - self.assertTrue(testutils.XMLTools.xmlEqual(xml_str, - '<token id="1" name="the token"/>')) - - def test_token_json_deserialization(self): - token = Token.from_json('{"name": "the token", "id": 1}', - hints={"contract_attributes": ['id', 'name']}) - self.assertIsInstance(token, Token) - self.assertEquals(token.id, 1) - self.assertEquals(token.name, "the token") - - def test_token_xml_deserialization(self): - token = Token(id=1, name="the token", blank=None) - self.assertIsInstance(token, Token) - - def test_token_inspection(self): - token = Token(id=1, name="the token", blank=None) - self.assertFalse(token.inspect()) - - def test_token_validation(self): - token = Token(id=1, name="the token", blank=None) - self.assertTrue(token.validate()) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_models_user.py b/keystone/test/unit/test_models_user.py deleted file mode 100644 index 1a3f7c4f..00000000 --- a/keystone/test/unit/test_models_user.py +++ /dev/null @@ -1,69 +0,0 @@ -import json -from lxml import etree -import unittest2 as unittest - -from keystone.models import User -from keystone.test import utils as testutils - - -class TestModelsUser(unittest.TestCase): - '''Unit tests for keystone/models.py:User class.''' - - def test_user(self): - user = User() - self.assertEquals(str(user.__class__), - "<class 'keystone.models.User'>", - "user should be of instance " - "class keystone.models.User but instead " - "was '%s'" % str(user.__class__)) - self.assertIsInstance(user, dict, "") - - def test_user_static_properties(self): - user = User(id=1, name="the user", blank=None) - self.assertEquals(user.id, 1) - self.assertEquals(user.name, "the user") - self.assertRaises(AttributeError, getattr, user, - 'some_bad_property') - - def test_user_properties(self): - user = User(id=1, name="the user", blank=None) - user["dynamic"] = "test" - self.assertEquals(user["dynamic"], "test") - - def test_user_json_serialization(self): - user = User(id=1, name="the user", blank=None) - user["dynamic"] = "test" - json_str = user.to_json() - d1 = json.loads(json_str) - d2 = json.loads('{"user": {"name": "the user", \ - "id": 1, "dynamic": "test"}}') - self.assertDictEqual(d1, d2) - - def test_user_xml_serialization(self): - user = User(id=1, name="the user", blank=None) - xml_str = user.to_xml() - self.assertTrue(testutils.XMLTools.xmlEqual(xml_str, - '<user name="the user" id="1"/>')) - - def test_user_json_deserialization(self): - user = User.from_json('{"name": "the user", "id": 1}', - hints={"contract_attributes": ['id', 'name']}) - self.assertIsInstance(user, User) - self.assertEquals(user.id, 1) - self.assertEquals(user.name, "the user") - - def test_user_xml_deserialization(self): - user = User(id=1, name="the user", blank=None) - self.assertIsInstance(user, User) - - def test_user_inspection(self): - user = User(id=1, name="the user", blank=None) - self.assertFalse(user.inspect()) - - def test_user_validation(self): - user = User(id=1, name="the user", blank=None) - self.assertTrue(user.validate()) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_normalizingfilter.py b/keystone/test/unit/test_normalizingfilter.py deleted file mode 100644 index 95b7ea93..00000000 --- a/keystone/test/unit/test_normalizingfilter.py +++ /dev/null @@ -1,94 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-2011 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 unittest2 as unittest -from keystone.frontends.normalizer import NormalizingFilter - - -class MockWsgiApp(object): - - def __init__(self): - pass - - def __call__(self, env, start_response): - pass - - -def _start_response(): - pass - - -class NormalizingFilterTest(unittest.TestCase): - - def setUp(self): - self.filter = NormalizingFilter(MockWsgiApp(), {}) - - def test_trailing_slash(self): - env = {'PATH_INFO': '/v2.0/'} - self.filter(env, _start_response) - self.assertEqual('/', env['PATH_INFO']) - - def test_remove_trailing_slash_from_empty_path(self): - """Empty paths should still equate to a slash""" - env = {'PATH_INFO': '/'} - self.filter(env, _start_response) - self.assertEqual('/', env['PATH_INFO']) - - def test_no_extension(self): - env = {'PATH_INFO': '/v2.0/someresource'} - self.filter(env, _start_response) - self.assertEqual('/someresource', env['PATH_INFO']) - self.assertEqual('application/json', env['HTTP_ACCEPT']) - - def test_xml_extension(self): - env = {'PATH_INFO': '/v2.0/someresource.xml'} - self.filter(env, _start_response) - self.assertEqual('/someresource', env['PATH_INFO']) - self.assertEqual('application/xml', env['HTTP_ACCEPT']) - - def test_atom_extension(self): - env = {'PATH_INFO': '/v2.0/someresource.atom'} - self.filter(env, _start_response) - self.assertEqual('/someresource', env['PATH_INFO']) - self.assertEqual('application/atom+xml', env['HTTP_ACCEPT']) - - def test_json_extension(self): - env = {'PATH_INFO': '/v2.0/someresource.json'} - self.filter(env, _start_response) - self.assertEqual('/someresource', env['PATH_INFO']) - self.assertEqual('application/json', env['HTTP_ACCEPT']) - - def test_version_header(self): - env = {'PATH_INFO': '/someresource', - 'HTTP_ACCEPT': - 'application/vnd.openstack.identity+xml;version=2.0'} - self.filter(env, _start_response) - self.assertEqual('/someresource', env['PATH_INFO']) - self.assertEqual('application/xml', env['HTTP_ACCEPT']) - self.assertEqual('2.0', env['KEYSTONE_API_VERSION']) - - def test_extension_overrides_header(self): - env = { - 'PATH_INFO': '/v2.0/someresource.json', - 'HTTP_ACCEPT': 'application/xml'} - self.filter(env, _start_response) - self.assertEqual('/someresource', env['PATH_INFO']) - self.assertEqual('application/json', env['HTTP_ACCEPT']) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_server.py b/keystone/test/unit/test_server.py deleted file mode 100644 index 5ecd46f7..00000000 --- a/keystone/test/unit/test_server.py +++ /dev/null @@ -1,88 +0,0 @@ -import unittest2 as unittest -from StringIO import StringIO -import datetime -import webob -from lxml import etree -import json - -from keystone import utils -from keystone.logic.types import auth -import keystone.logic.types.fault as fault - - -class TestServer(unittest.TestCase): - '''Unit tests for server.py.''' - - request = None - auth_data = None - - def setUp(self): - environ = {'wsgi.url_scheme': 'http'} - self.request = webob.Request(environ) - self.auth_data = auth.ValidateData(auth.Token(datetime.date.today(), - "2231312"), auth.User("id", "username", "12345", "aTenant")) - - #def tearDown(self): - - def test_is_xml_response(self): - self.assertFalse(utils.is_xml_response(self.request)) - self.request.headers["Accept"] = "application/xml" - self.request.content_type = "application/json" - self.assertTrue(utils.is_xml_response(self.request)) - - def test_send_result_xml(self): - self.request.headers["Accept"] = "application/xml" - response = utils.send_result(200, self.request, self.auth_data) - - self.assertTrue(response.headers['content-type'] == - "application/xml; charset=UTF-8") - xml = etree.fromstring(response.unicode_body) - - user = xml.find("{http://docs.openstack.org/identity/api/v2.0}user") - token = xml.find("{http://docs.openstack.org/identity/api/v2.0}token") - - self.assertTrue(user.get("name"), "username") - self.assertTrue(user.get("id"), "id") - self.assertTrue(user.get("tenantId"), '12345') - self.assertTrue(token.get("id"), '2231312') - self.assertTrue(token.get("expires"), datetime.date.today()) - - def test_send_result_json(self): - self.request.headers["Accept"] = "application/json" - response = utils.send_result(200, self.request, self.auth_data) - self.assertTrue(response.headers['content-type'] == - "application/json; charset=UTF-8") - dict = json.loads(response.unicode_body) - self.assertTrue(dict['access']['user']['id'], 'id') - self.assertTrue(dict['access']['user']['name'], 'username') - self.assertTrue(dict['access']['user']['tenantId'], '12345') - self.assertTrue(dict['access']['token']['id'], '2231312') - self.assertTrue(dict['access']['token']['expires'], - datetime.date.today()) - - def test_get_auth_token(self): - self.request.headers["X-Auth-Token"] = "Test token" - self.assertTrue(utils.get_auth_token(self.request), "Test Token") - - def test_get_normalized_request_content_exception(self): - self.assertRaises(fault.IdentityFault, - utils.get_normalized_request_content, None, self.request) - - def test_get_normalized_request_content_xml(self): - self.request.environ["CONTENT_TYPE"] = "application/xml" - auth.AuthWithPasswordCredentials("username", "password", "1") - body = '<?xml version="1.0" encoding="UTF-8"?> \ - <auth xmlns="http://docs.openstack.org/identity/api/v2.0">\ - <passwordCredentials \ - xmlns="http://docs.openstack.org/identity/api/v2.0" \ - password="secret" username="disabled" \ - /></auth>' - str = StringIO() - str.write(body) - self.request.environ["wsgi.input"] = str - self.request.environ["CONTENT_LENGTH"] = str.len - #TODO: I THINK THIS belongs in a test for auth.py. - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_service_logic.py b/keystone/test/unit/test_service_logic.py deleted file mode 100644 index d73d0e90..00000000 --- a/keystone/test/unit/test_service_logic.py +++ /dev/null @@ -1,65 +0,0 @@ -import datetime as dt -import unittest2 as unittest - -import keystone.logic.service as service -from keystone.test.unit.base import ServiceAPITest, AdminAPITest -from keystone.logic.types.fault import ItemNotFoundFault, UnauthorizedFault -from keystone.logic.types.auth import ValidateData - - -class TestServiceLogic(AdminAPITest): - """Unit tests for logic/service.py.""" - def __init__(self, *args, **kwargs): - super(TestServiceLogic, self).__init__(*args, **kwargs) - self.api_class = service.IdentityService - - def setUp(self): - super(TestServiceLogic, self).setUp() - user_attrs = {'id': 'test_user', - 'password': 'test_pass', - 'email': 'test_user@example.com', - 'enabled': True, - 'tenant_id': 'tenant1'} - self.test_user = self.fixture_create_user(**user_attrs) - - def test_get_user(self): - user_id = self.test_user["id"] - user = self.api.get_user(self.admin_token_id, user_id) - # The returned user object is a different type: - # keystone.logic.types.user.User_Update - self.assertEqual(self.test_user["email"], user.email) - self.assertEqual(self.test_user["enabled"], user.enabled) - self.assertEqual(self.test_user["id"], user.id) - self.assertEqual(self.test_user["tenant_id"], user.tenant_id) - - def test_require_admin(self): - user_id = self.test_user["id"] - self.assertRaises(UnauthorizedFault, self.api.get_user, - self.auth_token_id, user_id) - - def test_require_service_admin(self): - self.assertRaises(UnauthorizedFault, self.api.validate_token, - self.auth_token_id, "any_id") - - def test_has_admin_role(self): - self.assertTrue(self.api.has_admin_role(self.admin_token_id)) - self.assertFalse(self.api.has_admin_role(self.auth_token_id)) - - def test_validate_token(self): - data = self.api.validate_token(self.admin_token_id, self.auth_token_id) - self.assertTrue(isinstance(data, ValidateData)) - - def test_remove_role_from_user(self): - auth_userid = self.auth_user["id"] - regular_role_id = self.role_fixtures[0]["id"] - admin_role_id = self.role_fixtures[1]["id"] - # Attempting to remove the admin role should raise an error. - self.assertRaises(ItemNotFoundFault, self.api.remove_role_from_user, - self.admin_token_id, auth_userid, admin_role_id) - # This should run without error - self.api.remove_role_from_user(self.admin_token_id, - auth_userid, regular_role_id) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_utils.py b/keystone/test/unit/test_utils.py deleted file mode 100644 index 338a12c7..00000000 --- a/keystone/test/unit/test_utils.py +++ /dev/null @@ -1,89 +0,0 @@ -import json -import unittest2 as unittest - -from keystone import utils - - -class TestStringEmpty(unittest.TestCase): - """Unit tests for string functions of utils.py.""" - - def test_is_empty_for_a_valid_string(self): - self.assertFalse(utils.is_empty_string('asdfgf')) - - def test_is_empty_for_a_blank_string(self): - self.assertTrue(utils.is_empty_string('')) - - def test_is_empty_for_none(self): - self.assertTrue(utils.is_empty_string(None)) - - def test_is_empty_for_a_number(self): - self.assertFalse(utils.is_empty_string(0)) - - -class TestCredentialDetection(unittest.TestCase): - """Unit tests for credential type detection""" - - def test_detects_passwordCredentials(self): - self.content_type = "application/xml" - self.body = '<auth '\ - 'xmlns="http://docs.openstack.org/identity/api/v2.0">'\ - '<passwordCredentials/></auth>' - self.assertEquals(utils.detect_credential_type(self), - "passwordCredentials") - - def test_detects_passwordCredentials_unwrapped(self): - self.content_type = "application/xml" - self.body = '<passwordCredentials '\ - 'xmlns="http://docs.openstack.org/identity/api/v2.0"/>' - self.assertEquals(utils.detect_credential_type(self), - "passwordCredentials") - - def test_detects_no_creds(self): - self.content_type = "application/xml" - self.body = '<auth '\ - 'xmlns="http://docs.openstack.org/identity/api/v2.0"/>' - self.assertRaises(Exception, utils.detect_credential_type, self) - - def test_detects_blank_creds(self): - self.content_type = "application/xml" - self.body = '' - self.assertRaises(Exception, utils.detect_credential_type, self) - - def test_detects_anyCredentials(self): - self.content_type = "application/xml" - self.body = '<auth '\ - 'xmlns="http://docs.openstack.org/identity/api/v2.0">'\ - '<anyCredentials/></auth>' - self.assertEquals(utils.detect_credential_type(self), - "anyCredentials") - - def test_detects_anyCredentials_json(self): - self.content_type = "application/json" - self.body = json.dumps({'auth': {'anyCredentials': {}}}) - self.assertEquals(utils.detect_credential_type(self), - "anyCredentials") - - def test_detects_anyUnwrappedCredentials_json(self): - self.content_type = "application/json" - self.body = json.dumps({'anyCredentials': {}}) - self.assertEquals(utils.detect_credential_type(self), - "anyCredentials") - - def test_detects_anyCredentials_with_tenant_json(self): - self.content_type = "application/json" - self.body = json.dumps({'auth': {'tenantId': '1000', - 'anyCredentials': {}}}) - self.assertEquals(utils.detect_credential_type(self), - "anyCredentials") - - def test_detects_skips_tenant_json(self): - self.content_type = "application/json" - self.body = json.dumps({'auth': {'tenantId': '1000'}}) - self.assertRaises(Exception, utils.detect_credential_type, self) - - self.body = json.dumps({'auth': {'tenantName': '1000'}}) - self.assertRaises(Exception, utils.detect_credential_type, self) - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/unit/test_wsgi.py b/keystone/test/unit/test_wsgi.py deleted file mode 100644 index 48fe028f..00000000 --- a/keystone/test/unit/test_wsgi.py +++ /dev/null @@ -1,71 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# Copyright 2010 OpenStack LLC. -# All Rights Reserved. -# -# 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. - -""" -Test WSGI basics and provide some helper functions for other WSGI tests. -""" - -import unittest2 as unittest -import routes -import webob - -from keystone.common import wsgi -from keystone.test.functional import common - - -class TestWsgi(unittest.TestCase): - - def test_debug(self): - - class Application(common.BlankApp): - """Dummy application to test debug.""" - - def __call__(self, environ, start_response): - start_response("200", [("X-Test", "checking")]) - return ['Test result'] - - application = wsgi.Debug(Application()) - result = webob.Request.blank('/').get_response(application) - self.assertEqual(result.body, "Test result") - - def test_router(self): - - class Application(common.BlankApp): - """Test application to call from router.""" - - def __call__(self, environ, start_response): - start_response("200", []) - return ['Router result'] - - class Router(wsgi.Router): - """Test router.""" - - def __init__(self): - mapper = routes.Mapper() - mapper.connect("/test", controller=Application()) - super(Router, self).__init__(mapper) - - result = webob.Request.blank('/test').get_response(Router()) - self.assertEqual(result.body, "Router result") - result = webob.Request.blank('/bad').get_response(Router()) - self.assertNotEqual(result.body, "Router result") - - -if __name__ == '__main__': - unittest.main() diff --git a/keystone/test/utils.py b/keystone/test/utils.py deleted file mode 100644 index 40883e24..00000000 --- a/keystone/test/utils.py +++ /dev/null @@ -1,61 +0,0 @@ -# pylint: disable=C0103 -import re -import socket -from lxml import etree - - -# pylint: disable=W0232 -class XMLTools(): - @staticmethod - def xmlEqual(xmlStr1, xmlStr2): - et1 = etree.XML(xmlStr1) - et2 = etree.XML(xmlStr2) - - let1 = [x for x in et1.iter()] - let2 = [x for x in et2.iter()] - - if len(let1) != len(let2): - return False - - while let1: - el = let1.pop(0) - foundEl = XMLTools.findMatchingElem(el, let2) - if foundEl is None: - return False - let2.remove(foundEl) - return True - - @staticmethod - def findMatchingElem(el, eList): - for elem in eList: - if XMLTools.elemsEqual(el, elem): - return elem - return None - - @staticmethod - def elemsEqual(el1, el2): - if el1.tag != el2.tag or el1.attrib != el2.attrib: - return False - # no requirement for text checking for now - #if el1.text != el2.text or el1.tail != el2.tail: - #return False - path1 = el1.getroottree().getpath(el1) - path2 = el2.getroottree().getpath(el2) - idxRE = re.compile(r"(\[\d*\])") - path1 = idxRE.sub("", path1) - path2 = idxRE.sub("", path2) - if path1 != path2: - return False - - return True - - -def get_unused_port(): - """ - Returns an unused port on localhost. - """ - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.bind(('localhost', 0)) - addr, port = s.getsockname() # pylint: disable=W0612 - s.close() - return port diff --git a/keystone/token/__init__.py b/keystone/token/__init__.py new file mode 100644 index 00000000..bf971a5a --- /dev/null +++ b/keystone/token/__init__.py @@ -0,0 +1 @@ +from keystone.token.core import * diff --git a/keystone/contrib/extensions/service/raxgrp/__init__.py b/keystone/token/backends/__init__.py index e69de29b..e69de29b 100644 --- a/keystone/contrib/extensions/service/raxgrp/__init__.py +++ b/keystone/token/backends/__init__.py diff --git a/keystone/token/backends/kvs.py b/keystone/token/backends/kvs.py new file mode 100644 index 00000000..990e107b --- /dev/null +++ b/keystone/token/backends/kvs.py @@ -0,0 +1,32 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import copy +import datetime + +from keystone.common import kvs +from keystone import exception +from keystone import token + + +class Token(kvs.Base, token.Driver): + # Public interface + def get_token(self, token_id): + token = self.db.get('token-%s' % token_id) + if (token and (token['expires'] is None + or token['expires'] > datetime.datetime.now())): + return token + else: + raise exception.TokenNotFound(token_id=token_id) + + def create_token(self, token_id, data): + data_copy = copy.deepcopy(data) + if 'expires' not in data: + data_copy['expires'] = self._get_default_expire_time() + self.db.set('token-%s' % token_id, data_copy) + return copy.deepcopy(data_copy) + + def delete_token(self, token_id): + try: + return self.db.delete('token-%s' % token_id) + except KeyError: + raise exception.TokenNotFound(token_id=token_id) diff --git a/keystone/token/backends/memcache.py b/keystone/token/backends/memcache.py new file mode 100644 index 00000000..b9c2cbe1 --- /dev/null +++ b/keystone/token/backends/memcache.py @@ -0,0 +1,58 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +from __future__ import absolute_import +import copy + +import memcache + +from keystone import config +from keystone import exception +from keystone import token +from keystone.common import utils + + +CONF = config.CONF +config.register_str('servers', group='memcache', default='localhost:11211') + + +class Token(token.Driver): + def __init__(self, client=None): + self._memcache_client = client + + @property + def client(self): + return self._memcache_client or self._get_memcache_client() + + def _get_memcache_client(self): + memcache_servers = CONF.memcache.servers.split(',') + self._memcache_client = memcache.Client(memcache_servers, debug=0) + return self._memcache_client + + def _prefix_token_id(self, token_id): + return 'token-%s' % token_id.encode('utf-8') + + def get_token(self, token_id): + ptk = self._prefix_token_id(token_id) + token = self.client.get(ptk) + if token is None: + raise exception.TokenNotFound(token_id=token_id) + + return token + + def create_token(self, token_id, data): + data_copy = copy.deepcopy(data) + ptk = self._prefix_token_id(token_id) + if 'expires' not in data_copy: + data_copy['expires'] = self._get_default_expire_time() + kwargs = {} + if data_copy['expires'] is not None: + expires_ts = utils.unixtime(data_copy['expires']) + kwargs['time'] = expires_ts + self.client.set(ptk, data_copy, **kwargs) + return copy.deepcopy(data_copy) + + def delete_token(self, token_id): + # Test for existence + self.get_token(token_id) + ptk = self._prefix_token_id(token_id) + return self.client.delete(ptk) diff --git a/keystone/token/backends/sql.py b/keystone/token/backends/sql.py new file mode 100644 index 00000000..b57f32a8 --- /dev/null +++ b/keystone/token/backends/sql.py @@ -0,0 +1,69 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import copy +import datetime + +from keystone.common import sql +from keystone import exception +from keystone import token + + +class TokenModel(sql.ModelBase, sql.DictBase): + __tablename__ = 'token' + id = sql.Column(sql.String(64), primary_key=True) + expires = sql.Column(sql.DateTime(), default=None) + extra = sql.Column(sql.JsonBlob()) + + @classmethod + def from_dict(cls, token_dict): + # shove any non-indexed properties into extra + extra = copy.deepcopy(token_dict) + data = {} + for k in ('id', 'expires'): + data[k] = extra.pop(k, None) + data['extra'] = extra + return cls(**data) + + def to_dict(self): + out = copy.deepcopy(self.extra) + out['id'] = self.id + out['expires'] = self.expires + return out + + +class Token(sql.Base, token.Driver): + # Public interface + def get_token(self, token_id): + session = self.get_session() + token_ref = session.query(TokenModel).filter_by(id=token_id).first() + now = datetime.datetime.now() + if token_ref and (not token_ref.expires or now < token_ref.expires): + return token_ref.to_dict() + else: + raise exception.TokenNotFound(token_id=token_id) + + def create_token(self, token_id, data): + data_copy = copy.deepcopy(data) + if 'expires' not in data_copy: + data_copy['expires'] = self._get_default_expire_time() + + token_ref = TokenModel.from_dict(data_copy) + token_ref.id = token_id + + session = self.get_session() + with session.begin(): + session.add(token_ref) + session.flush() + return token_ref.to_dict() + + def delete_token(self, token_id): + session = self.get_session() + token_ref = session.query(TokenModel)\ + .filter_by(id=token_id)\ + .first() + if not token_ref: + raise exception.TokenNotFound(token_id=token_id) + + with session.begin(): + session.delete(token_ref) + session.flush() diff --git a/keystone/token/core.py b/keystone/token/core.py new file mode 100644 index 00000000..8c816efe --- /dev/null +++ b/keystone/token/core.py @@ -0,0 +1,82 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +"""Main entry point into the Token service.""" + +import datetime + +from keystone import config +from keystone.common import manager + + +CONF = config.CONF +config.register_int('expiration', group='token', default=86400) + + +class Manager(manager.Manager): + """Default pivot point for the Token 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.token.driver) + + +class Driver(object): + """Interface description for a Token driver.""" + + def get_token(self, token_id): + """Get a token by id. + + :param token_id: identity of the token + :type token_id: string + :returns: token_ref + :raises: keystone.exception.TokenNotFound + + """ + raise NotImplementedError() + + def create_token(self, token_id, data): + """Create a token by id and data. + + :param token_id: identity of the token + :type token_id: string + :param data: dictionary with additional reference information + + :: + + { + expires='' + id=token_id, + user=user_ref, + tenant=tenant_ref, + metadata=metadata_ref + } + + :type data: dict + :returns: token_ref or None. + + """ + raise NotImplementedError() + + def delete_token(self, token_id): + """Deletes a token by id. + + :param token_id: identity of the token + :type token_id: string + :returns: None. + :raises: keystone.exception.TokenNotFound + + """ + raise NotImplementedError() + + def _get_default_expire_time(self): + """Determine when a token should expire based on the config. + + :returns: datetime.datetime object + + """ + expire_delta = datetime.timedelta(seconds=CONF.token.expiration) + return datetime.datetime.now() + expire_delta diff --git a/keystone/tools/__init__.py b/keystone/tools/__init__.py deleted file mode 100644 index 2baa83b1..00000000 --- a/keystone/tools/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# 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. diff --git a/keystone/tools/buffout.py b/keystone/tools/buffout.py deleted file mode 100644 index 84f313eb..00000000 --- a/keystone/tools/buffout.py +++ /dev/null @@ -1,89 +0,0 @@ -import sys -import StringIO - - -class OutputBuffer(): - """Replaces stdout with a StringIO buffer""" - - def __init__(self): - """Initialize output buffering""" - # True if the OutputBuffer is started - self.buffering = False - - # a reference to the current StringIO buffer - self._buffer = None - - # stale if buffering is True; access buffer contents using .read() - self._contents = None - - def __enter__(self): - self.start() - return self - - def __exit__(self, exc_type, exc_value, traceback): - if exc_type is None: - self.stop() - - def __unicode__(self): - return self._contents - - def __str__(self): - return str(self._contents) - - def start(self): - """Replace stdout with a fresh buffer""" - assert not self.buffering - - self.buffering = True - self.old_stdout = sys.stdout - - self.clear() - - def read(self): - """Read the current buffer""" - if self.buffering: - self._contents = self._buffer.getvalue() - - return self._contents - - def read_lines(self): - """Returns the current buffer as a list - - Excludes the last line, which is empty. - - """ - return self.read().split("\n")[:-1] - - def clear(self): - """Resets the current buffer""" - assert self.buffering - - # dispose of the previous buffer, if any - if self._buffer is not None: - self._buffer.close() - - self._contents = '' - self._buffer = StringIO.StringIO() - sys.stdout = self._buffer - - def flush(self): - """Flushes and clears the current buffer contents""" - assert self.buffering - - self.old_stdout.write(self._contents) - self._contents = '' - self._buffer = StringIO.StringIO() - - def stop(self): - """Stop buffering and pass the output along""" - assert self.buffering - - # preserve the contents prior to closing the StringIO - self.read() - self._buffer.close() - - sys.stdout = self.old_stdout - print self.__unicode__() - self.buffering = False - - return unicode(self) diff --git a/keystone/tools/tracer.py b/keystone/tools/tracer.py deleted file mode 100644 index 6345e2ae..00000000 --- a/keystone/tools/tracer.py +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env python -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# Copyright 2011 OpenStack LLC. -# All Rights Reserved. -# -# 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. -# -# Author: Ziad Sawalha (http://launchpad.net/~ziad-sawalha) -# Original maintained at: https://github.com/ziadsawalha/Python-tracer -# - -""" -OpenStack Call Tracing Tool - -To use this: -1. include the tools directory in your project (__init__.py and tracer.py) -2. import tools.tracer as early as possible into your module -3. add --trace-calls or -t to any argument parsers if you want the argument -to be shown in the usage page - -Usage: -Add this as early as possible in the first module called in your service:: - - import tools.tracer # @UnusedImport # module runs on import - -If a '-t' or '--trace-calls' parameter is found, it will trace calls to stdout -and space them to show the call graph. Exceptions (errors) will be displayed in -red. - -""" - -import linecache -import os -import sys - - -if '--trace-calls' in sys.argv or '-t' in sys.argv: - # Pop the trace arguments - for i in range(len(sys.argv)): - if sys.argv[i] in ['-t', '--trace-calls']: - sys.argv.pop(i) - - STACK_DEPTH = 0 - - # Calculate root project path - POSSIBLE_TOPDIR = os.path.normpath(os.path.join( - os.path.abspath(sys.argv[0]), - os.pardir, - os.pardir)) - - class ConsoleColors(): - HEADER = '\033[95m' - OKBLUE = '\033[94m' - OKGREEN = '\033[92m' - WARNING = '\033[93m' - FAIL = '\033[91m' - ENDC = '\033[0m' - - def localtrace(frame, event, arg): - if event == "return": - global STACK_DEPTH # pylint: disable=W0603 - STACK_DEPTH = STACK_DEPTH - 1 - elif event == "exception": - output_exception(frame, arg) - return None - - def selectivetrace(frame, event, arg): # pylint: disable=R0911 - global STACK_DEPTH # pylint: disable=W0603 - if event == "exception": - output_exception(frame, arg) - if event == 'call': - co = frame.f_code - func_name = co.co_name - if func_name == 'write': - # Ignore write() calls from print statements - return - func_filename = co.co_filename - if func_filename == "<string>": - return - if func_filename.startswith(("/System", "/Library", - "/usr/lib/py")): - return - if 'python' in func_filename: - return - if 'macosx' in func_filename: - return - output_call(frame, arg) - global STACK_DEPTH # pylint: disable=W0603 - STACK_DEPTH = STACK_DEPTH + 1 - return localtrace - return - - def output_exception(frame, arg): - exc_type, exc_value, exc_traceback = arg # pylint: disable=W0612 - if exc_type is StopIteration: - return - global STACK_DEPTH # pylint: disable=W0603 - global POSSIBLE_TOPDIR # pylint: disable=W0603 - co = frame.f_code - local_vars = frame.f_locals - func_name = co.co_name - line_no = frame.f_lineno - func_filename = co.co_filename - func_filename = func_filename.replace(POSSIBLE_TOPDIR, '') - sys.stdout.write('%s%sERROR: %s %s in %s of %s:%s%s\n' - % (ConsoleColors.FAIL, ' ' * STACK_DEPTH, - exc_type.__name__, exc_value, func_name, - func_filename, line_no, ConsoleColors.ENDC)) - filename = co.co_filename - if filename == "<stdin>": - filename = "%s.py" % __file__ - if (filename.endswith(".pyc") or - filename.endswith(".pyo")): - filename = filename[:-1] - line = linecache.getline(filename, line_no) - name = frame.f_globals["__name__"] - sys.stdout.write('%s%s %s:%s: %s%s\n' % - (ConsoleColors.HEADER, ' ' * STACK_DEPTH, name, - line_no, line.rstrip(), ConsoleColors.ENDC)) - - sys.stdout.write('%s locals: %s\n' - % (' ' * STACK_DEPTH, - local_vars)) - - def output_call(frame, arg): - caller = frame.f_back - - if caller: - global STACK_DEPTH # pylint: disable=W0603 - global POSSIBLE_TOPDIR # pylint: disable=W0603 - co = frame.f_code - func_name = co.co_name - func_line_no = frame.f_lineno - func_filename = co.co_filename - func_filename = func_filename.replace(POSSIBLE_TOPDIR, '') - caller_line_no = caller.f_lineno - caller_filename = caller.f_code.co_filename.replace( - POSSIBLE_TOPDIR, '') - if caller_filename == func_filename: - caller_filename = 'line' - sys.stdout.write('%s%s::%s:%s (from %s:%s)\n' % - (' ' * STACK_DEPTH, func_filename, func_name, func_line_no, - caller_filename, caller_line_no)) - - sys.stdout.write('Starting OpenStack call tracer\n') - sys.settrace(selectivetrace) diff --git a/keystone/utils.py b/keystone/utils.py deleted file mode 100755 index 7f32d323..00000000 --- a/keystone/utils.py +++ /dev/null @@ -1,312 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2010-2011 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. - -# pylint: disable=W1201 - -import functools -import json -import logging -from lxml import etree -import os -import sys -import tempfile -from webob import Response - -from keystone import config -import keystone.logic.types.fault as fault - -logger = logging.getLogger(__name__) # pylint: disable=C0103 - -CONF = config.CONF - - -def is_xml_response(req): - """Returns True when the request wants an XML response, False otherwise""" - return "Accept" in req.headers and "application/xml" in req.accept - - -def is_json_response(req): - """Returns True when the request wants a JSON response, False otherwise""" - return "Accept" in req.headers and "application/json" in req.accept - - -def is_atom_response(req): - """Returns True when the request wants an ATOM response, False otherwise""" - return "Accept" in req.headers and "application/atom+xml" in req.accept - - -def get_app_root(): - return os.path.abspath(os.path.dirname(__file__)) - - -def get_auth_token(req): - """Returns the auth token from request headers""" - return req.headers.get("X-Auth-Token") - - -def get_auth_user(req): - """Returns the auth user from request headers""" - return req.headers.get("X-Auth-User") - - -def get_auth_key(req): - """Returns the auth key from request headers""" - return req.headers.get("X-Auth-Key") - - -def wrap_error(func): - - # pylint: disable=W0703 - @functools.wraps(func) - def check_error(*args, **kwargs): - try: - return func(*args, **kwargs) - except Exception as err: - if isinstance(err, fault.IdentityFault): - return send_error(err.code, kwargs['req'], err) - elif isinstance(err, fault.ItemNotFoundFault): - return send_error(err.code, kwargs['req'], err) - else: - logging.exception(err) - return send_error(500, kwargs['req'], - fault.IdentityFault("Unhandled error", - str(err))) - return check_error - - -def get_normalized_request_content(model, req): - """Initialize a model from json/xml contents of request body""" - - if req.content_type == "application/xml": - return model.from_xml(req.body) - elif req.content_type == "application/json": - return model.from_json(req.body) - else: - logging.debug("Unsupported content-type passed: %s" % req.content_type) - raise fault.IdentityFault("I don't understand the content type", - code=415) - - -# pylint: disable=R0912 -def detect_credential_type(req): - """Return the credential type name by detecting them in json/xml body""" - - if req.content_type == "application/xml": - dom = etree.Element("root") - dom.append(etree.fromstring(req.body)) - root = dom.find("{http://docs.openstack.org/identity/api/v2.0}" - "auth") - if root is None: - # Try legacy without wrapper - creds = dom.find("*") - if creds: - logger.warning("Received old syntax credentials not wrapped in" - "'auth'") - else: - creds = root.find("*") - - if creds is None: - raise fault.BadRequestFault("Request is missing credentials") - - name = creds.tag - if "}" in name: - #trim away namespace if it is there - name = name[name.rfind("}") + 1:] - - return name - elif req.content_type == "application/json": - obj = json.loads(req.body) - if len(obj) == 0: - raise fault.BadRequestFault("Expecting 'auth'") - tag = obj.keys()[0] - if tag == "auth": - if len(obj[tag]) == 0: - raise fault.BadRequestFault("Expecting Credentials") - for key, value in obj[tag].iteritems(): # pylint: disable=W0612 - if key not in ['tenantId', 'tenantName']: - return key - raise fault.BadRequestFault("Credentials missing from request") - else: - credentials_type = tag - return credentials_type - else: - logging.debug("Unsupported content-type passed: %s" % req.content_type) - raise fault.IdentityFault("I don't understand the content type", - code=415) - - -def send_error(code, req, result): - content = None - - resp = Response() - resp.headers['content-type'] = None - resp.headers['Vary'] = 'X-Auth-Token' - resp.status = code - - if result: - if is_xml_response(req): - content = result.to_xml() - resp.headers['content-type'] = "application/xml" - else: - content = result.to_json() - resp.headers['content-type'] = "application/json" - - resp.content_type_params = {'charset': 'UTF-8'} - resp.unicode_body = content.decode('UTF-8') - - return resp - - -def send_result(code, req, result=None): - content = None - - resp = Response() - resp.headers['content-type'] = None - resp.headers['Vary'] = 'X-Auth-Token' - resp.status = code - if code > 399: - return resp - - if result: - if is_xml_response(req): - content = result.to_xml() - resp.headers['content-type'] = "application/xml" - else: - content = result.to_json() - resp.headers['content-type'] = "application/json" - resp.content_type_params = {'charset': 'UTF-8'} - resp.unicode_body = content.decode('UTF-8') - - return resp - - -def send_legacy_result(code, headers): - resp = Response() - if 'content-type' not in headers: - headers['content-type'] = "text/plain" - resp.headers['Vary'] = 'X-Auth-Token' - - headers['Vary'] = 'X-Auth-Token' - - resp.headers = headers - resp.status = code - if code > 399: - return resp - - resp.content_type_params = {'charset': 'UTF-8'} - - return resp - - -def import_module(module_name, class_name=None): - '''Import a class given a full module.class name or seperate - module and options. If no class_name is given, it is assumed to - be the last part of the module_name string.''' - if class_name is None: - try: - if module_name not in sys.modules: - __import__(module_name) - return sys.modules[module_name] - except ImportError as exc: - logging.exception(exc) - module_name, _separator, class_name = module_name.rpartition('.') - if not exc.args[0].startswith('No module named %s' % class_name): - raise - try: - if module_name not in sys.modules: - __import__(module_name) - return getattr(sys.modules[module_name], class_name) - except (ImportError, ValueError, AttributeError), exception: - logging.exception(exception) - raise ImportError(_('Class %s.%s cannot be found (%s)') % - (module_name, class_name, exception)) - - -def check_empty_string(value, message): - """ - Checks whether a string is empty and raises - fault for empty string. - """ - if is_empty_string(value): - raise fault.BadRequestFault(message) - - -def is_empty_string(value): - """ - Checks whether string is empty. - """ - if value is None: - return True - if not isinstance(value, basestring): - return False - if len(value.strip()) == 0: - return True - return False - - -def write_temp_file(txt): - """ - Writes the supplied text to a temporary file and returns the file path. - - When the file is no longer needed, it is up to the calling program to - delete it. - """ - fd, tmpname = tempfile.mkstemp() - os.close(fd) - with file(tmpname, "w") as fconf: - fconf.write(txt) - return tmpname - - -def opt_to_conf(options, create_temp=False): - """ - Takes a dict of options and either returns a string that represents the - equivalent CONF configuration file (when create_temp is False), or writes - the temp file and returns the name of that temp file. NOTE: it is up to - the calling program to delete the temp file when it is no longer needed. - """ - def parse_opt(options, section=None): - out = [] - subsections = [] - if section is None: - section = "DEFAULT" - # Create the section header - out.append("[%s]" % section) - for key, val in options.iteritems(): - if isinstance(val, dict): - # This is a subsection; parse recursively. - subsections.append(parse_opt(val, section=key)) - else: - out.append("%s = %s" % (key.replace("-", "_"), val)) - - # Add the subsections - for subsection in subsections: - out.append("") - out.append(subsection) - return "\n".join(out) - - txt = parse_opt(options) - if create_temp: - return write_temp_file(txt) - else: - return txt - - -def set_configuration(options): - """ Given a dict of options, populates the config.CONF module to match.""" - _config_file = opt_to_conf(options, create_temp=True) - CONF(config_files=[_config_file]) - os.remove(_config_file) diff --git a/keystone/version.py b/keystone/version.py deleted file mode 100644 index 0529b279..00000000 --- a/keystone/version.py +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (C) 2011 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. - -API_VERSION = "2.0" -API_VERSION_STATUS = "beta" -API_VERSION_DATE = "2011-11-19T00:00:00Z" - -RELEASE_VERSION = "2012.1" -RELEASE_VERSION_FINAL = False # becomes true at Release Candidate time - - -def canonical_version(): - return RELEASE_VERSION - - -def version(): - if RELEASE_VERSION_FINAL: - return RELEASE_VERSION - else: - return '%s-dev' % (RELEASE_VERSION) |