示例#1
0
def ldap_init(ro, ldap_url, bind_dn, bind_pw, user_ou, group_ou, committee_ou):
    global user_search_ou, group_search_ou, committee_search_ou, ldap_conn, read_only
    read_only = ro
    user_search_ou = user_ou
    group_search_ou = group_ou
    committee_search_ou = committee_ou
    ldap_conn = ReconnectLDAPObject(ldap_url)
    ldap_conn.simple_bind_s(bind_dn, bind_pw)
示例#2
0
def ldap_connect(settings, use_cache=True):
    """Establishes an LDAP connection.

    Establishes a connection to the LDAP server from the `uri` in the
    ``settings``.

    To establish a connection, the settings must be specified:
     - ``uri``: valid URI which points to a LDAP server,
     - ``bind_dn``: `dn` used to initially bind every LDAP connection
     - ``bind_password``" password used for the initial bind
     - ``tls``: ``True`` if the connection should use TLS encryption
     - ``starttls``: ``True`` to negotiate TLS with the server

    `Note`: ``starttls`` is ignored if the URI uses LDAPS and ``tls`` is
    set to ``True``.

    This function re-uses an existing LDAP connection if there is one
    available in the application context, unless caching is disabled.

    :param settings: dict -- The settings for a LDAP provider.
    :param use_cache: bool -- If the connection should be cached.
    :return: The ldap connection.
    """

    if use_cache:
        cache = _get_ldap_cache()
        cache_key = frozenset(
            (k, hash(v)) for k, v in iteritems(settings) if k in conn_keys)
        conn = cache.get(cache_key)
        if conn is not None:
            return conn

    uri_info = urlparse(settings['uri'])
    use_ldaps = uri_info.scheme == 'ldaps'
    credentials = (settings['bind_dn'], settings['bind_password'])
    ldap_connection = ReconnectLDAPObject(settings['uri'])
    ldap_connection.protocol_version = ldap.VERSION3
    ldap_connection.set_option(ldap.OPT_REFERRALS, 0)
    ldap_connection.set_option(
        ldap.OPT_X_TLS,
        ldap.OPT_X_TLS_DEMAND if use_ldaps else ldap.OPT_X_TLS_NEVER)
    ldap_connection.set_option(
        ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_DEMAND
        if settings['verify_cert'] else ldap.OPT_X_TLS_ALLOW)
    # force the creation of a new TLS context. This must be the last TLS option.
    # see: http://stackoverflow.com/a/27713355/298479
    ldap_connection.set_option(ldap.OPT_X_TLS_NEWCTX, 0)
    if use_ldaps and settings['starttls']:
        warn(
            "Unable to start TLS, LDAP connection already secured over SSL (LDAPS)"
        )
    elif settings['starttls']:
        ldap_connection.start_tls_s()
    # TODO: allow anonymous bind
    ldap_connection.simple_bind_s(*credentials)
    if use_cache:
        cache[cache_key] = ldap_connection
    return ldap_connection
示例#3
0
class DatabaseWrapper(BaseDatabaseWrapper):
    def __init__(self, *args, **kwargs):
        super(DatabaseWrapper, self).__init__(*args, **kwargs)

        self.charset = "utf-8"
        self.creation = DatabaseCreation(self)
        self.features = DatabaseFeatures(self)
        if django.VERSION > (1, 4):
            self.ops = DatabaseOperations(self)
        else:
            self.ops = DatabaseOperations()
        self.settings_dict['SUPPORTS_TRANSACTIONS'] = False

    def close(self):
        pass

    def _commit(self):
        pass

    def _cursor(self):
        if self.connection is None:
