Files
GoogleDriveManagement/src/gam/gapi/vault.py

989 lines
38 KiB
Python

import datetime
import json
import sys
from time import sleep
import googleapiclient.http
import gam
from gam.var import *
from gam import controlflow
from gam import display
from gam import fileutils
from gam import gapi
from gam.gapi import storage as gapi_storage
from gam.gapi import directory as gapi_directory
from gam.gapi.directory import orgunits as gapi_directory_orgunits
from gam import utils
def buildGAPIObject():
return gam.buildGAPIObject('vault')
def validateCollaborators(collaboratorList, cd):
collaborators = []
for collaborator in collaboratorList.split(','):
collaborator_id = gam.convertEmailAddressToUID(collaborator, cd)
if not collaborator_id:
controlflow.system_error_exit(
4, f'failed to get a UID for '
f'{collaborator}. Please make '
f'sure this is a real user.')
collaborators.append({'email': collaborator, 'id': collaborator_id})
return collaborators
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 = gam.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)
VAULT_SEARCH_METHODS_MAP = {
'account': 'ACCOUNT',
'accounts': 'ACCOUNT',
'entireorg': 'ENTIRE_ORG',
'everyone': 'ENTIRE_ORG',
'orgunit': 'ORG_UNIT',
'ou': 'ORG_UNIT',
'room': 'ROOM',
'rooms': 'ROOM',
'shareddrive': 'SHARED_DRIVE',
'shareddrives': 'SHARED_DRIVE',
'teamdrive': 'SHARED_DRIVE',
'teamdrives': 'SHARED_DRIVE',
}
VAULT_SEARCH_METHODS_LIST = [
'accounts', 'orgunit', 'shareddrives', 'rooms', 'everyone'
]
QUERY_ARGS = ['corpus', 'scope', 'terms', 'start', 'starttime',
'end', 'endtime', 'timezone', 'excludedrafts',
'driveversiondate', 'includeshareddrives', 'includeteamdrives',
'includerooms'] + list(VAULT_SEARCH_METHODS_MAP.keys())
def _build_query(query, myarg, i, query_discovery):
if not query:
query = {'dataScope': 'ALL_DATA'}
if myarg == 'corpus':
query['corpus'] = sys.argv[i + 1].upper()
allowed_corpuses = gapi.get_enum_values_minus_unspecified(
query_discovery['properties']['corpus']['enum'])
if query['corpus'] not in allowed_corpuses:
controlflow.expected_argument_exit('corpus',
', '.join(allowed_corpuses),
sys.argv[i + 1])
i += 2
elif myarg in VAULT_SEARCH_METHODS_MAP:
if query.get('searchMethod'):
message = f'Multiple search methods ' \
f'({", ".join(VAULT_SEARCH_METHODS_LIST)})' \
f'specified, only one is allowed'
controlflow.system_error_exit(3, message)
searchMethod = VAULT_SEARCH_METHODS_MAP[myarg]
query['searchMethod'] = searchMethod
if searchMethod == 'ACCOUNT':
query['accountInfo'] = {
'emails': sys.argv[i + 1].split(',')
}
i += 2
elif searchMethod == 'ORG_UNIT':
query['orgUnitInfo'] = {
'orgUnitId': gapi_directory_orgunits.getOrgUnitId(sys.argv[i + 1])[1]
}
i += 2
elif searchMethod == 'SHARED_DRIVE':
query['sharedDriveInfo'] = {
'sharedDriveIds': sys.argv[i + 1].split(',')
}
i += 2
elif searchMethod == 'ROOM':
query['hangoutsChatInfo'] = {
'roomId': sys.argv[i + 1].split(',')
}
i += 2
else:
i += 1
elif myarg == 'scope':
query['dataScope'] = sys.argv[i + 1].upper()
allowed_scopes = gapi.get_enum_values_minus_unspecified(
query_discovery['properties']['dataScope']['enum'])
if query['dataScope'] not in allowed_scopes:
controlflow.expected_argument_exit('scope',
', '.join(allowed_scopes),
sys.argv[i + 1])
i += 2
elif myarg in ['terms']:
query['terms'] = sys.argv[i + 1]
i += 2
elif myarg in ['start', 'starttime']:
query['startTime'] = utils.get_date_zero_time_or_full_time(
sys.argv[i + 1])
i += 2
elif myarg in ['end', 'endtime']:
query['endTime'] = utils.get_date_zero_time_or_full_time(
sys.argv[i + 1])
i += 2
elif myarg in ['timezone']:
query['timeZone'] = sys.argv[i + 1]
i += 2
elif myarg in ['excludedrafts']:
query['mailOptions'] = {
'excludeDrafts': gam.getBoolean(sys.argv[i + 1], myarg)
}
i += 2
elif myarg in ['driveversiondate']:
query.setdefault('driveOptions', {})['versionDate'] = \
utils.get_date_zero_time_or_full_time(sys.argv[i+1])
i += 2
elif myarg in ['includeshareddrives', 'includeteamdrives']:
query.setdefault(
'driveOptions', {})['includeSharedDrives'] = gam.getBoolean(
sys.argv[i + 1], myarg)
i += 2
elif myarg in ['includerooms']:
query['hangoutsChatOptions'] = {
'includeRooms': gam.getBoolean(sys.argv[i + 1], myarg)
}
i += 2
return (query, i)
def _validate_query(query, query_discovery):
if 'corpus' not in query:
allowed_corpuses = gapi.get_enum_values_minus_unspecified(
query_discovery['properties']['corpus']['enum'])
controlflow.system_error_exit(3, 'you must specify a corpus. ' \
f'Choose one of {", ".join(allowed_corpuses)}')
if 'searchMethod' not in query:
controlflow.system_error_exit(3, f'you must specify a search method. ' \
'Choose one of ' \
f'{", ".join(VAULT_SEARCH_METHODS_LIST)}')
def createExport():
v = buildGAPIObject()
query_discovery = v._rootDesc['schemas']['Query']
allowed_formats = gapi.get_enum_values_minus_unspecified(
v._rootDesc['schemas']['MailExportOptions']['properties']
['exportFormat']['enum'])
export_format = 'MBOX'
showConfidentialModeContent = None # default to not even set
matterId = None
query = None
useNewExport = None
body = {'exportOptions': {}}
i = 3
while i < len(sys.argv):
myarg = sys.argv[i].lower().replace('_', '')
if myarg == 'matter':
matterId = getMatterItem(v, sys.argv[i + 1], state='OPEN')
body['matterId'] = matterId
i += 2
elif myarg == 'name':
body['name'] = sys.argv[i + 1]
i += 2
elif myarg in QUERY_ARGS:
query, i = _build_query(query, myarg, i, query_discovery)
elif myarg == 'usenewexport':
useNewExport = gam.getBoolean(sys.argv[i+1], myarg)
i += 2
elif myarg in ['format']:
export_format = sys.argv[i + 1].upper()
if export_format not in allowed_formats:
controlflow.expected_argument_exit('export format',
', '.join(allowed_formats),
export_format)
i += 2
elif myarg in ['showconfidentialmodecontent']:
showConfidentialModeContent = gam.getBoolean(sys.argv[i + 1], myarg)
i += 2
elif myarg in ['region']:
allowed_regions = gapi.get_enum_values_minus_unspecified(
v._rootDesc['schemas']['ExportOptions']['properties']['region']
['enum'])
body['exportOptions']['region'] = sys.argv[i + 1].upper()
if body['exportOptions']['region'] not in allowed_regions:
controlflow.expected_argument_exit(
'region', ', '.join(allowed_regions),
body['exportOptions']['region'])
i += 2
elif myarg in ['includeaccessinfo']:
body['exportOptions'].setdefault(
'driveOptions', {})['includeAccessInfo'] = gam.getBoolean(
sys.argv[i + 1], myarg)
i += 2
else:
controlflow.invalid_argument_exit(sys.argv[i], 'gam create export')
if not matterId:
controlflow.system_error_exit(
3, 'you must specify a matter for the new export.')
_validate_query(query, query_discovery)
body['query'] = query
if 'name' not in body:
corpus_name = body['query']['corpus']
corpus_date = datetime.datetime.now()
body['name'] = f'GAM {corpus_name} export - {corpus_date}'
options_field = None
if body['query']['corpus'] == 'MAIL':
options_field = 'mailOptions'
elif body['query']['corpus'] == 'GROUPS':
options_field = 'groupsOptions'
elif body['query']['corpus'] == 'HANGOUTS_CHAT':
options_field = 'hangoutsChatOptions'
if options_field:
body['exportOptions'].pop('driveOptions', None)
body['exportOptions'][options_field] = {'exportFormat': export_format}
if showConfidentialModeContent is not None:
body['exportOptions'][options_field][
'showConfidentialModeContent'] = showConfidentialModeContent
if useNewExport is not None:
body['exportOptions'][options_field][
'useNewExport'] = useNewExport
results = gapi.call(v.matters().exports(),
'create',
matterId=matterId,
body=body)
print(f'Created export {results["id"]}')
display.print_json(results)
def deleteExport():
v = buildGAPIObject()
matterId = getMatterItem(v, sys.argv[3])
exportId = convertExportNameToID(v, sys.argv[4], matterId)
print(f'Deleting export {sys.argv[4]} / {exportId}')
gapi.call(v.matters().exports(),
'delete',
matterId=matterId,
exportId=exportId)
def getExportInfo():
v = buildGAPIObject()
matterId = getMatterItem(v, sys.argv[3])
exportId = convertExportNameToID(v, sys.argv[4], matterId)
export = gapi.call(v.matters().exports(),
'get',
matterId=matterId,
exportId=exportId)
display.print_json(export)
def print_count():
v = buildGAPIObject()
query_discovery = v._rootDesc['schemas']['Query']
matterId = None
operation_wait = 15
query = None
body = {'view': 'ALL'}
name = None
todrive = False
i = 3
while i < len(sys.argv):
myarg = sys.argv[i].lower().replace('_', '')
if myarg == 'matter':
matterId = getMatterItem(v, sys.argv[i + 1])
i += 2
elif myarg == 'operation':
name = sys.argv[i+1]
i += 2
elif myarg == 'todrive':
todrive = True
i += 1
elif myarg in QUERY_ARGS:
query, i = _build_query(query, myarg, i, query_discovery)
elif myarg == 'wait':
operation_wait = int(sys.argv[i + 1])
i += 2
else:
controlflow.invalid_argument_exit(sys.argv[i], 'gam create export')
if not matterId:
controlflow.system_error_exit(
3, 'you must specify a matter for the count.')
if name:
operation = {'name': name}
else:
_validate_query(query, query_discovery)
body['query'] = query
operation = gapi.call(v.matters(), 'count', matterId=matterId, body=body)
print(f'Watching operation {operation["name"]}...')
while not operation.get('done'):
print(f' operation {operation["name"]} is not done yet. Checking again in {operation_wait} seconds')
sleep(operation_wait)
operation = gapi.call(v.operations(), 'get', name=operation['name'])
response = operation.get('response', {})
query = operation['metadata']['query']
search_method = query.get('searchMethod')
# ARGH count results don't include accounts with zero items.
# so we keep track of which accounts we searched and can report
# zero data for them.
if search_method == 'ACCOUNT':
query_accounts = query.get('accountInfo', {}).get('emails', [])
elif search_method == 'ENTIRE_ORG':
query_accounts = gam.getUsersToModify('all', 'users')
elif search_method == 'ORG_UNIT':
org_unit = query['orgUnitInfo']['orgUnitId']
query_accounts = gam.getUsersToModify('ou', org_unit)
mailcounts = response.get('mailCountResult', {})
groupcounts = response.get('groupsCountResult', {})
csv_rows = []
for a_count in [mailcounts, groupcounts]:
for errored_account in a_count.get('accountCountErrors', []):
account = errored_account.get('account')
csv_rows.append({'account': account, 'error': errored_account.get('errorType')})
if account in query_accounts: query_accounts.remove(account)
for account in a_count.get('nonQueryableAccounts', []):
csv_rows.append({'account': account, 'error': 'Not queried because not on hold'})
if account in query_accounts: query_accounts.remove(account)
for account in a_count.get('accountCounts', []):
email = account.get('account', {}).get('email', '')
if email:
csv_rows.append({'account': email, 'count': account.get('count', 0)})
if email in query_accounts: query_accounts.remove(email)
for account in query_accounts:
csv_rows.append({'account': account, 'count': 0})
titles = ['account', 'count', 'error']
display.write_csv_file(csv_rows, titles, 'Vault Counts', todrive)
def createHold():
v = buildGAPIObject()
allowed_corpuses = gapi.get_enum_values_minus_unspecified(
v._rootDesc['schemas']['Hold']['properties']['corpus']['enum'])
body = {'query': {}}
i = 3
query = None
start_time = None
end_time = None
matterId = None
accounts = []
while i < len(sys.argv):
myarg = sys.argv[i].lower().replace('_', '')
if myarg == 'name':
body['name'] = sys.argv[i + 1]
i += 2
elif myarg == 'query':
query = sys.argv[i + 1]
i += 2
elif myarg == 'corpus':
body['corpus'] = sys.argv[i + 1].upper()
if body['corpus'] not in allowed_corpuses:
controlflow.expected_argument_exit('corpus',
', '.join(allowed_corpuses),
sys.argv[i + 1])
i += 2
elif myarg in ['accounts', 'users', 'groups']:
accounts = sys.argv[i + 1].split(',')
i += 2
elif myarg in ['orgunit', 'ou']:
body['orgUnit'] = {
'orgUnitId': gapi_directory_orgunits.getOrgUnitId(sys.argv[i + 1])[1]
}
i += 2
elif myarg in ['start', 'starttime']:
start_time = utils.get_date_zero_time_or_full_time(sys.argv[i + 1])
i += 2
elif myarg in ['end', 'endtime']:
end_time = utils.get_date_zero_time_or_full_time(sys.argv[i + 1])
i += 2
elif myarg == 'matter':
matterId = getMatterItem(v, sys.argv[i + 1])
i += 2
else:
controlflow.invalid_argument_exit(sys.argv[i], 'gam create hold')
if not matterId:
controlflow.system_error_exit(
3, 'you must specify a matter for the new hold.')
if not body.get('name'):
controlflow.system_error_exit(
3, 'you must specify a name for the new hold.')
if not body.get('corpus'):
controlflow.system_error_exit(3, f'you must specify a corpus for ' \
f'the new hold. Choose one of {", ".join(allowed_corpuses)}')
if body['corpus'] == 'HANGOUTS_CHAT':
query_type = 'hangoutsChatQuery'
else:
query_type = f'{body["corpus"].lower()}Query'
body['query'][query_type] = {}
if body['corpus'] == 'DRIVE':
if query:
try:
body['query'][query_type] = json.loads(query)
except ValueError as e:
controlflow.system_error_exit(3, f'{str(e)}, query: {query}')
elif body['corpus'] in ['GROUPS', 'MAIL']:
if query:
body['query'][query_type] = {'terms': query}
if start_time:
body['query'][query_type]['startTime'] = start_time
if end_time:
body['query'][query_type]['endTime'] = end_time
if accounts:
body['accounts'] = []
cd = gam.buildGAPIObject('directory')
account_type = 'group' if body['corpus'] == 'GROUPS' else 'user'
for account in accounts:
body['accounts'].append({
'accountId':
gam.convertEmailAddressToUID(account, cd, account_type)
})
holdId = gapi.call(v.matters().holds(),
'create',
matterId=matterId,
body=body,
fields='holdId')
print(f'Created hold {holdId["holdId"]}')
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 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 = gam.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 = gam.convertUIDtoEmailAddress(uid, cd, [account_type])
results['accounts'][i]['email'] = acct_email
if 'orgUnit' in results:
results['orgUnit']['orgUnitPath'] = gapi_directory_orgunits.info(
results['orgUnit']['orgUnitId'], return_attrib='orgUnitPath')
display.print_json(results)
def convertExportNameToID(v, nameOrID, matterId):
nameOrID = nameOrID.lower()
cg = UID_PATTERN.match(nameOrID)
if cg:
return cg.group(1)
fields = 'exports(id,name),nextPageToken'
exports = gapi.get_all_pages(v.matters().exports(),
'list',
'exports',
matterId=matterId,
fields=fields)
for export in exports:
if export['name'].lower() == nameOrID:
return export['id']
controlflow.system_error_exit(
4, f'could not find export name {nameOrID} '
f'in matter {matterId}')
def convertHoldNameToID(v, nameOrID, matterId):
nameOrID = nameOrID.lower()
cg = UID_PATTERN.match(nameOrID)
if cg:
return cg.group(1)
fields = 'holds(holdId,name),nextPageToken'
holds = gapi.get_all_pages(v.matters().holds(),
'list',
'holds',
matterId=matterId,
fields=fields)
for hold in holds:
if hold['name'].lower() == nameOrID:
return hold['holdId']
controlflow.system_error_exit(
4, f'could not find hold name {nameOrID} '
f'in matter {matterId}')
def convertMatterNameToID(v, nameOrID, state=None):
nameOrID = nameOrID.lower()
cg = UID_PATTERN.match(nameOrID)
if cg:
return cg.group(1)
fields = 'matters(matterId,name),nextPageToken'
matters = gapi.get_all_pages(v.matters(),
'list',
'matters',
view='BASIC',
state=state,
fields=fields)
for matter in matters:
if matter['name'].lower() == nameOrID:
return matter['matterId']
return None
def getMatterItem(v, nameOrID, state=None):
matterId = convertMatterNameToID(v, nameOrID, state=state)
if not matterId:
controlflow.system_error_exit(4, f'could not find matter {nameOrID}')
return matterId
def updateHold():
v = buildGAPIObject()
hold = sys.argv[3]
matterId = None
body = {}
query = None
add_accounts = []
del_accounts = []
start_time = None
end_time = 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
elif myarg == 'query':
query = sys.argv[i + 1]
i += 2
elif myarg in ['orgunit', 'ou']:
body['orgUnit'] = {
'orgUnitId': gapi_directory_orgunits.getOrgUnitId(sys.argv[i + 1])[1]
}
i += 2
elif myarg in ['start', 'starttime']:
start_time = utils.get_date_zero_time_or_full_time(sys.argv[i + 1])
i += 2
elif myarg in ['end', 'endtime']:
end_time = utils.get_date_zero_time_or_full_time(sys.argv[i + 1])
i += 2
elif myarg in ['addusers', 'addaccounts', 'addgroups']:
add_accounts = sys.argv[i + 1].split(',')
i += 2
elif myarg in ['removeusers', 'removeaccounts', 'removegroups']:
del_accounts = sys.argv[i + 1].split(',')
i += 2
else:
controlflow.invalid_argument_exit(myarg, 'gam update hold')
if not matterId:
controlflow.system_error_exit(
3, 'you must specify a matter for the hold.')
if query or start_time or end_time or body.get('orgUnit'):
fields = 'corpus,query,orgUnit'
old_body = gapi.call(v.matters().holds(),
'get',
matterId=matterId,
holdId=holdId,
fields=fields)
body['query'] = old_body['query']
body['corpus'] = old_body['corpus']
if 'orgUnit' in old_body and 'orgUnit' not in body:
# bah, API requires this to be sent
# on update even when it's not changing
body['orgUnit'] = old_body['orgUnit']
query_type = f'{body["corpus"].lower()}Query'
if body['corpus'] == 'DRIVE':
if query:
try:
body['query'][query_type] = json.loads(query)
except ValueError as e:
message = f'{str(e)}, query: {query}'
controlflow.system_error_exit(3, message)
elif body['corpus'] in ['GROUPS', 'MAIL']:
if query:
body['query'][query_type]['terms'] = query
if start_time:
body['query'][query_type]['startTime'] = start_time
if end_time:
body['query'][query_type]['endTime'] = end_time
if body:
print(f'Updating hold {hold} / {holdId}')
gapi.call(v.matters().holds(),
'update',
matterId=matterId,
holdId=holdId,
body=body)
if add_accounts or del_accounts:
cd = gam.buildGAPIObject('directory')
for account in add_accounts:
print(f'adding {account} to hold.')
add_body = {'accountId': gam.convertEmailAddressToUID(account, cd)}
gapi.call(v.matters().holds().accounts(),
'create',
matterId=matterId,
holdId=holdId,
body=add_body)
for account in del_accounts:
print(f'removing {account} from hold.')
accountId = gam.convertEmailAddressToUID(account, cd)
gapi.call(v.matters().holds().accounts(),
'delete',
matterId=matterId,
holdId=holdId,
accountId=accountId)
def showHoldsForUsers(users):
cd = gapi_directory.build()
v = buildGAPIObject()
matterIds = _getAllMatterIds(v)
matterHolds = {}
for matterId in matterIds:
matterHolds[matterId] = gapi.get_all_pages(v.matters().holds(),
'list',
'holds',
fields='holds(holdId,name,accounts(accountId,email),orgUnit),nextPageToken',
matterId=matterId)
totalHolds = 0
for user in users:
user = user.lower()
orgUnits = gapi_directory_orgunits._getAllParentOrgUnitsForUser(user, cd)
for matterId in matterIds:
for hold in matterHolds[matterId]:
if 'orgUnit' in hold:
orgUnitId = hold['orgUnit'].get('orgUnitId')
if orgUnitId in orgUnits:
print(f'FOUND: OrgUnit {orgUnits[orgUnitId]} for user {user} is on hold in matterId {matterId} and holdId {hold["holdId"]} named "{hold["name"]}"')
totalHolds += 1
else:
for account in hold.get('accounts', []):
if (user == account.get('email', '').lower()) or (user == account.get('accountId', '')):
print(f'FOUND: User account {user} is on hold in matterId {matterId} and holdId {hold["holdId"]} named "{hold["name"]}"')
totalHolds += 1
break
sys.stdout.write(f'Total Holds: {totalHolds}\n')
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 = gam.buildGAPIObject('directory')
add_collaborators.extend(validateCollaborators(sys.argv[i + 1], cd))
i += 2
elif myarg in ['removecollaborator', 'removecollaborators']:
if not cd:
cd = gam.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 getMatterInfo():
v = buildGAPIObject()
matterId = getMatterItem(v, sys.argv[3])
result = gapi.call(v.matters(), 'get', matterId=matterId, view='FULL')
if 'matterPermissions' in result:
cd = gam.buildGAPIObject('directory')
for i in range(0, len(result['matterPermissions'])):
uid = f'uid:{result["matterPermissions"][i]["accountId"]}'
user_email = gam.convertUIDtoEmailAddress(uid, cd)
result['matterPermissions'][i]['email'] = user_email
display.print_json(result)
def downloadExport():
verifyFiles = True
extractFiles = True
v = buildGAPIObject()
s = gapi_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(f' Downloaded: {status.progress():>7.2%}\r')
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):
gam.extract_nested_zip(filename, targetFolder)
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')
gam.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 _getAllMatterIds(v=None, state='OPEN'):
if not v:
v = buildGAPIObject()
fields = 'matters(matterId),nextPageToken'
results = gapi.get_all_pages(v.matters(),
'list',
'matters',
view='BASIC',
state=state,
fields=fields)
return [matter['matterId'] for matter in results]
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:
matterIds = _getAllMatterIds(v)
else:
for matter in matters:
matterIds.append(getMatterItem(v, matter))
i = 0
matter_count = len(matterIds)
for matterId in matterIds:
i += 1
sys.stderr.write(f'Retrieving holds for matter {matterId} ({i}/{matter_count})\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)