def addACL(function): calendarId, cal = buildCalendarDataGAPIObject(sys.argv[2]) if not cal: return myarg = sys.argv[4].lower().replace('_', '') if myarg not in CALENDAR_ACL_ROLES_MAP: controlflow.expected_argument_exit("Role", ", ".join(CALENDAR_ACL_ROLES_MAP), myarg) body = {'role': CALENDAR_ACL_ROLES_MAP[myarg]} i = _getCalendarACLScope(5, body) sendNotifications = True while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg == 'sendnotifications': sendNotifications = __main__.getBoolean(sys.argv[i + 1], myarg) i += 2 else: controlflow.invalid_argument_exit( sys.argv[i], f"gam calendar <email> {function.lower()}") print(f'Calendar: {calendarId}, {function} ACL: {formatACLRule(body)}') gapi.call(cal.acl(), 'insert', calendarId=calendarId, body=body, sendNotifications=sendNotifications)
def getHoldInfo(): v = buildGAPIObject() hold = sys.argv[3] matterId = None i = 4 while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg == 'matter': matterId = getMatterItem(v, sys.argv[i+1]) holdId = convertHoldNameToID(v, hold, matterId) i += 2 else: controlflow.invalid_argument_exit(myarg, "gam info hold") if not matterId: controlflow.system_error_exit( 3, 'you must specify a matter for the hold.') results = gapi.call(v.matters().holds(), 'get', matterId=matterId, holdId=holdId) cd = __main__.buildGAPIObject('directory') if 'accounts' in results: account_type = 'group' if results['corpus'] == 'GROUPS' else 'user' for i in range(0, len(results['accounts'])): uid = f'uid:{results["accounts"][i]["accountId"]}' acct_email = __main__.convertUIDtoEmailAddress( uid, cd, [account_type]) results['accounts'][i]['email'] = acct_email if 'orgUnit' in results: results['orgUnit']['orgUnitPath'] = __main__.doGetOrgInfo( results['orgUnit']['orgUnitId'], return_attrib='orgUnitPath') display.print_json(results)
def updateMatter(action=None): v = buildGAPIObject() matterId = getMatterItem(v, sys.argv[3]) body = {} action_kwargs = {'body': {}} add_collaborators = [] remove_collaborators = [] cd = None i = 4 while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg == 'action': action = sys.argv[i+1].lower() if action not in VAULT_MATTER_ACTIONS: controlflow.system_error_exit(3, f'allowed actions are ' \ f'{", ".join(VAULT_MATTER_ACTIONS)}, got {action}') i += 2 elif myarg == 'name': body['name'] = sys.argv[i+1] i += 2 elif myarg == 'description': body['description'] = sys.argv[i+1] i += 2 elif myarg in ['addcollaborator', 'addcollaborators']: if not cd: cd = __main__.buildGAPIObject('directory') add_collaborators.extend(validateCollaborators(sys.argv[i+1], cd)) i += 2 elif myarg in ['removecollaborator', 'removecollaborators']: if not cd: cd = __main__.buildGAPIObject('directory') remove_collaborators.extend( validateCollaborators(sys.argv[i+1], cd)) i += 2 else: controlflow.invalid_argument_exit(sys.argv[i], "gam update matter") if action == 'delete': action_kwargs = {} if body: print(f'Updating matter {sys.argv[3]}...') if 'name' not in body or 'description' not in body: # bah, API requires name/description to be sent # on update even when it's not changing result = gapi.call(v.matters(), 'get', matterId=matterId, view='BASIC') body.setdefault('name', result['name']) body.setdefault('description', result.get('description')) gapi.call(v.matters(), 'update', body=body, matterId=matterId) if action: print(f'Performing {action} on matter {sys.argv[3]}') gapi.call(v.matters(), action, matterId=matterId, **action_kwargs) for collaborator in add_collaborators: print(f' adding collaborator {collaborator["email"]}') body = {'matterPermission': {'role': 'COLLABORATOR', 'accountId': collaborator['id']}} gapi.call(v.matters(), 'addPermissions', matterId=matterId, body=body) for collaborator in remove_collaborators: print(f' removing collaborator {collaborator["email"]}') gapi.call(v.matters(), 'removePermissions', matterId=matterId, body={'accountId': collaborator['id']})
def createMatter(): v = buildGAPIObject() matter_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") body = {'name': f'New Matter - {matter_time}'} collaborators = [] cd = None i = 3 while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg == 'name': body['name'] = sys.argv[i+1] i += 2 elif myarg == 'description': body['description'] = sys.argv[i+1] i += 2 elif myarg in ['collaborator', 'collaborators']: if not cd: cd = __main__.buildGAPIObject('directory') collaborators.extend(validateCollaborators(sys.argv[i+1], cd)) i += 2 else: controlflow.invalid_argument_exit(sys.argv[i], "gam create matter") matterId = gapi.call(v.matters(), 'create', body=body, fields='matterId')['matterId'] print(f'Created matter {matterId}') for collaborator in collaborators: print(f' adding collaborator {collaborator["email"]}') body = {'matterPermission': { 'role': 'COLLABORATOR', 'accountId': collaborator['id']}} gapi.call(v.matters(), 'addPermissions', matterId=matterId, body=body)
def printMatters(): v = buildGAPIObject() todrive = False csvRows = [] initialTitles = ['matterId', 'name', 'description', 'state'] titles = initialTitles[:] view = 'FULL' i = 3 while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg == 'todrive': todrive = True i += 1 elif myarg in PROJECTION_CHOICES_MAP: view = PROJECTION_CHOICES_MAP[myarg] i += 1 else: controlflow.invalid_argument_exit(myarg, "gam print matters") __main__.printGettingAllItems('Vault Matters', None) page_message = gapi.got_total_items_msg('Vault Matters', '...\n') matters = gapi.get_all_pages(v.matters(), 'list', 'matters', page_message=page_message, view=view) for matter in matters: display.add_row_titles_to_csv_file(utils.flatten_json(matter), csvRows, titles) display.sort_csv_titles(initialTitles, titles) display.write_csv_file(csvRows, titles, 'Vault Matters', todrive)
def doUpdateCustomer(): cd = gapi.directory.buildGAPIObject() body = {} i = 3 while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg in ADDRESS_FIELDS_ARGUMENT_MAP: body.setdefault('postalAddress', {}) arg = ADDRESS_FIELDS_ARGUMENT_MAP[myarg] body['postalAddress'][arg] = sys.argv[i + 1] i += 2 elif myarg in ['adminsecondaryemail', 'alternateemail']: body['alternateEmail'] = sys.argv[i + 1] i += 2 elif myarg in ['phone', 'phonenumber']: body['phoneNumber'] = sys.argv[i + 1] i += 2 elif myarg == 'language': body['language'] = sys.argv[i + 1] i += 2 else: controlflow.invalid_argument_exit(myarg, "gam update customer") if not body: controlflow.system_error_exit( 2, 'no arguments specified for "gam ' 'update customer"') gapi.call(cd.customers(), 'patch', customerKey=GC_Values[GC_CUSTOMER_ID], body=body) print('Updated customer')
def _getBuildingAttributes(args, body={}): i = 0 while i < len(args): myarg = args[i].lower().replace('_', '') if myarg == 'id': body['buildingId'] = args[i + 1] i += 2 elif myarg == 'name': body['buildingName'] = args[i + 1] i += 2 elif myarg in ['lat', 'latitude']: if 'coordinates' not in body: body['coordinates'] = {} body['coordinates']['latitude'] = args[i + 1] i += 2 elif myarg in ['long', 'lng', 'longitude']: if 'coordinates' not in body: body['coordinates'] = {} body['coordinates']['longitude'] = args[i + 1] i += 2 elif myarg == 'description': body['description'] = args[i + 1] i += 2 elif myarg == 'floors': body['floorNames'] = args[i + 1].split(',') i += 2 else: controlflow.invalid_argument_exit(myarg, "gam create|update building") return body
def transferSecCals(users): target_user = sys.argv[5] remove_source_user = sendNotifications = True i = 6 while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg == 'keepuser': remove_source_user = False i += 1 elif myarg == 'sendnotifications': sendNotifications = __main__.getBoolean(sys.argv[i + 1], myarg) i += 2 else: controlflow.invalid_argument_exit(sys.argv[i], "gam <users> transfer seccals") if remove_source_user: target_user, target_cal = buildCalendarGAPIObject(target_user) if not target_cal: return for user in users: user, source_cal = buildCalendarGAPIObject(user) if not source_cal: continue calendars = gapi.get_all_pages(source_cal.calendarList(), 'list', 'items', soft_errors=True, minAccessRole='owner', showHidden=True, fields='items(id),nextPageToken') for calendar in calendars: calendarId = calendar['id'] if calendarId.find('@group.calendar.google.com') != -1: body = { 'role': 'owner', 'scope': { 'type': 'user', 'value': target_user } } gapi.call(source_cal.acl(), 'insert', calendarId=calendarId, body=body, sendNotifications=sendNotifications) if remove_source_user: body = { 'role': 'none', 'scope': { 'type': 'user', 'value': user } } gapi.call(target_cal.acl(), 'insert', calendarId=calendarId, body=body, sendNotifications=sendNotifications)
def printEvents(): calendarId, cal = buildCalendarDataGAPIObject(sys.argv[2]) if not cal: return q = showDeleted = showHiddenInvitations = timeMin = \ timeMax = timeZone = updatedMin = None toDrive = False titles = [] csvRows = [] i = 4 while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg == 'query': q = sys.argv[i + 1] i += 2 elif myarg == 'includedeleted': showDeleted = True i += 1 elif myarg == 'includehidden': showHiddenInvitations = True i += 1 elif myarg == 'after': timeMin = utils.get_time_or_delta_from_now(sys.argv[i + 1]) i += 2 elif myarg == 'before': timeMax = utils.get_time_or_delta_from_now(sys.argv[i + 1]) i += 2 elif myarg == 'timezone': timeZone = sys.argv[i + 1] i += 2 elif myarg == 'updated': updatedMin = utils.get_time_or_delta_from_now(sys.argv[i + 1]) i += 2 elif myarg == 'todrive': toDrive = True i += 1 else: controlflow.invalid_argument_exit( sys.argv[i], "gam calendar <email> printevents") page_message = gapi.got_total_items_msg(f'Events for {calendarId}', '') results = gapi.get_all_pages(cal.events(), 'list', 'items', page_message=page_message, calendarId=calendarId, q=q, showDeleted=showDeleted, showHiddenInvitations=showHiddenInvitations, timeMin=timeMin, timeMax=timeMax, timeZone=timeZone, updatedMin=updatedMin) for result in results: row = {'calendarId': calendarId} display.add_row_titles_to_csv_file( utils.flatten_json(result, flattened=row), csvRows, titles) display.sort_csv_titles(['calendarId', 'id', 'summary', 'status'], titles) display.write_csv_file(csvRows, titles, 'Calendar Events', toDrive)
def _getFeatureAttributes(args, body={}): i = 0 while i < len(args): myarg = args[i].lower().replace('_', '') if myarg == 'name': body['name'] = args[i + 1] i += 2 else: controlflow.invalid_argument_exit(myarg, "gam create|update feature") return body
def printBuildings(): to_drive = False cd = gapi.directory.buildGAPIObject() titles = [] csvRows = [] fieldsList = ['buildingId'] # buildings.list() currently doesn't support paging # but should soon, attempt to use it now so we # won't break when it's turned on. fields = 'nextPageToken,buildings(%s)' possible_fields = {} for pfield in cd._rootDesc['schemas']['Building']['properties']: possible_fields[pfield.lower()] = pfield i = 3 while i < len(sys.argv): myarg = sys.argv[i].lower() if myarg == 'todrive': to_drive = True i += 1 elif myarg == 'allfields': fields = None i += 1 elif myarg in possible_fields: fieldsList.append(possible_fields[myarg]) i += 1 # Allows shorter arguments like "name" instead of "buildingname" elif 'building' + myarg in possible_fields: fieldsList.append(possible_fields['building' + myarg]) i += 1 else: controlflow.invalid_argument_exit(sys.argv[i], "gam print buildings") if fields: fields = fields % ','.join(fieldsList) buildings = gapi.get_all_pages(cd.resources().buildings(), 'list', 'buildings', customer=GC_Values[GC_CUSTOMER_ID], fields=fields) for building in buildings: building.pop('etags', None) building.pop('etag', None) building.pop('kind', None) if 'buildingId' in building: building['buildingId'] = f'id:{building["buildingId"]}' if 'floorNames' in building: building['floorNames'] = ','.join(building['floorNames']) building = utils.flatten_json(building) for item in building: if item not in titles: titles.append(item) csvRows.append(building) display.sort_csv_titles('buildingId', titles) display.write_csv_file(csvRows, titles, 'Buildings', to_drive)
def downloadExport(): verifyFiles = True extractFiles = True v = buildGAPIObject() s = storage.build_gapi() matterId = getMatterItem(v, sys.argv[3]) exportId = convertExportNameToID(v, sys.argv[4], matterId) targetFolder = GC_Values[GC_DRIVE_DIR] i = 5 while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg == 'targetfolder': targetFolder = os.path.expanduser(sys.argv[i + 1]) if not os.path.isdir(targetFolder): os.makedirs(targetFolder) i += 2 elif myarg == 'noverify': verifyFiles = False i += 1 elif myarg == 'noextract': extractFiles = False i += 1 else: controlflow.invalid_argument_exit(sys.argv[i], "gam download export") export = gapi.call(v.matters().exports(), 'get', matterId=matterId, exportId=exportId) for s_file in export['cloudStorageSink']['files']: bucket = s_file['bucketName'] s_object = s_file['objectName'] filename = os.path.join(targetFolder, s_object.replace('/', '-')) print(f'saving to {filename}') request = s.objects().get_media(bucket=bucket, object=s_object) f = fileutils.open_file(filename, 'wb') downloader = googleapiclient.http.MediaIoBaseDownload(f, request) done = False while not done: status, done = downloader.next_chunk() sys.stdout.write(' Downloaded: {0:>7.2%}\r'.format( status.progress())) sys.stdout.flush() sys.stdout.write('\n Download complete. Flushing to disk...\n') fileutils.close_file(f, True) if verifyFiles: expected_hash = s_file['md5Hash'] sys.stdout.write(f' Verifying file hash is {expected_hash}...') sys.stdout.flush() utils.md5_matches_file(filename, expected_hash, True) print('VERIFIED') if extractFiles and re.search(r'\.zip$', filename): __main__.extract_nested_zip(filename, targetFolder)
def printHolds(): v = buildGAPIObject() todrive = False csvRows = [] initialTitles = ['matterId', 'holdId', 'name', 'corpus', 'updateTime'] titles = initialTitles[:] matters = [] matterIds = [] i = 3 while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg == 'todrive': todrive = True i += 1 elif myarg in ['matter', 'matters']: matters = sys.argv[i + 1].split(',') i += 2 else: controlflow.invalid_argument_exit(myarg, "gam print holds") if not matters: fields = 'matters(matterId,state),nextPageToken' matters_results = gapi.get_all_pages(v.matters(), 'list', 'matters', view='BASIC', fields=fields) for matter in matters_results: matterState = matter['state'] matterId = matter['matterId'] if matterState != 'OPEN': print(f'ignoring matter {matterId} in state {matterState}') continue matterIds.append(matterId) else: for matter in matters: matterIds.append(getMatterItem(v, matter)) for matterId in matterIds: sys.stderr.write(f'Retrieving holds for matter {matterId}\n') holds = gapi.get_all_pages(v.matters().holds(), 'list', 'holds', matterId=matterId) for hold in holds: display.add_row_titles_to_csv_file( utils.flatten_json(hold, flattened={'matterId': matterId}), csvRows, titles) display.sort_csv_titles(initialTitles, titles) display.write_csv_file(csvRows, titles, 'Vault Holds', todrive)
def _getResourceCalendarAttributes(cd, args, body={}): i = 0 while i < len(args): myarg = args[i].lower().replace('_', '') if myarg == 'name': body['resourceName'] = args[i + 1] i += 2 elif myarg == 'description': body['resourceDescription'] = args[i + 1].replace('\\n', '\n') i += 2 elif myarg == 'type': body['resourceType'] = args[i + 1] i += 2 elif myarg in ['building', 'buildingid']: body['buildingId'] = getBuildingByNameOrId(cd, args[i + 1], minLen=0) i += 2 elif myarg in ['capacity']: body['capacity'] = __main__.getInteger(args[i + 1], myarg, minVal=0) i += 2 elif myarg in ['feature', 'features']: features = args[i + 1].split(',') body['featureInstances'] = [] for feature in features: instance = {'feature': {'name': feature}} body['featureInstances'].append(instance) i += 2 elif myarg in ['floor', 'floorname']: body['floorName'] = args[i + 1] i += 2 elif myarg in ['floorsection']: body['floorSection'] = args[i + 1] i += 2 elif myarg in ['category']: body['resourceCategory'] = args[i + 1].upper() if body['resourceCategory'] == 'ROOM': body['resourceCategory'] = 'CONFERENCE_ROOM' i += 2 elif myarg in ['uservisibledescription', 'userdescription']: body['userVisibleDescription'] = args[i + 1] i += 2 else: controlflow.invalid_argument_exit(args[i], "gam create|update resource") return body
def printFeatures(): to_drive = False cd = gapi.directory.buildGAPIObject() titles = [] csvRows = [] fieldsList = ['name'] fields = 'nextPageToken,features(%s)' possible_fields = {} for pfield in cd._rootDesc['schemas']['Feature']['properties']: possible_fields[pfield.lower()] = pfield i = 3 while i < len(sys.argv): myarg = sys.argv[i].lower() if myarg == 'todrive': to_drive = True i += 1 elif myarg == 'allfields': fields = None i += 1 elif myarg in possible_fields: fieldsList.append(possible_fields[myarg]) i += 1 elif 'feature' + myarg in possible_fields: fieldsList.append(possible_fields['feature' + myarg]) i += 1 else: controlflow.invalid_argument_exit(sys.argv[i], "gam print features") if fields: fields = fields % ','.join(fieldsList) features = gapi.get_all_pages(cd.resources().features(), 'list', 'features', customer=GC_Values[GC_CUSTOMER_ID], fields=fields) for feature in features: feature.pop('etags', None) feature.pop('etag', None) feature.pop('kind', None) feature = utils.flatten_json(feature) for item in feature: if item not in titles: titles.append(item) csvRows.append(feature) display.sort_csv_titles('name', titles) display.write_csv_file(csvRows, titles, 'Features', to_drive)
def printShowCalendars(users, csvFormat): if csvFormat: todrive = False titles = [] csvRows = [] i = 5 while i < len(sys.argv): myarg = sys.argv[i].lower() if csvFormat and myarg == 'todrive': todrive = True i += 1 else: controlflow.invalid_argument_exit( myarg, f"gam <users> {['show', 'print'][csvFormat]} calendars") i = 0 count = len(users) for user in users: i += 1 user, cal = buildCalendarGAPIObject(user) if not cal: continue result = gapi.get_all_pages(cal.calendarList(), 'list', 'items', soft_errors=True) jcount = len(result) if not csvFormat: print(f'User: {user}, Calendars:{display.current_count(i, count)}') if jcount == 0: continue j = 0 for userCalendar in result: j += 1 _showCalendar(userCalendar, j, jcount) else: if jcount == 0: continue for userCalendar in result: row = {'primaryEmail': user} display.add_row_titles_to_csv_file( utils.flatten_json(userCalendar, flattened=row), csvRows, titles) if csvFormat: display.sort_csv_titles(['primaryEmail', 'id'], titles) display.write_csv_file(csvRows, titles, 'Calendars', todrive)
def deleteHold(): v = buildGAPIObject() hold = sys.argv[3] matterId = None i = 4 while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg == 'matter': matterId = getMatterItem(v, sys.argv[i+1]) holdId = convertHoldNameToID(v, hold, matterId) i += 2 else: controlflow.invalid_argument_exit(myarg, "gam delete hold") if not matterId: controlflow.system_error_exit( 3, 'you must specify a matter for the hold.') print(f'Deleting hold {hold} / {holdId}') gapi.call(v.matters().holds(), 'delete', matterId=matterId, holdId=holdId)
def printShowACLs(csvFormat): calendarId, cal = buildCalendarDataGAPIObject(sys.argv[2]) if not cal: return toDrive = False i = 4 while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if csvFormat and myarg == 'todrive': toDrive = True i += 1 else: action = ['showacl', 'printacl'][csvFormat] message = f"gam calendar <email> {action}" controlflow.invalid_argument_exit(sys.argv[i], message) acls = gapi.get_all_pages(cal.acl(), 'list', 'items', calendarId=calendarId) i = 0 if csvFormat: titles = [] rows = [] else: count = len(acls) for rule in acls: i += 1 if csvFormat: row = utils.flatten_json(rule, None) for key in row: if key not in titles: titles.append(key) rows.append(row) else: formatted_acl = formatACLRule(rule) current_count = display.current_count(i, count) print( f'Calendar: {calendarId}, ACL: {formatted_acl}{current_count}') if csvFormat: display.write_csv_file(rows, titles, f'{calendarId} Calendar ACLs', toDrive)
def moveOrDeleteEvent(moveOrDelete): calendarId, cal = buildCalendarDataGAPIObject(sys.argv[2]) if not cal: return sendUpdates = None doit = False kwargs = {} i = 4 while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg in ['notifyattendees', 'sendnotifications', 'sendupdates']: sendUpdates, i = getSendUpdates(myarg, i, cal) elif myarg in ['id', 'eventid']: eventId = sys.argv[i + 1] i += 2 elif myarg in ['query', 'eventquery']: controlflow.system_error_exit( 2, f'query is no longer supported for {moveOrDelete}event. ' \ f'Use "gam calendar <email> printevents query <query> | ' \ f'gam csv - gam {moveOrDelete}event id ~id" instead.') elif myarg == 'doit': doit = True i += 1 elif moveOrDelete == 'move' and myarg == 'destination': kwargs['destination'] = sys.argv[i + 1] i += 2 else: controlflow.invalid_argument_exit( sys.argv[i], f"gam calendar <email> {moveOrDelete}event") if doit: print(f' going to {moveOrDelete} eventId {eventId}') gapi.call(cal.events(), moveOrDelete, calendarId=calendarId, eventId=eventId, sendUpdates=sendUpdates, **kwargs) else: print( f' would {moveOrDelete} eventId {eventId}. Add doit to command ' \ f'to actually {moveOrDelete} event')
def printMatters(): v = buildGAPIObject() todrive = False csvRows = [] initialTitles = ['matterId', 'name', 'description', 'state'] titles = initialTitles[:] view = 'FULL' state = None i = 3 while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg == 'todrive': todrive = True i += 1 elif myarg in PROJECTION_CHOICES_MAP: view = PROJECTION_CHOICES_MAP[myarg] i += 1 elif myarg == 'matterstate': valid_states = gapi.get_enum_values_minus_unspecified( v._rootDesc['schemas']['Matter']['properties']['state'][ 'enum']) state = sys.argv[i+1].upper() if state not in valid_states: controlflow.expected_argument_exit( 'state', ', '.join(valid_states), state) i += 2 else: controlflow.invalid_argument_exit(myarg, "gam print matters") __main__.printGettingAllItems('Vault Matters', None) page_message = gapi.got_total_items_msg('Vault Matters', '...\n') matters = gapi.get_all_pages( v.matters(), 'list', 'matters', page_message=page_message, view=view, state=state) for matter in matters: display.add_row_titles_to_csv_file( utils.flatten_json(matter), csvRows, titles) display.sort_csv_titles(initialTitles, titles) display.write_csv_file(csvRows, titles, 'Vault Matters', todrive)
def printExports(): v = buildGAPIObject() todrive = False csvRows = [] initialTitles = ['matterId', 'id', 'name', 'createTime', 'status'] titles = initialTitles[:] matters = [] matterIds = [] i = 3 while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg == 'todrive': todrive = True i += 1 elif myarg in ['matter', 'matters']: matters = sys.argv[i+1].split(',') i += 2 else: controlflow.invalid_argument_exit(myarg, "gam print exports") if not matters: fields = 'matters(matterId),nextPageToken' matters_results = gapi.get_all_pages(v.matters( ), 'list', 'matters', view='BASIC', state='OPEN', fields=fields) for matter in matters_results: matterIds.append(matter['matterId']) else: for matter in matters: matterIds.append(getMatterItem(v, matter)) for matterId in matterIds: sys.stderr.write(f'Retrieving exports for matter {matterId}\n') exports = gapi.get_all_pages( v.matters().exports(), 'list', 'exports', matterId=matterId) for export in exports: display.add_row_titles_to_csv_file(utils.flatten_json( export, flattened={'matterId': matterId}), csvRows, titles) display.sort_csv_titles(initialTitles, titles) display.write_csv_file(csvRows, titles, 'Vault Exports', todrive)
def modifySettings(): calendarId, cal = buildCalendarDataGAPIObject(sys.argv[2]) if not cal: return body = {} i = 4 while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg == 'description': body['description'] = sys.argv[i + 1] i += 2 elif myarg == 'location': body['location'] = sys.argv[i + 1] i += 2 elif myarg == 'summary': body['summary'] = sys.argv[i + 1] i += 2 elif myarg == 'timezone': body['timeZone'] = sys.argv[i + 1] i += 2 else: controlflow.invalid_argument_exit(sys.argv[i], "gam calendar <email> modify") gapi.call(cal.calendars(), 'patch', calendarId=calendarId, body=body)
def getCalendarAttributes(i, body, function): colorRgbFormat = False while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg == 'selected': body['selected'] = __main__.getBoolean(sys.argv[i + 1], myarg) i += 2 elif myarg == 'hidden': body['hidden'] = __main__.getBoolean(sys.argv[i + 1], myarg) i += 2 elif myarg == 'summary': body['summaryOverride'] = sys.argv[i + 1] i += 2 elif myarg == 'colorindex': body['colorId'] = __main__.getInteger( sys.argv[i + 1], myarg, minVal=CALENDAR_MIN_COLOR_INDEX, maxVal=CALENDAR_MAX_COLOR_INDEX) i += 2 elif myarg == 'backgroundcolor': body['backgroundColor'] = __main__.getColor(sys.argv[i + 1]) colorRgbFormat = True i += 2 elif myarg == 'foregroundcolor': body['foregroundColor'] = __main__.getColor(sys.argv[i + 1]) colorRgbFormat = True i += 2 elif myarg == 'reminder': body.setdefault('defaultReminders', []) method = sys.argv[i + 1].lower() if method not in CLEAR_NONE_ARGUMENT: if method not in CALENDAR_REMINDER_METHODS: controlflow.expected_argument_exit( "Method", ", ".join(CALENDAR_REMINDER_METHODS + CLEAR_NONE_ARGUMENT), method) minutes = __main__.getInteger( sys.argv[i + 2], myarg, minVal=0, maxVal=CALENDAR_REMINDER_MAX_MINUTES) body['defaultReminders'].append({ 'method': method, 'minutes': minutes }) i += 3 else: i += 2 elif myarg == 'notification': body.setdefault('notificationSettings', {'notifications': []}) method = sys.argv[i + 1].lower() if method not in CLEAR_NONE_ARGUMENT: if method not in CALENDAR_NOTIFICATION_METHODS: controlflow.expected_argument_exit( "Method", ", ".join(CALENDAR_NOTIFICATION_METHODS + CLEAR_NONE_ARGUMENT), method) eventType = sys.argv[i + 2].lower() if eventType not in CALENDAR_NOTIFICATION_TYPES_MAP: controlflow.expected_argument_exit( "Event", ", ".join(CALENDAR_NOTIFICATION_TYPES_MAP), eventType) notice = { 'method': method, 'type': CALENDAR_NOTIFICATION_TYPES_MAP[eventType] } body['notificationSettings']['notifications'].append(notice) i += 3 else: i += 2 else: controlflow.invalid_argument_exit(sys.argv[i], f"gam {function} calendar") return colorRgbFormat
def changeAttendees(users): do_it = True i = 5 allevents = False start_date = end_date = None while len(sys.argv) > i: myarg = sys.argv[i].lower() if myarg == 'csv': csv_file = sys.argv[i + 1] i += 2 elif myarg == 'dryrun': do_it = False i += 1 elif myarg == 'start': start_date = utils.get_time_or_delta_from_now(sys.argv[i + 1]) i += 2 elif myarg == 'end': end_date = utils.get_time_or_delta_from_now(sys.argv[i + 1]) i += 2 elif myarg == 'allevents': allevents = True i += 1 else: controlflow.invalid_argument_exit( sys.argv[i], "gam <users> update calattendees") attendee_map = {} f = fileutils.open_file(csv_file) csvFile = csv.reader(f) for row in csvFile: attendee_map[row[0].lower()] = row[1].lower() fileutils.close_file(f) for user in users: sys.stdout.write(f'Checking user {user}\n') user, cal = buildCalendarGAPIObject(user) if not cal: continue page_token = None while True: events_page = gapi.call(cal.events(), 'list', calendarId=user, pageToken=page_token, timeMin=start_date, timeMax=end_date, showDeleted=False, showHiddenInvitations=False) print(f'Got {len(events_page.get("items", []))}') for event in events_page.get('items', []): if event['status'] == 'cancelled': # print u' skipping cancelled event' continue try: event_summary = event['summary'] except (KeyError, UnicodeEncodeError, UnicodeDecodeError): event_summary = event['id'] try: organizer = event['organizer']['email'].lower() if not allevents and organizer != user: #print(f' skipping not-my-event {event_summary}') continue except KeyError: pass # no email for organizer needs_update = False try: for attendee in event['attendees']: try: if attendee['email'].lower() in attendee_map: old_email = attendee['email'].lower() new_email = attendee_map[ attendee['email'].lower()] print(f' SWITCHING attendee {old_email} to ' \ f'{new_email} for {event_summary}') event['attendees'].remove(attendee) event['attendees'].append({'email': new_email}) needs_update = True except KeyError: # no email for that attendee pass except KeyError: continue # no attendees if needs_update: body = {} body['attendees'] = event['attendees'] print(f'UPDATING {event_summary}') if do_it: gapi.call(cal.events(), 'patch', calendarId=user, eventId=event['id'], sendNotifications=False, body=body) else: print(' not pulling the trigger.') # else: # print(f' no update needed for {event_summary}') try: page_token = events_page['nextPageToken'] except KeyError: break
def getEventAttributes(i, calendarId, cal, body, action): # Default to external only so non-Google # calendars are notified of changes sendUpdates = 'externalOnly' action = 'update' if body else 'add' while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg in ['notifyattendees', 'sendnotifications', 'sendupdates']: sendUpdates, i = getSendUpdates(myarg, i, cal) elif myarg == 'attendee': body.setdefault('attendees', []) body['attendees'].append({'email': sys.argv[i + 1]}) i += 2 elif myarg == 'removeattendee' and action == 'update': remove_email = sys.argv[i + 1].lower() if 'attendees' in body: body['attendees'] = _remove_attendee(body['attendees'], remove_email) i += 2 elif myarg == 'optionalattendee': body.setdefault('attendees', []) body['attendees'].append({ 'email': sys.argv[i + 1], 'optional': True }) i += 2 elif myarg == 'anyonecanaddself': body['anyoneCanAddSelf'] = True i += 1 elif myarg == 'description': body['description'] = sys.argv[i + 1].replace('\\n', '\n') i += 2 elif myarg == 'replacedescription' and action == 'update': search = sys.argv[i + 1] replace = sys.argv[i + 2] if 'description' in body: body['description'] = re.sub(search, replace, body['description']) i += 3 elif myarg == 'start': if sys.argv[i + 1].lower() == 'allday': body['start'] = {'date': utils.get_yyyymmdd(sys.argv[i + 2])} i += 3 else: start_time = utils.get_time_or_delta_from_now(sys.argv[i + 1]) body['start'] = {'dateTime': start_time} i += 2 elif myarg == 'end': if sys.argv[i + 1].lower() == 'allday': body['end'] = {'date': utils.get_yyyymmdd(sys.argv[i + 2])} i += 3 else: end_time = utils.get_time_or_delta_from_now(sys.argv[i + 1]) body['end'] = {'dateTime': end_time} i += 2 elif myarg == 'guestscantinviteothers': body['guestsCanInviteOthers'] = False i += 1 elif myarg == 'guestscaninviteothers': body['guestsCanInviteTohters'] = __main__.getBoolean( sys.argv[i + 1], 'guestscaninviteothers') i += 2 elif myarg == 'guestscantseeothers': body['guestsCanSeeOtherGuests'] = False i += 1 elif myarg == 'guestscanseeothers': body['guestsCanSeeOtherGuests'] = __main__.getBoolean( sys.argv[i + 1], 'guestscanseeothers') i += 2 elif myarg == 'guestscanmodify': body['guestsCanModify'] = __main__.getBoolean( sys.argv[i + 1], 'guestscanmodify') i += 2 elif myarg == 'id': if action == 'update': controlflow.invalid_argument_exit( 'id', 'gam calendar <calendar> updateevent') body['id'] = sys.argv[i + 1] i += 2 elif myarg == 'summary': body['summary'] = sys.argv[i + 1] i += 2 elif myarg == 'location': body['location'] = sys.argv[i + 1] i += 2 elif myarg == 'available': body['transparency'] = 'transparent' i += 1 elif myarg == 'transparency': validTransparency = ['opaque', 'transparent'] if sys.argv[i + 1].lower() in validTransparency: body['transparency'] = sys.argv[i + 1].lower() else: controlflow.expected_argument_exit( 'transparency', ", ".join(validTransparency), sys.argv[i + 1]) i += 2 elif myarg == 'visibility': validVisibility = ['default', 'public', 'private'] if sys.argv[i + 1].lower() in validVisibility: body['visibility'] = sys.argv[i + 1].lower() else: controlflow.expected_argument_exit("visibility", ", ".join(validVisibility), sys.argv[i + 1]) i += 2 elif myarg == 'tentative': body['status'] = 'tentative' i += 1 elif myarg == 'status': validStatus = ['confirmed', 'tentative', 'cancelled'] if sys.argv[i + 1].lower() in validStatus: body['status'] = sys.argv[i + 1].lower() else: controlflow.expected_argument_exit('visibility', ', '.join(validStatus), sys.argv[i + 1]) i += 2 elif myarg == 'source': body['source'] = {'title': sys.argv[i + 1], 'url': sys.argv[i + 2]} i += 3 elif myarg == 'noreminders': body['reminders'] = {'useDefault': False} i += 1 elif myarg == 'reminder': minutes = \ __main__.getInteger(sys.argv[i+1], myarg, minVal=0, maxVal=CALENDAR_REMINDER_MAX_MINUTES) reminder = {'minutes': minutes, 'method': sys.argv[i + 2]} body.setdefault('reminders', { 'overrides': [], 'useDefault': False }) body['reminders']['overrides'].append(reminder) i += 3 elif myarg == 'recurrence': body.setdefault('recurrence', []) body['recurrence'].append(sys.argv[i + 1]) i += 2 elif myarg == 'timezone': timeZone = sys.argv[i + 1] i += 2 elif myarg == 'privateproperty': if 'extendedProperties' not in body: body['extendedProperties'] = {'private': {}, 'shared': {}} body['extendedProperties']['private'][sys.argv[i + 1]] = sys.argv[i + 2] i += 3 elif myarg == 'sharedproperty': if 'extendedProperties' not in body: body['extendedProperties'] = {'private': {}, 'shared': {}} body['extendedProperties']['shared'][sys.argv[i + 1]] = sys.argv[i + 2] i += 3 elif myarg == 'colorindex': body['colorId'] = __main__.getInteger( sys.argv[i + 1], myarg, CALENDAR_EVENT_MIN_COLOR_INDEX, CALENDAR_EVENT_MAX_COLOR_INDEX) i += 2 elif myarg == 'hangoutsmeet': body['conferenceData'] = { 'createRequest': { 'requestId': f'{str(uuid.uuid4())}' } } i += 1 else: controlflow.invalid_argument_exit( sys.argv[i], f'gam calendar <email> {action}event') if ('recurrence' in body) and (('start' in body) or ('end' in body)): if not timeZone: timeZone = gapi.call(cal.calendars(), 'get', calendarId=calendarId, fields='timeZone')['timeZone'] if 'start' in body: body['start']['timeZone'] = timeZone if 'end' in body: body['end']['timeZone'] = timeZone return (sendUpdates, body)
def doPrintCrosDevices(): def _getSelectedLists(myarg): if myarg in CROS_ACTIVE_TIME_RANGES_ARGUMENTS: selectedLists['activeTimeRanges'] = True elif myarg in CROS_RECENT_USERS_ARGUMENTS: selectedLists['recentUsers'] = True elif myarg in CROS_DEVICE_FILES_ARGUMENTS: selectedLists['deviceFiles'] = True elif myarg in CROS_CPU_STATUS_REPORTS_ARGUMENTS: selectedLists['cpuStatusReports'] = True elif myarg in CROS_DISK_VOLUME_REPORTS_ARGUMENTS: selectedLists['diskVolumeReports'] = True elif myarg in CROS_SYSTEM_RAM_FREE_REPORTS_ARGUMENTS: selectedLists['systemRamFreeReports'] = True cd = gapi.directory.buildGAPIObject() todrive = False fieldsList = [] fieldsTitles = {} titles = [] csvRows = [] display.add_field_to_csv_file( 'deviceid', CROS_ARGUMENT_TO_PROPERTY_MAP, fieldsList, fieldsTitles, titles) projection = orderBy = sortOrder = orgUnitPath = None queries = [None] noLists = sortHeaders = False selectedLists = {} startDate = endDate = None listLimit = 0 i = 3 while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg in ['query', 'queries']: queries = __main__.getQueries(myarg, sys.argv[i+1]) i += 2 elif myarg == 'limittoou': orgUnitPath = __main__.getOrgUnitItem(sys.argv[i+1]) i += 2 elif myarg == 'todrive': todrive = True i += 1 elif myarg == 'nolists': noLists = True selectedLists = {} i += 1 elif myarg == 'listlimit': listLimit = __main__.getInteger(sys.argv[i+1], myarg, minVal=0) i += 2 elif myarg in CROS_START_ARGUMENTS: startDate = _getFilterDate(sys.argv[i+1]) i += 2 elif myarg in CROS_END_ARGUMENTS: endDate = _getFilterDate(sys.argv[i+1]) i += 2 elif myarg == 'orderby': orderBy = sys.argv[i+1].lower().replace('_', '') validOrderBy = ['location', 'user', 'lastsync', 'notes', 'serialnumber', 'status', 'supportenddate'] if orderBy not in validOrderBy: controlflow.expected_argument_exit( "orderby", ", ".join(validOrderBy), orderBy) if orderBy == 'location': orderBy = 'annotatedLocation' elif orderBy == 'user': orderBy = 'annotatedUser' elif orderBy == 'lastsync': orderBy = 'lastSync' elif orderBy == 'serialnumber': orderBy = 'serialNumber' elif orderBy == 'supportenddate': orderBy = 'supportEndDate' i += 2 elif myarg in SORTORDER_CHOICES_MAP: sortOrder = SORTORDER_CHOICES_MAP[myarg] i += 1 elif myarg in PROJECTION_CHOICES_MAP: projection = PROJECTION_CHOICES_MAP[myarg] sortHeaders = True if projection == 'FULL': fieldsList = [] else: fieldsList = CROS_BASIC_FIELDS_LIST[:] i += 1 elif myarg == 'allfields': projection = 'FULL' sortHeaders = True fieldsList = [] i += 1 elif myarg == 'sortheaders': sortHeaders = True i += 1 elif myarg in CROS_LISTS_ARGUMENTS: _getSelectedLists(myarg) i += 1 elif myarg in CROS_ARGUMENT_TO_PROPERTY_MAP: display.add_field_to_fields_list( myarg, CROS_ARGUMENT_TO_PROPERTY_MAP, fieldsList) i += 1 elif myarg == 'fields': fieldNameList = sys.argv[i+1] for field in fieldNameList.lower().replace(',', ' ').split(): if field in CROS_LISTS_ARGUMENTS: _getSelectedLists(field) elif field in CROS_ARGUMENT_TO_PROPERTY_MAP: display.add_field_to_fields_list( field, CROS_ARGUMENT_TO_PROPERTY_MAP, fieldsList) else: controlflow.invalid_argument_exit( field, "gam print cros fields") i += 2 else: controlflow.invalid_argument_exit(sys.argv[i], "gam print cros") if selectedLists: noLists = False projection = 'FULL' for selectList in selectedLists: display.add_field_to_fields_list( selectList, CROS_ARGUMENT_TO_PROPERTY_MAP, fieldsList) if fieldsList: fieldsList.append('deviceId') fields = f'nextPageToken,chromeosdevices({",".join(set(fieldsList))})'.replace( '.', '/') else: fields = None for query in queries: __main__.printGettingAllItems('CrOS Devices', query) page_message = gapi.got_total_items_msg('CrOS Devices', '...\n') all_cros = gapi.get_all_pages(cd.chromeosdevices(), 'list', 'chromeosdevices', page_message=page_message, query=query, customerId=GC_Values[GC_CUSTOMER_ID], projection=projection, orgUnitPath=orgUnitPath, orderBy=orderBy, sortOrder=sortOrder, fields=fields) for cros in all_cros: _checkTPMVulnerability(cros) if not noLists and not selectedLists: for cros in all_cros: if 'notes' in cros: cros['notes'] = cros['notes'].replace('\n', '\\n') if 'autoUpdateExpiration' in cros: cros['autoUpdateExpiration'] = utils.formatTimestampYMD( cros['autoUpdateExpiration']) for cpuStatusReport in cros.get('cpuStatusReports', []): tempInfos = cpuStatusReport.get('cpuTemperatureInfo', []) for tempInfo in tempInfos: tempInfo['label'] = tempInfo['label'].strip() display.add_row_titles_to_csv_file(utils.flatten_json( cros, listLimit=listLimit), csvRows, titles) continue for cros in all_cros: if 'notes' in cros: cros['notes'] = cros['notes'].replace('\n', '\\n') if 'autoUpdateExpiration' in cros: cros['autoUpdateExpiration'] = utils.formatTimestampYMD( cros['autoUpdateExpiration']) row = {} for attrib in cros: if attrib not in set(['kind', 'etag', 'tpmVersionInfo', 'recentUsers', 'activeTimeRanges', 'deviceFiles', 'cpuStatusReports', 'diskVolumeReports', 'systemRamFreeReports']): row[attrib] = cros[attrib] if selectedLists.get('activeTimeRanges'): timergs = cros.get('activeTimeRanges', []) else: timergs = [] activeTimeRanges = _filterTimeRanges(timergs, startDate, endDate) if selectedLists.get('recentUsers'): recentUsers = cros.get('recentUsers', []) else: recentUsers = [] if selectedLists.get('deviceFiles'): device_files = cros.get('deviceFiles', []) else: device_files = [] deviceFiles = _filterCreateReportTime(device_files, 'createTime', startDate, endDate) if selectedLists.get('cpuStatusReports'): cpu_reports = cros.get('cpuStatusReports', []) else: cpu_reports = [] cpuStatusReports = _filterCreateReportTime(cpu_reports, 'reportTime', startDate, endDate) if selectedLists.get('diskVolumeReports'): diskVolumeReports = cros.get('diskVolumeReports', []) else: diskVolumeReports = [] if selectedLists.get('systemRamFreeReports'): ram_reports = cros.get('systemRamFreeReports', []) else: ram_reports = [] systemRamFreeReports = _filterCreateReportTime(ram_reports, 'reportTime', startDate, endDate) if noLists or (not activeTimeRanges and \ not recentUsers and \ not deviceFiles and \ not cpuStatusReports and \ not diskVolumeReports and \ not systemRamFreeReports): display.add_row_titles_to_csv_file(row, csvRows, titles) continue lenATR = len(activeTimeRanges) lenRU = len(recentUsers) lenDF = len(deviceFiles) lenCSR = len(cpuStatusReports) lenDVR = len(diskVolumeReports) lenSRFR = len(systemRamFreeReports) max_len = max(lenATR, lenRU, lenDF, lenCSR, lenDVR, lenSRFR) for i in range(min(max_len, listLimit or max_len)): nrow = row.copy() if i < lenATR: nrow['activeTimeRanges.date'] = \ activeTimeRanges[i]['date'] nrow['activeTimeRanges.activeTime'] = \ str(activeTimeRanges[i]['activeTime']) active_time = activeTimeRanges[i]['activeTime'] nrow['activeTimeRanges.duration'] = \ utils.formatMilliSeconds(active_time) nrow['activeTimeRanges.minutes'] = active_time // 60000 if i < lenRU: nrow['recentUsers.type'] = recentUsers[i]['type'] nrow['recentUsers.email'] = recentUsers[i].get('email') if not nrow['recentUsers.email']: if nrow['recentUsers.type'] == 'USER_TYPE_UNMANAGED': nrow['recentUsers.email'] = 'UnmanagedUser' else: nrow['recentUsers.email'] = 'Unknown' if i < lenDF: nrow['deviceFiles.type'] = deviceFiles[i]['type'] nrow['deviceFiles.createTime'] = \ deviceFiles[i]['createTime'] if i < lenCSR: nrow['cpuStatusReports.reportTime'] = \ cpuStatusReports[i]['reportTime'] tempInfos = cpuStatusReports[i].get('cpuTemperatureInfo', []) for tempInfo in tempInfos: label = tempInfo["label"].strip() base = 'cpuStatusReports.cpuTemperatureInfo.' nrow[f'{base}{label}'] = tempInfo['temperature'] cpu_field = 'cpuUtilizationPercentageInfo' cpu_reports = cpuStatusReports[i][cpu_field] cpu_pcts = [str(x) for x in cpu_reports] nrow[f'cpuStatusReports.{cpu_field}'] = ','.join(cpu_pcts) if i < lenDVR: volumeInfo = diskVolumeReports[i]['volumeInfo'] j = 0 vfield = 'diskVolumeReports.volumeInfo.' for volume in volumeInfo: nrow[f'{vfield}{j}.volumeId'] = \ volume['volumeId'] nrow[f'{vfield}{j}.storageFree'] = \ volume['storageFree'] nrow[f'{vfield}{j}.storageTotal'] = \ volume['storageTotal'] j += 1 if i < lenSRFR: nrow['systemRamFreeReports.reportTime'] = \ systemRamFreeReports[i]['reportTime'] ram_reports = systemRamFreeReports[i]['systemRamFreeInfo'] ram_info = [str(x) for x in ram_reports] nrow['systenRamFreeReports.systemRamFreeInfo'] = \ ','.join(ram_info) display.add_row_titles_to_csv_file(nrow, csvRows, titles) if sortHeaders: display.sort_csv_titles(['deviceId', ], titles) display.write_csv_file(csvRows, titles, 'CrOS', todrive)
def doPrintCrosActivity(): cd = gapi.directory.buildGAPIObject() todrive = False titles = ['deviceId', 'annotatedAssetId', 'annotatedLocation', 'serialNumber', 'orgUnitPath'] csvRows = [] fieldsList = ['deviceId', 'annotatedAssetId', 'annotatedLocation', 'serialNumber', 'orgUnitPath'] startDate = endDate = None selectActiveTimeRanges = selectDeviceFiles = selectRecentUsers = False listLimit = 0 delimiter = ',' orgUnitPath = None queries = [None] i = 3 while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg in ['query', 'queries']: queries = __main__.getQueries(myarg, sys.argv[i+1]) i += 2 elif myarg == 'limittoou': orgUnitPath = __main__.getOrgUnitItem(sys.argv[i+1]) i += 2 elif myarg == 'todrive': todrive = True i += 1 elif myarg in CROS_ACTIVE_TIME_RANGES_ARGUMENTS: selectActiveTimeRanges = True i += 1 elif myarg in CROS_DEVICE_FILES_ARGUMENTS: selectDeviceFiles = True i += 1 elif myarg in CROS_RECENT_USERS_ARGUMENTS: selectRecentUsers = True i += 1 elif myarg == 'both': selectActiveTimeRanges = selectRecentUsers = True i += 1 elif myarg == 'all': selectActiveTimeRanges = selectDeviceFiles = True selectRecentUsers = True i += 1 elif myarg in CROS_START_ARGUMENTS: startDate = _getFilterDate(sys.argv[i+1]) i += 2 elif myarg in CROS_END_ARGUMENTS: endDate = _getFilterDate(sys.argv[i+1]) i += 2 elif myarg == 'listlimit': listLimit = __main__.getInteger(sys.argv[i+1], myarg, minVal=0) i += 2 elif myarg == 'delimiter': delimiter = sys.argv[i+1] i += 2 else: controlflow.invalid_argument_exit( sys.argv[i], "gam print crosactivity") if not selectActiveTimeRanges and \ not selectDeviceFiles and \ not selectRecentUsers: selectActiveTimeRanges = selectRecentUsers = True if selectRecentUsers: fieldsList.append('recentUsers') display.add_titles_to_csv_file(['recentUsers.email', ], titles) if selectActiveTimeRanges: fieldsList.append('activeTimeRanges') titles_to_add = ['activeTimeRanges.date', 'activeTimeRanges.duration', 'activeTimeRanges.minutes'] display.add_titles_to_csv_file(titles_to_add, titles) if selectDeviceFiles: fieldsList.append('deviceFiles') titles_to_add = ['deviceFiles.type', 'deviceFiles.createTime'] display.add_titles_to_csv_file(titles_to_add, titles) fields = f'nextPageToken,chromeosdevices({",".join(fieldsList)})' for query in queries: __main__.printGettingAllItems('CrOS Devices', query) page_message = gapi.got_total_items_msg('CrOS Devices', '...\n') all_cros = gapi.get_all_pages(cd.chromeosdevices(), 'list', 'chromeosdevices', page_message=page_message, query=query, customerId=GC_Values[GC_CUSTOMER_ID], projection='FULL', fields=fields, orgUnitPath=orgUnitPath) for cros in all_cros: row = {} skip_attribs = ['recentUsers', 'activeTimeRanges', 'deviceFiles'] for attrib in cros: if attrib not in skip_attribs: row[attrib] = cros[attrib] if selectActiveTimeRanges: activeTimeRanges = _filterTimeRanges( cros.get('activeTimeRanges', []), startDate, endDate) lenATR = len(activeTimeRanges) num_ranges = min(lenATR, listLimit or lenATR) for activeTimeRange in activeTimeRanges[:num_ranges]: newrow = row.copy() newrow['activeTimeRanges.date'] = activeTimeRange['date'] active_time = activeTimeRange['activeTime'] newrow['activeTimeRanges.duration'] = \ utils.formatMilliSeconds(active_time) newrow['activeTimeRanges.minutes'] = \ activeTimeRange['activeTime']//60000 csvRows.append(newrow) if selectRecentUsers: recentUsers = cros.get('recentUsers', []) lenRU = len(recentUsers) num_ranges = min(lenRU, listLimit or lenRU) recent_users = [] for recentUser in recentUsers[:num_ranges]: useremail = recentUser.get("email") if not useremail: if recentUser["type"] == "USER_TYPE_UNMANAGED": useremail = 'UnmanagedUser' else: useremail = 'Unknown' recent_users.append(useremail) row['recentUsers.email'] = delimiter.join(recent_users) csvRows.append(row) if selectDeviceFiles: deviceFiles = _filterCreateReportTime( cros.get('deviceFiles', []), 'createTime', startDate, endDate) lenDF = len(deviceFiles) num_ranges = min(lenDF, listLimit or lenDF) for deviceFile in deviceFiles[:num_ranges]: newrow = row.copy() newrow['deviceFiles.type'] = deviceFile['type'] create_time = deviceFile['createTime'] newrow['deviceFiles.createTime'] = create_time csvRows.append(newrow) display.write_csv_file(csvRows, titles, 'CrOS Activity', todrive)
def doUpdateCros(): cd = gapi.directory.buildGAPIObject() i, devices = getCrOSDeviceEntity(3, cd) update_body = {} action_body = {} orgUnitPath = None ack_wipe = False while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg == 'user': update_body['annotatedUser'] = sys.argv[i+1] i += 2 elif myarg == 'location': update_body['annotatedLocation'] = sys.argv[i+1] i += 2 elif myarg == 'notes': update_body['notes'] = sys.argv[i+1].replace('\\n', '\n') i += 2 elif myarg in ['tag', 'asset', 'assetid']: update_body['annotatedAssetId'] = sys.argv[i+1] i += 2 elif myarg in ['ou', 'org']: orgUnitPath = __main__.getOrgUnitItem(sys.argv[i+1]) i += 2 elif myarg == 'action': action = sys.argv[i+1].lower().replace('_', '').replace('-', '') deprovisionReason = None if action in ['deprovisionsamemodelreplace', 'deprovisionsamemodelreplacement']: action = 'deprovision' deprovisionReason = 'same_model_replacement' elif action in ['deprovisiondifferentmodelreplace', 'deprovisiondifferentmodelreplacement']: action = 'deprovision' deprovisionReason = 'different_model_replacement' elif action in ['deprovisionretiringdevice']: action = 'deprovision' deprovisionReason = 'retiring_device' elif action not in ['disable', 'reenable']: controlflow.system_error_exit(2, f'expected action of ' \ f'deprovision_same_model_replace, ' \ f'deprovision_different_model_replace, ' \ f'deprovision_retiring_device, disable or reenable,' f' got {action}') action_body = {'action': action} if deprovisionReason: action_body['deprovisionReason'] = deprovisionReason i += 2 elif myarg == 'acknowledgedevicetouchrequirement': ack_wipe = True i += 1 else: controlflow.invalid_argument_exit(sys.argv[i], "gam update cros") i = 0 count = len(devices) if action_body: if action_body['action'] == 'deprovision' and not ack_wipe: print(f'WARNING: Refusing to deprovision {count} devices because ' 'acknowledge_device_touch_requirement not specified. ' \ 'Deprovisioning a device means the device will have to ' \ 'be physically wiped and re-enrolled to be managed by ' \ 'your domain again. This requires physical access to ' \ 'the device and is very time consuming to perform for ' \ 'each device. Please add ' \ '"acknowledge_device_touch_requirement" to the GAM ' \ 'command if you understand this and wish to proceed ' \ 'with the deprovision. Please also be aware that ' \ 'deprovisioning can have an effect on your device ' \ 'license count. See ' \ 'https://support.google.com/chrome/a/answer/3523633 '\ 'for full details.') sys.exit(3) for deviceId in devices: i += 1 cur_count = __main__.currentCount(i, count) print(f' performing action {action} for {deviceId}{cur_count}') gapi.call(cd.chromeosdevices(), function='action', customerId=GC_Values[GC_CUSTOMER_ID], resourceId=deviceId, body=action_body) else: if update_body: for deviceId in devices: i += 1 current_count = __main__.currentCount(i, count) print(f' updating {deviceId}{current_count}') gapi.call(cd.chromeosdevices(), 'update', customerId=GC_Values[GC_CUSTOMER_ID], deviceId=deviceId, body=update_body) if orgUnitPath: # split moves into max 50 devices per batch for l in range(0, len(devices), 50): move_body = {'deviceIds': devices[l:l+50]} print(f' moving {len(move_body["deviceIds"])} devices to ' \ f'{orgUnitPath}') gapi.call(cd.chromeosdevices(), 'moveDevicesToOu', customerId=GC_Values[GC_CUSTOMER_ID], orgUnitPath=orgUnitPath, body=move_body)
def showReport(): rep = buildGAPIObject() throw_reasons = [gapi.errors.ErrorReason.INVALID] report = sys.argv[2].lower() report = REPORT_CHOICE_MAP.get(report.replace('_', ''), report) valid_apps = gapi.get_enum_values_minus_unspecified( rep._rootDesc['resources']['activities']['methods']['list'] ['parameters']['applicationName']['enum']) + ['customer', 'user'] if report not in valid_apps: controlflow.expected_argument_exit("report", ", ".join(sorted(valid_apps)), report) customerId = GC_Values[GC_CUSTOMER_ID] if customerId == MY_CUSTOMER: customerId = None filters = parameters = actorIpAddress = startTime = endTime = eventName = orgUnitId = None tryDate = datetime.date.today().strftime(YYYYMMDD_FORMAT) to_drive = False userKey = 'all' fullDataRequired = None i = 3 while i < len(sys.argv): myarg = sys.argv[i].lower() if myarg == 'date': tryDate = utils.get_yyyymmdd(sys.argv[i + 1]) i += 2 elif myarg in ['orgunit', 'org', 'ou']: _, orgUnitId = __main__.getOrgUnitId(sys.argv[i + 1]) i += 2 elif myarg == 'fulldatarequired': fullDataRequired = [] fdr = sys.argv[i + 1].lower() if fdr and fdr != 'all': fullDataRequired = fdr.replace(',', ' ').split() i += 2 elif myarg == 'start': startTime = utils.get_time_or_delta_from_now(sys.argv[i + 1]) i += 2 elif myarg == 'end': endTime = utils.get_time_or_delta_from_now(sys.argv[i + 1]) i += 2 elif myarg == 'event': eventName = sys.argv[i + 1] i += 2 elif myarg == 'user': userKey = __main__.normalizeEmailAddressOrUID(sys.argv[i + 1]) i += 2 elif myarg in ['filter', 'filters']: filters = sys.argv[i + 1] i += 2 elif myarg in ['fields', 'parameters']: parameters = sys.argv[i + 1] i += 2 elif myarg == 'ip': actorIpAddress = sys.argv[i + 1] i += 2 elif myarg == 'todrive': to_drive = True i += 1 else: controlflow.invalid_argument_exit(sys.argv[i], "gam report") if report == 'user': while True: try: if fullDataRequired is not None: warnings = gapi.get_items(rep.userUsageReport(), 'get', 'warnings', throw_reasons=throw_reasons, date=tryDate, userKey=userKey, customerId=customerId, orgUnitID=orgUnitId, fields='warnings') fullData, tryDate = _check_full_data_available( warnings, tryDate, fullDataRequired) if fullData < 0: print('No user report available.') sys.exit(1) if fullData == 0: continue page_message = gapi.got_total_items_msg('Users', '...\n') usage = gapi.get_all_pages(rep.userUsageReport(), 'get', 'usageReports', page_message=page_message, throw_reasons=throw_reasons, date=tryDate, userKey=userKey, customerId=customerId, orgUnitID=orgUnitId, filters=filters, parameters=parameters) break except gapi.errors.GapiInvalidError as e: tryDate = _adjust_date(str(e)) if not usage: print('No user report available.') sys.exit(1) titles = ['email', 'date'] csvRows = [] ptypes = ['intValue', 'boolValue', 'datetimeValue', 'stringValue'] for user_report in usage: if 'entity' not in user_report: continue row = { 'email': user_report['entity']['userEmail'], 'date': tryDate } for item in user_report.get('parameters', []): if 'name' not in item: continue name = item['name'] if not name in titles: titles.append(name) for ptype in ptypes: if ptype in item: row[name] = item[ptype] break else: row[name] = '' csvRows.append(row) display.write_csv_file(csvRows, titles, f'User Reports - {tryDate}', to_drive) elif report == 'customer': while True: try: if fullDataRequired is not None: warnings = gapi.get_items(rep.customerUsageReports(), 'get', 'warnings', throw_reasons=throw_reasons, customerId=customerId, date=tryDate, fields='warnings') fullData, tryDate = _check_full_data_available( warnings, tryDate, fullDataRequired) if fullData < 0: print('No customer report available.') sys.exit(1) if fullData == 0: continue usage = gapi.get_all_pages(rep.customerUsageReports(), 'get', 'usageReports', throw_reasons=throw_reasons, customerId=customerId, date=tryDate, parameters=parameters) break except gapi.errors.GapiInvalidError as e: tryDate = _adjust_date(str(e)) if not usage: print('No customer report available.') sys.exit(1) titles = ['name', 'value', 'client_id'] csvRows = [] auth_apps = list() for item in usage[0]['parameters']: if 'name' not in item: continue name = item['name'] if 'intValue' in item: value = item['intValue'] elif 'msgValue' in item: if name == 'accounts:authorized_apps': for subitem in item['msgValue']: app = {} for an_item in subitem: if an_item == 'client_name': app['name'] = 'App: ' + \ subitem[an_item].replace('\n', '\\n') elif an_item == 'num_users': app['value'] = f'{subitem[an_item]} users' elif an_item == 'client_id': app['client_id'] = subitem[an_item] auth_apps.append(app) continue values = [] for subitem in item['msgValue']: if 'count' in subitem: mycount = myvalue = None for key, value in list(subitem.items()): if key == 'count': mycount = value else: myvalue = value if mycount and myvalue: values.append(f'{myvalue}:{mycount}') value = ' '.join(values) elif 'version_number' in subitem \ and 'num_devices' in subitem: values.append(f'{subitem["version_number"]}:' f'{subitem["num_devices"]}') else: continue value = ' '.join(sorted(values, reverse=True)) csvRows.append({'name': name, 'value': value}) for app in auth_apps: # put apps at bottom csvRows.append(app) display.write_csv_file(csvRows, titles, f'Customer Report - {tryDate}', todrive=to_drive) else: page_message = gapi.got_total_items_msg('Activities', '...\n') activities = gapi.get_all_pages(rep.activities(), 'list', 'items', page_message=page_message, applicationName=report, userKey=userKey, customerId=customerId, actorIpAddress=actorIpAddress, startTime=startTime, endTime=endTime, eventName=eventName, filters=filters, orgUnitID=orgUnitId) if activities: titles = ['name'] csvRows = [] for activity in activities: events = activity['events'] del activity['events'] activity_row = utils.flatten_json(activity) purge_parameters = True for event in events: for item in event.get('parameters', []): if set(item) == set(['value', 'name']): event[item['name']] = item['value'] elif set(item) == set(['intValue', 'name']): if item['name'] in ['start_time', 'end_time']: val = item.get('intValue') if val is not None: val = int(val) if val >= 62135683200: event[item['name']] = \ datetime.datetime.fromtimestamp( val-62135683200).isoformat() else: event[item['name']] = item['intValue'] elif set(item) == set(['boolValue', 'name']): event[item['name']] = item['boolValue'] elif set(item) == set(['multiValue', 'name']): event[item['name']] = ' '.join(item['multiValue']) elif item['name'] == 'scope_data': parts = {} for message in item['multiMessageValue']: for mess in message['parameter']: value = mess.get( 'value', ' '.join(mess.get('multiValue', []))) parts[mess['name']] = parts.get( mess['name'], []) + [value] for part, v in parts.items(): if part == 'scope_name': part = 'scope' event[part] = ' '.join(v) else: purge_parameters = False if purge_parameters: event.pop('parameters', None) row = utils.flatten_json(event) row.update(activity_row) for item in row: if item not in titles: titles.append(item) csvRows.append(row) display.sort_csv_titles([ 'name', ], titles) display.write_csv_file(csvRows, titles, f'{report.capitalize()} Activity Report', to_drive)
def doGetCrosInfo(): cd = gapi.directory.buildGAPIObject() i, devices = getCrOSDeviceEntity(3, cd) downloadfile = None targetFolder = GC_Values[GC_DRIVE_DIR] projection = None fieldsList = [] noLists = False startDate = endDate = None listLimit = 0 while i < len(sys.argv): myarg = sys.argv[i].lower().replace('_', '') if myarg == 'nolists': noLists = True i += 1 elif myarg == 'listlimit': listLimit = __main__.getInteger(sys.argv[i+1], myarg, minVal=-1) i += 2 elif myarg in CROS_START_ARGUMENTS: startDate = _getFilterDate(sys.argv[i+1]) i += 2 elif myarg in CROS_END_ARGUMENTS: endDate = _getFilterDate(sys.argv[i+1]) i += 2 elif myarg == 'allfields': projection = 'FULL' fieldsList = [] i += 1 elif myarg in PROJECTION_CHOICES_MAP: projection = PROJECTION_CHOICES_MAP[myarg] if projection == 'FULL': fieldsList = [] else: fieldsList = CROS_BASIC_FIELDS_LIST[:] i += 1 elif myarg in CROS_ARGUMENT_TO_PROPERTY_MAP: fieldsList.extend(CROS_ARGUMENT_TO_PROPERTY_MAP[myarg]) i += 1 elif myarg == 'fields': fieldNameList = sys.argv[i+1] for field in fieldNameList.lower().replace(',', ' ').split(): if field in CROS_ARGUMENT_TO_PROPERTY_MAP: fieldsList.extend(CROS_ARGUMENT_TO_PROPERTY_MAP[field]) if field in CROS_ACTIVE_TIME_RANGES_ARGUMENTS + \ CROS_DEVICE_FILES_ARGUMENTS + \ CROS_RECENT_USERS_ARGUMENTS: projection = 'FULL' noLists = False else: controlflow.invalid_argument_exit( field, "gam info cros fields") i += 2 elif myarg == 'downloadfile': downloadfile = sys.argv[i+1] if downloadfile.lower() == 'latest': downloadfile = downloadfile.lower() i += 2 elif myarg == 'targetfolder': targetFolder = os.path.expanduser(sys.argv[i+1]) if not os.path.isdir(targetFolder): os.makedirs(targetFolder) i += 2 else: controlflow.invalid_argument_exit(sys.argv[i], "gam info cros") if fieldsList: fieldsList.append('deviceId') fields = ','.join(set(fieldsList)).replace('.', '/') else: fields = None i = 0 device_count = len(devices) for deviceId in devices: i += 1 cros = gapi.call(cd.chromeosdevices(), 'get', customerId=GC_Values[GC_CUSTOMER_ID], deviceId=deviceId, projection=projection, fields=fields) print(f'CrOS Device: {deviceId} ({i} of {device_count})') if 'notes' in cros: cros['notes'] = cros['notes'].replace('\n', '\\n') if 'autoUpdateExpiration' in cros: cros['autoUpdateExpiration'] = utils.formatTimestampYMD( cros['autoUpdateExpiration']) _checkTPMVulnerability(cros) for up in CROS_SCALAR_PROPERTY_PRINT_ORDER: if up in cros: if isinstance(cros[up], str): print(f' {up}: {cros[up]}') else: sys.stdout.write(f' {up}:') display.print_json(cros[up], ' ') if not noLists: activeTimeRanges = _filterTimeRanges( cros.get('activeTimeRanges', []), startDate, endDate) lenATR = len(activeTimeRanges) if lenATR: print(' activeTimeRanges') num_ranges = min(lenATR, listLimit or lenATR) for activeTimeRange in activeTimeRanges[:num_ranges]: active_date = activeTimeRange["date"] active_time = activeTimeRange["activeTime"] duration = utils.formatMilliSeconds(active_time) minutes = active_time // 60000 print(f' date: {active_date}') print(f' activeTime: {active_time}') print(f' duration: {duration}') print(f' minutes: {minutes}') recentUsers = cros.get('recentUsers', []) lenRU = len(recentUsers) if lenRU: print(' recentUsers') num_ranges = min(lenRU, listLimit or lenRU) for recentUser in recentUsers[:num_ranges]: useremail = recentUser.get("email") if not useremail: if recentUser["type"] == "USER_TYPE_UNMANAGED": useremail = 'UnmanagedUser' else: useremail = 'Unknown' print(f' type: {recentUser["type"]}') print(f' email: {useremail}') deviceFiles = _filterCreateReportTime( cros.get('deviceFiles', []), 'createTime', startDate, endDate) lenDF = len(deviceFiles) if lenDF: num_ranges = min(lenDF, listLimit or lenDF) print(' deviceFiles') for deviceFile in deviceFiles[:num_ranges]: device_type = deviceFile['type'] create_time = deviceFile['createTime'] print(f' {device_type}: {create_time}') if downloadfile: deviceFiles = cros.get('deviceFiles', []) lenDF = len(deviceFiles) if lenDF: if downloadfile == 'latest': deviceFile = deviceFiles[-1] else: for deviceFile in deviceFiles: if deviceFile['createTime'] == downloadfile: break else: print(f'ERROR: file {downloadfile} not ' \ f'available to download.') deviceFile = None if deviceFile: created = deviceFile["createTime"] downloadfile = f'cros-logs-{deviceId}-{created}.zip' downloadfilename = os.path.join(targetFolder, downloadfile) dl_url = deviceFile['downloadUrl'] _, content = cd._http.request(dl_url) fileutils.write_file(downloadfilename, content, mode='wb', continue_on_error=True) print(f'Downloaded: {downloadfilename}') elif downloadfile: print('ERROR: no files to download.') cpuStatusReports = _filterCreateReportTime( cros.get('cpuStatusReports', []), 'reportTime', startDate, endDate) lenCSR = len(cpuStatusReports) if lenCSR: print(' cpuStatusReports') num_ranges = min(lenCSR, listLimit or lenCSR) for cpuStatusReport in cpuStatusReports[:num_ranges]: print(f' reportTime: {cpuStatusReport["reportTime"]}') print(' cpuTemperatureInfo') tempInfos = cpuStatusReport.get('cpuTemperatureInfo', []) for tempInfo in tempInfos: temp_label = tempInfo['label'].strip() temperature = tempInfo['temperature'] print(f' {temp_label}: {temperature}') pct_info = cpuStatusReport["cpuUtilizationPercentageInfo"] util = ",".join([str(x) for x in pct_info]) print(f' cpuUtilizationPercentageInfo: {util}') diskVolumeReports = cros.get('diskVolumeReports', []) lenDVR = len(diskVolumeReports) if lenDVR: print(' diskVolumeReports') print(' volumeInfo') num_ranges = min(lenDVR, listLimit or lenDVR) for diskVolumeReport in diskVolumeReports[:num_ranges]: volumeInfo = diskVolumeReport['volumeInfo'] for volume in volumeInfo: vid = volume['volumeId'] vstorage_free = volume['storageFree'] vstorage_total = volume['storageTotal'] print(f' volumeId: {vid}') print(f' storageFree: {vstorage_free}') print(f' storageTotal: {vstorage_total}') systemRamFreeReports = _filterCreateReportTime( cros.get('systemRamFreeReports', []), 'reportTime', startDate, endDate) lenSRFR = len(systemRamFreeReports) if lenSRFR: print(' systemRamFreeReports') num_ranges = min(lenSRFR, listLimit or lenSRFR) for systemRamFreeReport in systemRamFreeReports[:num_ranges]: report_time = systemRamFreeReport["reportTime"] free_info = systemRamFreeReport["systemRamFreeInfo"] free_ram = ",".join(free_info) print(f' reportTime: {report_time}') print(f' systemRamFreeInfo: {free_ram}')