#            self.connection = ldap.initialize(self.settings_dict['NAME'])
            self.connection = ReconnectLDAPObject(self.settings_dict['NAME'])
            self.connection.simple_bind_s(
                self.settings_dict['USER'],
                self.settings_dict['PASSWORD'])
        return DatabaseCursor(self.connection)

    def _rollback(self):
        pass

    def add_s(self, dn, modlist):
        cursor = self._cursor()
        return cursor.connection.add_s(dn.encode(self.charset), modlist)

    def delete_s(self, dn):
        cursor = self._cursor()
        return cursor.connection.delete_s(dn.encode(self.charset))

    def modify_s(self, dn, modlist):
        cursor = self._cursor()
        return cursor.connection.modify_s(dn.encode(self.charset), modlist)

    def rename_s(self, dn, newrdn):
        cursor = self._cursor()
        return cursor.connection.rename_s(dn.encode(self.charset), newrdn.encode(self.charset))

    def search_s(self, base, scope, filterstr='(objectClass=*)',attrlist=None):
        logger.debug("base: %s; scope: %s; filter: %s, attrs: %s" % (base, scope, filterstr, attrlist))
        cursor = self._cursor()
        results = cursor.connection.search_s(base, scope, filterstr.encode(self.charset), attrlist)
        output = []
        for dn, attrs in results:
            output.append((dn.decode(self.charset), attrs))
        return output
示例#4
0
def ldap_connect(settings, use_cache=True):
    """Establishes an LDAP connection.

    Establishes a connection to the LDAP server from the `uri` in the
    ``settings``.

    To establish a connection, the settings must be specified:
     - ``uri``: valid URI which points to a LDAP server,
     - ``bind_dn``: `dn` used to initially bind every LDAP connection
     - ``bind_password``" password used for the initial bind
     - ``tls``: ``True`` if the connection should use TLS encryption
     - ``starttls``: ``True`` to negotiate TLS with the server

    `Note`: ``starttls`` is ignored if the URI uses LDAPS and ``tls`` is
    set to ``True``.

    This function re-uses an existing LDAP connection if there is one
    available in the application context, unless caching is disabled.

    :param settings: dict -- The settings for a LDAP provider.
    :param use_cache: bool -- If the connection should be cached.
    :return: The ldap connection.
    """

    if use_cache:
        cache = _get_ldap_cache()
        cache_key = frozenset((k, hash(v)) for k, v in iteritems(settings) if k in conn_keys)
        conn = cache.get(cache_key)
        if conn is not None:
            return conn

    uri_info = urlparse(settings['uri'])
    use_ldaps = uri_info.scheme == 'ldaps'
    credentials = (settings['bind_dn'], settings['bind_password'])
    ldap_connection = ReconnectLDAPObject(settings['uri'])
    ldap_connection.protocol_version = ldap.VERSION3
    ldap_connection.set_option(ldap.OPT_REFERRALS, 0)
    ldap_connection.set_option(ldap.OPT_X_TLS, ldap.OPT_X_TLS_DEMAND if use_ldaps else ldap.OPT_X_TLS_NEVER)
    if settings['cert_file']:
        ldap_connection.set_option(ldap.OPT_X_TLS_CACERTFILE, settings['cert_file'])
    ldap_connection.set_option(ldap.OPT_X_TLS_REQUIRE_CERT,
                               ldap.OPT_X_TLS_DEMAND if settings['verify_cert'] else ldap.OPT_X_TLS_ALLOW)
    # force the creation of a new TLS context. This must be the last TLS option.
    # see: http://stackoverflow.com/a/27713355/298479
    ldap_connection.set_option(ldap.OPT_X_TLS_NEWCTX, 0)
    if use_ldaps and settings['starttls']:
        warn("Unable to start TLS, LDAP connection already secured over SSL (LDAPS)")
    elif settings['starttls']:
        ldap_connection.start_tls_s()
    # TODO: allow anonymous bind
    ldap_connection.simple_bind_s(*credentials)
    if use_cache:
        cache[cache_key] = ldap_connection
    return ldap_connection
示例#5
0
 def simple_bind_s(self, who='', cred='', serverctrls=None,
                   clientctrls=None):
     res = ReconnectLDAPObject.simple_bind_s(self, who, cred, serverctrls,
                                             clientctrls)
     self.connected = True
     self.who = who
     self.cred = cred
     return res
示例#6
0
文件: __init__.py 项目: keis/ldappool
 def simple_bind_s(self, who='', cred='', serverctrls=None,
                   clientctrls=None):
     res = ReconnectLDAPObject.simple_bind_s(self, who, cred, serverctrls,
                                             clientctrls)
     self.connected = True
     self.who = who
     self.cred = cred
     if self._connection_time is None:
         self._connection_time = time.time()
     return res
 def simple_bind_s(self, who='', cred='', serverctrls=None,
                   clientctrls=None):
     res = ReconnectLDAPObject.simple_bind_s(self, who, cred, serverctrls,
                                             clientctrls)
     self.connected = True
     self.who = who
     self.cred = cred
     if self._connection_time is None:
         self._connection_time = time.time()
     return res
