Version for python 3.6
- basic syntax fixes - bytestring to utf8 conversions - __init__.py import problem fix
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
import logging
|
||||
from ldapuserdir import LdapUserDir, __version__ as libversion, LdapUserDirError
|
||||
@@ -7,7 +7,7 @@ import sys
|
||||
import os
|
||||
import pwd
|
||||
from optparse import OptionParser
|
||||
import ConfigParser
|
||||
import configparser
|
||||
import getpass
|
||||
|
||||
class MyOptParser(OptionParser):
|
||||
@@ -17,7 +17,7 @@ class MyOptParser(OptionParser):
|
||||
return self.epilog.replace('%prog',os.path.basename(sys.argv[0]))
|
||||
|
||||
def read_cfg(filename):
|
||||
cfg = ConfigParser.ConfigParser()
|
||||
cfg = configparser.ConfigParser()
|
||||
|
||||
try:
|
||||
mylogger.debug('reading config from %s' % filename)
|
||||
@@ -29,7 +29,7 @@ def read_cfg(filename):
|
||||
'default_user_dn' : cfg.get('Ldap','default_user_dn'),
|
||||
'default_user_pw' : cfg.get('Ldap','default_user_pw'),
|
||||
}
|
||||
except Exception, err:
|
||||
except Exception as err:
|
||||
sys.stderr.write("Error in reading configuration from %s\n" % filename)
|
||||
sys.stderr.write(str(err)+'\n')
|
||||
sys.exit(1)
|
||||
@@ -93,14 +93,15 @@ usage += """
|
||||
usage_epilog = """
|
||||
Examples:
|
||||
List group members
|
||||
%prog svc-ra_x06sa
|
||||
%prog 'svc-ra_*'
|
||||
%prog svc-cluster_merlin5
|
||||
%prog 'svc-cluster_*'
|
||||
|
||||
Get group memberships for user mueller (optionally with a group filter)
|
||||
%prog -g mueller
|
||||
%prog -g mueller 'svc-ra*'
|
||||
%prog -g mueller 'svc-cluster_merlin5'
|
||||
%prog -g mueller 'svc-cluster_*'
|
||||
|
||||
Add/delete users to/from a group (requires access rights!)
|
||||
Add/delete users to/from a group (requires access rights defined in LDAP service!)
|
||||
%prog -a svc-ra_x06sa user1 user2 user3
|
||||
%prog -d svc-ra_x06sa user1 user2
|
||||
|
||||
@@ -108,8 +109,9 @@ usage_epilog = """
|
||||
%prog -u 'mueller*'
|
||||
|
||||
List users matching a mail address pattern
|
||||
%prog -m '*mueller@psi*
|
||||
%prog -m '*mueller@psi*'
|
||||
|
||||
Author: 2013-19 D. Feichtinger <derek.feichtinger@psi.ch>
|
||||
"""
|
||||
|
||||
examplecfg = """# Configuration file example:
|
||||
@@ -305,7 +307,7 @@ if flag_needprivileges and user_dn == config['default_user_dn']:
|
||||
logger = mylogger)
|
||||
try:
|
||||
user_dn = l_unpriv.systemuser2dn(loginname)
|
||||
except LdapUserDirError, err:
|
||||
except LdapUserDirError as err:
|
||||
if str(err) == 'No such user':
|
||||
sys.stderr.write('''
|
||||
Error: Need priviledged user and cannot map your system user "%s"
|
||||
@@ -315,7 +317,7 @@ to LDAP DN for binding (you may want to use the explicit -D user_dn option)
|
||||
else:
|
||||
sys.stderr.write('Uncaught Error: %s' % str(err))
|
||||
|
||||
except ldap.LDAPError, e:
|
||||
except ldap.LDAPError as e:
|
||||
sys.stderr.write('LDAP error: %s\n' % str(e))
|
||||
sys.exit(1)
|
||||
|
||||
@@ -352,7 +354,7 @@ try:
|
||||
|
||||
|
||||
elif mode == 'userlist':
|
||||
records = ldapdir.get_users(userfilter, config['user_ou'], mssfu=flag_mssfu)
|
||||
records = ldapdir.get_users(userfilter, config['user_ou'], mssfu=flag_mssfu)
|
||||
ldapdir.list_users_etcpwd(records, verbose = flag_verbose)
|
||||
|
||||
elif mode == 'maillist':
|
||||
@@ -372,7 +374,7 @@ try:
|
||||
+ '\n')
|
||||
#sys.stdout.write("\n".join(ldapdir.get_groups_for_user(user_to_group))
|
||||
# + "\n")
|
||||
except LdapUserDirError, err:
|
||||
except LdapUserDirError as err:
|
||||
if str(err) == "No such user":
|
||||
sys.stderr.write('Error: No such user (%s)\n' % user_to_group)
|
||||
else:
|
||||
@@ -393,9 +395,9 @@ try:
|
||||
group = args.pop(0)
|
||||
ldapdir.del_groupmembers(group, args)
|
||||
|
||||
except ldap.INVALID_CREDENTIALS, e:
|
||||
except ldap.INVALID_CREDENTIALS as e:
|
||||
sys.exit(1)
|
||||
except ldap.LDAPError, e:
|
||||
except ldap.LDAPError as e:
|
||||
sys.stderr.write('Unhandled LDAP error: %s\n' % str(e))
|
||||
sys.exit(1)
|
||||
# except Exception, err:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package:
|
||||
name: ldapuserdir
|
||||
version: "2.1.5"
|
||||
version: "2.2.0"
|
||||
|
||||
source:
|
||||
path: ../../
|
||||
@@ -15,6 +15,7 @@ requirements:
|
||||
run:
|
||||
- python
|
||||
- python-ldap
|
||||
- configparser
|
||||
|
||||
build:
|
||||
preserve_egg_dir: True
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
from ldapuserdir import LdapUserDir, LdapUserDirError
|
||||
from version import __version__
|
||||
from ldapuserdir.ldapuserdir import LdapUserDir, LdapUserDirError
|
||||
from ldapuserdir.version import __version__
|
||||
|
||||
@@ -90,7 +90,8 @@ class LdapUserDir(object):
|
||||
self.logger = logger
|
||||
|
||||
self._ldap = ldap.initialize(self.serverurl, trace_level=0,
|
||||
trace_file=sys.stderr)
|
||||
trace_file=sys.stderr,
|
||||
bytes_mode=False)
|
||||
self.logger.debug('binding to: %s\n' % serverurl)
|
||||
self.logger.debug('binding as user: %s\n' % user_dn)
|
||||
|
||||
@@ -108,6 +109,12 @@ class LdapUserDir(object):
|
||||
except ldap.LDAPError:
|
||||
raise
|
||||
|
||||
@staticmethod
|
||||
def ensure_utf8(bstr):
|
||||
if type(bstr) == bytes:
|
||||
return bstr.decode('utf-8')
|
||||
return bstr
|
||||
|
||||
@staticmethod
|
||||
def has_dn_format(name):
|
||||
"""Returns true if name has the format of a distinguished name
|
||||
@@ -148,6 +155,11 @@ class LdapUserDir(object):
|
||||
"""Helper for search_s_reconn.
|
||||
|
||||
Wraps ldap.search_ext to use paged results if desired (see self.page_size).
|
||||
|
||||
Returns
|
||||
-------
|
||||
list of tuples
|
||||
list of tuples of the form (dn, attributes)
|
||||
"""
|
||||
if self.page_size == 0:
|
||||
# Do not use paged results
|
||||
@@ -165,18 +177,22 @@ class LdapUserDir(object):
|
||||
serverctrls=[page_ctrl])
|
||||
|
||||
results = []
|
||||
npages=0
|
||||
while True:
|
||||
_, rdata, _, resp_ctrls = self._ldap.result3(msgid)
|
||||
npages += 1
|
||||
results.extend(rdata)
|
||||
|
||||
# Extract the SimplePagedResultsControl to get the cookie.
|
||||
page_ctrls = [c for c in resp_ctrls if c.controlType == SimplePagedResultsControl.controlType]
|
||||
if page_ctrls == [] or page_ctrls[0].cookie == '':
|
||||
pagecontrols = [c for c in resp_ctrls if c.controlType == SimplePagedResultsControl.controlType]
|
||||
self.logger.debug('Retrieved page %d of results' % npages)
|
||||
if not pagecontrols:
|
||||
raise RuntimeError("The server ignores RFC 2696 control (paged results)")
|
||||
if not pagecontrols[0].cookie:
|
||||
# We're done.
|
||||
break
|
||||
else:
|
||||
# Update the cookie to retrieve the next page.
|
||||
page_ctrl.cookie = page_ctrls[0].cookie
|
||||
# Update the cookie to retrieve the next page.
|
||||
page_ctrl.cookie = pagecontrols[0].cookie
|
||||
|
||||
msgid = self._ldap.search_ext(base, scope, filterstr, attrlist, attrsonly,
|
||||
serverctrls=[page_ctrl])
|
||||
@@ -226,7 +242,7 @@ class LdapUserDir(object):
|
||||
attempts += 1
|
||||
repl = self._search_s(base, scope, filterstr, attrlist,
|
||||
attrsonly)
|
||||
except Exception, err:
|
||||
except Exception as err:
|
||||
ok = False
|
||||
|
||||
self.logger.warning("Got ldap error: %s" % (err,))
|
||||
@@ -236,7 +252,7 @@ class LdapUserDir(object):
|
||||
# we try to reconnect and rebind
|
||||
try:
|
||||
del self._ldap
|
||||
except Exception, err:
|
||||
except Exception as err:
|
||||
self.logger.warning("failed to delete LDAP object: %s"
|
||||
% (err,))
|
||||
|
||||
@@ -246,13 +262,14 @@ class LdapUserDir(object):
|
||||
|
||||
try:
|
||||
self._ldap = ldap.initialize(self.serverurl, trace_level=0,
|
||||
trace_file=sys.stderr)
|
||||
trace_file=sys.stderr,
|
||||
bytes_mode=False)
|
||||
except ldap.SERVER_DOWN:
|
||||
self.logger.warning("ldap initialization error" +
|
||||
", server down (server: %s)" %
|
||||
self.serverurl
|
||||
+ ": %s" % (err,))
|
||||
except Exception, err:
|
||||
except Exception as err:
|
||||
self.logger.warning("ldap initialization error" +
|
||||
" (server: %s)" %
|
||||
self.serverurl
|
||||
@@ -263,7 +280,7 @@ class LdapUserDir(object):
|
||||
except ldap.INVALID_CREDENTIALS:
|
||||
self.logger.error('Authentication failure for dn:"%s"\n'
|
||||
% self.user_dn)
|
||||
except Exception, err:
|
||||
except Exception as err:
|
||||
self.logger.warning("ldap binding error" +
|
||||
" (server: %s)" %
|
||||
self.serverurl
|
||||
@@ -342,14 +359,14 @@ class LdapUserDir(object):
|
||||
if verbose:
|
||||
for k in fields + ['description', 'mail', 'mobile']:
|
||||
if k in entry:
|
||||
sys.stdout.write('[%s:]%s:' % (k, entry[k][0]))
|
||||
sys.stdout.write('[%s:]%s:' % (k, self.ensure_utf8(entry[k][0])))
|
||||
else:
|
||||
sys.stdout.write('[%s:]N.A.:' % k)
|
||||
sys.stdout.write('\n')
|
||||
else:
|
||||
for k in fields:
|
||||
if k in entry:
|
||||
sys.stdout.write('%s:' % (entry[k][0],))
|
||||
sys.stdout.write('%s:' % (self.ensure_utf8(entry[k][0])))
|
||||
else:
|
||||
sys.stdout.write('N.A.:')
|
||||
sys.stdout.write('\n')
|
||||
@@ -384,8 +401,9 @@ class LdapUserDir(object):
|
||||
if len(r) == 0:
|
||||
raise LdapUserDirError("No such user")
|
||||
|
||||
self.logger.debug('systemuser2dn: dn = %s' % r[0][0])
|
||||
return r[0][0]
|
||||
dn = self.ensure_utf8(r[0][0])
|
||||
self.logger.debug('systemuser2dn: dn = %s' % dn)
|
||||
return dn
|
||||
|
||||
def get_groups_struct(self, gfilter='*', ou=None, mssfu=False):
|
||||
"""searches for groups that match filter
|
||||
@@ -463,7 +481,7 @@ class LdapUserDir(object):
|
||||
|
||||
grplist = []
|
||||
if 'memberOf' in r[0][1]:
|
||||
grplist = r[0][1]['memberOf']
|
||||
grplist = [self.ensure_utf8(g) for g in r[0][1]['memberOf']]
|
||||
|
||||
if mssfu:
|
||||
srch = '(msSFU30GidNumber=*)'
|
||||
@@ -522,7 +540,7 @@ class LdapUserDir(object):
|
||||
if not returndn:
|
||||
try:
|
||||
reslist = [self.dn_to_cn(grp) for grp in reslist]
|
||||
except RuntimeError, e:
|
||||
except RuntimeError as e:
|
||||
self.logger.error(str(e))
|
||||
|
||||
if gfilter:
|
||||
@@ -563,16 +581,16 @@ class LdapUserDir(object):
|
||||
indent_increment = 3 # amount to indent members
|
||||
for dn, entry in r:
|
||||
if returndn:
|
||||
print("%sgroup: %s" % (' '*indent, dn)),
|
||||
print("%sgroup: %s" % (' '*indent, dn), end=''),
|
||||
else:
|
||||
print("%sgroup: %s" % (' '*indent, entry['cn'][0])),
|
||||
print("%sgroup: %s" % (' '*indent, self.ensure_utf8(entry['cn'][0])), end='')
|
||||
if not 'msSFU30GidNumber' in entry:
|
||||
gid = '---'
|
||||
else:
|
||||
gid = entry['msSFU30GidNumber'][0]
|
||||
print "(%s)" % gid
|
||||
gid = self.ensure_utf8(entry['msSFU30GidNumber'][0])
|
||||
print("(%s)" % gid)
|
||||
if 'member' in entry:
|
||||
for member in entry['member']:
|
||||
for member in (self.ensure_utf8(m) for m in entry['member']):
|
||||
# Check if member is itself a group. This might be PSI-specific
|
||||
is_group = self._is_group(member)
|
||||
if recursive and is_group:
|
||||
@@ -595,11 +613,11 @@ class LdapUserDir(object):
|
||||
if not 'msSFU30GidNumber' in entry:
|
||||
gid = '---'
|
||||
else:
|
||||
gid = entry['msSFU30GidNumber'][0]
|
||||
gid = self.ensure_utf8(entry['msSFU30GidNumber'][0])
|
||||
|
||||
sys.stdout.write("%s:IGNORE:%s:" % (entry['cn'][0], gid))
|
||||
sys.stdout.write("%s:IGNORE:%s:" % (self.ensure_utf8(entry['cn'][0]), gid))
|
||||
if 'member' in entry:
|
||||
members = [self.dn_to_cn(dn) for dn in entry['member']]
|
||||
members = [self.dn_to_cn(self.ensure_utf8(dn)) for dn in entry['member']]
|
||||
if recursive:
|
||||
members = [m for cn in members for m in self._get_all_members(cn)]
|
||||
sys.stdout.write(",".join(members) + "\n")
|
||||
@@ -641,6 +659,7 @@ class LdapUserDir(object):
|
||||
if "member" in entry:
|
||||
# not an empty group
|
||||
for member in entry["member"]:
|
||||
member = self.ensure_utf8(member)
|
||||
# Shortcut recursion for known leaves
|
||||
#if not LdapUserDir._is_group(member):
|
||||
# yield member
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "2.1.5"
|
||||
__version__ = "2.2.0"
|
||||
|
||||
5
setup.py
5
setup.py
@@ -8,7 +8,8 @@ from setuptools import setup
|
||||
# we get the package version from inside our package, since we then
|
||||
# can use it also from within the package
|
||||
#from ldapuserdir.version import __version__
|
||||
execfile('ldapuserdir/version.py')
|
||||
# py2.7 way: execfile('ldapuserdir/version.py')
|
||||
exec(open("ldapuserdir/version.py").read())
|
||||
|
||||
setup(
|
||||
name="ldapuserdir",
|
||||
@@ -25,5 +26,5 @@ setup(
|
||||
# following format is (targetdir,[list of files])
|
||||
data_files=[('etc', ['etc/ldapuserdir-ctl.cfg'])],
|
||||
|
||||
install_requires=['python-ldap']
|
||||
#install_requires=['python-ldap']
|
||||
)
|
||||
|
||||
85
todo.org
85
todo.org
@@ -384,3 +384,88 @@ KeyError: 'displayName'
|
||||
I implemented a workaround by filtering out the None elements.
|
||||
|
||||
|
||||
** [2019-05-10 Fri] compatibility with python-3.6
|
||||
*** RESOLVED simple fixes
|
||||
CLOSED: [2019-05-11 Sat 09:54]
|
||||
:LOGBOOK:
|
||||
- State "RESOLVED" from "BUG" [2019-05-11 Sat 09:54]
|
||||
- State "BUG" from [2019-05-11 Sat 09:54]
|
||||
:END:
|
||||
- Exceptions: use new syntax
|
||||
#+begin_src python
|
||||
except SomeException as err
|
||||
#+end_src
|
||||
- print statements
|
||||
*** RESOLVED importer namespace problem
|
||||
CLOSED: [2019-05-11 Sat 09:55]
|
||||
:LOGBOOK:
|
||||
- State "RESOLVED" from "BUG" [2019-05-11 Sat 09:55]
|
||||
- State "BUG" from [2019-05-11 Sat 09:54]
|
||||
:END:
|
||||
- __init__.py only works with changing to relative import
|
||||
: from ldapuserdir import LdapUserDir, LdapUserDirError
|
||||
now must be made explicit with
|
||||
: from ldapuserdir.ldapuserdir import LdapUserDir, LdapUserDirError
|
||||
*** RESOLVED hangs in LDAP paging call
|
||||
CLOSED: [2019-05-11 Sat 12:28]
|
||||
:LOGBOOK:
|
||||
- State "RESOLVED" from "BUG" [2019-05-11 Sat 12:28]
|
||||
- State "BUG" from [2019-05-11 Sat 10:05]
|
||||
:END:
|
||||
The loop for reading the paged results never reaches the break condition
|
||||
|
||||
in ldapuserdir.py:_search_s
|
||||
#+begin_src python
|
||||
page_ctrl = SimplePagedResultsControl(criticality=True,
|
||||
size=self.page_size,
|
||||
cookie='')
|
||||
msgid = self._ldap.search_ext(base, scope, filterstr, attrlist,
|
||||
attrsonly,
|
||||
serverctrls=[page_ctrl])
|
||||
|
||||
results = []
|
||||
while True:
|
||||
_, rdata, _, resp_ctrls = self._ldap.result3(msgid)
|
||||
results.extend(rdata)
|
||||
self.logger.debug('DEREK: in paging result call: results= %s' % results)
|
||||
# .... CUT ....
|
||||
# Extract the SimplePagedResultsControl to get the cookie.
|
||||
page_ctrls = [c for c in resp_ctrls if c.controlType == SimplePagedResultsControl.controlType]
|
||||
if page_ctrls == [] or page_ctrls[0].cookie == '':
|
||||
# We're done.
|
||||
break
|
||||
else:
|
||||
# Update the cookie to retrieve the next page.
|
||||
page_ctrl.cookie = page_ctrls[0].cookie
|
||||
|
||||
#+end_src
|
||||
|
||||
The conditions for the break need to be changed.
|
||||
Good resource: https://medium.com/@alpolishchuk/pagination-of-ldap-search-results-with-python-ldap-845de60b90d2
|
||||
|
||||
#+begin_src python
|
||||
if not page_ctrls:
|
||||
raise RuntimeError("The server ignores RFC 2696 control (paged results)")
|
||||
if not page_ctrls[0].cookie:
|
||||
# We're done.
|
||||
break
|
||||
# Update the cookie to retrieve the next page.
|
||||
page_ctrl.cookie = page_ctrls[0].cookie
|
||||
#+end_src
|
||||
|
||||
*** RESOLVED In python3 the ldap calls return bytestrings
|
||||
CLOSED: [2019-05-11 Sat 12:28]
|
||||
:LOGBOOK:
|
||||
- State "RESOLVED" from "BUG" [2019-05-11 Sat 12:28]
|
||||
- State "BUG" from [2019-05-11 Sat 12:28]
|
||||
:END:
|
||||
|
||||
: ldapuserdir-ctl --debug -u feichtinger
|
||||
: b'feichtinger':b'3896':b'3896':b'840':b'Feichtinger Derek Heinrich':b'/bin/bash':b'/afs/psi.ch/user/f/feichtinger':
|
||||
|
||||
The (dn, attributes) that are returned by _search_s contain attributes the
|
||||
values of which all are bytestrings.
|
||||
|
||||
python-ldap returns bytestrings and in py3 a standard string is now utf-8.
|
||||
This leads to all kinds of problems. I define a function
|
||||
ensure_utf8 ẗo fix the issue.
|
||||
|
||||
Reference in New Issue
Block a user