#!/usr/bin/python2.6 # -*- coding: utf-8 -*- # # Univention PAM # Dump all ldap groups with members to a single file # # Copyright 2011-2013 Univention GmbH # # http://www.univention.de/ # # All rights reserved. # # The source code of this program is made available # under the terms of the GNU Affero General Public License version 3 # (GNU AGPL V3) as published by the Free Software Foundation. # # Binary versions of this program provided by Univention to you as # well as other copyrighted, protected or trademarked materials like # Logos, graphics, fonts, specific documentations and configurations, # cryptographic keys etc. are subject to a license agreement between # you and Univention and not subject to the GNU AGPL V3. # # In the case you use this program under the terms of the GNU AGPL V3, # the program is provided in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public # License with the Debian GNU/Linux or Univention distribution in file # /usr/share/common-licenses/AGPL-3; if not, see # . import univention.uldap import optparse import ldap import shutil import sys import os import tempfile import subprocess def _get_members(lo, group, recursion_set, check_member=False): group_dn, group_attr = group if group_dn in recursion_set: return set() recursion_set.add(group_dn) result = set() for member in group_attr.get('uniqueMember', []): if member.startswith('uid='): result |= _get_member_user(lo, member, check_member) elif member.startswith('cn='): result |= _get_member_group_or_host(lo, member, recursion_set, check_member) return result def _get_member_user(lo, member_dn, check_member=False): if check_member: try: res = lo.search( base=member_dn, scope=ldap.SCOPE_BASE, filter='uid=*', attr=['uid'], ) if not res: return set() except ldap.NO_SUCH_OBJECT: return set() mrdn = ldap.explode_rdn(member_dn) _, value = mrdn[0].split('=', 1) return set((value,)) def _get_member_group_or_host(lo, member_dn, recursion_set, check_member=False): try: res = lo.search( base=member_dn, scope=ldap.SCOPE_BASE, filter='objectClass=*', attr=['uniqueMember', 'gidNumber', 'objectClass', 'cn'], ) if not res: return set() except ldap.NO_SUCH_OBJECT: return set() assert len(res) == 1 member = res[0] assert member[0] == member_dn if 'univentionGroup' in member[1].get('objectClass', []): return _get_members(lo, member, recursion_set, check_member) else: host = member[1].get('cn')[0] + '$' return set((host,)) def parse_commandline(): parser = optparse.OptionParser( ) parser.add_option("--file", dest="file", default='/var/lib/extrausers/group', action="store", help="write result to the given file, default is /var/lib/extrausers/group") parser.add_option("--verbose", dest="verbose", default=False, action="store_true", help="verbose output") parser.add_option("--check_member", dest="check_member", default=False, action="store_true", help="checks if the member exists") (options, _args) = parser.parse_args() return options def main(): options = parse_commandline() try: lo = univention.uldap.getMachineConnection( ldap_master=False ) except ldap.SERVER_DOWN: print >> sys.stderr, "Abort: Can't contact LDAP server." sys.exit(1) groups = lo.search('objectClass=univentionGroup', attr=['uniqueMember', 'cn', 'gidNumber']) if options.verbose: print >> sys.stderr, 'Found %d LDAP groups' % (len(groups),) if not groups: print >> sys.stderr, 'Abort: Did not found any LDAP group.' sys.exit(1) # Write to a temporary file (fdtemp, fdname) = tempfile.mkstemp() fd = os.fdopen(fdtemp, 'w') for group in groups: group_dn, group_attr = group rdn = ldap.explode_rdn(group_dn) _, groupname = rdn[0].split('=', 1) members = _get_members(lo, group, set(), options.check_member) fd.write('%s:*:%s:%s\n' % ( groupname, group_attr.get('gidNumber', [''])[0], ','.join(members) )) fd.close() os.chmod(fdname, 0644) # Move the file shutil.move(fdname, options.file) if options.verbose: print >> sys.stderr, 'The file %s was created.' % (options.file,) run_hooks(options.verbose) sys.exit(0) HOOK_DIR = '/var/lib/ldap-group-to-file-hooks.d' def run_hooks(verbose=False): if os.path.exists(HOOK_DIR): cmd = ('/bin/run-parts', '--verbose', HOOK_DIR) with open(os.path.devnull, 'w+') as null: if verbose: proc = subprocess.Popen(cmd, stdin=null) else: proc = subprocess.Popen(cmd, stdin=null, stdout=null, stderr=null) _stdout, _stderr = proc.communicate() elif verbose: print >> sys.stderr, '%s does not exist' % (HOOK_DIR,) if __name__ == '__main__': main()