示例#8
0
class LdapLookup(object):

    connection = None
    uri = None
    user = None
    password = None

    user_searchbase = ''
    group_searchbase = ''

    user_searchfilter = {'objectClass': 'user'}
    group_searchfilter = {'objectClass': 'group'}

    def __init__(self, **kw):
        for key, item in kw.items():
            if hasattr(self, key) and not key.startswith('_'):
                setattr(self, key, item)

    def connect(self):
        try:
            self.connection = ReconnectLDAPObject(self.uri)
            if self.user is not None:
                self.connection.simple_bind_s(self.user, self.password)
        except:
            logger.exception("LDAP connection failed")
            return False
        return True

    def get_safe(self, basedn, **kw):
        return self.get(
            basedn,
            **dict([(ldap_escape(k), ldap_escape(v))
                    for k, v in kw.iteritems()]))

    def get(self, basedn, **kw):
        search = '(&%s)' % ''.join(
            ['(%s=%s)' % item for item in kw.iteritems()])
        result = self.connection.search_s(basedn, ldap.SCOPE_SUBTREE, search)
        return result

    def get_dn(self, dn):
        res = self.connection.search_s(dn, ldap.SCOPE_BASE, '(objectClass=*)')
        if len(res) == 0:
            return None
        else:
            return res[0]

    def get_users(self):
        return self.get(self.user_searchbase, **self.user_searchfilter)

    def get_user(self, username):
        search = self.user_searchfilter.copy()
        if '@' in username:
            search['userPrincipalName'] = username
        else:
            search['sAMAccountName'] = username

        res = self.get_safe(self.user_searchbase, **search)
        if len(res) == 0:
            return None
        else:
            return res[0]

    def get_groups(self):
        return self.get(self.group_searchbase, **self.group_searchfilter)

    def get_group(self, groupname):
        search = self.group_searchfilter.copy()
        search['name'] = groupname
        res = self.get_safe(self.group_searchbase, **search)

        if len(res) == 0:
            return None
        else:
            return res[0]
示例#9
0
class LdapLookup(object):

    connection = None
    uri = None
    user = None
    password = None

    user_searchbase = ''
    group_searchbase = ''

    user_searchfilter = {'objectClass': 'user'}
    group_searchfilter = {'objectClass': 'group'}

    def __init__(self, **kw):
        for key, item in kw.items():
            if hasattr(self, key) and not key.startswith('_'):
                setattr(self, key, item)

    def connect(self):
        try:
            self.connection = ReconnectLDAPObject(self.uri)
            if self.user is not None:
                self.connection.simple_bind_s(self.user, self.password)
        except:
            logger.exception("LDAP connection failed")
            return False
        return True

    def get_safe(self, basedn, **kw):
        return self.get(basedn, **dict([(ldap_escape(k), ldap_escape(v)) for k, v in kw.iteritems()]))

    def get(self, basedn, **kw):
        search = '(&%s)' % ''.join(['(%s=%s)' % item for item in kw.iteritems()])
        result = self.connection.search_s(basedn, ldap.SCOPE_SUBTREE, search)
        return result

    def get_dn(self, dn):
        res = self.connection.search_s(dn, ldap.SCOPE_BASE, '(objectClass=*)')
        if len(res) == 0:
            return None
        else:
            return res[0]

    def get_users(self):
        return self.get(self.user_searchbase, **self.user_searchfilter)

    def get_user(self, username):
        search = self.user_searchfilter.copy()
        if '@' in username:
            search['userPrincipalName'] = username
        else:
            search['sAMAccountName'] = username

        res = self.get_safe(self.user_searchbase, **search)
        if len(res) == 0:
            return None
        else:
            return res[0]

    def get_groups(self):
        return self.get(self.group_searchbase, **self.group_searchfilter)

    def get_group(self, groupname):
        search = self.group_searchfilter.copy()
        search['name'] = groupname
        res = self.get_safe(self.group_searchbase, **search)

        if len(res) == 0:
            return None
        else:
            return res[0]
