¡@

Home 

OpenStack Study: ldap.py

OpenStack Index

**** CubicPower OpenStack Study ****

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

from __future__ import absolute_import

import uuid

import ldap

import ldap.filter

from keystone import clean

from keystone.common import dependency

from keystone.common import driver_hints

from keystone.common import ldap as common_ldap

from keystone.common import models

from keystone.common import utils

from keystone import config

from keystone import exception

from keystone import identity

from keystone.openstack.common.gettextutils import _

from keystone.openstack.common import log

CONF = config.CONF

LOG = log.getLogger(__name__)

@dependency.requires('assignment_api')

**** CubicPower OpenStack Study ****

class Identity(identity.Driver):

**** CubicPower OpenStack Study ****

    def __init__(self, conf=None):

        super(Identity, self).__init__()

        if conf is None:

            conf = CONF

        self.user = UserApi(conf)

        self.group = GroupApi(conf)

**** CubicPower OpenStack Study ****

    def default_assignment_driver(self):

        return "keystone.assignment.backends.ldap.Assignment"

**** CubicPower OpenStack Study ****

    def is_domain_aware(self):

        return False

    # Identity interface

**** CubicPower OpenStack Study ****

    def authenticate(self, user_id, password):

        try:

            user_ref = self._get_user(user_id)

        except exception.UserNotFound:

            raise AssertionError(_('Invalid user / password'))

        if not user_id or not password:

            raise AssertionError(_('Invalid user / password'))

        conn = None

        try:

            conn = self.user.get_connection(self.user._id_to_dn(user_id),

                                            password)

            if not conn:

                raise AssertionError(_('Invalid user / password'))

        except Exception:

            raise AssertionError(_('Invalid user / password'))

        finally:

            if conn:

                conn.unbind_s()

        return identity.filter_user(user_ref)

**** CubicPower OpenStack Study ****

    def _get_user(self, user_id):

        return self.user.get(user_id)

**** CubicPower OpenStack Study ****

    def get_user(self, user_id):

        return identity.filter_user(self._get_user(user_id))

**** CubicPower OpenStack Study ****

    def list_users(self, hints):

        return self.user.get_all_filtered()

**** CubicPower OpenStack Study ****

    def get_user_by_name(self, user_name, domain_id):

        # domain_id will already have been handled in the Manager layer,

        # parameter left in so this matches the Driver specification

        return identity.filter_user(self.user.get_by_name(user_name))

    # CRUD

**** CubicPower OpenStack Study ****

    def create_user(self, user_id, user):

        self.user.check_allow_create()

        user_ref = self.user.create(user)

        return identity.filter_user(user_ref)

**** CubicPower OpenStack Study ****

    def update_user(self, user_id, user):

        self.user.check_allow_update()

        if 'id' in user and user['id'] != user_id:

            raise exception.ValidationError(_('Cannot change user ID'))

        old_obj = self.user.get(user_id)

        if 'name' in user and old_obj.get('name') != user['name']:

            raise exception.Conflict(_('Cannot change user name'))

        user = utils.hash_ldap_user_password(user)

        if self.user.enabled_mask:

            self.user.mask_enabled_attribute(user)

        self.user.update(user_id, user, old_obj)

        return self.user.get_filtered(user_id)

**** CubicPower OpenStack Study ****

    def delete_user(self, user_id):

        self.user.check_allow_delete()

        self.assignment_api.delete_user(user_id)

        user_dn = self.user._id_to_dn(user_id)

        groups = self.group.list_user_groups(user_dn)

        for group in groups:

            self.group.remove_user(user_dn, group['id'], user_id)

        user = self.user.get(user_id)

        if hasattr(user, 'tenant_id'):

            self.project.remove_user(user.tenant_id,

                                     self.user._id_to_dn(user_id))

        self.user.delete(user_id)

**** CubicPower OpenStack Study ****

    def create_group(self, group_id, group):

        self.group.check_allow_create()

        group['name'] = clean.group_name(group['name'])

        return self.group.create(group)

**** CubicPower OpenStack Study ****

    def get_group(self, group_id):

        return self.group.get(group_id)

**** CubicPower OpenStack Study ****

    def update_group(self, group_id, group):

        self.group.check_allow_update()

        if 'name' in group:

            group['name'] = clean.group_name(group['name'])

        return self.group.update(group_id, group)

