import sys import gam from gam.var import * from gam import controlflow from gam import display from gam import gapi from gam.gapi import directory as gapi_directory from gam.gapi import errors as gapi_errors def _getAllParentOrgUnitsForUser(user, cd=None): if not cd: cd = gapi_directory.build() parent_path = gapi.call(cd.users(), 'get', userKey=user, fields='orgUnitPath', projection='basic')['orgUnitPath'] if parent_path == '/': orgUnitPath, orgUnitId = getOrgUnitId('/', cd) return {orgUnitId: orgUnitPath} parent_path = encodeOrgUnitPath(makeOrgUnitPathRelative(parent_path)) orgUnits = {} while True: result = gapi.call(cd.orgunits(), 'get', customerId=GC_Values[GC_CUSTOMER_ID], orgUnitPath=parent_path, fields='orgUnitId,orgUnitPath,parentOrgUnitId') orgUnits[result['orgUnitId']] = result['orgUnitPath'] if 'parentOrgUnitId' not in result: break parent_path = result['parentOrgUnitId'] return orgUnits def create(): cd = gapi_directory.build() name = getOrgUnitItem(sys.argv[3], pathOnly=True, absolutePath=False) parent = '' body = {} i = 4 while i < len(sys.argv): myarg = sys.argv[i].lower() if myarg == 'description': body['description'] = sys.argv[i + 1].replace('\\n', '\n') i += 2 elif myarg == 'parent': parent = getOrgUnitItem(sys.argv[i + 1]) i += 2 elif myarg == 'noinherit': body['blockInheritance'] = True i += 1 elif myarg == 'inherit': body['blockInheritance'] = False i += 1 else: controlflow.invalid_argument_exit(sys.argv[i], 'gam create org') if parent.startswith('id:'): parent = gapi.call(cd.orgunits(), 'get', customerId=GC_Values[GC_CUSTOMER_ID], orgUnitPath=parent, fields='orgUnitPath')['orgUnitPath'] if parent == '/': orgUnitPath = parent + name else: orgUnitPath = parent + '/' + name if orgUnitPath.count('/') > 1: body['parentOrgUnitPath'], body['name'] = orgUnitPath.rsplit('/', 1) else: body['parentOrgUnitPath'] = '/' body['name'] = orgUnitPath[1:] parent = body['parentOrgUnitPath'] gapi.call(cd.orgunits(), 'insert', customerId=GC_Values[GC_CUSTOMER_ID], body=body, retry_reasons=[gapi_errors.ErrorReason.DAILY_LIMIT_EXCEEDED]) print(f'Created OrgUnit {body["name"]}') def delete(): cd = gapi_directory.build() name = getOrgUnitItem(sys.argv[3]) print(f'Deleting organization {name}') gapi.call(cd.orgunits(), 'delete', customerId=GC_Values[GC_CUSTOMER_ID], orgUnitPath=encodeOrgUnitPath(makeOrgUnitPathRelative(name))) def info(name=None, return_attrib=None): cd = gapi_directory.build() checkSuspended = None if not name: name = getOrgUnitItem(sys.argv[3]) get_users = True show_children = False i = 4 while i < len(sys.argv): myarg = sys.argv[i].lower() if myarg == 'nousers': get_users = False i += 1 elif myarg in ['children', 'child']: show_children = True i += 1 elif myarg in ['suspended', 'notsuspended']: checkSuspended = myarg == 'suspended' i += 1 else: controlflow.invalid_argument_exit(sys.argv[i], 'gam info org') if name == '/': orgs = gapi.call(cd.orgunits(), 'list', customerId=GC_Values[GC_CUSTOMER_ID], type='children', fields='organizationUnits/parentOrgUnitId') if 'organizationUnits' in orgs and orgs['organizationUnits']: name = orgs['organizationUnits'][0]['parentOrgUnitId'] else: topLevelOrgId = getTopLevelOrgId(cd, '/') if topLevelOrgId: name = topLevelOrgId else: name = makeOrgUnitPathRelative(name) result = gapi.call(cd.orgunits(), 'get', customerId=GC_Values[GC_CUSTOMER_ID], orgUnitPath=encodeOrgUnitPath(name)) if return_attrib: return result[return_attrib] display.print_json(result) if get_users: name = result['orgUnitPath'] page_message = gapi.got_total_items_first_last_msg('Users') users = gapi.get_all_pages( cd.users(), 'list', 'users', page_message=page_message, message_attribute='primaryEmail', customer=GC_Values[GC_CUSTOMER_ID], query=orgUnitPathQuery(name, checkSuspended), fields='users(primaryEmail,orgUnitPath),nextPageToken') if checkSuspended is None: print('Users:') elif not checkSuspended: print('Users (Not suspended):') else: print('Users (Suspended):') for user in users: if show_children or (name.lower() == user['orgUnitPath'].lower()): sys.stdout.write(f' {user["primaryEmail"]}') if name.lower() != user['orgUnitPath'].lower(): print(' (child)') else: print('') def list_orgunits(listType='all', orgUnitPath=None, fields=None): retrievedOrgIds = [] parentOrgIds = [] cd = gapi_directory.build() if fields: # Always get parentOrgUnitId so we can # find missing parents if 'parentOrgUnitId' not in fields: fields.append('parentOrgUnitId') get_fields = ','.join(fields) list_fields = f'organizationUnits({get_fields})' else: list_fields = None get_fields = None orgs = gapi.call(cd.orgunits(), 'list', customerId=GC_Values[GC_CUSTOMER_ID], type=listType, orgUnitPath=orgUnitPath, fields=list_fields) if not 'organizationUnits' in orgs: topLevelOrgId = getTopLevelOrgId(cd, orgUnitPath) if topLevelOrgId: parentOrgIds.append(topLevelOrgId) orgunits = [] else: orgunits = orgs['organizationUnits'] for row in orgunits: retrievedOrgIds.append(row['orgUnitId']) if row['parentOrgUnitId'] not in parentOrgIds: parentOrgIds.append(row['parentOrgUnitId']) missing_parents = set(parentOrgIds) - set(retrievedOrgIds) for missing_parent in missing_parents: try: result = gapi.call(cd.orgunits(), 'get', throw_reasons=['required'], customerId=GC_Values[GC_CUSTOMER_ID], orgUnitPath=missing_parent, fields=get_fields) orgunits.append(result) except: pass return orgunits def print_(): print_order = [ 'orgUnitPath', 'orgUnitId', 'name', 'description', 'parentOrgUnitPath', 'parentOrgUnitId', 'blockInheritance' ] listType = 'all' orgUnitPath = '/' todrive = False fields = ['orgUnitPath', 'name', 'orgUnitId', 'parentOrgUnitId'] titles = [] csvRows = [] i = 3 while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg == 'todrive': todrive = True i += 1 elif myarg == 'toplevelonly': listType = 'children' i += 1 elif myarg == 'fromparent': orgUnitPath = getOrgUnitItem(sys.argv[i + 1]) i += 2 elif myarg == 'allfields': fields = None i += 1 elif myarg == 'fields': fields += sys.argv[i + 1].split(',') i += 2 else: controlflow.invalid_argument_exit(sys.argv[i], 'gam print orgs') gam.printGettingAllItems('Organizational Units', None) orgunits = list_orgunits(listType=listType, orgUnitPath=orgUnitPath, fields=fields) for row in orgunits: orgEntity = {} for key, value in list(row.items()): if key in ['kind', 'etag', 'etags']: continue if key not in titles: titles.append(key) orgEntity[key] = value csvRows.append(orgEntity) for title in titles: if title not in print_order: print_order.append(title) titles = sorted(titles, key=print_order.index) # sort results similar to how they list in admin console csvRows.sort(key=lambda x: x['orgUnitPath'].lower(), reverse=False) display.write_csv_file(csvRows, titles, 'Orgs', todrive) def orgid_to_org_map(): orgunits = list_orgunits(fields=['orgUnitPath', 'orgUnitId']) result = {ou['orgUnitId']:ou['orgUnitPath'] for ou in orgunits} return result def update(): cd = gapi_directory.build() orgUnitPath = getOrgUnitItem(sys.argv[3]) if sys.argv[4].lower() in ['move', 'add']: entity_type = sys.argv[5].lower() if entity_type in usergroup_types: users = gam.getUsersToModify(entity_type=entity_type, entity=sys.argv[6]) else: entity_type = 'users' users = gam.getUsersToModify(entity_type=entity_type, entity=sys.argv[5]) if (entity_type.startswith('cros')) or ( (entity_type == 'all') and (sys.argv[6].lower() == 'cros')): for l in range(0, len(users), 50): move_body = {'deviceIds': users[l:l + 50]} print( f' moving {len(move_body["deviceIds"])} devices to {orgUnitPath}' ) gapi.call(cd.chromeosdevices(), 'moveDevicesToOu', customerId=GC_Values[GC_CUSTOMER_ID], orgUnitPath=orgUnitPath, body=move_body) else: i = 0 count = len(users) for user in users: i += 1 sys.stderr.write( f' moving {user} to {orgUnitPath}{gam.currentCountNL(i, count)}' ) try: gapi.call(cd.users(), 'update', throw_reasons=[ gapi_errors.ErrorReason.CONDITION_NOT_MET ], userKey=user, body={'orgUnitPath': orgUnitPath}) except gapi_errors.GapiConditionNotMetError: pass else: body = {} i = 4 while i < len(sys.argv): myarg = sys.argv[i].lower() if myarg == 'name': body['name'] = sys.argv[i + 1] i += 2 elif myarg == 'description': body['description'] = sys.argv[i + 1].replace('\\n', '\n') i += 2 elif myarg == 'parent': parent = getOrgUnitItem(sys.argv[i + 1]) if parent.startswith('id:'): body['parentOrgUnitId'] = parent else: body['parentOrgUnitPath'] = parent i += 2 elif myarg == 'noinherit': body['blockInheritance'] = True i += 1 elif myarg == 'inherit': body['blockInheritance'] = False i += 1 else: controlflow.invalid_argument_exit(sys.argv[i], 'gam update org') gapi.call(cd.orgunits(), 'update', customerId=GC_Values[GC_CUSTOMER_ID], orgUnitPath=encodeOrgUnitPath( makeOrgUnitPathRelative(orgUnitPath)), body=body) def orgUnitPathQuery(path, checkSuspended): query = "orgUnitPath='{}'".format(path.replace( "'", "\\'")) if path != '/' else '' if checkSuspended is not None: query += f' isSuspended={checkSuspended}' return query def makeOrgUnitPathAbsolute(path): if path == '/': return path if path.startswith('/'): return path.rstrip('/') if path.startswith('id:'): return path if path.startswith('uid:'): return path[1:] return '/' + path.rstrip('/') def makeOrgUnitPathRelative(path): if path == '/': return path if path.startswith('/'): return path[1:].rstrip('/') if path.startswith('id:'): return path if path.startswith('uid:'): return path[1:] return path.rstrip('/') def encodeOrgUnitPath(path): # 6.22 - This method no longer works. # % no longer needs encoding and + is handled incorrectly in API with or without encoding return path # if path.find('+') == -1 and path.find('%') == -1: # return path # encpath = '' # for c in path: # if c == '+': # encpath += '%2B' # elif c == '%': # encpath += '%25' # else: # encpath += c # return encpath def getOrgUnitItem(orgUnit, pathOnly=False, absolutePath=True): if pathOnly and (orgUnit.startswith('id:') or orgUnit.startswith('uid:')): controlflow.system_error_exit( 2, f'{orgUnit} is not valid in this context') if absolutePath: return makeOrgUnitPathAbsolute(orgUnit) return makeOrgUnitPathRelative(orgUnit) def getTopLevelOrgId(cd, orgUnitPath): try: # create a temp org so we can learn what the top level org ID is (sigh) temp_org = gapi.call(cd.orgunits(), 'insert', customerId=GC_Values[GC_CUSTOMER_ID], body={ 'name': 'temp-delete-me', 'parentOrgUnitPath': orgUnitPath }, fields='parentOrgUnitId,orgUnitId') gapi.call(cd.orgunits(), 'delete', customerId=GC_Values[GC_CUSTOMER_ID], orgUnitPath=temp_org['orgUnitId']) return temp_org['parentOrgUnitId'] except: pass return None def getOrgUnitId(orgUnit, cd=None): if cd is None: cd = gapi_directory.build() orgUnit = getOrgUnitItem(orgUnit) if orgUnit[:3] == 'id:': return (orgUnit, orgUnit) if orgUnit == '/': result = gapi.call(cd.orgunits(), 'list', customerId=GC_Values[GC_CUSTOMER_ID], orgUnitPath='/', type='children', fields='organizationUnits(parentOrgUnitId)') if result.get('organizationUnits', []): return (orgUnit, result['organizationUnits'][0]['parentOrgUnitId']) topLevelOrgId = getTopLevelOrgId(cd, '/') if topLevelOrgId: return (orgUnit, topLevelOrgId) return (orgUnit, '/') #Bogus but should never happen result = gapi.call(cd.orgunits(), 'get', customerId=GC_Values[GC_CUSTOMER_ID], orgUnitPath=encodeOrgUnitPath( makeOrgUnitPathRelative(orgUnit)), fields='orgUnitId') return (orgUnit, result['orgUnitId']) def orgunit_from_orgunitid(orgunitid, cd=None): if cd is None: cd = gapi_directory.build() orgunitpath = GM_Globals[GM_MAP_ORGUNIT_ID_TO_NAME].get(orgunitid) if not orgunitpath: try: orgunitpath = gapi.call(cd.orgunits(), 'get', customerId=GC_Values[GC_CUSTOMER_ID], orgUnitPath=f'id:{orgunitid}' if not orgunitid.startswith('id:') else orgunitid, fields='orgUnitPath')['orgUnitPath'] except: orgunitpath = orgunitid GM_Globals[GM_MAP_ORGUNIT_ID_TO_NAME][orgunitid] = orgunitpath return orgunitpath