deb-keystone/keystone/tests/test_ldap_livetest.py
John Dennis cbf805161b Properly handle unicode & utf-8 in LDAP
This patch adds all the necessary type conversions between the LDAP
API's.

* string literals are unicode

* unicode strings are utf-8 encoded before calling LDAP

* utf-8 strings received from the LDAP API are decoded into unicode

* string classes use the six.text_type for Python 2 vs. Python 3
  compatibility

* the fake LDAP implementation was reworked such that it's external
  API only handles UTF-8 encoded strings but only uses unicode
  internally. This is because internally it must be able to operate on
  logical characters in order to perform string operations on it's
  data. This is very much akin to what happens in a real LDAP
  implementation, the interface is UTF-8 but operations occur on
  decoded logical characters.

Unicode tests that were skipped are now re-enabled.

Partial-Bug: 1172106
Change-Id: Icce6b508f748214e241de40c3c9389b2caccea83
2014-03-27 10:39:05 -04:00

231 lines
8.9 KiB
Python

# Copyright 2012 OpenStack Foundation
#
# 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 ldap.modlist
import subprocess
import uuid
from keystone import config
from keystone import exception
from keystone.identity.backends import ldap as identity_ldap
from keystone import tests
from keystone.tests import test_backend_ldap
CONF = config.CONF
def create_object(dn, attrs):
conn = ldap.initialize(CONF.ldap.url)
conn.simple_bind_s(CONF.ldap.user, CONF.ldap.password)
ldif = ldap.modlist.addModlist(attrs)
conn.add_s(dn, ldif)
conn.unbind_s()
class LiveLDAPIdentity(test_backend_ldap.LDAPIdentity):
def setUp(self):
self._ldap_skip_live()
super(LiveLDAPIdentity, self).setUp()
def _ldap_skip_live(self):
self.skip_if_env_not_set('ENABLE_LDAP_LIVE_TEST')
def clear_database(self):
devnull = open('/dev/null', 'w')
subprocess.call(['ldapdelete',
'-x',
'-D', CONF.ldap.user,
'-H', CONF.ldap.url,
'-w', CONF.ldap.password,
'-r', CONF.ldap.suffix],
stderr=devnull)
if CONF.ldap.suffix.startswith('ou='):
tree_dn_attrs = {'objectclass': 'organizationalUnit',
'ou': 'openstack'}
else:
tree_dn_attrs = {'objectclass': ['dcObject', 'organizationalUnit'],
'dc': 'openstack',
'ou': 'openstack'}
create_object(CONF.ldap.suffix, tree_dn_attrs)
create_object(CONF.ldap.user_tree_dn,
{'objectclass': 'organizationalUnit',
'ou': 'Users'})
create_object(CONF.ldap.role_tree_dn,
{'objectclass': 'organizationalUnit',
'ou': 'Roles'})
create_object(CONF.ldap.tenant_tree_dn,
{'objectclass': 'organizationalUnit',
'ou': 'Projects'})
create_object(CONF.ldap.group_tree_dn,
{'objectclass': 'organizationalUnit',
'ou': 'UserGroups'})
def config_files(self):
config_files = super(LiveLDAPIdentity, self).config_files()
config_files.append(tests.dirs.tests_conf('backend_liveldap.conf'))
return config_files
def config_overrides(self):
super(LiveLDAPIdentity, self).config_overrides()
self.config_fixture.config(
group='identity',
driver='keystone.identity.backends.ldap.Identity')
def test_build_tree(self):
"""Regression test for building the tree names
"""
# logic is different from the fake backend.
user_api = identity_ldap.UserApi(CONF)
self.assertTrue(user_api)
self.assertEqual(user_api.tree_dn, CONF.ldap.user_tree_dn)
def tearDown(self):
tests.TestCase.tearDown(self)
def test_ldap_dereferencing(self):
alt_users_ldif = {'objectclass': ['top', 'organizationalUnit'],
'ou': 'alt_users'}
alt_fake_user_ldif = {'objectclass': ['person', 'inetOrgPerson'],
'cn': 'alt_fake1',
'sn': 'alt_fake1'}
aliased_users_ldif = {'objectclass': ['alias', 'extensibleObject'],
'aliasedobjectname': "ou=alt_users,%s" %
CONF.ldap.suffix}
create_object("ou=alt_users,%s" % CONF.ldap.suffix, alt_users_ldif)
create_object("%s=alt_fake1,ou=alt_users,%s" %
(CONF.ldap.user_id_attribute, CONF.ldap.suffix),
alt_fake_user_ldif)
create_object("ou=alt_users,%s" % CONF.ldap.user_tree_dn,
aliased_users_ldif)
self.config_fixture.config(group='ldap',
query_scope='sub',
alias_dereferencing='never')
self.identity_api = identity_ldap.Identity()
self.assertRaises(exception.UserNotFound,
self.identity_api.get_user,
'alt_fake1')
self.config_fixture.config(group='ldap',
alias_dereferencing='searching')
self.identity_api = identity_ldap.Identity()
user_ref = self.identity_api.get_user('alt_fake1')
self.assertEqual(user_ref['id'], 'alt_fake1')
self.config_fixture.config(group='ldap', alias_dereferencing='always')
self.identity_api = identity_ldap.Identity()
user_ref = self.identity_api.get_user('alt_fake1')
self.assertEqual(user_ref['id'], 'alt_fake1')
# FakeLDAP does not correctly process filters, so this test can only be
# run against a live LDAP server
def test_list_groups_for_user_filtered(self):
domain = self._get_domain_fixture()
test_groups = []
test_users = []
GROUP_COUNT = 3
USER_COUNT = 2
for x in range(0, USER_COUNT):
new_user = {'id': uuid.uuid4().hex, 'name': uuid.uuid4().hex,
'password': uuid.uuid4().hex, 'enabled': True,
'domain_id': domain['id']}
test_users.append(new_user)
self.identity_api.create_user(new_user['id'], new_user)
positive_user = test_users[0]
negative_user = test_users[1]
for x in range(0, USER_COUNT):
group_refs = self.identity_api.list_groups_for_user(
test_users[x]['id'])
self.assertEqual(len(group_refs), 0)
for x in range(0, GROUP_COUNT):
new_group = {'id': uuid.uuid4().hex,
'domain_id': domain['id'],
'name': uuid.uuid4().hex}
self.identity_api.create_group(new_group['id'], new_group)
test_groups.append(new_group)
group_refs = self.identity_api.list_groups_for_user(
positive_user['id'])
self.assertEqual(len(group_refs), x)
self.identity_api.add_user_to_group(
positive_user['id'],
new_group['id'])
group_refs = self.identity_api.list_groups_for_user(
positive_user['id'])
self.assertEqual(len(group_refs), x + 1)
group_refs = self.identity_api.list_groups_for_user(
negative_user['id'])
self.assertEqual(len(group_refs), 0)
self.config_fixture.config(group='ldap', group_filter='(dn=xx)')
self.reload_backends(CONF.identity.default_domain_id)
group_refs = self.identity_api.list_groups_for_user(
positive_user['id'])
self.assertEqual(len(group_refs), 0)
group_refs = self.identity_api.list_groups_for_user(
negative_user['id'])
self.assertEqual(len(group_refs), 0)
self.config_fixture.config(group='ldap',
group_filter='(objectclass=*)')
self.reload_backends(CONF.identity.default_domain_id)
group_refs = self.identity_api.list_groups_for_user(
positive_user['id'])
self.assertEqual(len(group_refs), GROUP_COUNT)
group_refs = self.identity_api.list_groups_for_user(
negative_user['id'])
self.assertEqual(len(group_refs), 0)
def test_user_enable_attribute_mask(self):
self.config_fixture.config(
group='ldap',
user_enabled_emulation=False,
user_enabled_attribute='employeeType')
super(LiveLDAPIdentity, self).test_user_enable_attribute_mask()
def test_create_project_case_sensitivity(self):
# The attribute used for the live LDAP tests is case insensitive.
def call_super():
super(LiveLDAPIdentity, self).\
test_create_project_case_sensitivity()
self.assertRaises(exception.Conflict, call_super)
def test_create_user_case_sensitivity(self):
# The attribute used for the live LDAP tests is case insensitive.
def call_super():
super(LiveLDAPIdentity, self).test_create_user_case_sensitivity()
self.assertRaises(exception.Conflict, call_super)
def test_project_update_missing_attrs_with_a_falsey_value(self):
# The description attribute doesn't allow an empty value.
def call_super():
super(LiveLDAPIdentity, self).\
test_project_update_missing_attrs_with_a_falsey_value()
self.assertRaises(ldap.INVALID_SYNTAX, call_super)