Add -r option for recursive resolution of groups
This commit is contained in:
@@ -93,16 +93,16 @@ usage += """
|
||||
usage_epilog = """
|
||||
Examples:
|
||||
List group members
|
||||
%prog svc_ra_x06sa
|
||||
%prog 'svc_ra_*'
|
||||
%prog svc-ra_x06sa
|
||||
%prog 'svc-ra_*'
|
||||
|
||||
Get group memberships for user mueller (optionally with a group filter)
|
||||
%prog -g mueller
|
||||
%prog -g mueller 'svc-ra*'
|
||||
|
||||
Add/delete users to/from a group (requires access rights!)
|
||||
%prog -a svc_ra_x06sa user1 user2 user3
|
||||
%prog -d svc_ra_x06sa user1 user2
|
||||
%prog -a svc-ra_x06sa user1 user2 user3
|
||||
%prog -d svc-ra_x06sa user1 user2
|
||||
|
||||
List users matching a pattern
|
||||
%prog -u 'mueller*'
|
||||
@@ -227,6 +227,13 @@ parser.add_option('-V',
|
||||
default = False
|
||||
)
|
||||
|
||||
parser.add_option('-r',
|
||||
action = 'store_true',
|
||||
dest = 'recursive',
|
||||
help = 'Recursively resolve groups',
|
||||
default = False
|
||||
)
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if options.flag_debug:
|
||||
@@ -340,7 +347,9 @@ try:
|
||||
if args:
|
||||
sfilter = args.pop(0)
|
||||
ldapdir.list_groups(sfilter, mssfu=flag_mssfu, returndn=flag_showdn,
|
||||
verbose=flag_verbose)
|
||||
verbose=flag_verbose, recursive=options.recursive)
|
||||
|
||||
|
||||
|
||||
elif mode == 'userlist':
|
||||
records = ldapdir.get_users(userfilter, config['user_ou'], mssfu=flag_mssfu)
|
||||
|
||||
@@ -17,6 +17,7 @@ import re
|
||||
from glob import fnmatch
|
||||
import logging
|
||||
import time
|
||||
import itertools
|
||||
|
||||
|
||||
class LdapUserDirError(Exception):
|
||||
@@ -144,8 +145,10 @@ class LdapUserDir(object):
|
||||
|
||||
def _search_s(self, base, scope, filterstr='(objectClass=*)',
|
||||
attrlist=None, attrsonly=0):
|
||||
"""Helper for search_s_reconn. Wraps ldap.search_ext to use paged results if
|
||||
desired (see self.page_size)."""
|
||||
"""Helper for search_s_reconn.
|
||||
|
||||
Wraps ldap.search_ext to use paged results if desired (see self.page_size).
|
||||
"""
|
||||
if self.page_size == 0:
|
||||
# Do not use paged results
|
||||
self.logger.debug('not using paging since page_size is %d\n' % self.page_size)
|
||||
@@ -393,6 +396,7 @@ desired (see self.page_size)."""
|
||||
gfilter : str, optional
|
||||
filter expression used for the cn part of the ldap dn
|
||||
ou : str, optional
|
||||
group OU
|
||||
mssfu : bool, optional
|
||||
Whether to only show users with mssfu mappings
|
||||
|
||||
@@ -522,7 +526,7 @@ desired (see self.page_size)."""
|
||||
return reslist
|
||||
|
||||
def list_groups(self, filter='*', ou=None, mssfu=False,
|
||||
returndn=False, verbose=False):
|
||||
returndn=False, verbose=False, recursive=False,indent=0):
|
||||
"""Prints a list of groups from the LDAP directory to stdout
|
||||
|
||||
Parameters
|
||||
@@ -533,48 +537,126 @@ desired (see self.page_size)."""
|
||||
organisational unit to be used in the ldap search
|
||||
mssfu : bool, optional
|
||||
Whether to only show users with mssfu mappings
|
||||
returndn : bool
|
||||
returndn : bool, optional
|
||||
If true, return full DNs
|
||||
verbose : bool
|
||||
verbose : bool, optional
|
||||
If true, print one name per line
|
||||
recursive : bool, optional
|
||||
If true, any groups contained within the output will be resolved recursively to users
|
||||
indent : int, optional
|
||||
For internal use only. Indicates indent level for verbose recursive mode. Otherwise ignored.
|
||||
"""
|
||||
if returndn:
|
||||
verbose = True
|
||||
|
||||
r = self.get_groups_struct(filter, ou, mssfu)
|
||||
if len(r) == 0:
|
||||
sys.stderr.write("Error: no groups found (filter: %s)\n" % filter)
|
||||
sys.stderr.write("%sError: no groups found (filter: %s)\n" % (' '*indent, filter))
|
||||
return
|
||||
|
||||
if verbose:
|
||||
indent_increment = 3 # amount to indent members
|
||||
for dn, entry in r:
|
||||
if returndn:
|
||||
print "group: %s" % dn,
|
||||
print("%sgroup: %s" % (' '*indent, dn)),
|
||||
else:
|
||||
print "group: %s" % entry['cn'][0],
|
||||
print("%sgroup: %s" % (' '*indent, entry['cn'][0])),
|
||||
if not 'msSFU30GidNumber' in entry:
|
||||
gid = '---'
|
||||
else:
|
||||
gid = entry['msSFU30GidNumber'][0]
|
||||
print "(%s)" % gid
|
||||
if 'member' in entry:
|
||||
for cn in entry['member']:
|
||||
if returndn:
|
||||
print ' member: ', cn
|
||||
for member 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:
|
||||
self.list_groups(
|
||||
filter=self.dn_to_cn(member),
|
||||
ou=ou,
|
||||
mssfu=mssfu,
|
||||
returndn=returndn,
|
||||
verbose=verbose,
|
||||
recursive=recursive,
|
||||
indent=indent+indent_increment)
|
||||
else:
|
||||
print ' member: ', self.dn_to_cn(cn)
|
||||
if returndn:
|
||||
print('%smember: %s' % (' '*(indent+indent_increment), member ))
|
||||
else:
|
||||
print('%smember: %s' % (' '*(indent+indent_increment),
|
||||
self.dn_to_cn(member)))
|
||||
else:
|
||||
for dn, entry in r:
|
||||
if not 'msSFU30GidNumber' in entry:
|
||||
gid = '---'
|
||||
else:
|
||||
gid = entry['msSFU30GidNumber'][0]
|
||||
|
||||
sys.stdout.write("%s:IGNORE:%s:" % (entry['cn'][0], gid))
|
||||
if 'member' in entry:
|
||||
sys.stdout.write(",".join([self.dn_to_cn(dn) for dn in entry['member']]) + "\n")
|
||||
members = [self.dn_to_cn(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")
|
||||
else:
|
||||
sys.stdout.write("\n")
|
||||
|
||||
def _get_all_members(self, filter, include_groups=False, ou=None, mssfu=False):
|
||||
"""Generator to recursively get the CN for all members of a group.
|
||||
|
||||
If filter does not match any groups it is assumed to be a user and is yeilded directly.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
filter : str
|
||||
filter expression used for the cn part of the ldap dn. If it matches
|
||||
multiple entries they will be concatenated.
|
||||
include_groups : bool
|
||||
if true, the CN of any member groups will be included in the results
|
||||
(preordered traversal)
|
||||
ou : str, optional
|
||||
organisational unit to be used in the ldap search
|
||||
mssfu : bool, optional
|
||||
Whether to only show users with mssfu mappings
|
||||
|
||||
Returns
|
||||
-------
|
||||
Generator of str : CN for all members of groups matching the filter.
|
||||
"""
|
||||
r = self.get_groups_struct(filter, ou, mssfu)
|
||||
if len(r) == 0:
|
||||
# no group entry, so input must have been a user
|
||||
yield filter
|
||||
return
|
||||
|
||||
for dn, entry in r:
|
||||
# preorder the group
|
||||
if include_groups:
|
||||
yield self.dn_to_cn(dn)
|
||||
if "member" in entry:
|
||||
# not an empty group
|
||||
for member in entry["member"]:
|
||||
# Shortcut recursion for known leaves
|
||||
#if not LdapUserDir._is_group(member):
|
||||
# yield member
|
||||
#else:
|
||||
cn = self.dn_to_cn(member)
|
||||
for submember in self._get_all_members(cn, include_groups=include_groups, ou=ou, mssfu=mssfu):
|
||||
yield submember
|
||||
|
||||
@staticmethod
|
||||
def _is_group(dn):
|
||||
"""Quick check if a DN is a group. That is, if it contains OU=Groups.
|
||||
|
||||
This may be a PSI-specific check
|
||||
|
||||
Parameters
|
||||
----------
|
||||
dn : str
|
||||
DN of the entry
|
||||
"""
|
||||
return "OU=Groups" in dn
|
||||
|
||||
def _mod_groupmembers(self, ldapmode, dngroup, usernames):
|
||||
"""modifies (adds/deletes) members of an LDAP group entry
|
||||
|
||||
|
||||
Reference in New Issue
Block a user