示例#10
0
class CSHLDAP:
    __domain__ = "csh.rit.edu"

    def __init__(self, bind_dn, bind_pw, batch_mods=False,
                 sasl=False, ro=False):
        """Handler for bindings to CSH LDAP.

        Keyword arguments:
        batch_mods -- whether or not to batch LDAP writes (default False)
        sasl -- whether or not to bypass bind_dn and bind_pw and use SASL bind
        ro -- whether or not CSH LDAP is in read only mode (default False)
        """
        if ro:
            print("########################################\n"
                  "#                                      #\n"
                  "#    CSH LDAP IS IN READ ONLY MODE     #\n"
                  "#                                      #\n"
                  "########################################")
        ldap_srvs = srvlookup.lookup("ldap", "tcp", self.__domain__)
        ldap_uris = ""
        for uri in ldap_srvs:
            ldap_uris += "ldaps://"+uri.hostname+","
        self.__con__ = ReconnectLDAPObject(ldap_uris)
        # Allow connections with self-signed certs 
        self.__con__.set_option(self.__con__.OPT_X_TLS_REQUIRE_CERT, self.__con__.OPT_X_TLS_ALLOW)
        if sasl:
            self.__con__.sasl_non_interactive_bind_s('')
        else:
            self.__con__.simple_bind_s(bind_dn, bind_pw)
        self.__mod_queue__ = {}
        self.__pending_mod_dn__ = []
        self.__batch_mods__ = batch_mods
        self.__ro__ = ro

    def get_member(self, val, uid=False):
        """Get a CSHMember object.

        Arguments:
        val -- the uuid (or uid) of the member

        Keyword arguments:
        uid -- whether or not val is a uid (default False)
        """
        return CSHMember(self, val, uid)

    def get_member_ibutton(self, val):
        """Get a CSHMember object.

        Arguments:
        val -- the iButton ID of the member

        Returns:
        None if the iButton supplied does not correspond to a CSH Member
        """
        members = self.__con__.search_s(
            CSHMember.__ldap_user_ou__,
            ldap.SCOPE_SUBTREE,
            "(ibutton=%s)" % val,
            ['ipaUniqueID'])
        if members:
            return CSHMember(
                    self,
                    members[0][1]['ipaUniqueID'][0].decode('utf-8'),
                    False)
        return None

    def get_member_slackuid(self, slack):
        """Get a CSHMember object.

        Arguments:
        slack -- the Slack UID of the member

        Returns:
        None if the Slack UID provided does not correspond to a CSH Member
        """
        members = self.__con__.search_s(
            CSHMember.__ldap_user_ou__,
            ldap.SCOPE_SUBTREE,
            "(slackuid=%s)" % slack,
            ['ipaUniqueID'])
        if members:
            return CSHMember(
                    self,
                    members[0][1]['ipaUniqueID'][0].decode('utf-8'),
                    False)
        return None

    def get_group(self, val):
        """Get a CSHGroup object.

        Arguments:
        val -- the cn of the group

        """
        return CSHGroup(self, val)

    def get_con(self):
        """Get the PyLDAP Connection"""
        return self.__con__

    def get_directorship_heads(self, val):
        """Get the head of a directorship

        Arguments:
        val -- the cn of the directorship
        """

        __ldap_group_ou__ = "cn=groups,cn=accounts,dc=csh,dc=rit,dc=edu"

        res = self.__con__.search_s(
                __ldap_group_ou__,
                ldap.SCOPE_SUBTREE,
                "(cn=eboard-%s)" % val,
                ['member'])

        ret = []
        for member in res[0][1]['member']:
            try:
                ret.append(member.decode('utf-8'))
            except UnicodeDecodeError:
                ret.append(member)
            except KeyError:
                continue

        return [CSHMember(self,
                dn.split('=')[1].split(',')[0],
                True)
                for dn in ret]

    def enqueue_mod(self, dn, mod):
        """Enqueue a LDAP modification.

        Arguments:
        dn -- the distinguished name of the object to modify
        mod -- an ldap modfication entry to enqueue
        """
        # mark for update
        if dn not in self.__pending_mod_dn__:
            self.__pending_mod_dn__.append(dn)
            self.__mod_queue__[dn] = []

        self.__mod_queue__[dn].append(mod)

    def flush_mod(self):
        """Flush all pending LDAP modifications."""
        for dn in self.__pending_mod_dn__:
            try:
                if self.__ro__:
                    for mod in self.__mod_queue__[dn]:
                        if mod[0] == ldap.MOD_DELETE:
                            mod_str = "DELETE"
                        elif mod[0] == ldap.MOD_ADD:
                            mod_str = "ADD"
                        else:
                            mod_str = "REPLACE"
                        print("{} VALUE {} = {} FOR {}".format(mod_str,
                                                               mod[1],
                                                               mod[2],
                                                               dn))
                else:
                    self.__con__.modify_s(dn, self.__mod_queue__[dn])
            except ldap.TYPE_OR_VALUE_EXISTS:
                print("Error! Conflicting Batch Modification: %s"
                      % str(self.__mod_queue__[dn]))
                continue
            except ldap.NO_SUCH_ATTRIBUTE:
                print("Error! Conflicting Batch Modification: %s"
                      % str(self.__mod_queue__[dn]))
                continue
            self.__mod_queue__[dn] = None
        self.__pending_mod_dn__ = []
