diff options
26 files changed, 181 insertions, 480 deletions
diff --git a/etc/keystone.conf.sample b/etc/keystone.conf.sample index 4c0327cf..a49a9a5e 100644 --- a/etc/keystone.conf.sample +++ b/etc/keystone.conf.sample @@ -128,7 +128,8 @@ # driver = keystone.token.backends.sql.Token # Controls the token construction, validation, and revocation operations. -# provider = keystone.token.providers.pki.Provider +# Core providers are keystone.token.providers.[pki|uuid].Provider +# provider = # Amount of time a token should remain valid (in seconds) # expiration = 86400 @@ -165,7 +166,8 @@ [signing] # Deprecated in favor of provider in the [token] section -#token_format = PKI +# Allowed values are PKI or UUID +#token_format = #certfile = /etc/keystone/pki/certs/signing_cert.pem #keyfile = /etc/keystone/pki/private/signing_key.pem diff --git a/keystone/assignment/backends/ldap.py b/keystone/assignment/backends/ldap.py index b1b3f99f..f8c81eae 100644 --- a/keystone/assignment/backends/ldap.py +++ b/keystone/assignment/backends/ldap.py @@ -55,20 +55,8 @@ class Assignment(assignment.Driver): self.project = ProjectApi(CONF) self.role = RoleApi(CONF) - self._identity_api = None - @property - def identity_api(self): - return self._identity_api - - @identity_api.setter - def identity_api(self, value): - self._identity_api = value - #TODO(ayoung): only left here to prevent unit test from breaking - #once we remove here. the getter and setter can be removed as well. - self._identity_api.driver.project = self.project - def get_project(self, tenant_id): return self._set_default_domain(self.project.get(tenant_id)) diff --git a/keystone/common/config.py b/keystone/common/config.py index b0a534f8..10c47a35 100644 --- a/keystone/common/config.py +++ b/keystone/common/config.py @@ -240,7 +240,7 @@ def configure(): # signing register_str( - 'token_format', group='signing', default="PKI") + 'token_format', group='signing', default=None) register_str( 'certfile', group='signing', diff --git a/keystone/common/sql/core.py b/keystone/common/sql/core.py index 7978fcc5..2d3114f2 100644 --- a/keystone/common/sql/core.py +++ b/keystone/common/sql/core.py @@ -243,9 +243,10 @@ class Base(object): def get_session(self, autocommit=True, expire_on_commit=False): """Return a SQLAlchemy session.""" - self._engine = self._engine or self.get_engine() - self._sessionmaker = self._sessionmaker or self.get_sessionmaker( - self._engine) + if not self._engine: + self._engine = self.get_engine() + self._sessionmaker = self.get_sessionmaker(self._engine) + register_global_engine_callback(self.clear_engine) return self._sessionmaker(autocommit=autocommit, expire_on_commit=expire_on_commit) @@ -300,6 +301,10 @@ class Base(object): autocommit=autocommit, expire_on_commit=expire_on_commit) + def clear_engine(self): + self._engine = None + self._sessionmaker = None + def handle_conflicts(type='object'): """Converts IntegrityError into HTTP 409 Conflict.""" diff --git a/keystone/common/wsgi.py b/keystone/common/wsgi.py index c7e30576..381f1ff0 100644 --- a/keystone/common/wsgi.py +++ b/keystone/common/wsgi.py @@ -583,7 +583,7 @@ def render_exception(error): body = {'error': { 'code': error.code, 'title': error.title, - 'message': str(error) + 'message': unicode(error) }} if isinstance(error, exception.AuthPluginException): body['error']['identity'] = error.authentication diff --git a/keystone/exception.py b/keystone/exception.py index db5f5005..5e1defba 100644 --- a/keystone/exception.py +++ b/keystone/exception.py @@ -13,10 +13,10 @@ # 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 re from keystone.common import config from keystone.common import logging +from keystone.openstack.common.gettextutils import _ # noqa CONF = config.CONF @@ -29,15 +29,15 @@ _FATAL_EXCEPTION_FORMAT_ERRORS = False class Error(StandardError): """Base error class. - Child classes should define an HTTP status code, title, and a doc string. + Child classes should define an HTTP status code, title, and a + message_format. """ code = None title = None + message_format = None def __init__(self, message=None, **kwargs): - """Use the doc string as the error message by default.""" - try: message = self._build_message(message, **kwargs) except KeyError: @@ -45,8 +45,8 @@ class Error(StandardError): if _FATAL_EXCEPTION_FORMAT_ERRORS: raise else: - LOG.warning('missing exception kwargs (programmer error)') - message = self.__doc__ + LOG.warning(_('missing exception kwargs (programmer error)')) + message = self.message_format super(Error, self).__init__(message) @@ -57,42 +57,31 @@ class Error(StandardError): """ if not message: - message = re.sub('[ \n]+', ' ', self.__doc__ % kwargs) - message = message.strip() + message = self.message_format % kwargs return message 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. - - """ + message_format = _("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 StringLengthExceeded(ValidationError): - """String length exceeded. - - The length of string "%(string)s" exceeded the limit of column - %(type)s(CHAR(%(length)d)). - - """ + message_format = _("String length exceeded.The length of" + " string '%(string)s' exceeded the limit" + " of column %(type)s(CHAR(%(length)d)).") class ValidationSizeError(Error): - """Request attribute %(attribute)s must be less than or equal to %(size)i. - - The server could not comply with the request because the attribute - size is invalid (too large). - - The client is assumed to be in error. - - """ + message_format = _("Request attribute %(attribute)s must be" + " less than or equal to %(size)i. The server" + " could not comply with the request because" + " the attribute size is invalid (too large)." + " The client is assumed to be in error.") code = 400 title = 'Bad Request' @@ -103,19 +92,19 @@ class SecurityError(Error): def _build_message(self, message, **kwargs): """Only returns detailed messages in debug mode.""" if CONF.debug: - return message or self.__doc__ % kwargs + return message or self.message_format % kwargs else: - return self.__doc__ % kwargs + return self.message_format % kwargs class Unauthorized(SecurityError): - """The request you have made requires authentication.""" + message_format = _("The request you have made requires authentication.") code = 401 title = 'Unauthorized' class AuthPluginException(Unauthorized): - """Authentication plugin error.""" + message_format = _("Authentication plugin error.") def __init__(self, *args, **kwargs): super(AuthPluginException, self).__init__(*args, **kwargs) @@ -123,7 +112,7 @@ class AuthPluginException(Unauthorized): class AuthMethodNotSupported(AuthPluginException): - """Attempted to authenticate with an unsupported method.""" + message_format = _("Attempted to authenticate with an unsupported method.") def __init__(self, *args, **kwargs): super(AuthMethodNotSupported, self).__init__(*args, **kwargs) @@ -131,7 +120,7 @@ class AuthMethodNotSupported(AuthPluginException): class AdditionalAuthRequired(AuthPluginException): - """Additional authentications steps required.""" + message_format = _("Additional authentications steps required.") def __init__(self, auth_response=None, **kwargs): super(AdditionalAuthRequired, self).__init__(message=None, **kwargs) @@ -139,112 +128,111 @@ class AdditionalAuthRequired(AuthPluginException): class Forbidden(SecurityError): - """You are not authorized to perform the requested action.""" + message_format = _("You are not authorized to perform the" + " requested action.") code = 403 title = 'Forbidden' class ForbiddenAction(Forbidden): - """You are not authorized to perform the requested action, %(action)s.""" + message_format = _("You are not authorized to perform the" + " requested action, %(action)s.") class NotFound(Error): - """Could not find, %(target)s.""" + message_format = _("Could not find, %(target)s.") code = 404 title = 'Not Found' class EndpointNotFound(NotFound): - """Could not find endpoint, %(endpoint_id)s.""" + message_format = _("Could not find endpoint, %(endpoint_id)s.") class MetadataNotFound(NotFound): - """An unhandled exception has occurred: Could not find metadata.""" - # (dolph): metadata is not a user-facing concept, - # so this exception should not be exposed + """(dolph): metadata is not a user-facing concept, + so this exception should not be exposed + """ + message_format = _("An unhandled exception has occurred:" + " Could not find metadata.") class PolicyNotFound(NotFound): - """Could not find policy, %(policy_id)s.""" + message_format = _("Could not find policy, %(policy_id)s.") class RoleNotFound(NotFound): - """Could not find role, %(role_id)s.""" + message_format = _("Could not find role, %(role_id)s.") class ServiceNotFound(NotFound): - """Could not find service, %(service_id)s.""" + message_format = _("Could not find service, %(service_id)s.") class DomainNotFound(NotFound): - """Could not find domain, %(domain_id)s.""" + message_format = _("Could not find domain, %(domain_id)s.") class ProjectNotFound(NotFound): - """Could not find project, %(project_id)s.""" + message_format = _("Could not find project, %(project_id)s.") class TokenNotFound(NotFound): - """Could not find token, %(token_id)s.""" + message_format = _("Could not find token, %(token_id)s.") class UserNotFound(NotFound): - """Could not find user, %(user_id)s.""" + message_format = _("Could not find user, %(user_id)s.") class GroupNotFound(NotFound): - """Could not find group, %(group_id)s.""" + message_format = _("Could not find group, %(group_id)s.") class TrustNotFound(NotFound): - """Could not find trust, %(trust_id)s.""" + message_format = _("Could not find trust, %(trust_id)s.") class CredentialNotFound(NotFound): - """Could not find credential, %(credential_id)s.""" + message_format = _("Could not find credential, %(credential_id)s.") class VersionNotFound(NotFound): - """Could not find version, %(version)s.""" + message_format = _("Could not find version, %(version)s.") class Conflict(Error): - """Conflict occurred attempting to store %(type)s. - - %(details)s - - """ + message_format = _("Conflict occurred attempting to store %(type)s." + " %(details)s") code = 409 title = 'Conflict' class RequestTooLarge(Error): - """Request is too large.""" + message_format = _("Request is too large.") code = 413 title = 'Request is too large.' class UnexpectedError(Error): - """An unexpected error prevented the server from fulfilling your request. - - %(exception)s - - """ + message_format = _("An unexpected error prevented the server" + " from fulfilling your request. %(exception)s") code = 500 title = 'Internal Server Error' class MalformedEndpoint(UnexpectedError): - """Malformed endpoint URL (%(endpoint)s), see ERROR log for details.""" + message_format = _("Malformed endpoint URL (%(endpoint)s)," + " see ERROR log for details.") class NotImplemented(Error): - """The action you have requested has not been implemented.""" + message_format = _("The action you have requested has not" + " been implemented.") code = 501 title = 'Not Implemented' class PasteConfigNotFound(UnexpectedError): - """The Keystone paste configuration file %(config_file)s could not be - found. - """ + message_format = _("The Keystone paste configuration file" + " %(config_file)s could not be found.") diff --git a/keystone/identity/backends/kvs.py b/keystone/identity/backends/kvs.py index 83535108..0323d3d0 100644 --- a/keystone/identity/backends/kvs.py +++ b/keystone/identity/backends/kvs.py @@ -28,7 +28,7 @@ class Identity(kvs.Base, identity.Driver): return "keystone.assignment.backends.kvs.Assignment" # Public interface - def authenticate(self, user_id=None, password=None): + def authenticate(self, user_id, password): user_ref = None try: user_ref = self._get_user(user_id) diff --git a/keystone/identity/backends/ldap.py b/keystone/identity/backends/ldap.py index 1bdec3a3..f9e546a9 100644 --- a/keystone/identity/backends/ldap.py +++ b/keystone/identity/backends/ldap.py @@ -41,11 +41,6 @@ DEFAULT_DOMAIN = { class Identity(identity.Driver): def __init__(self): super(Identity, self).__init__() - self.LDAP_URL = CONF.ldap.url - self.LDAP_USER = CONF.ldap.user - self.LDAP_PASSWORD = CONF.ldap.password - self.suffix = CONF.ldap.suffix - self.user = UserApi(CONF) self.group = GroupApi(CONF) @@ -57,10 +52,7 @@ class Identity(identity.Driver): def create_project(self, project_id, project): return self.assignment_api.create_project(project_id, project) - def get_project(self, project_id): - return self.assignment_api.get_project(project_id) - - def authenticate(self, user_id=None, password=None): + def authenticate(self, user_id, password): try: user_ref = self._get_user(user_id) except exception.UserNotFound: @@ -273,293 +265,6 @@ class UserApi(common_ldap.EnabledEmuMixIn, common_ldap.BaseLdap): return identity.filter_user(user) -# TODO(termie): turn this into a data object and move logic to driver -class ProjectApi(common_ldap.EnabledEmuMixIn, common_ldap.BaseLdap): - DEFAULT_OU = 'ou=Projects' - DEFAULT_STRUCTURAL_CLASSES = [] - DEFAULT_OBJECTCLASS = 'groupOfNames' - DEFAULT_ID_ATTR = 'cn' - DEFAULT_MEMBER_ATTRIBUTE = 'member' - DEFAULT_ATTRIBUTE_IGNORE = [] - NotFound = exception.ProjectNotFound - notfound_arg = 'project_id' # NOTE(yorik-sar): while options_name = tenant - options_name = 'tenant' - attribute_mapping = {'name': 'ou', - 'description': 'description', - 'tenantId': 'cn', - 'enabled': 'enabled', - 'domain_id': 'domain_id'} - model = models.Project - - def __init__(self, conf): - super(ProjectApi, self).__init__(conf) - self.attribute_mapping['name'] = conf.ldap.tenant_name_attribute - self.attribute_mapping['description'] = conf.ldap.tenant_desc_attribute - self.attribute_mapping['enabled'] = conf.ldap.tenant_enabled_attribute - self.attribute_mapping['domain_id'] = ( - conf.ldap.tenant_domain_id_attribute) - self.member_attribute = (getattr(conf.ldap, 'tenant_member_attribute') - or self.DEFAULT_MEMBER_ATTRIBUTE) - self.attribute_ignore = (getattr(conf.ldap, 'tenant_attribute_ignore') - or self.DEFAULT_ATTRIBUTE_IGNORE) - - def create(self, values): - self.affirm_unique(values) - data = values.copy() - if data.get('id') is None: - data['id'] = uuid.uuid4().hex - return super(ProjectApi, self).create(data) - - def get_user_projects(self, user_dn, associations): - """Returns list of tenants a user has access to - """ - - project_ids = set() - for assoc in associations: - project_ids.add(self._dn_to_id(assoc.project_dn)) - projects = [] - for project_id in project_ids: - #slower to get them one at a time, but a huge list could blow out - #the connection. This is the safer way - projects.append(self.get(project_id)) - return projects - - def add_user(self, tenant_id, user_dn): - conn = self.get_connection() - try: - conn.modify_s( - self._id_to_dn(tenant_id), - [(ldap.MOD_ADD, - self.member_attribute, - user_dn)]) - except ldap.TYPE_OR_VALUE_EXISTS: - # As adding a user to a tenant is done implicitly in several - # places, and is not part of the exposed API, it's easier for us to - # just ignore this instead of raising exception.Conflict. - pass - - def remove_user(self, tenant_id, user_dn, user_id): - conn = self.get_connection() - try: - conn.modify_s(self._id_to_dn(tenant_id), - [(ldap.MOD_DELETE, - self.member_attribute, - user_dn)]) - except ldap.NO_SUCH_ATTRIBUTE: - raise exception.NotFound(user_id) - - def get_user_dns(self, tenant_id, rolegrants, role_dn=None): - tenant = self._ldap_get(tenant_id) - res = set() - if not role_dn: - # Get users who have default tenant mapping - for user_dn in tenant[1].get(self.member_attribute, []): - if self.use_dumb_member and user_dn == self.dumb_member: - continue - res.add(user_dn) - - # Get users who are explicitly mapped via a tenant - for rolegrant in rolegrants: - if role_dn is None or rolegrant.role_dn == role_dn: - res.add(rolegrant.user_dn) - return list(res) - - def update(self, id, values): - old_obj = self.get(id) - if old_obj['name'] != values['name']: - msg = 'Changing Name not supported by LDAP' - raise exception.NotImplemented(message=msg) - return super(ProjectApi, self).update(id, values, old_obj) - - -class UserRoleAssociation(object): - """Role Grant model.""" - - def __init__(self, user_dn=None, role_dn=None, tenant_dn=None, - *args, **kw): - self.user_dn = user_dn - self.role_dn = role_dn - self.project_dn = tenant_dn - - -class GroupRoleAssociation(object): - """Role Grant model.""" - - def __init__(self, group_dn=None, role_dn=None, tenant_dn=None, - *args, **kw): - self.group_dn = group_dn - self.role_dn = role_dn - self.project_dn = tenant_dn - - -# TODO(termie): turn this into a data object and move logic to driver -class RoleApi(common_ldap.BaseLdap): - DEFAULT_OU = 'ou=Roles' - DEFAULT_STRUCTURAL_CLASSES = [] - DEFAULT_OBJECTCLASS = 'organizationalRole' - DEFAULT_MEMBER_ATTRIBUTE = 'roleOccupant' - DEFAULT_ATTRIBUTE_IGNORE = [] - NotFound = exception.RoleNotFound - options_name = 'role' - attribute_mapping = {'name': 'ou', - #'serviceId': 'service_id', - } - model = models.Role - - def __init__(self, conf): - super(RoleApi, self).__init__(conf) - self.attribute_mapping['name'] = conf.ldap.role_name_attribute - self.member_attribute = (getattr(conf.ldap, 'role_member_attribute') - or self.DEFAULT_MEMBER_ATTRIBUTE) - self.attribute_ignore = (getattr(conf.ldap, 'role_attribute_ignore') - or self.DEFAULT_ATTRIBUTE_IGNORE) - - def get(self, id, filter=None): - model = super(RoleApi, self).get(id, filter) - return model - - def create(self, values): - return super(RoleApi, self).create(values) - - def add_user(self, role_id, role_dn, user_dn, user_id, tenant_id=None): - conn = self.get_connection() - try: - conn.modify_s(role_dn, [(ldap.MOD_ADD, - self.member_attribute, user_dn)]) - except ldap.TYPE_OR_VALUE_EXISTS: - msg = ('User %s already has role %s in tenant %s' - % (user_id, role_id, tenant_id)) - raise exception.Conflict(type='role grant', details=msg) - except ldap.NO_SUCH_OBJECT: - if tenant_id is None or self.get(role_id) is None: - raise Exception(_("Role %s not found") % (role_id,)) - - attrs = [('objectClass', [self.object_class]), - (self.member_attribute, [user_dn])] - - if self.use_dumb_member: - attrs[1][1].append(self.dumb_member) - try: - conn.add_s(role_dn, attrs) - except Exception as inst: - raise inst - - def delete_user(self, role_dn, user_dn, tenant_dn, - user_id, role_id): - conn = self.get_connection() - try: - conn.modify_s(role_dn, [(ldap.MOD_DELETE, - self.member_attribute, user_dn)]) - except ldap.NO_SUCH_OBJECT: - if tenant_dn is None: - raise exception.RoleNotFound(role_id=role_id) - attrs = [('objectClass', [self.object_class]), - (self.member_attribute, [user_dn])] - - if self.use_dumb_member: - attrs[1][1].append(self.dumb_member) - try: - conn.add_s(role_dn, attrs) - except Exception as inst: - raise inst - except ldap.NO_SUCH_ATTRIBUTE: - raise exception.UserNotFound(user_id=user_id) - - def get_role_assignments(self, tenant_dn): - conn = self.get_connection() - query = '(objectClass=%s)' % self.object_class - - 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[self.member_attribute] - except KeyError: - continue - for user_dn in user_dns: - if self.use_dumb_member and user_dn == self.dumb_member: - continue - res.append(UserRoleAssociation( - user_dn=user_dn, - role_dn=role_dn, - tenant_dn=tenant_dn)) - - return res - - def list_global_roles_for_user(self, user_dn): - roles = self.get_all('(%s=%s)' % (self.member_attribute, user_dn)) - return [UserRoleAssociation( - role_dn=role.dn, - user_dn=user_dn) for role in roles] - - def list_project_roles_for_user(self, user_dn, project_subtree): - conn = self.get_connection() - query = '(&(objectClass=%s)(%s=%s))' % (self.object_class, - self.member_attribute, - user_dn) - try: - roles = conn.search_s(project_subtree, - ldap.SCOPE_SUBTREE, - query) - except ldap.NO_SUCH_OBJECT: - return [] - - res = [] - for role_dn, _ in roles: - #ldap.dn.dn2str returns an array, where the first - #element is the first segment. - #For a role assignment, this contains the role ID, - #The remainder is the DN of the tenant. - tenant = ldap.dn.str2dn(role_dn) - tenant.pop(0) - tenant_dn = ldap.dn.dn2str(tenant) - res.append(UserRoleAssociation( - user_dn=user_dn, - role_dn=role_dn, - tenant_dn=tenant_dn)) - return res - - def roles_delete_subtree_by_project(self, tenant_dn): - conn = self.get_connection() - query = '(objectClass=%s)' % self.object_class - try: - roles = conn.search_s(tenant_dn, ldap.SCOPE_ONELEVEL, query) - for role_dn, _ in roles: - try: - conn.delete_s(role_dn) - except Exception as inst: - raise inst - except ldap.NO_SUCH_OBJECT: - pass - - def update(self, role_id, role): - if role['id'] != role_id: - raise exception.ValidationError('Cannot change role ID') - try: - old_name = self.get_by_name(role['name']) - raise exception.Conflict('Cannot duplicate name %s' % old_name) - except exception.NotFound: - pass - return super(RoleApi, self).update(role_id, role) - - def delete(self, id, tenant_dn): - conn = self.get_connection() - query = '(&(objectClass=%s)(%s=%s))' % (self.object_class, - self.id_attr, id) - try: - for role_dn, _ in conn.search_s(tenant_dn, - ldap.SCOPE_SUBTREE, - query): - conn.delete_s(role_dn) - except ldap.NO_SUCH_OBJECT: - pass - super(RoleApi, self).delete(id) - - class GroupApi(common_ldap.BaseLdap): DEFAULT_OU = 'ou=UserGroups' DEFAULT_STRUCTURAL_CLASSES = [] diff --git a/keystone/identity/backends/pam.py b/keystone/identity/backends/pam.py index 5cfa5b16..2a6ee621 100644 --- a/keystone/identity/backends/pam.py +++ b/keystone/identity/backends/pam.py @@ -58,7 +58,7 @@ class PamIdentity(identity.Driver): Tenant is always the same as User, root user has admin role. """ - def authenticate(self, user_id=None, password=None): + def authenticate(self, user_id, password): auth = pam.authenticate if pam else PAM_authenticate if not auth(user_id, password): raise AssertionError('Invalid user / password') diff --git a/keystone/identity/backends/sql.py b/keystone/identity/backends/sql.py index 2c00088e..ba97758c 100644 --- a/keystone/identity/backends/sql.py +++ b/keystone/identity/backends/sql.py @@ -84,7 +84,7 @@ class Identity(sql.Base, identity.Driver): return utils.check_password(password, user_ref.password) # Identity interface - def authenticate(self, user_id=None, password=None): + def authenticate(self, user_id, password): session = self.get_session() user_ref = None try: diff --git a/keystone/identity/core.py b/keystone/identity/core.py index b3efc0a7..3d9bcf62 100644 --- a/keystone/identity/core.py +++ b/keystone/identity/core.py @@ -216,7 +216,7 @@ class Manager(manager.Manager): class Driver(object): """Interface description for an Identity driver.""" - def authenticate_user(self, user_id, password): + def authenticate(self, user_id, password): """Authenticate a given user and password. :returns: user_ref :raises: AssertionError diff --git a/keystone/test.py b/keystone/test.py index 0c51d76d..2c2be577 100644 --- a/keystone/test.py +++ b/keystone/test.py @@ -44,6 +44,7 @@ from keystone.common import sql from keystone.common import utils from keystone.common import wsgi from keystone import config +from keystone.contrib import ec2 from keystone import credential from keystone import exception from keystone import identity @@ -252,7 +253,7 @@ class TestCase(NoModule, unittest.TestCase): def load_backends(self): """Initializes each manager and assigns them to an attribute.""" - for manager in [assignment, catalog, credential, identity, policy, + for manager in [assignment, catalog, credential, ec2, identity, policy, token, trust]: manager_name = '%s_api' % manager.__name__.split('.')[-1] setattr(self, manager_name, manager.Manager()) diff --git a/keystone/token/provider.py b/keystone/token/provider.py index 2459f843..2864be6f 100644 --- a/keystone/token/provider.py +++ b/keystone/token/provider.py @@ -77,6 +77,10 @@ class Manager(manager.Manager): 'conflicts with keystone.conf [token] provider')) return CONF.token.provider else: + if not CONF.signing.token_format: + # No token provider and no format, so use default (PKI) + return PKI_PROVIDER + msg = _('keystone.conf [signing] token_format is deprecated in ' 'favor of keystone.conf [token] provider') if CONF.signing.token_format == 'PKI': diff --git a/tests/_ldap_livetest.py b/tests/_ldap_livetest.py index 0bd707a4..ead54ea7 100644 --- a/tests/_ldap_livetest.py +++ b/tests/_ldap_livetest.py @@ -16,7 +16,6 @@ import ldap import ldap.modlist -import nose.exc import subprocess from keystone.common import ldap as ldap_common @@ -89,7 +88,7 @@ class LiveLDAPIdentity(test_backend_ldap.LDAPIdentity): test.TestCase.tearDown(self) def test_user_enable_attribute_mask(self): - raise nose.exc.SkipTest('Test is for Active Directory Only') + self.skipTest('Test is for Active Directory Only') def test_ldap_dereferencing(self): alt_users_ldif = {'objectclass': ['top', 'organizationalUnit'], diff --git a/tests/test_backend_kvs.py b/tests/test_backend_kvs.py index 0c6c2abe..d92a7510 100644 --- a/tests/test_backend_kvs.py +++ b/tests/test_backend_kvs.py @@ -15,8 +15,6 @@ # under the License. import uuid -import nose.exc - from keystone import test from keystone import exception @@ -36,34 +34,34 @@ class KvsIdentity(test.TestCase, test_backend.IdentityTests): def test_list_user_projects(self): # NOTE(chungg): not implemented - raise nose.exc.SkipTest('Blocked by bug 1119770') + self.skipTest('Blocked by bug 1119770') def test_create_duplicate_group_name_in_different_domains(self): - raise nose.exc.SkipTest('Blocked by bug 1119770') + self.skipTest('Blocked by bug 1119770') def test_create_duplicate_user_name_in_different_domains(self): - raise nose.exc.SkipTest('Blocked by bug 1119770') + self.skipTest('Blocked by bug 1119770') def test_create_duplicate_project_name_in_different_domains(self): - raise nose.exc.SkipTest('Blocked by bug 1119770') + self.skipTest('Blocked by bug 1119770') def test_move_user_between_domains(self): - raise nose.exc.SkipTest('Blocked by bug 1119770') + self.skipTest('Blocked by bug 1119770') def test_move_user_between_domains_with_clashing_names_fails(self): - raise nose.exc.SkipTest('Blocked by bug 1119770') + self.skipTest('Blocked by bug 1119770') def test_move_group_between_domains(self): - raise nose.exc.SkipTest('Blocked by bug 1119770') + self.skipTest('Blocked by bug 1119770') def test_move_group_between_domains_with_clashing_names_fails(self): - raise nose.exc.SkipTest('Blocked by bug 1119770') + self.skipTest('Blocked by bug 1119770') def test_move_project_between_domains(self): - raise nose.exc.SkipTest('Blocked by bug 1119770') + self.skipTest('Blocked by bug 1119770') def test_move_project_between_domains_with_clashing_names_fails(self): - raise nose.exc.SkipTest('Blocked by bug 1119770') + self.skipTest('Blocked by bug 1119770') class KvsToken(test.TestCase, test_backend.TokenTests): diff --git a/tests/test_backend_ldap.py b/tests/test_backend_ldap.py index ce4a297c..ec2b2737 100644 --- a/tests/test_backend_ldap.py +++ b/tests/test_backend_ldap.py @@ -17,8 +17,6 @@ import uuid -import nose.exc - from keystone import assignment from keystone.common.ldap import fakeldap from keystone.common import sql @@ -109,93 +107,93 @@ class BaseLDAPIdentity(test_backend.IdentityTests): self.user_foo['id']) def test_get_role_grant_by_user_and_project(self): - raise nose.exc.SkipTest('Blocked by bug 1101287') + self.skipTest('Blocked by bug 1101287') def test_get_role_grants_for_user_and_project_404(self): - raise nose.exc.SkipTest('Blocked by bug 1101287') + self.skipTest('Blocked by bug 1101287') def test_add_role_grant_to_user_and_project_404(self): - raise nose.exc.SkipTest('Blocked by bug 1101287') + self.skipTest('Blocked by bug 1101287') def test_remove_role_grant_from_user_and_project(self): - raise nose.exc.SkipTest('Blocked by bug 1101287') + self.skipTest('Blocked by bug 1101287') def test_get_and_remove_role_grant_by_group_and_project(self): - raise nose.exc.SkipTest('Blocked by bug 1101287') + self.skipTest('Blocked by bug 1101287') def test_get_and_remove_role_grant_by_group_and_domain(self): - raise nose.exc.SkipTest('N/A: LDAP does not support multiple domains') + self.skipTest('N/A: LDAP does not support multiple domains') def test_get_and_remove_role_grant_by_user_and_domain(self): - raise nose.exc.SkipTest('N/A: LDAP does not support multiple domains') + self.skipTest('N/A: LDAP does not support multiple domains') def test_get_and_remove_correct_role_grant_from_a_mix(self): - raise nose.exc.SkipTest('Blocked by bug 1101287') + self.skipTest('Blocked by bug 1101287') def test_get_and_remove_role_grant_by_group_and_cross_domain(self): - raise nose.exc.SkipTest('N/A: LDAP does not support multiple domains') + self.skipTest('N/A: LDAP does not support multiple domains') def test_get_and_remove_role_grant_by_user_and_cross_domain(self): - raise nose.exc.SkipTest('N/A: LDAP does not support multiple domains') + self.skipTest('N/A: LDAP does not support multiple domains') def test_role_grant_by_group_and_cross_domain_project(self): - raise nose.exc.SkipTest('N/A: LDAP does not support multiple domains') + self.skipTest('N/A: LDAP does not support multiple domains') def test_role_grant_by_user_and_cross_domain_project(self): - raise nose.exc.SkipTest('N/A: LDAP does not support multiple domains') + self.skipTest('N/A: LDAP does not support multiple domains') def test_multi_role_grant_by_user_group_on_project_domain(self): - raise nose.exc.SkipTest('N/A: LDAP does not support multiple domains') + self.skipTest('N/A: LDAP does not support multiple domains') def test_delete_role_with_user_and_group_grants(self): - raise nose.exc.SkipTest('Blocked by bug 1101287') + self.skipTest('Blocked by bug 1101287') def test_delete_user_with_group_project_domain_links(self): - raise nose.exc.SkipTest('N/A: LDAP does not support multiple domains') + self.skipTest('N/A: LDAP does not support multiple domains') def test_delete_group_with_user_project_domain_links(self): - raise nose.exc.SkipTest('N/A: LDAP does not support multiple domains') + self.skipTest('N/A: LDAP does not support multiple domains') def test_list_user_projects(self): - raise nose.exc.SkipTest('Blocked by bug 1101287') + self.skipTest('Blocked by bug 1101287') def test_create_duplicate_user_name_in_different_domains(self): - raise nose.exc.SkipTest('Blocked by bug 1101276') + self.skipTest('Blocked by bug 1101276') def test_create_duplicate_project_name_in_different_domains(self): - raise nose.exc.SkipTest('Blocked by bug 1101276') + self.skipTest('Blocked by bug 1101276') def test_create_duplicate_group_name_in_different_domains(self): - raise nose.exc.SkipTest( + self.skipTest( 'N/A: LDAP does not support multiple domains') def test_move_user_between_domains(self): - raise nose.exc.SkipTest('Blocked by bug 1101276') + self.skipTest('Blocked by bug 1101276') def test_move_user_between_domains_with_clashing_names_fails(self): - raise nose.exc.SkipTest('Blocked by bug 1101276') + self.skipTest('Blocked by bug 1101276') def test_move_group_between_domains(self): - raise nose.exc.SkipTest( + self.skipTest( 'N/A: LDAP does not support multiple domains') def test_move_group_between_domains_with_clashing_names_fails(self): - raise nose.exc.SkipTest('Blocked by bug 1101276') + self.skipTest('Blocked by bug 1101276') def test_move_project_between_domains(self): - raise nose.exc.SkipTest('Blocked by bug 1101276') + self.skipTest('Blocked by bug 1101276') def test_move_project_between_domains_with_clashing_names_fails(self): - raise nose.exc.SkipTest('Blocked by bug 1101276') + self.skipTest('Blocked by bug 1101276') def test_get_roles_for_user_and_domain(self): - raise nose.exc.SkipTest('N/A: LDAP does not support multiple domains') + self.skipTest('N/A: LDAP does not support multiple domains') def test_list_role_assignments_unfiltered(self): - raise nose.exc.SkipTest('Blocked by bug 1195019') + self.skipTest('Blocked by bug 1195019') def test_multi_group_grants_on_project_domain(self): - raise nose.exc.SkipTest('Blocked by bug 1101287') + self.skipTest('Blocked by bug 1101287') def test_list_group_members_missing_entry(self): """List group members with deleted user. @@ -562,12 +560,12 @@ class LDAPIdentity(test.TestCase, BaseLDAPIdentity): 'domain_id': CONF.identity.default_domain_id, 'description': uuid.uuid4().hex } - self.identity_api.driver.create_project(project['id'], project) - project_ref = self.identity_api.driver.get_project(project['id']) + self.assignment_api.create_project(project['id'], project) + project_ref = self.assignment_api.get_project(project['id']) # NOTE(crazed): If running live test with emulation, there will be # an enabled key in the project_ref. - if self.identity_api.driver.project.enabled_emulation: + if self.assignment_api.driver.project.enabled_emulation: project['enabled'] = True self.assertDictEqual(project_ref, project) @@ -631,7 +629,7 @@ class LDAPIdentity(test.TestCase, BaseLDAPIdentity): self.assertEquals(len(combined_role_list), 0) def test_list_projects_for_alternate_domain(self): - raise nose.exc.SkipTest( + self.skipTest( 'N/A: LDAP does not support multiple domains') @@ -705,7 +703,7 @@ class LDAPIdentityEnabledEmulation(LDAPIdentity): user['id']) def test_user_enable_attribute_mask(self): - raise nose.exc.SkipTest( + self.skipTest( "Enabled emulation conflicts with enabled mask") @@ -739,9 +737,9 @@ class LdapIdentitySqlAssignment(sql.Base, test.TestCase, BaseLDAPIdentity): self.assertEquals(domains, [assignment.DEFAULT_DOMAIN]) def test_project_filter(self): - raise nose.exc.SkipTest( + self.skipTest( 'N/A: Not part of SQL backend') def test_role_filter(self): - raise nose.exc.SkipTest( + self.skipTest( 'N/A: Not part of SQL backend') diff --git a/tests/test_content_types.py b/tests/test_content_types.py index 3213656c..ebb5dcef 100644 --- a/tests/test_content_types.py +++ b/tests/test_content_types.py @@ -18,7 +18,6 @@ import io import uuid from lxml import etree -import nose.exc import webtest from keystone import test @@ -471,7 +470,7 @@ class CoreApiTests(object): self.assertValidTenantResponse(r) def test_get_user_roles(self): - raise nose.exc.SkipTest('Blocked by bug 933565') + self.skipTest('Blocked by bug 933565') token = self.get_scoped_token() r = self.admin_request( diff --git a/tests/test_exception.py b/tests/test_exception.py index 33250835..d442d572 100644 --- a/tests/test_exception.py +++ b/tests/test_exception.py @@ -67,14 +67,14 @@ class ExceptionTestCase(test.TestCase): attribute = uuid.uuid4().hex e = exception.ValidationError(target=target, attribute=attribute) self.assertValidJsonRendering(e) - self.assertIn(target, str(e)) - self.assertIn(attribute, str(e)) + self.assertIn(target, unicode(e)) + self.assertIn(attribute, unicode(e)) def test_not_found(self): target = uuid.uuid4().hex e = exception.NotFound(target=target) self.assertValidJsonRendering(e) - self.assertIn(target, str(e)) + self.assertIn(target, unicode(e)) def test_403_title(self): e = exception.Forbidden() @@ -101,7 +101,7 @@ class SecurityErrorTestCase(ExceptionTestCase): risky_info = uuid.uuid4().hex e = exception.Unauthorized(message=risky_info) self.assertValidJsonRendering(e) - self.assertNotIn(risky_info, str(e)) + self.assertNotIn(risky_info, unicode(e)) def test_unauthorized_exposure_in_debug(self): self.opt(debug=True) @@ -109,7 +109,7 @@ class SecurityErrorTestCase(ExceptionTestCase): risky_info = uuid.uuid4().hex e = exception.Unauthorized(message=risky_info) self.assertValidJsonRendering(e) - self.assertIn(risky_info, str(e)) + self.assertIn(risky_info, unicode(e)) def test_forbidden_exposure(self): self.opt(debug=False) @@ -117,7 +117,7 @@ class SecurityErrorTestCase(ExceptionTestCase): risky_info = uuid.uuid4().hex e = exception.Forbidden(message=risky_info) self.assertValidJsonRendering(e) - self.assertNotIn(risky_info, str(e)) + self.assertNotIn(risky_info, unicode(e)) def test_forbidden_exposure_in_debug(self): self.opt(debug=True) @@ -125,7 +125,7 @@ class SecurityErrorTestCase(ExceptionTestCase): risky_info = uuid.uuid4().hex e = exception.Forbidden(message=risky_info) self.assertValidJsonRendering(e) - self.assertIn(risky_info, str(e)) + self.assertIn(risky_info, unicode(e)) def test_forbidden_action_exposure(self): self.opt(debug=False) @@ -134,12 +134,12 @@ class SecurityErrorTestCase(ExceptionTestCase): action = uuid.uuid4().hex e = exception.ForbiddenAction(message=risky_info, action=action) self.assertValidJsonRendering(e) - self.assertNotIn(risky_info, str(e)) - self.assertIn(action, str(e)) + self.assertNotIn(risky_info, unicode(e)) + self.assertIn(action, unicode(e)) e = exception.ForbiddenAction(action=risky_info) self.assertValidJsonRendering(e) - self.assertIn(risky_info, str(e)) + self.assertIn(risky_info, unicode(e)) def test_forbidden_action_exposure_in_debug(self): self.opt(debug=True) @@ -148,8 +148,16 @@ class SecurityErrorTestCase(ExceptionTestCase): e = exception.ForbiddenAction(message=risky_info) self.assertValidJsonRendering(e) - self.assertIn(risky_info, str(e)) + self.assertIn(risky_info, unicode(e)) e = exception.ForbiddenAction(action=risky_info) self.assertValidJsonRendering(e) - self.assertIn(risky_info, str(e)) + self.assertIn(risky_info, unicode(e)) + + def test_unicode_argument_message(self): + self.opt(debug=False) + + risky_info = u'\u7ee7\u7eed\u884c\u7f29\u8fdb\u6216' + e = exception.Forbidden(message=risky_info) + self.assertValidJsonRendering(e) + self.assertNotIn(risky_info, unicode(e)) diff --git a/tests/test_keystoneclient.py b/tests/test_keystoneclient.py index dc494b3f..a26d6595 100644 --- a/tests/test_keystoneclient.py +++ b/tests/test_keystoneclient.py @@ -17,8 +17,6 @@ import uuid import webob -import nose.exc - from keystone import test from keystone import token @@ -541,7 +539,7 @@ class KeystoneClientTests(object): user=uuid.uuid4().hex) def test_user_update_tenant_404(self): - raise nose.exc.SkipTest('N/A') + self.skipTest('N/A') from keystoneclient import exceptions as client_exceptions client = self.get_client(admin=True) self.assertRaises(client_exceptions.NotFound, @@ -1112,10 +1110,10 @@ class KcEssex3TestCase(CompatTestCase, KeystoneClientTests): self.assertTrue(len(roles) > 0) def test_role_list_404(self): - raise nose.exc.SkipTest('N/A') + self.skipTest('N/A') def test_authenticate_and_delete_token(self): - raise nose.exc.SkipTest('N/A') + self.skipTest('N/A') def test_user_create_update_delete(self): from keystoneclient import exceptions as client_exceptions @@ -1159,16 +1157,16 @@ class KcEssex3TestCase(CompatTestCase, KeystoneClientTests): user.id) def test_user_update_404(self): - raise nose.exc.SkipTest('N/A') + self.skipTest('N/A') def test_endpoint_create_404(self): - raise nose.exc.SkipTest('N/A') + self.skipTest('N/A') def test_endpoint_delete_404(self): - raise nose.exc.SkipTest('N/A') + self.skipTest('N/A') def test_policy_crud(self): - raise nose.exc.SkipTest('N/A due to lack of endpoint CRUD') + self.skipTest('N/A due to lack of endpoint CRUD') class Kc11TestCase(CompatTestCase, KeystoneClientTests): @@ -1176,4 +1174,4 @@ class Kc11TestCase(CompatTestCase, KeystoneClientTests): return KEYSTONECLIENT_REPO, '0.1.1' def test_policy_crud(self): - raise nose.exc.SkipTest('N/A') + self.skipTest('N/A') diff --git a/tests/test_keystoneclient_sql.py b/tests/test_keystoneclient_sql.py index 5706bef5..166d808c 100644 --- a/tests/test_keystoneclient_sql.py +++ b/tests/test_keystoneclient_sql.py @@ -16,8 +16,6 @@ import uuid -import nose.exc - from keystone import test from keystone.common import sql @@ -109,7 +107,7 @@ class KcMasterSqlTestCase(test_keystoneclient.KcMasterTestCase, sql.Base): # the client and essentially refers to a non-existent # policy manager in the v2 client. this test needs to be # moved to a test suite running against the v3 api - raise nose.exc.SkipTest('Written prior to v3 client; needs refactor') + self.skipTest('Written prior to v3 client; needs refactor') from keystoneclient import exceptions as client_exceptions client = self.get_client(admin=True) diff --git a/tests/test_pki_token_provider.conf b/tests/test_pki_token_provider.conf index ec8df231..255972c3 100644 --- a/tests/test_pki_token_provider.conf +++ b/tests/test_pki_token_provider.conf @@ -1,5 +1,2 @@ -[signing] -token_format = PKI - [token] provider = keystone.token.providers.pki.Provider diff --git a/tests/test_sql_core.py b/tests/test_sql_core.py index bb413485..e60005f5 100644 --- a/tests/test_sql_core.py +++ b/tests/test_sql_core.py @@ -172,3 +172,11 @@ class TestBase(test.TestCase): self.assertFalse(session.autocommit) self.assertTrue(session.expire_on_commit) + + def test_get_session_invalidated(self): + # If clear the global engine, a new engine is used for get_session(). + base = sql.Base() + session1 = base.get_session() + sql.set_global_engine(None) + session2 = base.get_session() + self.assertIsNot(session1.bind, session2.bind) diff --git a/tests/test_token_provider.py b/tests/test_token_provider.py index 1bcf1a21..ac0b0d6b 100644 --- a/tests/test_token_provider.py +++ b/tests/test_token_provider.py @@ -410,11 +410,16 @@ class TestTokenProvider(test.TestCase): self.assertRaises(exception.UnexpectedError, token.provider.Manager.get_token_provider) + def test_uuid_provider(self): + self.opt_in_group('token', provider=token.provider.UUID_PROVIDER) + self.assertEqual(token.provider.Manager.get_token_provider(), + token.provider.UUID_PROVIDER) + def test_provider_override_token_format(self): self.opt_in_group('token', provider='keystone.token.providers.pki.Test') - self.assertRaises(exception.UnexpectedError, - token.provider.Manager.get_token_provider) + self.assertEqual(token.provider.Manager.get_token_provider(), + 'keystone.token.providers.pki.Test') self.opt_in_group('signing', token_format='UUID') self.opt_in_group('token', diff --git a/tests/test_uuid_token_provider.conf b/tests/test_uuid_token_provider.conf index d1ac5fdf..d127ea3b 100644 --- a/tests/test_uuid_token_provider.conf +++ b/tests/test_uuid_token_provider.conf @@ -1,5 +1,2 @@ -[signing] -token_format = UUID - [token] provider = keystone.token.providers.uuid.Provider diff --git a/tests/test_v3_auth.py b/tests/test_v3_auth.py index 2e354f71..11d66700 100644 --- a/tests/test_v3_auth.py +++ b/tests/test_v3_auth.py @@ -15,8 +15,6 @@ import json import uuid -import nose.exc - from keystone import auth from keystone.common import cms from keystone import config @@ -1438,7 +1436,7 @@ class TestTrustAuth(TestAuthInfo): self.identity_api.create_user(self.trustee_user_id, self.trustee_user) def test_create_trust_400(self): - raise nose.exc.SkipTest('Blocked by bug 1133435') + self.skipTest('Blocked by bug 1133435') self.post('/OS-TRUST/trusts', body={'trust': {}}, expected_status=400) def test_create_unscoped_trust(self): diff --git a/tests/test_wsgi.py b/tests/test_wsgi.py index 8ac594a8..369dd952 100644 --- a/tests/test_wsgi.py +++ b/tests/test_wsgi.py @@ -134,6 +134,11 @@ class ApplicationTest(BaseWSGITest): self.assertIn("testkey", app.kwargs) self.assertEquals("test", app.kwargs["testkey"]) + def test_render_exception(self): + e = exception.Unauthorized(message=u'\u7f51\u7edc') + resp = wsgi.render_exception(e) + self.assertEqual(resp.status_int, 401) + class ExtensionRouterTest(BaseWSGITest): def test_extensionrouter_local_config(self): |