**** CubicPower OpenStack Study ****

    def delete_group(self, group_id):

        self.group.check_allow_delete()

        return self.group.delete(group_id)

**** CubicPower OpenStack Study ****

    def add_user_to_group(self, user_id, group_id):

        self.get_user(user_id)

        self.get_group(group_id)

        user_dn = self.user._id_to_dn(user_id)

        self.group.add_user(user_dn, group_id, user_id)

**** CubicPower OpenStack Study ****

    def remove_user_from_group(self, user_id, group_id):

        self.get_user(user_id)

        self.get_group(group_id)

        user_dn = self.user._id_to_dn(user_id)

        self.group.remove_user(user_dn, group_id, user_id)

**** CubicPower OpenStack Study ****

    def list_groups_for_user(self, user_id, hints):

        self.get_user(user_id)

        user_dn = self.user._id_to_dn(user_id)

        return self.group.list_user_groups(user_dn)

**** CubicPower OpenStack Study ****

    def list_groups(self, hints):

        return self.group.get_all()

**** CubicPower OpenStack Study ****

    def list_users_in_group(self, group_id, hints):

        self.get_group(group_id)

        users = []

        for user_dn in self.group.list_group_users(group_id):

            user_id = self.user._dn_to_id(user_dn)

            try:

                users.append(self.user.get_filtered(user_id))

            except exception.UserNotFound:

                LOG.debug(_("Group member '%(user_dn)s' not found in"

                            " '%(group_id)s'. The user should be removed"

                            " from the group. The user will be ignored."),

                          dict(user_dn=user_dn, group_id=group_id))

        return users

**** CubicPower OpenStack Study ****

    def check_user_in_group(self, user_id, group_id):

        self.get_user(user_id)

        self.get_group(group_id)

        user_refs = self.list_users_in_group(group_id, driver_hints.Hints())

        found = False

        for x in user_refs:

            if x['id'] == user_id:

                found = True

                break

        if not found:

            raise exception.NotFound(_('User not found in group'))

# TODO(termie): turn this into a data object and move logic to driver

**** CubicPower OpenStack Study ****

class UserApi(common_ldap.EnabledEmuMixIn, common_ldap.BaseLdap):

DEFAULT_OU = 'ou=Users'

DEFAULT_STRUCTURAL_CLASSES = ['person']

DEFAULT_ID_ATTR = 'cn'

DEFAULT_OBJECTCLASS = 'inetOrgPerson'

NotFound = exception.UserNotFound

options_name = 'user'