示例#11
0
class DatabaseWrapper(BaseDatabaseWrapper):
    def __init__(self, *args, **kwargs):
        super(DatabaseWrapper, self).__init__(*args, **kwargs)

        self.charset = "utf-8"
        self.creation = DatabaseCreation(self)
        self.features = DatabaseFeatures(self)
        if django.VERSION > (1, 4):
            self.ops = DatabaseOperations(self)
        else:
            self.ops = DatabaseOperations()
        self.settings_dict['SUPPORTS_TRANSACTIONS'] = False

    def close(self):
        if hasattr(self, 'validate_thread_sharing'):
            # django >= 1.4
            self.validate_thread_sharing()
        if self.connection is not None:
            self.connection.unbind_s()
            self.connection = None

    def _commit(self):
        pass

    def _cursor(self):
        if self.connection is None:
#            self.connection = ldap.initialize(self.settings_dict['NAME'])
            self.connection = ReconnectLDAPObject(self.settings_dict['NAME'])
            self.connection.simple_bind_s(
                self.settings_dict['USER'],
                self.settings_dict['PASSWORD'])

        return DatabaseCursor(self.connection)

    def _rollback(self):
        pass

    def add_s(self, dn, modlist):
        cursor = self._cursor()
        return cursor.connection.add_s(dn.encode(self.charset), modlist)

    def delete_s(self, dn):
        cursor = self._cursor()
        return cursor.connection.delete_s(dn.encode(self.charset))

    def modify_s(self, dn, modlist):
        cursor = self._cursor()
        return cursor.connection.modify_s(dn.encode(self.charset), modlist)

    def rename_s(self, dn, newrdn):
        cursor = self._cursor()
        return cursor.connection.rename_s(dn.encode(self.charset),
                                          newrdn.encode(self.charset))

    def search_s(self, base, scope, filterstr='(objectClass=*)',
                 attrlist=None):
        logger.debug("base: %s; scope: %s; filter: %s, attrs: %s" % (base, scope, filterstr, attrlist))
        cursor = self._cursor()
        results = cursor.connection.search_s(base, scope,
                                             filterstr.encode(self.charset),
                                             attrlist)
        output = []
        for dn, attrs in results:
            output.append((dn.decode(self.charset), attrs))
        return output
