Add -r option for recursive resolution of groups

This commit is contained in:
Spencer Bliven
2019-01-31 10:58:02 +01:00
parent fbb9078e2a
commit 0ffd91aa96
2 changed files with 109 additions and 18 deletions

View File

@@ -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)

View File

@@ -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