summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/source/configuration.rst15
-rw-r--r--etc/keystone.conf.sample2
-rw-r--r--keystone/catalog/backends/sql.py2
-rw-r--r--keystone/common/cms.py12
-rw-r--r--keystone/common/sql/migrate_repo/versions/002_mysql_downgrade.sql3
-rw-r--r--keystone/common/sql/migrate_repo/versions/002_mysql_upgrade.sql2
-rw-r--r--keystone/common/sql/migrate_repo/versions/002_token_id_hash.py43
-rw-r--r--keystone/config.py4
-rw-r--r--keystone/identity/backends/ldap/core.py2
-rw-r--r--keystone/middleware/auth_token.py16
-rw-r--r--keystone/service.py85
-rw-r--r--keystone/token/backends/sql.py4
-rw-r--r--tests/test_backend.py12
-rw-r--r--tests/test_backend_sql.py34
14 files changed, 147 insertions, 89 deletions
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index da184641..917e8659 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -773,7 +773,7 @@ example::
Configuring the LDAP Identity Provider
===========================================================
-As an alternative to the SQL Databse backing store, Keystone can use a
+As an alternative to the SQL Database backing store, Keystone can use a
directory server to provide the Identity service. An example Schema
for openstack would look like this::
@@ -805,3 +805,16 @@ The corresponding entries in the Keystone configuration file are::
suffix = dc=openstack,dc=org
user = dc=Manager,dc=openstack,dc=org
password = badpassword
+
+The default object classes and attributes are intentionally simplistic. They
+reflect the common standard objects according to the LDAP RFCs. However,
+in a live deployment, the correct attributes can be overridden to support a
+preexisting, more complex schema. For example, in the user object, the
+objectClass posixAccount from RFC2307 is very common. If this is the
+underlying objectclass, then the *uid* field should probably be *uidNumber* and
+*username* field either *uid* or *cn*. To change these two fields, the
+corresponding entries in the Keystone configuration file are::
+
+ [ldap]
+ user_id_attribute = uidNumber
+ user_name_attribute = cn
diff --git a/etc/keystone.conf.sample b/etc/keystone.conf.sample
index e98c22d1..3f37c713 100644
--- a/etc/keystone.conf.sample
+++ b/etc/keystone.conf.sample
@@ -109,11 +109,13 @@
# user_tree_dn = ou=Users,dc=example,dc=com
# user_objectclass = inetOrgPerson
# user_id_attribute = cn
+# user_name_attribute = sn
# tenant_tree_dn = ou=Groups,dc=example,dc=com
# tenant_objectclass = groupOfNames
# tenant_id_attribute = cn
# tenant_member_attribute = member
+# tenant_name_attribute = ou
# role_tree_dn = ou=Roles,dc=example,dc=com
# role_objectclass = organizationalRole
diff --git a/keystone/catalog/backends/sql.py b/keystone/catalog/backends/sql.py
index 785baeaf..c47d337a 100644
--- a/keystone/catalog/backends/sql.py
+++ b/keystone/catalog/backends/sql.py
@@ -127,6 +127,8 @@ class Catalog(sql.Base, catalog.Driver):
session = self.get_session()
endpoint_ref = session.query(Endpoint)
endpoint_ref = endpoint_ref.filter_by(id=endpoint_id).first()
+ if not endpoint_ref:
+ raise exception.EndpointNotFound(endpoint_id=endpoint_id)
return endpoint_ref.to_dict()
def list_endpoints(self):
diff --git a/keystone/common/cms.py b/keystone/common/cms.py
index 1f0b8fc0..22dadfcc 100644
--- a/keystone/common/cms.py
+++ b/keystone/common/cms.py
@@ -2,13 +2,16 @@ import os
import stat
import subprocess
+from keystone.common import logging
+
+LOG = logging.getLogger(__name__)
UUID_TOKEN_LENGTH = 32
def cms_verify(formatted, signing_cert_file_name, ca_file_name):
"""
- verifies the signature of the contensts IAW CMS syntax
+ verifies the signature of the contents IAW CMS syntax
"""
process = subprocess.Popen(["openssl", "cms", "-verify",
"-certfile", signing_cert_file_name,
@@ -22,6 +25,7 @@ def cms_verify(formatted, signing_cert_file_name, ca_file_name):
output, err = process.communicate(formatted)
retcode = process.poll()
if retcode:
+ LOG.error('Verify error: %s' % err)
raise subprocess.CalledProcessError(retcode, "openssl", output=err)
return output
@@ -64,10 +68,12 @@ def cms_sign_text(text, signing_cert_file_name, signing_key_file_name):
"-nosmimecap", "-nodetach",
"-nocerts", "-noattr"],
stdin=subprocess.PIPE,
- stdout=subprocess.PIPE)
- output, unused_err = process.communicate(text)
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ output, err = process.communicate(text)
retcode = process.poll()
if retcode:
+ LOG.error('Signing error: %s' % err)
raise subprocess.CalledProcessError(retcode,
"openssl", output=output)
return cms_to_token(output)
diff --git a/keystone/common/sql/migrate_repo/versions/002_mysql_downgrade.sql b/keystone/common/sql/migrate_repo/versions/002_mysql_downgrade.sql
deleted file mode 100644
index f1337acd..00000000
--- a/keystone/common/sql/migrate_repo/versions/002_mysql_downgrade.sql
+++ /dev/null
@@ -1,3 +0,0 @@
-alter table token drop id;
-alter table token change id_hash id varchar(64);
-
diff --git a/keystone/common/sql/migrate_repo/versions/002_mysql_upgrade.sql b/keystone/common/sql/migrate_repo/versions/002_mysql_upgrade.sql
deleted file mode 100644
index 1cb8d747..00000000
--- a/keystone/common/sql/migrate_repo/versions/002_mysql_upgrade.sql
+++ /dev/null
@@ -1,2 +0,0 @@
-alter table token change id id_hash varchar(64);
-alter table token add id varchar(2048);
diff --git a/keystone/common/sql/migrate_repo/versions/002_token_id_hash.py b/keystone/common/sql/migrate_repo/versions/002_token_id_hash.py
new file mode 100644
index 00000000..4d38b525
--- /dev/null
+++ b/keystone/common/sql/migrate_repo/versions/002_token_id_hash.py
@@ -0,0 +1,43 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2012 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.
+
+from sqlalchemy import Column, MetaData, String, Table
+
+
+def upgrade(migrate_engine):
+ meta = MetaData()
+ meta.bind = migrate_engine
+ token = Table('token', meta, autoload=True)
+ old_id_col = token.c.id
+ old_id_col.alter(name='id_hash')
+ # Note: We obtain a new metadata reference to avoid
+ # sqlalchemy.exc.ArgumentError:
+ # Trying to redefine primary-key column 'id' as a non-primary-key...
+ meta = MetaData()
+ meta.bind = migrate_engine
+ token = Table('token', meta, autoload=True)
+ new_id = Column("id", String(2048))
+ token.create_column(new_id)
+
+
+def downgrade(migrate_engine):
+ meta = MetaData()
+ meta.bind = migrate_engine
+ token = Table('token', meta, autoload=True)
+ token.drop_column('id')
+ token = Table('token', meta, autoload=True)
+ id_col = token.c.id_hash
+ id_col.alter(name='id')
diff --git a/keystone/config.py b/keystone/config.py
index 8954d36b..33f1e3ba 100644
--- a/keystone/config.py
+++ b/keystone/config.py
@@ -162,6 +162,8 @@ register_str('user', group='ldap', default='dc=Manager,dc=example,dc=com')
register_str('password', group='ldap', default='freeipa4all')
register_str('suffix', group='ldap', default='cn=example,cn=com')
register_bool('use_dumb_member', group='ldap', default=False)
+register_str('user_name_attribute', group='ldap', default='sn')
+
register_str('user_tree_dn', group='ldap', default=None)
register_str('user_objectclass', group='ldap', default='inetOrgPerson')
@@ -171,7 +173,7 @@ register_str('tenant_tree_dn', group='ldap', default=None)
register_str('tenant_objectclass', group='ldap', default='groupOfNames')
register_str('tenant_id_attribute', group='ldap', default='cn')
register_str('tenant_member_attribute', group='ldap', default='member')
-
+register_str('tenant_name_attribute', group='ldap', default='ou')
register_str('role_tree_dn', group='ldap', default=None)
register_str('role_objectclass', group='ldap', default='organizationalRole')
diff --git a/keystone/identity/backends/ldap/core.py b/keystone/identity/backends/ldap/core.py
index 166f7f58..25aaebae 100644
--- a/keystone/identity/backends/ldap/core.py
+++ b/keystone/identity/backends/ldap/core.py
@@ -337,6 +337,7 @@ class UserApi(common_ldap.BaseLdap, ApiShimMixin):
def __init__(self, conf):
super(UserApi, self).__init__(conf)
+ self.attribute_mapping['name'] = conf.ldap.user_name_attribute
self.api = ApiShim(conf)
def get(self, id, filter=None):
@@ -462,6 +463,7 @@ class TenantApi(common_ldap.BaseLdap, ApiShimMixin):
def __init__(self, conf):
super(TenantApi, self).__init__(conf)
self.api = ApiShim(conf)
+ self.attribute_mapping['name'] = conf.ldap.tenant_name_attribute
self.member_attribute = (getattr(conf.ldap, 'tenant_member_attribute')
or self.DEFAULT_MEMBER_ATTRIBUTE)
diff --git a/keystone/middleware/auth_token.py b/keystone/middleware/auth_token.py
index c82e5ef5..75ab67c7 100644
--- a/keystone/middleware/auth_token.py
+++ b/keystone/middleware/auth_token.py
@@ -117,6 +117,10 @@ class ServiceError(Exception):
pass
+class ConfigurationError(Exception):
+ pass
+
+
class AuthProtocol(object):
"""Auth Middleware that handles authenticating client calls."""
@@ -150,10 +154,14 @@ class AuthProtocol(object):
self.key_file = conf.get('keyfile')
#signing
- self.signing_dirname = conf.get('signing_dir', '/tmp/keystone-signing')
+ default_signing_dir = '%s/keystone-signing' % os.environ['HOME']
+ self.signing_dirname = conf.get('signing_dir', default_signing_dir)
+ LOG.info('Using %s as cache directory for signing certificate' %
+ self.signing_dirname)
if (os.path.exists(self.signing_dirname) and
not os.access(self.signing_dirname, os.W_OK)):
- raise "TODO: Need to find an Exception to raise here."
+ raise ConfigurationError("unable to access signing dir %s" %
+ self.signing_dirname)
if not os.path.exists(self.signing_dirname):
os.makedirs(self.signing_dirname)
@@ -565,8 +573,8 @@ class AuthProtocol(object):
time=self.token_cache_time)
def cert_file_missing(self, called_proc_err, file_name):
- return (called_proc_err.output.find(self.signing_cert_file_name)
- and not os.path.exists(self.signing_cert_file_name))
+ return (called_proc_err.output.find(file_name)
+ and not os.path.exists(file_name))
def verify_uuid_token(self, user_token, retry=True):
"""Authenticate user token with keystone.
diff --git a/keystone/service.py b/keystone/service.py
index 2119e873..0ee34e88 100644
--- a/keystone/service.py
+++ b/keystone/service.py
@@ -20,6 +20,7 @@ import json
from keystone import config
from keystone import catalog
+from keystone.common import cms
from keystone.common import logging
from keystone.common import wsgi
from keystone import exception
@@ -28,11 +29,6 @@ from keystone.openstack.common import timeutils
from keystone import policy
from keystone import token
-from keystone.common import cms
-from keystone.common import logging
-from keystone.common import utils
-from keystone.common import wsgi
-
LOG = logging.getLogger(__name__)
@@ -328,7 +324,7 @@ class TokenController(wsgi.Application):
raise exception.Unauthorized()
except AssertionError as e:
raise exception.Unauthorized(e.message)
- auth_token_data = dict(zip(["user", "tenant", "metadata"],
+ auth_token_data = dict(zip(['user', 'tenant', 'metadata'],
auth_info))
expiry = self.token_api._get_default_expire_time(context=context)
@@ -340,9 +336,7 @@ class TokenController(wsgi.Application):
metadata=metadata_ref)
else:
catalog_ref = {}
-
elif 'token' in auth:
-
old_token = auth['token'].get('id', None)
tenant_name = auth.get('tenantName')
@@ -351,19 +345,22 @@ class TokenController(wsgi.Application):
token_id=old_token)
except exception.NotFound:
raise exception.Unauthorized()
+
user_ref = old_token_ref['user']
user_id = user_ref['id']
current_user_ref = self.identity_api.get_user(context=context,
user_id=user_id)
+
+ # If the user is disabled don't allow them to authenticate
if not current_user_ref.get('enabled', True):
LOG.warning('User %s is disabled' % user_id)
raise exception.Unauthorized()
if tenant_name:
- tenant_ref = self.identity_api.\
- get_tenant_by_name(context=context,
- tenant_name=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)
@@ -375,17 +372,17 @@ class TokenController(wsgi.Application):
% (user_id, tenant_id))
raise exception.Unauthorized()
- #if the old token is sufficient unpack and return it.
- if (old_token_ref['tenant']) and \
- (tenant_id == old_token_ref['tenant']['id']) and\
- len(old_token) > cms.UUID_TOKEN_LENGTH:
- return_data = \
- json.loads(cms.verify_token
- (old_token,
- config.CONF.signing.certfile,
- config.CONF.signing.ca_certs))
- return_data['access']['token']['id'] = old_token
- return return_data
+ # if the old token is sufficient unpack and return it
+ if (old_token_ref['tenant']
+ and tenant_id == old_token_ref['tenant']['id']
+ and len(old_token) > cms.UUID_TOKEN_LENGTH):
+ json_data = cms.verify_token(
+ old_token,
+ config.CONF.signing.certfile,
+ config.CONF.signing.ca_certs)
+ return_data = json.loads(json_data)
+ return_data['access']['token']['id'] = old_token
+ return return_data
expiry = old_token_ref['expires']
try:
@@ -395,7 +392,6 @@ class TokenController(wsgi.Application):
tenant_ref = None
metadata_ref = {}
catalog_ref = {}
-
except exception.MetadataNotFound:
metadata_ref = {}
catalog_ref = {}
@@ -435,29 +431,28 @@ class TokenController(wsgi.Application):
if config.CONF.signing.disable_pki:
token_id = uuid.uuid4().hex
- signed = token_id
else:
- signed = cms.cms_sign_text(json.dumps(token_data),
- config.CONF.signing.certfile,
- config.CONF.signing.keyfile)
- token_id = signed
+ token_id = cms.cms_sign_text(json.dumps(token_data),
+ config.CONF.signing.certfile,
+ config.CONF.signing.keyfile)
+
try:
- token_ref = self.token_api.create_token(
+ self.token_api.create_token(
context, token_id, dict(key=token_id,
- id=signed,
+ id=token_id,
user=user_ref,
tenant=tenant_ref,
metadata=metadata_ref))
- except Exception as ex:
- #an identical token may have been created already.
- #if so, return the token_data as it is also identical
+ except Exception as e:
+ # an identical token may have been created already.
+ # if so, return the token_data as it is also identical
try:
- exist_token = self.token_api.get_token(context=context,
- token_id=token_id)
+ self.token_api.get_token(context=context,
+ token_id=token_id)
except exception.TokenNotFound:
- raise ex
+ raise e
- token_data['access']['token']['id'] = signed
+ token_data['access']['token']['id'] = token_id
return token_data
@@ -468,19 +463,17 @@ class TokenController(wsgi.Application):
"""
# TODO(termie): this stuff should probably be moved to middleware
+ self.assert_admin(context)
+
if len(token_id) > cms.UUID_TOKEN_LENGTH:
- self.assert_admin(context)
data = json.loads(cms.cms_verify(cms.token_to_cms(token_id),
config.CONF.signing.certfile,
config.CONF.signing.ca_certs))
- access_data = data['access']
- token_ref = access_data['token']
- user_data = access_data['user']
- token_ref['metadata'] = access_data['metadata']
- token_ref['user'] = user_data
+ data['access']['token']['user'] = data['access']['user']
+ data['access']['token']['metadata'] = data['access']['metadata']
if belongs_to:
- assert token_ref['tenant']['id'] == belongs_to
- token_ref['expires']
+ assert data['access']['token']['tenant']['id'] == belongs_to
+ token_ref = data['access']['token']
else:
token_ref = self.token_api.get_token(context=context,
token_id=token_id)
@@ -495,7 +488,7 @@ class TokenController(wsgi.Application):
Identical to ``validate_token``, except does not return a response.
"""
- belongs_to = context['query_string'].get("belongsTo")
+ belongs_to = context['query_string'].get('belongsTo')
assert self._get_token_ref(context, token_id, belongs_to)
# admin only
diff --git a/keystone/token/backends/sql.py b/keystone/token/backends/sql.py
index fd31eeef..fa0dbb76 100644
--- a/keystone/token/backends/sql.py
+++ b/keystone/token/backends/sql.py
@@ -17,9 +17,9 @@
import copy
import datetime
import hashlib
-import uuid
-from keystone.common import sql, cms
+from keystone.common import cms
+from keystone.common import sql
from keystone import exception
from keystone.openstack.common import timeutils
from keystone import token
diff --git a/tests/test_backend.py b/tests/test_backend.py
index 9aace406..66d2019f 100644
--- a/tests/test_backend.py
+++ b/tests/test_backend.py
@@ -651,12 +651,14 @@ class TokenTests(object):
class CatalogTests(object):
def test_service_crud(self):
new_service = {
- 'id': 'MY_SERVICE',
- 'type': 'myservice',
- 'name': 'My Service',
- 'description': 'My description'
+ 'id': uuid.uuid4().hex,
+ 'type': uuid.uuid4().hex,
+ 'name': uuid.uuid4().hex,
+ 'description': uuid.uuid4().hex,
}
- res = self.catalog_api.create_service(new_service['id'], new_service)
+ res = self.catalog_api.create_service(
+ new_service['id'],
+ new_service.copy())
self.assertDictEqual(res, new_service)
service_id = new_service['id']
diff --git a/tests/test_backend_sql.py b/tests/test_backend_sql.py
index 9a0bcc00..ee181dee 100644
--- a/tests/test_backend_sql.py
+++ b/tests/test_backend_sql.py
@@ -16,6 +16,8 @@
import uuid
+from keystone import catalog
+from keystone.catalog.backends import sql as catalog_sql
from keystone.common.sql import util as sql_util
from keystone import config
from keystone import exception
@@ -142,25 +144,13 @@ class SqlToken(test.TestCase, test_backend.TokenTests):
self.token_api = token_sql.Token()
-#class SqlCatalog(test_backend_kvs.KvsCatalog):
-# def setUp(self):
-# super(SqlCatalog, self).setUp()
-# self.catalog_api = sql.SqlCatalog()
-# self._load_fixtures()
-
-# def _load_fixtures(self):
-# self.catalog_foobar = self.catalog_api._create_catalog(
-# 'foo', 'bar',
-# {'RegionFoo': {'service_bar': {'foo': 'bar'}}})
-
-# def test_get_catalog_bad_user(self):
-# catalog_ref = self.catalog_api.get_catalog('foo' + 'WRONG', 'bar')
-# self.assert_(catalog_ref is None)
-
-# def test_get_catalog_bad_tenant(self):
-# catalog_ref = self.catalog_api.get_catalog('foo', 'bar' + 'WRONG')
-# self.assert_(catalog_ref is None)
-
-# def test_get_catalog(self):
-# catalog_ref = self.catalog_api.get_catalog('foo', 'bar')
-# self.assertDictEqual(catalog_ref, self.catalog_foobar)
+class SqlCatalog(test.TestCase, test_backend.CatalogTests):
+ def setUp(self):
+ super(SqlCatalog, self).setUp()
+ self.config([test.etcdir('keystone.conf.sample'),
+ test.testsdir('test_overrides.conf'),
+ test.testsdir('backend_sql.conf')])
+ sql_util.setup_test_database()
+ self.catalog_api = catalog_sql.Catalog()
+ self.catalog_man = catalog.Manager()
+ self.load_fixtures(default_fixtures)