示例#12
0
class DatabaseWrapper(BaseDatabaseWrapper):
    vendor = 'ldap'

    # NOTE: These are copied from the mysql DatabaseWrapper
    operators = {
        'exact': '= %s',
        'iexact': 'LIKE %s',
        'contains': 'LIKE BINARY %s',
        'icontains': 'LIKE %s',
        'regex': 'REGEXP BINARY %s',
        'iregex': 'REGEXP %s',
        'gt': '> %s',
        'gte': '>= %s',
        'lt': '< %s',
        'lte': '<= %s',
        'startswith': 'LIKE BINARY %s',
        'endswith': 'LIKE BINARY %s',
        'istartswith': 'LIKE %s',
        'iendswith': 'LIKE %s',
    }

    def __init__(self, *args, **kwargs):
        super(DatabaseWrapper, self).__init__(*args, **kwargs)

        self.charset = "utf-8"
        self.creation = DatabaseCreation(self)
        self.features = DatabaseFeatures(self)
        if django.VERSION > (1, 4):
            self.ops = DatabaseOperations(self)
        else:
            self.ops = DatabaseOperations()
        self.settings_dict['SUPPORTS_TRANSACTIONS'] = True
        self.autocommit = True

    def close(self):
        if hasattr(self, 'validate_thread_sharing'):
            # django >= 1.4
            self.validate_thread_sharing()
        if self.connection is not None:
            self.connection.unbind_s()
            self.connection = None

    def ensure_connection(self):
        if self.connection is None:
            #self.connection = ldap.initialize(self.settings_dict['NAME'])
            self.connection = ReconnectLDAPObject(self.settings_dict['NAME'])

            options = self.settings_dict.get('CONNECTION_OPTIONS', {})
            for opt, value in options.items():
                self.connection.set_option(opt, value)

            if self.settings_dict.get('TLS', False):
                self.connection.start_tls_s()

            self.connection.simple_bind_s(
                self.settings_dict['USER'],
                self.settings_dict['PASSWORD'])

    def _commit(self):
        pass

    def _cursor(self):
        self.ensure_connection()
        return DatabaseCursor(self.connection)

    def _rollback(self):
        pass

    def _set_autocommit(self, autocommit):
        pass

    def add_s(self, dn, modlist):
        cursor = self._cursor()
        return cursor.connection.add_s(dn.encode(self.charset), modlist)

    def delete_s(self, dn):
        cursor = self._cursor()
        return cursor.connection.delete_s(dn.encode(self.charset))

    def modify_s(self, dn, modlist):
        cursor = self._cursor()
        return cursor.connection.modify_s(dn.encode(self.charset), modlist)

    def rename_s(self, dn, newrdn):
        cursor = self._cursor()
        return cursor.connection.rename_s(dn.encode(self.charset),
                                          newrdn.encode(self.charset))

    def search_s(self, base, scope, filterstr='(objectClass=*)',
                 attrlist=None):
        cursor = self._cursor()
        results = cursor.connection.search_s(base, scope,
                                             filterstr.encode(self.charset),
                                             attrlist)
        output = []
        for dn, attrs in results:
            # skip referrals
            if dn is not None:
                output.append((dn.decode(self.charset), attrs))
        return output