attribute_options_names = {'password': 'pass',

'email': 'mail',

'name': 'name',

'enabled': 'enabled',

'

**** CubicPower OpenStack Study ****

    def __init__(self, conf):

        super(UserApi, self).__init__(conf)

        self.enabled_mask = conf.ldap.user_enabled_mask

        self.enabled_default = conf.ldap.user_enabled_default

**** CubicPower OpenStack Study ****

    def _ldap_res_to_model(self, res):

        obj = super(UserApi, self)._ldap_res_to_model(res)

        if self.enabled_mask != 0:

            enabled = int(obj.get('enabled', self.enabled_default))

            obj['enabled'] = ((enabled & self.enabled_mask) !=

                              self.enabled_mask)

        return obj

**** CubicPower OpenStack Study ****

    def mask_enabled_attribute(self, values):

        value = values['enabled']

        values.setdefault('enabled_nomask', int(self.enabled_default))

        if value != ((values['enabled_nomask'] & self.enabled_mask) !=

                     self.enabled_mask):

            values['enabled_nomask'] ^= self.enabled_mask

        values['enabled'] = values['enabled_nomask']

        del values['enabled_nomask']

**** CubicPower OpenStack Study ****

    def create(self, values):

        values = utils.hash_ldap_user_password(values)

        if self.enabled_mask:

            orig_enabled = values['enabled']

            self.mask_enabled_attribute(values)

        values = super(UserApi, self).create(values)

        if self.enabled_mask:

            values['enabled'] = orig_enabled

        return values

**** CubicPower OpenStack Study ****

    def check_password(self, user_id, password):

        user = self.get(user_id)

        return utils.check_password(password, user.password)

**** CubicPower OpenStack Study ****

    def get_filtered(self, user_id):

        user = self.get(user_id)

        return identity.filter_user(user)

**** CubicPower OpenStack Study ****

    def get_all_filtered(self):

        return [identity.filter_user(user) for user in self.get_all()]

**** CubicPower OpenStack Study ****

class GroupApi(common_ldap.BaseLdap):

DEFAULT_OU = 'ou=UserGroups'

DEFAULT_STRUCTURAL_CLASSES = []

DEFAULT_OBJECTCLASS = 'groupOfNames'

DEFAULT_ID_ATTR = 'cn'

DEFAULT_MEMBER_ATTRIBUTE = 'member'

NotFound = exception.GroupNotFound

options_name = 'group'

attribute_options_names = {'description': 'desc',

'name': 'name'}

immutable_attrs = ['name']

model = models.Group

**** CubicPower OpenStack Study ****

    def __init__(self, conf):

        super(GroupApi, self).__init__(conf)

        self.member_attribute = (getattr(conf.ldap, 'group_member_attribute')

                                 or self.DEFAULT_MEMBER_ATTRIBUTE)

**** CubicPower OpenStack Study ****

    def create(self, values):

        data = values.copy()

        if data.get('id') is None:

            data['id'] = uuid.uuid4().hex

        if 'description' in data and data['description'] in ['', None]:

            data.pop('description')

        return super(GroupApi, self).create(data)

**** CubicPower OpenStack Study ****

    def delete(self, group_id):

        if self.subtree_delete_enabled:

            super(GroupApi, self).deleteTree(group_id)

        else:

            # TODO(spzala): this is only placeholder for group and domain

            # role support which will be added under bug 1101287

            query = '(objectClass=%s)' % self.object_class

            dn = None

            dn = self._id_to_dn(group_id)

            if dn:

                try:

                    conn = self.get_connection()

                    roles = conn.search_s(dn, ldap.SCOPE_ONELEVEL,

                                          query, ['%s' % '1.1'])

                    for role_dn, _ in roles:

                        conn.delete_s(role_dn)

                except ldap.NO_SUCH_OBJECT:

                    pass

                finally:

                    conn.unbind_s()

            super(GroupApi, self).delete(group_id)

**** CubicPower OpenStack Study ****

    def update(self, group_id, values):

        old_obj = self.get(group_id)

        return super(GroupApi, self).update(group_id, values, old_obj)

**** CubicPower OpenStack Study ****

    def add_user(self, user_dn, group_id, user_id):

        conn = self.get_connection()

        try:

            conn.modify_s(

                self._id_to_dn(group_id),

                [(ldap.MOD_ADD,

                  self.member_attribute,

                  user_dn)])

        except ldap.TYPE_OR_VALUE_EXISTS:

            raise exception.Conflict(_(

                'User %(user_id)s is already a member of group %(group_id)s') %

                {'user_id': user_id, 'group_id': group_id})

        finally:

            conn.unbind_s()

**** CubicPower OpenStack Study ****

    def remove_user(self, user_dn, group_id, user_id):

        conn = self.get_connection()

        try:

            conn.modify_s(

                self._id_to_dn(group_id),

                [(ldap.MOD_DELETE,

                  self.member_attribute,

                  user_dn)])

        except ldap.NO_SUCH_ATTRIBUTE:

            raise exception.UserNotFound(user_id=user_id)

        finally:

            conn.unbind_s()

**** CubicPower OpenStack Study ****

    def list_user_groups(self, user_dn):

        """Return a list of groups for which the user is a member."""

        user_dn_esc = ldap.filter.escape_filter_chars(user_dn)

        query = '(&(objectClass=%s)(%s=%s)%s)' % (self.object_class,

                                                  self.member_attribute,

                                                  user_dn_esc,

                                                  self.ldap_filter or '')

        memberships = self.get_all(query)

        return memberships

**** CubicPower OpenStack Study ****

    def list_group_users(self, group_id):

        """Return a list of user dns which are members of a group."""

        query = '(objectClass=%s)' % self.object_class

        conn = self.get_connection()

        group_dn = self._id_to_dn(group_id)

        try:

            attrs = conn.search_s(group_dn,

                                  ldap.SCOPE_BASE,

                                  query, ['%s' % self.member_attribute])

        except ldap.NO_SUCH_OBJECT:

            return []

        finally:

            conn.unbind_s()

        users = []

        for dn, member in attrs:

            user_dns = member.get(self.member_attribute, [])

            for user_dn in user_dns:

                if self.use_dumb_member and user_dn == self.dumb_member:

                    continue

                users.append(user_dn)

        return users