#!/usr/bin/python3 """ Get the list of group members with enable time and disable time, options: all members effective (indirect) immediate (direct) composite nonimmediate Example: grouper_find_members_with_expiration.py -m dev -g target:group:path -f all """ import argparse import requests from requests.auth import HTTPBasicAuth import json import sys,string def grouperGetMembershipsWithFilter(grouper_credentials, grouper_ws_uri, groupName, enabledFlag, memberFilter): # get group members with enabled/disabled status and enable/disable dates and times # enabledFlag can be T/F/A (enabled/disabled/all) # use memberFilter=ALL to fetch all members # use memberFilter=IMMEDIATE to fetch direct members only # date format is YYYY/MM/DD HH:MM:SS.sss if enabledFlag == '': enabledFlag = 'A' if enabledFlag != 'T' and enabledFlag != 'F' and enabledFlag != 'A': print("enabledFlag "+enabledFlag+" must be T or F or A") exit(1) if memberFilter == '': memberFilter = 'all' if memberFilter != 'all' and memberFilter != 'effective' and memberFilter != 'immediate' and memberFilter != 'composite' and memberFilter != 'nonimmediate': print(f"memberFilter {memberFilter} must be all or effective or immediate or composite or nonimmediate") exit(1) body = { "WsRestGetMembershipsRequest": { "memberFilter": memberFilter, "wsGroupLookups": [{ "groupName": groupName }], "enabled":enabledFlag } } result = grouperWSRequest(grouper_credentials, grouper_ws_uri+"/memberships", "POST", body) return result def grouperGetUuid(grouper_credentials, grouper_ws_uri, groupName): """Get UUID for the specified group""" thisuuid = 0 body = { "WsRestFindGroupsRequest": { "wsQueryFilter": { "groupName": groupName, "queryFilterType": "FIND_BY_GROUP_NAME_EXACT", } } } findGroups = grouperWSRequest(grouper_credentials, grouper_ws_uri+"/groups", "POST", body) if findGroups and findGroups['WsFindGroupsResults']['resultMetadata']['success'] and 'groupResults' in findGroups['WsFindGroupsResults']: thisuuid = findGroups['WsFindGroupsResults']['groupResults'][0]['uuid'] return thisuuid def grouperWSRequest(grouper_credentials, url, method, body=None): """Send a request to the Grouper Web Service.""" try: response = requests.request(method, url, json=body, auth=grouper_credentials) response.raise_for_status() except requests.RequestException as e: raise SystemExit(e) from None return response.json() def find_members(mode, group_id_path, member_filter): """Find group members with enable time and disable time Args: mode (str): The environment mode ('dev', 'prod'). group_id_path (str): The group to be examined. member_filter (str): The member filter. """ # set the Grouper Web Service username and password grouper_username = 'abc1234' grouper_password = 'xxxxxxxxxxxxxxxxxxxx' grouper_credentials = HTTPBasicAuth(grouper_username,grouper_password) # the Grouper Web Service URI should point to dev or prod Grouper devGrouperURI = 'https://grouper-dev.cc.columbia.edu/grouper-ws/servicesRest/v2_4_000' prodGrouperURI = 'https://grouper.cc.columbia.edu/grouper-ws/servicesRest/v2_4_000' if mode == 'prod': grouper_ws_uri = prodGrouperURI else: grouper_ws_uri = devGrouperURI # Verify that the target group exists and is accessible by this user if grouperGetUuid(grouper_credentials,grouper_ws_uri,group_id_path) == 0: print(f"{group_id_path} group not found (does not exist or is not accessible)") return # Find the group members result = grouperGetMembershipsWithFilter(grouper_credentials,grouper_ws_uri,group_id_path,'A',member_filter) if ( not result or not result['WsGetMembershipsResults']['resultMetadata']['resultCode'] ): print(f"Unable to get members of {group_id_path}") return result_code = result['WsGetMembershipsResults']['resultMetadata']['resultCode'] if result_code != 'SUCCESS': print(f"Unable to get members of {group_id_path}") print(result_code) return memberships = result['WsGetMembershipsResults'].get('wsMemberships') if memberships == None: # no members found using this member filter print("None") return for nmemberships in range(0,len(memberships)): membership = memberships[nmemberships] enabled = membership['enabled'] subjectSource = membership['subjectSourceId'] subjectId = membership['subjectId'] # grouper date format is YYYY/MM/DD HH:MM:SS.sss # if milliseconds is "000" remove milliseconds from enabled time and disabled time enabledTime = '' disabledTime = '' if 'enabledTime' in membership: enabledTime = membership['enabledTime'] if enabledTime[-4:] == '.000': enabledTime = enabledTime[:-4] if 'disabledTime' in membership: disabledTime = membership['disabledTime'] if disabledTime[-4:] == '.000': disabledTime = disabledTime[:-4] # subjectSource can be idm, g:gsa, or externalUsers # which indicates the member type is UNI, group, or external user print(f"{subjectSource} {subjectId} {enabled} {enabledTime} {disabledTime}") def main(): """Parses command-line arguments and calls find_members.""" parser = argparse.ArgumentParser( description="Get the list of group members.", epilog=("Effective is another word for Indirect, Immediate is another word for Direct."), ) parser.add_argument( "-m", action="store", dest="mode", required=True, choices=["dev", "prod"], type=str.lower, help="the Grouper environment mode", ) parser.add_argument( "-g", action="store", dest="group_id_path", required=True, help="the group ID path", ) parser.add_argument( "-f", action="store", dest="member_filter", required=True, choices=["all", "effective", "immediate", "composite", "nonimmediate"], type=str.lower, help="the member filter", ) args = parser.parse_args() find_members(args.mode, args.group_id_path, args.member_filter) if __name__ == "__main__": main()