示例#13
0
class LDAPUser(object):

    EPOCH_DATE = datetime.datetime.fromtimestamp(0)
    LOGIN_SHELL = "/bin/bash"
    HOME_BASE_DIRECTORY = "/home"
    GROUP_ID_NUMBER = None
    CREATOR = None
    VERIFY_CREATOR = True

    password_compile = re.compile("^{(%s)}(\S+)$" % "|".join(SUPPORT_METHOD))

    def __init__(self,
                 ldap_uri,
                 base_dn,
                 admin_user,
                 admin_password,
                 home_base_directory=None,
                 group_id=None):
        self.ldap_uri = ldap_uri
        self.ldap_com = ReconnectLDAPObject(ldap_uri,
                                            retry_max=10,
                                            retry_delay=3)
        # self.ldap_com = ldap.initialize(ldap_uri)
        self.ldap_base_dn = base_dn
        if home_base_directory is not None:
            self.HOME_BASE_DIRECTORY = home_base_directory
        if group_id is not None:
            self.GROUP_ID_NUMBER = "%s" % group_id
        self.ldap_admin = "cn=%s,%s" % (admin_user, self.ldap_base_dn)
        self.people_base_cn = "ou=People,%s" % self.ldap_base_dn
        self.ldap_com.bind_s(self.ldap_admin, admin_password)

    def __get_next_uid_number(self):
        filter_str = "uid=*"
        attributes = ["uidNumber", "gidNumber"]
        items = self.ldap_com.search_s(self.people_base_cn, LDAP_SCOPE_SUBTREE,
                                       filter_str, attributes)
        max_number = 1000
        for item in items:
            if "uidNumber" in item[1]:
                u_number = int(item[1]["uidNumber"][0])
                if max_number < u_number:
                    max_number = u_number
        return max_number + 1

    def delete_user(self, user_name):
        if self.exist(user_name) is None:
            return True
        dn = "uid=%s,%s" % (user_name, self.people_base_cn)
        return self.ldap_com.delete_s(dn)

    def add_user(self,
                 user_name,
                 uid_number=None,
                 gid_number=None,
                 home_directory=None):
        dn = "uid=%s,%s" % (user_name, self.people_base_cn)
        attributes = dict()
        for key in ("uid", "cn", "sn"):
            attributes[key] = encode(user_name)
        attributes["objectClass"] = [
            "top", "person", "inetOrgPerson", "posixAccount",
            "organizationalPerson", "shadowAccount"
        ]
        if uid_number is None:
            attributes["uidNumber"] = encode(self.__get_next_uid_number())
        else:
            attributes["uidNumber"] = encode(uid_number)
        if gid_number is None:
            attributes["gidNumber"] = encode(self.GROUP_ID_NUMBER)
        else:
            attributes["gidNumber"] = encode(gid_number)
        if attributes["gidNumber"] is None:
            raise LDAPError("please set gidNumber")
        if home_directory is None:
            home_directory = os.path.join(self.HOME_BASE_DIRECTORY, user_name)
        attributes["homeDirectory"] = encode(home_directory)
        attributes["loginShell"] = encode(self.LOGIN_SHELL)
        if self.CREATOR is not None:
            attributes["givenName"] = encode(self.CREATOR)
        mod_list = ldap.modlist.addModlist(attributes)
        self.ldap_com.add_s(dn, mod_list)

    def expire_user(self, user_name, shadow_expire=None):
        user_entry = self.exist(user_name)
        if user_entry is None:
            return False
        old_attributes = user_entry[1]
        attributes = dict(**old_attributes)
        if shadow_expire is None:
            attributes["shadowExpire"] = [
                "%s" % (datetime.datetime.now() - self.EPOCH_DATE).days
            ]
        else:
            attributes["shadowExpire"] = ""
        mod_list = ldap.modlist.modifyModlist(old_attributes, attributes)
        return self.ldap_com.modify_s(user_entry[0], mod_list)

    def block_user(self, user_name):
        return self.expire_user(user_name)

    def unlock_user(self, user_name):
        user_entry = self.exist(user_name, "pwdAccountLockedTime",
                                "shadowExpire")
        if user_entry is None:
            return False
        old_attributes = user_entry[1]
        attributes = dict(**old_attributes)
        if "pwdAccountLockedTime" in attributes:
            attributes["pwdAccountLockedTime"] = ""
        if "shadowExpire" in attributes:
            attributes["shadowExpire"] = ""
        mod_list = ldap.modlist.modifyModlist(old_attributes, attributes)
        return self.ldap_com.modify_s(user_entry[0], mod_list)

    def _verify_creator(self, user_entry):
        if self.CREATOR is None or self.VERIFY_CREATOR is False:
            return True
        if "givenName" not in user_entry:
            return False
        return self.CREATOR in user_entry["givenName"]

    def exist(self, user_name, *attributes):
        l_attributes = set(attributes)
        sr = self.ldap_com.search_s(self.ldap_base_dn, LDAP_SCOPE_SUBTREE,
                                    "uid=%s" % user_name, l_attributes)
        if len(sr) <= 0:
            return None
        return sr[0]

    def verify_password(self, password, ldap_password):
        match_r = self.password_compile.match(ldap_password)
        if match_r is not None:
            method = match_r.groups()[0]
            cipher_text = match_r.groups()[1]
            return verify(method, password, cipher_text)
        return password == ldap_password

    def login(self, user_name, password):
        user_entry = self.exist(user_name)
        if user_entry is None:
            return False
        attr = user_entry[1]
        if "userPassword" not in attr:
            return False
        ldap_password = attr["userPassword"][0]
        return self.verify_password(password, ldap_password)

    def login2(self, user_name, password):
        user_entry = self.exist(user_name)
        if user_entry is None:
            return False
        attr = user_entry[1]
        if "userPassword" not in attr:
            return False
        try:
            self.ldap_com.simple_bind_s(user_entry[0], password)
            return True
        except ldap.INVALID_CREDENTIALS:
            return False

    def set_password(self, user_name, new_password):
        user_entry = self.exist(user_name)
        if user_entry is None:
            return False
        if self._verify_creator(user_entry) is False:
            return False
        pr = self.ldap_com.passwd_s(user_entry[0], None, new_password)
        if pr[0] is None and pr[1] is None:
            return True
        return False