mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-04 06:11:39 +00:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cfd36c2836 | ||
|
|
7689ac7bed | ||
|
|
7a5ba99b36 | ||
|
|
8f4a40bc9a | ||
|
|
e3ab846d70 | ||
|
|
29db574bc5 | ||
|
|
4851d5b62f | ||
|
|
caef16bdee | ||
|
|
1a0f9ab66a | ||
|
|
021c3bfb13 | ||
|
|
b3dfa41df6 | ||
|
|
5f94263db2 | ||
|
|
68d8e46b4c | ||
|
|
ed221b0d7b | ||
|
|
1243563cd4 | ||
|
|
1170457a39 | ||
|
|
435ed9f568 | ||
|
|
81884e48d0 | ||
|
|
a7be6d233b | ||
|
|
584ddba1a5 | ||
|
|
2bc6c8bca0 | ||
|
|
fc1e81a01d | ||
|
|
eebfaaf373 | ||
|
|
652223d9bc | ||
|
|
e75664fd2e | ||
|
|
556278b216 | ||
|
|
f9bfaa98bb | ||
|
|
b6bd2da6ce | ||
|
|
7c36a6b601 | ||
|
|
413924b11a | ||
|
|
251883dae5 | ||
|
|
7e4d0da8fb | ||
|
|
3fc2aeed4d | ||
|
|
7f4f785f0b |
22
.travis.yml
22
.travis.yml
@@ -5,7 +5,7 @@ env:
|
||||
- BUILD_PYTHON_VERSION=3.7.4
|
||||
- BUILD_OPENSSL_VERSION=1.1.1c
|
||||
- PATCHELF_VERSION=0.9
|
||||
- MUSL_VERSION=1.1.22
|
||||
- MUSL_VERSION=1.1.23
|
||||
- PYINSTALLER_VERSION=3.5
|
||||
- secure: "FSKvLaiqhKz21SVgAQZI3bSX34Ffyev4l+R2G//QXNDu6UVQcuFsykzw+eZEG7fkhotXr8BMDL7xIkookiL8eLwUtcd/Z95HCjPBBHcmCSQleyvuuJBxdrQ9xldmiGLzMCYiumSH9OH4uJhQ39Yjnjsa8TK+PlTci6a/BTzlYyBSyDYDf7Iv/uhfQPDHL3pNwrQPHf4fL6/jcvo+uaPcv83AVZkNzZjjyoi9Aa+uh9xlbyHg11jp44463qqxoxTdYik3pYuXRBPjknjOGcnFHqn+QOVSdRQoiwbmT8xVuYuCzTv9THhuJ//i5u7s4y3Xyl7u17B3tdm86UlMpQHy/w9EsYaSBPOU4oPNomRtOnTSugh0v9ZBwptP5XfbslII/iA+LQdzTHhchn0W0CRyDqjOMSestWlrsq5NZJtBJTYHbebllOhEI7xbj9tY+re1zFWSPMOPgHJP23ovsdk3hD9OT93AzRHInCx5IxL6QvEgRhAancRuGkf2rGP0g/vX9fQ0Il3rNMSQxHB5CyHUBtUJ9nhU79YkMDZicD0jFMEwjWJO3itAp3ynoLXRgktgQCYUfgc9SpdWKD5SXLCYnSo22JD3D1P6h2EertRHaoKRLb+CRXQC/lM8uh/W+BjA2Xe6Vut2I/72ndjM+10T7E2xk1CFyCH37a5p8cH26Fs="
|
||||
- secure: "J9380tGLOZWa7dSH1y5Il8T5JQpN6ad81gI6VR1HIU0svpRdjgikyDA7ca2MKYDUYYY9yVSkTV6gCl6iIU/9+SKaYugpP+tkvdGYkC2moJdcTgYM/WOnIK9ExQ3BPhN1neGxJjPTwKo1ft27mtZ2I5vuCiBwIcnKWLnKPyW3PD+mWpfqiLuEzkHoAh6G3jC4qbcCrZDeX/knE+PzqESUEi+8k1G8gYcSDWujba9ypSsqZ8T/MXagGla6l7y2Rz+/KZTJmFHwKAA10V+xPLVqxoiqi4ar66yUqy0BamwRXPcseI+ns3Q+4lUpMqVQ5GlRy7LF1xC8myjmcAexXk0F9hg+CMzewKI8UgmQH/ZJvQZEh8s6mW26+CqA4d3zMQkWaR0WtEtpiuH7AGHCflIqvEQ6UiG7ia3B8iZfW2wl0j/kqx4OuHkS3r0pWKVVIIvCj9Ow2BHP7SpiV1AcUGsVxzwbgTh67fitna3Z3c6Uj8ccQlNr7ZIt1az6Wf3w5njijkLOiBpQSLKunTTCTSge/JzBTKUcie3RE9vzirl58gUxAt36nDtPWnory+RttMZrOkBVbTeSxp+IUe8pNwLFPHABsafXsjkfzBOtFmm+0ZXWt2Rlog5NvlemJfQUWDlsL4g+BSakzN+4sIPKzSauWDHyaEeULY7Uprkil6c5zwo="
|
||||
@@ -70,7 +70,7 @@ matrix:
|
||||
- PLATFORM=x86_64
|
||||
- VMTYPE=build
|
||||
- os: linux
|
||||
name: "Linux 64-bit Xenial - Python 3.5 Source Testing"
|
||||
name: "Linux 64-bit - Python 3.5 Source Testing"
|
||||
dist: xenial
|
||||
language: python
|
||||
python:
|
||||
@@ -80,8 +80,8 @@ matrix:
|
||||
- PLATFORM=x86_64
|
||||
- VMTYPE=test
|
||||
- os: linux
|
||||
name: "Linux 64-bit Xenial - Python 3.6 Source Testing"
|
||||
dist: xenial
|
||||
name: "Linux 64-bit - Python 3.6 Source Testing"
|
||||
dist: bionic
|
||||
language: python
|
||||
python:
|
||||
- "3.6"
|
||||
@@ -90,8 +90,8 @@ matrix:
|
||||
- PLATFORM=x86_64
|
||||
- VMTYPE=test
|
||||
- os: linux
|
||||
name: "Linux 64-bit Xenial - Python 3.8-dev Source Testing"
|
||||
dist: xenial
|
||||
name: "Linux 64-bit - Python 3.8-dev Source Testing"
|
||||
dist: bionic
|
||||
language: python
|
||||
python:
|
||||
- "3.8-dev"
|
||||
@@ -100,8 +100,8 @@ matrix:
|
||||
- PLATFORM=x86_64
|
||||
- VMTYPE=test
|
||||
- os: linux
|
||||
name: "Linux 64-bit Xenial - Python nightly Source Testing"
|
||||
dist: xenial
|
||||
name: "Linux 64-bit - Python nightly Source Testing"
|
||||
dist: bionic
|
||||
language: python
|
||||
python:
|
||||
- "nightly"
|
||||
@@ -170,13 +170,15 @@ script:
|
||||
for i in {01..20};
|
||||
do echo $newbase-bulkuser-$i >> sample.csv;
|
||||
done; fi
|
||||
- if [ "$e2e" = true ]; then $gam create user $newuser firstname Travis lastname $jid password random travis.jid $jid; fi
|
||||
- if [ "$e2e" = true ]; then $gam create user $newuser firstname Travis lastname $jid password random recoveryphone 12125121110 recoveryemail jay0lee@gmail.com travis.jid $jid; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $gam_user sendemail recipient $newuser subject "test message $newbase" message "Travis test message"; fi
|
||||
- if [ "$e2e" = true ]; then $gam create group $newgroup name "Travis $jid group" description "This is a description" isarchived true; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $newuser add license gsuitebusiness; fi
|
||||
- if [ "$e2e" = true ]; then $gam update group $newgroup add owner $gam_user; fi
|
||||
- if [ "$e2e" = true ]; then $gam update group $newgroup add member $newuser; fi
|
||||
- if [ "$e2e" = true ]; then $gam csv sample.csv gam create user ~~email~~ firstname "Travis Bulk" lastname ~~email~~ travis.jid $jid; fi
|
||||
- if [ "$e2e" = true ]; then $gam csv sample.csv gam update user ~~email~~ recoveryphone 12125121110 recoveryemail jay0lee@gmail.com password random; fi
|
||||
- if [ "$e2e" = true ]; then $gam csv sample.csv gam update user ~~email~~ recoveryphone "" recoveryemail ""; fi
|
||||
- if [ "$e2e" = true ]; then $gam csv sample.csv gam user ~email add license gsuitebusiness; fi
|
||||
- if [ "$e2e" = true ]; then $gam csv sample.csv gam user $gam_user sendemail recipient ~~email~~@pdl.jaylee.us subject "test message $newbase" message "Travis test message"; fi
|
||||
- if [ "$e2e" = true ]; then $gam csv sample.csv gam update group $newgroup add member ~email; fi
|
||||
@@ -227,6 +229,8 @@ script:
|
||||
- if [ "$e2e" = true ]; then $gam user $gam_user show tokens; fi
|
||||
- if [ "$e2e" = true ]; then $gam delete user $newuser; fi
|
||||
- if [ "$e2e" = true ]; then $gam print users query "travis.jid=$jid" | $gam csv - gam delete user ~primaryEmail; fi
|
||||
- if [ "$e2e" = true ]; then $gam print mobile; fi
|
||||
- if [ "$e2e" = true ]; then $gam print cros allfields nolists; fi
|
||||
|
||||
before_deploy:
|
||||
- export TRAVIS_TAG="preview"
|
||||
|
||||
@@ -257,16 +257,20 @@ If an item contains spaces, it should be surrounded by ".
|
||||
annotatedassetid|assedid|asset|
|
||||
annotatedlocation|location|
|
||||
annotateduser|user|
|
||||
autoupdateexpiration|
|
||||
bootmode|
|
||||
cpustatusreports|
|
||||
devicefiles|
|
||||
deviceid|
|
||||
diskvolumereports|
|
||||
dockmacaddress|
|
||||
ethernetmacaddress|
|
||||
ethernetmacaddress0|
|
||||
firmwareversion|
|
||||
lastenrollmenttime|
|
||||
lastsync|
|
||||
macaddress|
|
||||
manufacturedate|
|
||||
meid|
|
||||
model|
|
||||
notes|
|
||||
@@ -521,6 +525,8 @@ If an item contains spaces, it should be surrounded by ".
|
||||
phones|phone|
|
||||
posixaccounts|posix|
|
||||
primaryemail|username|
|
||||
recoveryemail|
|
||||
recoveryphone|
|
||||
relations|relation|
|
||||
ssh|sshkeys|sshpublickeys|
|
||||
suspended|
|
||||
@@ -776,6 +782,8 @@ Specify a collection of Users by directly specifying them or by specifiying item
|
||||
(note clear|([text_html|text_plain] <String>|(file <FileName> [charset <Charset>])))|
|
||||
(org|ou|orgunitpath <OrgUnitPath>)
|
||||
(password random|<Password>)|
|
||||
(recoveryemail <EmailAddress>)|
|
||||
(recoveryphone <string>)|
|
||||
(suspended <Boolean>)|
|
||||
(<SchemaName>.<FieldName> [multivalued|multivalue|value|multinonempty [type home|other|work|(custom <String>)]] <String>)
|
||||
<UserMultiAttributes> ::=
|
||||
@@ -799,7 +807,7 @@ Specify a collection of Users by directly specifying them or by specifiying item
|
||||
<UserBasicAttribute>|
|
||||
<UserMultiAttribute>
|
||||
|
||||
gam version [check|checkrc|simple|extended] [location <HostName>]
|
||||
gam version [check|checkrc|simple|extended] [timeoffset] [location <HostName>]
|
||||
gam help
|
||||
|
||||
gam batch <FileName>|- [charset <Charset>]
|
||||
@@ -874,7 +882,7 @@ gam info resoldsubscriptions <CustomerID> [customer_auth_token <String>]
|
||||
<ReportsAppList> ::= "<ReportsApp>(,<ReportsApp>)*"
|
||||
|
||||
gam report users|user [todrive] [date <Date>] [fulldatarequired all|<ReportsAppList>]
|
||||
[(user all|<UserItem>)|(orgunit|org|ou <OrgUnitPath>)] [filter|filters <String>] [fields|parameters <String>]
|
||||
[(user <UserItem>)|(orgunit|org|ou <OrgUnitPath>)] [filter|filters <String>] [fields|parameters <String>]
|
||||
gam report customers|customer|domain [todrive] [date <Date>] [fulldatarequired all|<ReportsAppList>]
|
||||
[fields|parameters <String>]
|
||||
gam report admin|calendar|calendars|drive|docs|doc|groups|group|logins|login|mobile|tokens|token [todrive]
|
||||
@@ -1295,7 +1303,7 @@ gam <UserTypeEntity> draftemail [recipient|to <EmailAddress>] [from <EmailAddres
|
||||
gam <UserTypeEntity> importemail [recipient|to <EmailAddress>] [from <EmailAddress>]
|
||||
[subject <String>] [(message <String>)|(file <FileName> [charset <Charset>])]
|
||||
[labels <LabelNameList>] (header <String> <String>)*
|
||||
[deleted] [date <Time>]
|
||||
[deleted] [date <Time>]
|
||||
[nevercheckspam] [processforcalendar]
|
||||
gam <UserTypeEntity> insertemail [recipient|to <EmailAddress>] [from <EmailAddress>]
|
||||
[subject <String>] [(message <String>)|(file <FileName> [charset <Charset>])]
|
||||
|
||||
353
src/gam.py
353
src/gam.py
@@ -106,11 +106,11 @@ google_auth_httplib2.Request.__call__ = _request_with_user_agent(
|
||||
google_auth_httplib2.AuthorizedHttp.request = _request_with_user_agent(
|
||||
google_auth_httplib2.AuthorizedHttp.request)
|
||||
|
||||
def _createHttpObj(cache=None, override_min_tls=None, override_max_tls=None):
|
||||
def _createHttpObj(cache=None, timeout=None, override_min_tls=None, override_max_tls=None):
|
||||
tls_minimum_version = override_min_tls if override_min_tls else GC_Values[GC_TLS_MIN_VERSION]
|
||||
tls_maximum_version = override_max_tls if override_max_tls else GC_Values[GC_TLS_MAX_VERSION]
|
||||
return httplib2.Http(ca_certs=GC_Values[GC_CA_FILE], tls_maximum_version=tls_maximum_version, tls_minimum_version=tls_minimum_version,
|
||||
cache=cache)
|
||||
cache=cache, timeout=timeout)
|
||||
|
||||
def showUsage():
|
||||
doGAMVersion(checkForArgs=False)
|
||||
@@ -669,13 +669,8 @@ def SetGlobalVariables():
|
||||
_getOldEnvVar(GC_CUSTOMER_ID, 'CUSTOMER_ID')
|
||||
_getOldEnvVar(GC_CHARSET, 'GAM_CHARSET')
|
||||
_getOldEnvVar(GC_NUM_THREADS, 'GAM_THREADS')
|
||||
_getOldEnvVar(GC_ACTIVITY_MAX_RESULTS, 'GAM_ACTIVITY_MAX_RESULTS')
|
||||
_getOldEnvVar(GC_AUTO_BATCH_MIN, 'GAM_AUTOBATCH')
|
||||
_getOldEnvVar(GC_BATCH_SIZE, 'GAM_BATCH_SIZE')
|
||||
_getOldEnvVar(GC_DEVICE_MAX_RESULTS, 'GAM_DEVICE_MAX_RESULTS')
|
||||
_getOldEnvVar(GC_DRIVE_MAX_RESULTS, 'GAM_DRIVE_MAX_RESULTS')
|
||||
_getOldEnvVar(GC_MEMBER_MAX_RESULTS, 'GAM_MEMBER_MAX_RESULTS')
|
||||
_getOldEnvVar(GC_USER_MAX_RESULTS, 'GAM_USER_MAX_RESULTS')
|
||||
_getOldEnvVar(GC_CSV_HEADER_FILTER, 'GAM_CSV_HEADER_FILTER')
|
||||
_getOldEnvVar(GC_CSV_ROW_FILTER, 'GAM_CSV_ROW_FILTER')
|
||||
_getOldEnvVar(GC_TLS_MIN_VERSION, 'GAM_TLS_MIN_VERSION')
|
||||
@@ -683,6 +678,7 @@ def SetGlobalVariables():
|
||||
_getOldEnvVar(GC_CA_FILE, 'GAM_CA_FILE')
|
||||
_getOldSignalFile(GC_DEBUG_LEVEL, 'debug.gam', filePresentValue=4, fileAbsentValue=0)
|
||||
_getOldSignalFile(GC_NO_BROWSER, 'nobrowser.txt')
|
||||
_getOldSignalFile(GC_OAUTH_BROWSER, 'oauthbrowser.txt')
|
||||
# _getOldSignalFile(GC_NO_CACHE, u'nocache.txt')
|
||||
# _getOldSignalFile(GC_CACHE_DISCOVERY_ONLY, u'allcache.txt', filePresentValue=False, fileAbsentValue=True)
|
||||
_getOldSignalFile(GC_NO_CACHE, 'allcache.txt', filePresentValue=False, fileAbsentValue=True)
|
||||
@@ -724,6 +720,33 @@ def SetGlobalVariables():
|
||||
GM_Globals[GM_CACHE_DISCOVERY_ONLY] = False
|
||||
return True
|
||||
|
||||
def getLocalGoogleTimeOffset(testLocation='www.googleapis.com'):
|
||||
localUTC = datetime.datetime.now(datetime.timezone.utc)
|
||||
try:
|
||||
# we disable SSL verify so we can still get time even if clock
|
||||
# is way off. This could be spoofed / MitM but we'll fail for those
|
||||
# situations everywhere else but here.
|
||||
badhttp = _createHttpObj()
|
||||
badhttp.disable_ssl_certificate_validation = True
|
||||
googleUTC = dateutil.parser.parse(badhttp.request('https://'+testLocation, 'HEAD')[0]['date'])
|
||||
offset = abs(localUTC-googleUTC).total_seconds()
|
||||
days, remainder = divmod(offset, 86400)
|
||||
hours, remainder = divmod(remainder, 3600)
|
||||
minutes, seconds = divmod(remainder, 60)
|
||||
timeoff = []
|
||||
if days:
|
||||
timeoff.append('%d days' % days)
|
||||
if hours:
|
||||
timeoff.append('%d hours' % hours)
|
||||
if minutes:
|
||||
timeoff.append('%d minutes' % minutes)
|
||||
if seconds:
|
||||
timeoff.append('%d seconds' % seconds)
|
||||
nicetime = ', '.join(timeoff)
|
||||
return (offset, nicetime)
|
||||
except (httplib2.ServerNotFoundError, RuntimeError, ValueError) as e:
|
||||
systemErrorExit(4, str(e))
|
||||
|
||||
def doGAMCheckForUpdates(forceCheck=False):
|
||||
|
||||
def _gamLatestVersionNotAvailable():
|
||||
@@ -741,8 +764,7 @@ def doGAMCheckForUpdates(forceCheck=False):
|
||||
return
|
||||
check_url = GAM_LATEST_RELEASE # latest full release
|
||||
headers = {'Accept': 'application/vnd.github.v3.text+json'}
|
||||
simplehttp = _createHttpObj()
|
||||
simplehttp.timeout = 10
|
||||
simplehttp = _createHttpObj(timeout=10)
|
||||
try:
|
||||
(_, c) = simplehttp.request(check_url, 'GET', headers=headers)
|
||||
try:
|
||||
@@ -779,9 +801,7 @@ def doGAMCheckForUpdates(forceCheck=False):
|
||||
return
|
||||
|
||||
def doGAMVersion(checkForArgs=True):
|
||||
force_check = False
|
||||
simple = False
|
||||
extended = False
|
||||
force_check = extended = simple = timeOffset = False
|
||||
testLocation = 'www.googleapis.com'
|
||||
if checkForArgs:
|
||||
i = 2
|
||||
@@ -795,6 +815,10 @@ def doGAMVersion(checkForArgs=True):
|
||||
i += 1
|
||||
elif myarg == 'extended':
|
||||
extended = True
|
||||
timeOffset = True
|
||||
i += 1
|
||||
elif myarg == 'timeoffset':
|
||||
timeOffset = True
|
||||
i += 1
|
||||
elif myarg == 'location':
|
||||
testLocation = sys.argv[i+1]
|
||||
@@ -809,6 +833,11 @@ def doGAMVersion(checkForArgs=True):
|
||||
sys.version_info[1], sys.version_info[2], struct.calcsize('P')*8,
|
||||
sys.version_info[3], googleapiclient.__version__,
|
||||
platform.platform(), platform.machine(), GM_Globals[GM_GAM_PATH]))
|
||||
if timeOffset:
|
||||
offset, nicetime = getLocalGoogleTimeOffset(testLocation)
|
||||
print('Your computer is %s off from Google\'s time.' % (nicetime))
|
||||
if offset > MAX_LOCAL_GOOGLE_TIME_OFFSET:
|
||||
systemErrorExit(4, 'Please fix your system clock.')
|
||||
if force_check:
|
||||
doGAMCheckForUpdates(forceCheck=True)
|
||||
if extended:
|
||||
@@ -858,7 +887,8 @@ def getSvcAcctCredentials(scopes, act_as):
|
||||
GM_Globals[GM_OAUTH2SERVICE_JSON_DATA] = json.loads(json_string)
|
||||
credentials = google.oauth2.service_account.Credentials.from_service_account_info(GM_Globals[GM_OAUTH2SERVICE_JSON_DATA])
|
||||
credentials = credentials.with_scopes(scopes)
|
||||
credentials = credentials.with_subject(act_as)
|
||||
if act_as:
|
||||
credentials = credentials.with_subject(act_as)
|
||||
GM_Globals[GM_OAUTH2SERVICE_ACCOUNT_CLIENT_ID] = GM_Globals[GM_OAUTH2SERVICE_JSON_DATA]['client_id']
|
||||
return credentials
|
||||
except (ValueError, KeyError):
|
||||
@@ -1082,6 +1112,22 @@ def callGAPI(service, function,
|
||||
except TypeError as e:
|
||||
systemErrorExit(4, str(e))
|
||||
|
||||
def getPageSize(service, function, kwargs):
|
||||
"""Gets maximum maxResults value for API call. Uses value from discovery if
|
||||
it exists, otherwise value from MAX_RESULTS_API_EXCEPTIONS, otherwise None"""
|
||||
|
||||
method = getattr(service, function)
|
||||
api_id = method(**kwargs).methodId
|
||||
for resource in service._rootDesc.get('resources', {}).values():
|
||||
for a_method in resource.get('methods', {}).values():
|
||||
if a_method.get('id') == api_id:
|
||||
if not a_method.get('parameters') or a_method['parameters'].get('pageSize') or not a_method['parameters'].get('maxResults'):
|
||||
# make sure API call supports maxResults. For now we don't care to
|
||||
# set pageSize since all known pageSize API calls have
|
||||
# default pageSize == max pageSize
|
||||
return
|
||||
return {'maxResults': a_method['parameters']['maxResults'].get('maximum', MAX_RESULTS_API_EXCEPTIONS.get(api_id, None))}
|
||||
|
||||
def callGAPIpages(service, function, items='items',
|
||||
page_message=None, message_attribute=None,
|
||||
soft_errors=False, throw_reasons=None, retry_reasons=None,
|
||||
@@ -1124,6 +1170,10 @@ def callGAPIpages(service, function, items='items',
|
||||
Returns:
|
||||
A list of all items received from all paged responses.
|
||||
"""
|
||||
if 'maxResults' not in kwargs and 'pageSize' not in kwargs:
|
||||
page_key = getPageSize(service, function, kwargs)
|
||||
if page_key:
|
||||
kwargs.update(page_key)
|
||||
all_items = []
|
||||
page_token = None
|
||||
total_items = 0
|
||||
@@ -1311,7 +1361,6 @@ def buildGAPIObject(api):
|
||||
GM_Globals[GM_CURRENT_API_USER] = None
|
||||
credentials = getValidOauth2TxtCredentials()
|
||||
credentials.user_agent = GAM_INFO
|
||||
#http = credentials.authorize(httplib2.Http(cache=GM_Globals[GM_CACHE_DIR]))
|
||||
http = google_auth_httplib2.AuthorizedHttp(credentials, _createHttpObj(cache=GM_Globals[GM_CACHE_DIR]))
|
||||
service = getService(api, http)
|
||||
if GC_Values[GC_DOMAIN]:
|
||||
@@ -1468,7 +1517,39 @@ def buildGmailGAPIObject(user):
|
||||
userEmail = convertUIDtoEmailAddress(user)
|
||||
return (userEmail, buildGAPIServiceObject('gmail', userEmail))
|
||||
|
||||
def printPassFail(description, result):
|
||||
padding = 80 - len(description) - 2
|
||||
print(' {} {:>{padding}}'.format(description, result, padding=str(padding)))
|
||||
|
||||
def doCheckServiceAccount(users):
|
||||
something_failed = False
|
||||
print('Computer clock status:')
|
||||
timeOffset, nicetime = getLocalGoogleTimeOffset()
|
||||
if timeOffset < MAX_LOCAL_GOOGLE_TIME_OFFSET:
|
||||
time_status = 'PASS'
|
||||
else:
|
||||
time_status = 'FAIL'
|
||||
something_failed = True
|
||||
printPassFail('Your computer clock differs from Google by %s' % nicetime, time_status)
|
||||
oa2 = googleapiclient.discovery.build('oauth2', 'v1', _createHttpObj())
|
||||
print('Service Account Private Key Authentication:')
|
||||
# We are explicitly not doing DwD here, just confirming service account can auth
|
||||
auth_error = ''
|
||||
try:
|
||||
credentials = getSvcAcctCredentials([USERINFO_EMAIL_SCOPE], None)
|
||||
request = google_auth_httplib2.Request(_createHttpObj())
|
||||
credentials.refresh(request)
|
||||
sa_token_info = callGAPI(oa2, 'tokeninfo', access_token=credentials.token)
|
||||
if sa_token_info:
|
||||
sa_token_result = 'PASS'
|
||||
else:
|
||||
sa_token_result = 'FAIL'
|
||||
something_failed = True
|
||||
except google.auth.exceptions.RefreshError as e:
|
||||
sa_token_result = 'FAIL'
|
||||
something_failed = True
|
||||
auth_error = str(e.args[0])
|
||||
printPassFail('Authenticating...%s' % auth_error, sa_token_result)
|
||||
all_scopes = []
|
||||
for _, scopes in list(API_SCOPE_MAPPING.items()):
|
||||
for scope in scopes:
|
||||
@@ -1476,25 +1557,40 @@ def doCheckServiceAccount(users):
|
||||
all_scopes.append(scope)
|
||||
all_scopes.sort()
|
||||
for user in users:
|
||||
user = user.lower()
|
||||
all_scopes_pass = True
|
||||
print('User: %s' % (user))
|
||||
oa2 = googleapiclient.discovery.build('oauth2', 'v1', _createHttpObj())
|
||||
print('Domain-Wide Delegation authentication as %s:' % (user))
|
||||
for scope in all_scopes:
|
||||
try:
|
||||
credentials = getSvcAcctCredentials([scope], user)
|
||||
request = google_auth_httplib2.Request(_createHttpObj())
|
||||
credentials.refresh(request)
|
||||
result = 'PASS'
|
||||
except (httplib2.ServerNotFoundError, RuntimeError) as e:
|
||||
systemErrorExit(4, e)
|
||||
except google.auth.exceptions.RefreshError:
|
||||
# try with and without email scope
|
||||
for scopes in [[scope, USERINFO_EMAIL_SCOPE], [scope]]:
|
||||
try:
|
||||
credentials = getSvcAcctCredentials(scopes, user)
|
||||
credentials.refresh(request)
|
||||
break
|
||||
except (httplib2.ServerNotFoundError, RuntimeError) as e:
|
||||
systemErrorExit(4, e)
|
||||
except google.auth.exceptions.RefreshError:
|
||||
continue
|
||||
if credentials.token:
|
||||
token_info = callGAPI(oa2, 'tokeninfo', access_token=credentials.token)
|
||||
if scope in token_info.get('scope', '').split(' ') and \
|
||||
user == token_info.get('email', user).lower():
|
||||
result = 'PASS'
|
||||
else:
|
||||
result = 'FAIL'
|
||||
all_scopes_pass = False
|
||||
else:
|
||||
result = 'FAIL'
|
||||
all_scopes_pass = False
|
||||
print(' Scope: {0:60} {1}'.format(scope, result))
|
||||
printPassFail(scope, result)
|
||||
service_account = GM_Globals[GM_OAUTH2SERVICE_ACCOUNT_CLIENT_ID]
|
||||
if all_scopes_pass:
|
||||
print('\nAll scopes passed!\nService account %s is fully authorized.' % service_account)
|
||||
return
|
||||
user_domain = user[user.find('@')+1:]
|
||||
# Tack on email scope for more accurate checking
|
||||
all_scopes.append('https://www.googleapis.com/auth/userinfo.email')
|
||||
scopes_failed = '''Some scopes failed! Please go to:
|
||||
|
||||
https://admin.google.com/%s/AdminHome?#OGX:ManageOauthClients
|
||||
@@ -2082,13 +2178,14 @@ def doGetCustomerInfo():
|
||||
# If customer has changed primary domain customerCreationTime is date
|
||||
# of current primary being added, not customer create date.
|
||||
# We should also get all domains and use oldest date
|
||||
domains = doPrintDomains(return_results=True)
|
||||
oldest = datetime.datetime.strptime(customer_info['customerCreationTime'], '%Y-%m-%dT%H:%M:%S.%fZ')
|
||||
domains = callGAPIitems(cd.domains(), 'list', 'domains',
|
||||
customer=GC_Values[GC_CUSTOMER_ID], fields='domains(creationTime)')
|
||||
for domain in domains:
|
||||
domain_creation = datetime.datetime.strptime(domain['creationTime'], '%Y-%m-%d %H:%M:%S.%f')
|
||||
domain_creation = datetime.datetime.fromtimestamp(int(domain['creationTime'])/1000)
|
||||
if domain_creation < oldest:
|
||||
oldest = domain_creation
|
||||
print('Customer Creation Time: %s' % oldest)
|
||||
print('Customer Creation Time: %s' % oldest.strftime('%Y-%m-%dT%H:%M:%SZ'))
|
||||
print('Default Language: %s' % customer_info.get('language', 'Unset (defaults to en)'))
|
||||
if 'postalAddress' in customer_info:
|
||||
print('Address:')
|
||||
@@ -2167,7 +2264,7 @@ def doDelDomainAlias():
|
||||
domainAliasName = sys.argv[3]
|
||||
callGAPI(cd.domainAliases(), 'delete', customer=GC_Values[GC_CUSTOMER_ID], domainAliasName=domainAliasName)
|
||||
|
||||
def doPrintDomains(return_results=False):
|
||||
def doPrintDomains():
|
||||
cd = buildGAPIObject('directory')
|
||||
todrive = False
|
||||
titles = ['domainName',]
|
||||
@@ -2208,8 +2305,6 @@ def doPrintDomains(return_results=False):
|
||||
titles.append(attr)
|
||||
aliasdomain_attributes[attr] = aliasdomain[attr]
|
||||
csvRows.append(aliasdomain_attributes)
|
||||
if return_results:
|
||||
return csvRows
|
||||
writeCSVfile(csvRows, titles, 'Domains', todrive)
|
||||
|
||||
def doPrintDomainAliases():
|
||||
@@ -2344,8 +2439,7 @@ def buildRoleIdToNameToIdMap():
|
||||
cd = buildGAPIObject('directory')
|
||||
result = callGAPIpages(cd.roles(), 'list', 'items',
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
fields='nextPageToken,items(roleId,roleName)',
|
||||
maxResults=100)
|
||||
fields='nextPageToken,items(roleId,roleName)')
|
||||
GM_Globals[GM_MAP_ROLE_ID_TO_NAME] = {}
|
||||
GM_Globals[GM_MAP_ROLE_NAME_TO_ID] = {}
|
||||
for role in result:
|
||||
@@ -2376,8 +2470,7 @@ def buildUserIdToNameMap():
|
||||
cd = buildGAPIObject('directory')
|
||||
result = callGAPIpages(cd.users(), 'list', 'users',
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
fields='nextPageToken,users(id,primaryEmail)',
|
||||
maxResults=GC_Values[GC_USER_MAX_RESULTS])
|
||||
fields='nextPageToken,users(id,primaryEmail)')
|
||||
GM_Globals[GM_MAP_USER_ID_TO_NAME] = {}
|
||||
for user in result:
|
||||
GM_Globals[GM_MAP_USER_ID_TO_NAME][user['id']] = user['primaryEmail']
|
||||
@@ -3162,7 +3255,10 @@ def changeCalendarAttendees(users):
|
||||
continue
|
||||
page_token = None
|
||||
while True:
|
||||
events_page = callGAPI(cal.events(), 'list', calendarId=user, pageToken=page_token, timeMin=start_date, timeMax=end_date, showDeleted=False, showHiddenInvitations=False)
|
||||
events_page = callGAPI(cal.events(), 'list', calendarId=user,
|
||||
pageToken=page_token, timeMin=start_date,
|
||||
timeMax=end_date, showDeleted=False,
|
||||
showHiddenInvitations=False)
|
||||
print('Got %s items' % len(events_page.get('items', [])))
|
||||
for event in events_page.get('items', []):
|
||||
if event['status'] == 'cancelled':
|
||||
@@ -3843,8 +3939,8 @@ def doCalendarPrintEvents():
|
||||
systemErrorExit(2, '%s is not a valid argument for "gam calendar <email> printevents"' % sys.argv[i])
|
||||
page_message = 'Got %%%%total_items%%%% events for %s' % calendarId
|
||||
results = callGAPIpages(cal.events(), 'list', 'items', page_message=page_message,
|
||||
maxResults=2500, calendarId=calendarId,
|
||||
q=q, showDeleted=showDeleted, showHiddenInvitations=showHiddenInvitations,
|
||||
calendarId=calendarId, q=q, showDeleted=showDeleted,
|
||||
showHiddenInvitations=showHiddenInvitations,
|
||||
timeMin=timeMin, timeMax=timeMax, timeZone=timeZone, updatedMin=updatedMin)
|
||||
for result in results:
|
||||
row = {'calendarId': calendarId}
|
||||
@@ -4309,7 +4405,7 @@ def printDriveActivity(users):
|
||||
feed = callGAPIpages(activity.activities(), 'list', 'activities',
|
||||
page_message=page_message, source='drive.google.com', userId='me',
|
||||
drive_ancestorId=drive_ancestorId, groupingStrategy='none',
|
||||
drive_fileId=drive_fileId, pageSize=GC_Values[GC_ACTIVITY_MAX_RESULTS])
|
||||
drive_fileId=drive_fileId)
|
||||
for item in feed:
|
||||
addRowTitlesToCSVfile(flatten_json(item['combinedEvent']), csvRows, titles)
|
||||
writeCSVfile(csvRows, titles, 'Drive Activity', todrive)
|
||||
@@ -4589,7 +4685,7 @@ def printDriveFileList(users):
|
||||
page_message = ' Got %%%%total_items%%%% files for %s...\n' % user
|
||||
feed = callGAPIpages(drive.files(), 'list', 'items',
|
||||
page_message=page_message, soft_errors=True,
|
||||
q=query, orderBy=orderBy, fields=fields, maxResults=GC_Values[GC_DRIVE_MAX_RESULTS])
|
||||
q=query, orderBy=orderBy, fields=fields)
|
||||
for f_file in feed:
|
||||
a_file = {'Owner': user}
|
||||
for attrib in f_file:
|
||||
@@ -4643,7 +4739,7 @@ def doDriveSearch(drive, query=None, quiet=False):
|
||||
page_message = None
|
||||
files = callGAPIpages(drive.files(), 'list', 'items',
|
||||
page_message=page_message,
|
||||
q=query, fields='nextPageToken,items(id)', maxResults=GC_Values[GC_DRIVE_MAX_RESULTS])
|
||||
q=query, fields='nextPageToken,items(id)')
|
||||
ids = list()
|
||||
for f_file in files:
|
||||
ids.append(f_file['id'])
|
||||
@@ -4763,7 +4859,7 @@ def showDriveFileTree(users):
|
||||
sys.stderr.write('Getting all files for %s...\n' % user)
|
||||
page_message = ' Got %%%%total_items%%%% files for %s...\n' % user
|
||||
feed = callGAPIpages(drive.files(), 'list', 'items', page_message=page_message,
|
||||
q=query, orderBy=orderBy, fields='items(id,title,parents(id),mimeType),nextPageToken', maxResults=GC_Values[GC_DRIVE_MAX_RESULTS])
|
||||
q=query, orderBy=orderBy, fields='items(id,title,parents(id),mimeType),nextPageToken')
|
||||
printDriveFolderContents(feed, root_folder, 0)
|
||||
|
||||
def deleteEmptyDriveFolders(users):
|
||||
@@ -4777,7 +4873,7 @@ def deleteEmptyDriveFolders(users):
|
||||
sys.stderr.write('Getting folders for %s...\n' % user)
|
||||
page_message = ' Got %%%%total_items%%%% folders for %s...\n' % user
|
||||
feed = callGAPIpages(drive.files(), 'list', 'items', page_message=page_message,
|
||||
q=query, fields='items(title,id),nextPageToken', maxResults=GC_Values[GC_DRIVE_MAX_RESULTS])
|
||||
q=query, fields='items(title,id),nextPageToken')
|
||||
deleted_empty = False
|
||||
for folder in feed:
|
||||
children = callGAPI(drive.children(), 'list',
|
||||
@@ -7551,6 +7647,14 @@ def getUserAttributes(i, cd, updateCmd):
|
||||
keyword['value'] = sys.argv[i]
|
||||
i += 1
|
||||
appendItemToBodyList(body, 'keywords', keyword)
|
||||
elif myarg in ['recoveryemail']:
|
||||
body['recoveryEmail'] = sys.argv[i+1]
|
||||
i += 2
|
||||
elif myarg in ['recoveryphone']:
|
||||
body['recoveryPhone'] = sys.argv[i+1]
|
||||
if body['recoveryPhone'] and body['recoveryPhone'][0] != '+':
|
||||
body['recoveryPhone'] = '+' + body['recoveryPhone']
|
||||
i += 2
|
||||
elif myarg == 'clearschema':
|
||||
if not updateCmd:
|
||||
systemErrorExit(2, '%s is not a valid create user argument.' % sys.argv[i])
|
||||
@@ -7605,6 +7709,24 @@ def getUserAttributes(i, cd, updateCmd):
|
||||
body['hashFunction'] = 'crypt'
|
||||
return body
|
||||
|
||||
class ShortURLFlow(google_auth_oauthlib.flow.InstalledAppFlow):
|
||||
def authorization_url(self, **kwargs):
|
||||
long_url, state = super(ShortURLFlow, self).authorization_url(**kwargs)
|
||||
simplehttp = _createHttpObj(timeout=10)
|
||||
url_shortnr = 'https://gam-shortn.appspot.com/create'
|
||||
headers = {'Content-Type': 'application/json',
|
||||
'user-agent': GAM_INFO}
|
||||
try:
|
||||
resp, content = simplehttp.request(url_shortnr, 'POST', '{"long_url": "%s"}' % long_url, headers=headers)
|
||||
except:
|
||||
return long_url, state
|
||||
if resp.status != 200:
|
||||
return long_url, state
|
||||
try:
|
||||
return json.loads(content).get('short_url', long_url), state
|
||||
except:
|
||||
return long_url, state
|
||||
|
||||
def _run_oauth_flow(client_id, client_secret, scopes, access_type, login_hint=None):
|
||||
client_config = {
|
||||
'installed': {
|
||||
@@ -7616,11 +7738,11 @@ def _run_oauth_flow(client_id, client_secret, scopes, access_type, login_hint=No
|
||||
}
|
||||
}
|
||||
|
||||
flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_config(client_config, scopes)
|
||||
flow = ShortURLFlow.from_client_config(client_config, scopes)
|
||||
kwargs = {'access_type': access_type}
|
||||
if login_hint:
|
||||
kwargs['login_hint'] = login_hint
|
||||
if GC_Values[GC_NO_BROWSER]:
|
||||
if not GC_Values[GC_OAUTH_BROWSER]:
|
||||
flow.run_console(
|
||||
authorization_prompt_message=MESSAGE_CONSOLE_AUTHORIZATION_PROMPT,
|
||||
authorization_code_message=MESSAGE_CONSOLE_AUTHORIZATION_CODE,
|
||||
@@ -8431,11 +8553,10 @@ def _getCloudStorageObject(s, bucket, object_, local_file=None, expectedMd5=None
|
||||
if expectedMd5:
|
||||
sys.stdout.write('Verifying %s hash...' % expectedMd5)
|
||||
sys.stdout.flush()
|
||||
if md5MatchesFile(local_file, expectedMd5):
|
||||
if md5MatchesFile(local_file, expectedMd5, False):
|
||||
print('VERIFIED')
|
||||
return
|
||||
else:
|
||||
print('not verified. Downloading again and over-writing...')
|
||||
print('not verified. Downloading again and over-writing...')
|
||||
else:
|
||||
return # nothing to verify, just assume we're good.
|
||||
print('saving to %s' % local_file)
|
||||
@@ -8460,19 +8581,18 @@ def _getCloudStorageObject(s, bucket, object_, local_file=None, expectedMd5=None
|
||||
f = openFile(local_file, 'rb')
|
||||
sys.stdout.write(' Verifying file hash is %s...' % expectedMd5)
|
||||
sys.stdout.flush()
|
||||
if md5MatchesFile(local_file, expectedMd5):
|
||||
print('VERIFIED')
|
||||
else:
|
||||
print('ERROR: actual hash was %s. Exiting on corrupt file.' % actual_hash)
|
||||
sys.exit(6)
|
||||
md5MatchesFile(local_file, expectedMd5, True)
|
||||
print('VERIFIED')
|
||||
closeFile(f)
|
||||
|
||||
def md5MatchesFile(local_file, expected_md5):
|
||||
def md5MatchesFile(local_file, expected_md5, exitOnError):
|
||||
f = openFile(local_file, 'rb')
|
||||
hash_md5 = hashlib.md5()
|
||||
for chunk in iter(lambda: f.read(4096), b""):
|
||||
hash_md5.update(chunk)
|
||||
actual_hash = hash_md5.hexdigest()
|
||||
if exitOnError and actual_hash != expected_md5:
|
||||
systemErrorExit(6, 'actual hash was %s. Exiting on corrupt file.' % actual_hash)
|
||||
return actual_hash == expected_md5
|
||||
|
||||
def doDownloadCloudStorageBucket():
|
||||
@@ -8541,11 +8661,8 @@ def doDownloadVaultExport():
|
||||
expected_hash = s_file['md5Hash']
|
||||
sys.stdout.write(' Verifying file hash is %s...' % expected_hash)
|
||||
sys.stdout.flush()
|
||||
if md5MatchesFile(filename, expected_hash):
|
||||
print('VERIFIED')
|
||||
else:
|
||||
print('ERROR: actual hash was %s. Exiting on corrupt file.' % actual_hash)
|
||||
sys.exit(6)
|
||||
md5MatchesFile(filename, expected_hash, True)
|
||||
print('VERIFIED')
|
||||
if extractFiles and re.search(r'\.zip$', filename):
|
||||
extract_nested_zip(filename, targetFolder)
|
||||
|
||||
@@ -8869,21 +8986,11 @@ def doCreateUser():
|
||||
def GroupIsAbuseOrPostmaster(emailAddr):
|
||||
return emailAddr.startswith('abuse@') or emailAddr.startswith('postmaster@')
|
||||
|
||||
def mapCollaborativeACL(myarg, value):
|
||||
value = value.lower().replace('_', '')
|
||||
if value in COLLABORATIVE_ACL_CHOICES:
|
||||
return COLLABORATIVE_ACL_CHOICES[value]
|
||||
systemErrorExit(3, 'allowed choices for %s are %s, got %s' % (myarg, ', '.join(sorted(COLLABORATIVE_ACL_CHOICES)), value))
|
||||
GROUP_SETTINGS_LIST_PATTERN = re.compile(r'([A-Z][A-Z_]+[A-Z]?)')
|
||||
|
||||
def getGroupAttrValue(myarg, value, gs_object, gs_body, function):
|
||||
if myarg == 'collaborative':
|
||||
value = mapCollaborativeACL(myarg, value)
|
||||
for attrName, attrValue in list(COLLABORATIVE_INBOX_ATTRIBUTES.items()):
|
||||
if attrValue == 'acl':
|
||||
gs_body[attrName] = value
|
||||
else:
|
||||
gs_body[attrName] = attrValue
|
||||
return
|
||||
myarg = 'enablecollaborativeinbox'
|
||||
for (attrib, params) in list(gs_object['schemas']['Groups']['properties'].items()):
|
||||
if attrib in ['kind', 'etag', 'email']:
|
||||
continue
|
||||
@@ -8905,19 +9012,19 @@ def getGroupAttrValue(myarg, value, gs_object, gs_body, function):
|
||||
value = value.replace('\\n', '\n')
|
||||
elif attrib == 'primaryLanguage':
|
||||
value = LANGUAGE_CODES_MAP.get(value.lower(), value)
|
||||
elif COLLABORATIVE_INBOX_ATTRIBUTES.get(attrib) == 'acl':
|
||||
value = mapCollaborativeACL(myarg, value)
|
||||
elif params['description'].find(value.upper()) != -1: # ugly hack because API wants some values uppercased.
|
||||
elif attrib in GROUP_SETTINGS_LIST_ATTRIBUTES:
|
||||
value = value.upper()
|
||||
elif value.lower() in true_values:
|
||||
value = 'true'
|
||||
elif value.lower() in false_values:
|
||||
value = 'false'
|
||||
# Another ugly hack because Groups Settings API doesn't have proper enumerator values set in discovery file.
|
||||
if 'description' in params and params['description'].find('Possible values are: ') != -1:
|
||||
possible_values = params['description'][params['description'].find('Possible values are: ')+21:].split(' ')
|
||||
if value not in possible_values:
|
||||
systemErrorExit(2, 'value for %s must be one of %s. Got %s.' % (attrib, ', '.join(possible_values), value))
|
||||
possible_values = GROUP_SETTINGS_LIST_PATTERN.findall(params['description'])
|
||||
if value not in possible_values:
|
||||
systemErrorExit(2, 'value for %s must be one of %s. Got %s.' % (attrib, ', '.join(possible_values), value))
|
||||
elif attrib in GROUP_SETTINGS_BOOLEAN_ATTRIBUTES:
|
||||
value = value.lower()
|
||||
if value in true_values:
|
||||
value = 'true'
|
||||
elif value in false_values:
|
||||
value = 'false'
|
||||
else:
|
||||
systemErrorExit(2, 'value for %s must be true|false. Got %s.' % (attrib, value))
|
||||
gs_body[attrib] = value
|
||||
return
|
||||
systemErrorExit(2, '%s is not a valid argument for "gam %s group"' % (myarg, function))
|
||||
@@ -9518,7 +9625,7 @@ def doUpdateGroup():
|
||||
result = callGAPIpages(cd.members(), 'list', 'members',
|
||||
page_message=page_message,
|
||||
throw_reasons=GAPI_MEMBERS_THROW_REASONS,
|
||||
groupKey=group, roles=listRoles, fields=listFields, maxResults=GC_Values[GC_MEMBER_MAX_RESULTS])
|
||||
groupKey=group, roles=listRoles, fields=listFields)
|
||||
if not result:
|
||||
print('Group already has 0 members')
|
||||
return
|
||||
@@ -9697,9 +9804,12 @@ def doUpdateMobile():
|
||||
doit = False
|
||||
if resourceIds[:6] == 'query:':
|
||||
query = resourceIds[6:]
|
||||
fields='nextPageToken,mobiledevices(resourceId,email)'
|
||||
fields = 'nextPageToken,mobiledevices(resourceId,email)'
|
||||
page_message = 'Got %%total_items%% mobile devices...\n'
|
||||
devices = callGAPIpages(cd.mobiledevices(), 'list', page_message=page_message, customerId=GC_Values[GC_CUSTOMER_ID], items='mobiledevices', query=query, fields=fields)
|
||||
devices = callGAPIpages(cd.mobiledevices(), 'list',
|
||||
page_message=page_message,
|
||||
customerId=GC_Values[GC_CUSTOMER_ID],
|
||||
items='mobiledevices', query=query, fields=fields)
|
||||
else:
|
||||
devices = [{'resourceId': resourceIds, 'email': ['not set']}]
|
||||
doit = True
|
||||
@@ -10104,9 +10214,13 @@ def doGetUserInfo(user_email=None):
|
||||
else:
|
||||
print('Last login time: %s' % user['lastLoginTime'])
|
||||
if 'orgUnitPath' in user:
|
||||
print('Google Org Unit Path: %s\n' % user['orgUnitPath'])
|
||||
print('Google Org Unit Path: %s' % user['orgUnitPath'])
|
||||
if 'thumbnailPhotoUrl' in user:
|
||||
print('Photo URL: %s\n' % user['thumbnailPhotoUrl'])
|
||||
if 'recoveryPhone' in user:
|
||||
print('Recovery Phone: %s' % user['recoveryPhone'])
|
||||
if 'recoveryEmail' in user:
|
||||
print('Recovery Email: %s' % user['recoveryEmail'])
|
||||
if 'notes' in user:
|
||||
print('Notes:')
|
||||
notes = user['notes']
|
||||
@@ -10344,7 +10458,7 @@ def doGetGroupInfo(group_name=None):
|
||||
for groupm in groups:
|
||||
print(' %s: %s' % (groupm['name'], groupm['email']))
|
||||
if getUsers:
|
||||
members = callGAPIpages(cd.members(), 'list', 'members', groupKey=group_name, fields='nextPageToken,members(email,id,role,type)', maxResults=GC_Values[GC_MEMBER_MAX_RESULTS])
|
||||
members = callGAPIpages(cd.members(), 'list', 'members', groupKey=group_name, fields='nextPageToken,members(email,id,role,type)')
|
||||
print('Members:')
|
||||
for member in members:
|
||||
print(' %s: %s (%s)' % (member.get('role', ROLE_MEMBER).lower(), member.get('email', member['id']), member['type'].lower()))
|
||||
@@ -10483,6 +10597,8 @@ def doGetCrosInfo():
|
||||
print('CrOS Device: {0} ({1} of {2})'.format(deviceId, i, 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)
|
||||
if guess_aue:
|
||||
_guessAUE(cros, guessedAUEs)
|
||||
@@ -10824,7 +10940,7 @@ def doGetOrgInfo(name=None, return_attrib=None):
|
||||
page_message = 'Got %%total_items%% Users: %%first_item%% - %%last_item%%\n'
|
||||
users = callGAPIpages(cd.users(), 'list', 'users', page_message=page_message,
|
||||
message_attribute='primaryEmail', customer=GC_Values[GC_CUSTOMER_ID], query=orgUnitPathQuery(name, checkSuspended),
|
||||
fields='users(primaryEmail,orgUnitPath),nextPageToken', maxResults=GC_Values[GC_USER_MAX_RESULTS])
|
||||
fields='users(primaryEmail,orgUnitPath),nextPageToken')
|
||||
if checkSuspended is None:
|
||||
print('Users:')
|
||||
elif not checkSuspended:
|
||||
@@ -11067,7 +11183,7 @@ def doUndeleteUser():
|
||||
else:
|
||||
print('Looking up UID for %s...' % user)
|
||||
deleted_users = callGAPIpages(cd.users(), 'list', 'users',
|
||||
customer=GC_Values[GC_CUSTOMER_ID], showDeleted=True, maxResults=GC_Values[GC_USER_MAX_RESULTS])
|
||||
customer=GC_Values[GC_CUSTOMER_ID], showDeleted=True)
|
||||
matching_users = list()
|
||||
for deleted_user in deleted_users:
|
||||
if str(deleted_user['primaryEmail']).lower() == user:
|
||||
@@ -11559,7 +11675,7 @@ def doPrintUsers():
|
||||
all_users = callGAPIpages(cd.users(), 'list', 'users', page_message=page_message,
|
||||
message_attribute='primaryEmail', customer=customer, domain=domain, fields=fields,
|
||||
showDeleted=deleted_only, orderBy=orderBy, sortOrder=sortOrder, viewType=viewType,
|
||||
query=query, projection=projection, customFieldMask=customFieldMask, maxResults=GC_Values[GC_USER_MAX_RESULTS])
|
||||
query=query, projection=projection, customFieldMask=customFieldMask)
|
||||
for user in all_users:
|
||||
if email_parts and ('primaryEmail' in user):
|
||||
user_email = user['primaryEmail']
|
||||
@@ -11639,11 +11755,14 @@ GROUP_ARGUMENT_TO_PROPERTY_TITLE_MAP = {
|
||||
|
||||
GROUP_ATTRIBUTES_ARGUMENT_TO_PROPERTY_MAP = {
|
||||
'allowexternalmembers': 'allowExternalMembers',
|
||||
'allowgooglecommunication': 'allowGoogleCommunication',
|
||||
'allowwebposting': 'allowWebPosting',
|
||||
'archiveonly': 'archiveOnly',
|
||||
'customfootertext': 'customFooterText',
|
||||
'customreplyto': 'customReplyTo',
|
||||
'defaultmessagedenynotificationtext': 'defaultMessageDenyNotificationText',
|
||||
'enablecollaborativeinbox': 'enableCollaborativeInbox',
|
||||
'favoriterepliesontop': 'favoriteRepliesOnTop',
|
||||
'gal': 'includeInGlobalAddressList',
|
||||
'includecustomfooter': 'includeCustomFooter',
|
||||
'includeinglobaladdresslist': 'includeInGlobalAddressList',
|
||||
@@ -11656,16 +11775,33 @@ GROUP_ATTRIBUTES_ARGUMENT_TO_PROPERTY_MAP = {
|
||||
'showingroupdirectory': 'showInGroupDirectory',
|
||||
'spammoderationlevel': 'spamModerationLevel',
|
||||
'whocanadd': 'whoCanAdd',
|
||||
'whocanapprovemembers': 'whoCanApproveMembers',
|
||||
'whocanapprovemessages': 'whoCanApproveMessages',
|
||||
'whocanassigntopics': 'whoCanAssignTopics',
|
||||
'whocanassistcontent': 'whoCanAssistContent',
|
||||
'whocanbanusers': 'whoCanBanUsers',
|
||||
'whocancontactowner': 'whoCanContactOwner',
|
||||
'whocandeleteanypost': 'whoCanDeleteAnyPost',
|
||||
'whocandeletetopics': 'whoCanDeleteTopics',
|
||||
'whocandiscovergroup': 'whoCanDiscoverGroup',
|
||||
'whocanenterfreeformtags': 'whoCanEnterFreeFormTags',
|
||||
'whocanhideabuse': 'whoCanHideAbuse',
|
||||
'whocaninvite': 'whoCanInvite',
|
||||
'whocanjoin': 'whoCanJoin',
|
||||
'whocanleavegroup': 'whoCanLeaveGroup',
|
||||
'whocanlocktopics': 'whoCanLockTopics',
|
||||
'whocanmaketopicssticky': 'whoCanMakeTopicsSticky',
|
||||
'whocanmarkduplicate': 'whoCanMarkDuplicate',
|
||||
'whocanmarkfavoritereplyonanytopic': 'whoCanMarkFavoriteReplyOnAnyTopic',
|
||||
'whocanmarkfavoritereplyonowntopic': 'whoCanMarkFavoriteReplyOnOwnTopic',
|
||||
'whocanmarknoresponseneeded': 'whoCanMarkNoResponseNeeded',
|
||||
'whocanmoderatecontent': 'whoCanModerateContent',
|
||||
'whocanmoderatemembers': 'whoCanModerateMembers',
|
||||
'whocanmodifymembers': 'whoCanModifyMembers',
|
||||
'whocanmodifytagsandcategories': 'whoCanModifyTagsAndCategories',
|
||||
'whocanmovetopicsin': 'whoCanMoveTopicsIn',
|
||||
'whocanmovetopicsout': 'whoCanMoveTopicsOut',
|
||||
'whocanpostannouncements': 'whoCanPostAnnouncements',
|
||||
'whocanpostmessage': 'whoCanPostMessage',
|
||||
'whocantaketopics': 'whoCanTakeTopics',
|
||||
'whocanunassigntopic': 'whoCanUnassignTopic',
|
||||
@@ -11689,7 +11825,6 @@ def doPrintGroups():
|
||||
titles = []
|
||||
csvRows = []
|
||||
addFieldTitleToCSVfile('email', GROUP_ARGUMENT_TO_PROPERTY_TITLE_MAP, cdfieldsList, fieldsTitles, titles)
|
||||
maxResults = None
|
||||
roles = []
|
||||
getSettings = sortHeaders = False
|
||||
while i < len(sys.argv):
|
||||
@@ -11710,7 +11845,7 @@ def doPrintGroups():
|
||||
usemember = None
|
||||
i += 2
|
||||
elif myarg == 'maxresults':
|
||||
maxResults = getInteger(sys.argv[i+1], myarg, minVal=0)
|
||||
# deprecated argument
|
||||
i += 2
|
||||
elif myarg == 'delimiter':
|
||||
aliasDelimiter = memberDelimiter = sys.argv[i+1]
|
||||
@@ -11792,8 +11927,7 @@ def doPrintGroups():
|
||||
entityList = callGAPIpages(cd.groups(), 'list', 'groups',
|
||||
page_message=page_message, message_attribute='email',
|
||||
customer=customer, domain=usedomain, userKey=usemember, query=usequery,
|
||||
fields='nextPageToken,groups({0})'.format(cdfields),
|
||||
maxResults=maxResults)
|
||||
fields='nextPageToken,groups({0})'.format(cdfields))
|
||||
i = 0
|
||||
count = len(entityList)
|
||||
for groupEntity in entityList:
|
||||
@@ -11813,7 +11947,7 @@ def doPrintGroups():
|
||||
groupMembers = callGAPIpages(cd.members(), 'list', 'members',
|
||||
page_message=page_message, message_attribute='email',
|
||||
soft_errors=True,
|
||||
groupKey=groupEmail, roles=listRoles, fields=listFields, maxResults=GC_Values[GC_MEMBER_MAX_RESULTS])
|
||||
groupKey=groupEmail, roles=listRoles, fields=listFields)
|
||||
if members:
|
||||
membersList = []
|
||||
membersCount = 0
|
||||
@@ -12000,7 +12134,7 @@ def doPrintAliases():
|
||||
page_message = 'Got %%num_items%% Users %%first_item%% - %%last_item%%\n'
|
||||
all_users = callGAPIpages(cd.users(), 'list', 'users', page_message=page_message,
|
||||
message_attribute='primaryEmail', customer=GC_Values[GC_CUSTOMER_ID], query=query,
|
||||
fields='nextPageToken,users({0})'.format(','.join(userFields)), maxResults=GC_Values[GC_USER_MAX_RESULTS])
|
||||
fields='nextPageToken,users({0})'.format(','.join(userFields)))
|
||||
for user in all_users:
|
||||
for alias in user.get('aliases', []):
|
||||
csvRows.append({'Alias': alias, 'Target': user['primaryEmail'], 'TargetType': 'User'})
|
||||
@@ -12089,7 +12223,7 @@ def doPrintGroupMembers():
|
||||
validRoles, listRoles, listFields = _getRoleVerification(','.join(roles), fields)
|
||||
group_members = callGAPIpages(cd.members(), 'list', 'members',
|
||||
soft_errors=True,
|
||||
groupKey=group_email, roles=listRoles, fields=listFields, maxResults=GC_Values[GC_MEMBER_MAX_RESULTS])
|
||||
groupKey=group_email, roles=listRoles, fields=listFields)
|
||||
for member in group_members:
|
||||
if not _checkMemberRoleIsSuspended(member, validRoles, checkSuspended):
|
||||
continue
|
||||
@@ -12283,7 +12417,7 @@ def doPrintMobileDevices():
|
||||
page_message = 'Got %%num_items%% Mobile Devices...\n'
|
||||
all_mobile = callGAPIpages(cd.mobiledevices(), 'list', 'mobiledevices', page_message=page_message,
|
||||
customerId=GC_Values[GC_CUSTOMER_ID], query=query, projection=projection, fields=fields,
|
||||
orderBy=orderBy, sortOrder=sortOrder, maxResults=GC_Values[GC_DEVICE_MAX_RESULTS])
|
||||
orderBy=orderBy, sortOrder=sortOrder)
|
||||
for mobile in all_mobile:
|
||||
row = {}
|
||||
for attrib in mobile:
|
||||
@@ -12400,7 +12534,7 @@ def doPrintCrosActivity():
|
||||
page_message = 'Got %%total_items%% CrOS Devices...\n'
|
||||
all_cros = callGAPIpages(cd.chromeosdevices(), 'list', 'chromeosdevices', page_message=page_message,
|
||||
query=query, customerId=GC_Values[GC_CUSTOMER_ID], projection='FULL',
|
||||
fields=fields, maxResults=GC_Values[GC_DEVICE_MAX_RESULTS], orgUnitPath=orgUnitPath)
|
||||
fields=fields, orgUnitPath=orgUnitPath)
|
||||
for cros in all_cros:
|
||||
row = {}
|
||||
for attrib in cros:
|
||||
@@ -12581,7 +12715,7 @@ def doPrintCrosDevices():
|
||||
page_message = 'Got %%total_items%% CrOS Devices...\n'
|
||||
all_cros = callGAPIpages(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, maxResults=GC_Values[GC_DEVICE_MAX_RESULTS])
|
||||
orderBy=orderBy, sortOrder=sortOrder, fields=fields)
|
||||
for cros in all_cros:
|
||||
_checkTPMVulnerability(cros)
|
||||
if guess_aue:
|
||||
@@ -12590,6 +12724,8 @@ def doPrintCrosDevices():
|
||||
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', []):
|
||||
for tempInfo in cpuStatusReport.get('cpuTemperatureInfo', []):
|
||||
tempInfo['label'] = tempInfo['label'].strip()
|
||||
@@ -12598,6 +12734,8 @@ def doPrintCrosDevices():
|
||||
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',
|
||||
@@ -12985,7 +13123,7 @@ def getUsersToModify(entity_type=None, entity=None, silent=False, member_type=No
|
||||
page_message = 'Got %%%%total_items%%%% %s...' % member_type_message
|
||||
validRoles, listRoles, listFields = _getRoleVerification(member_type, 'nextPageToken,members(email,id,type,status)')
|
||||
members = callGAPIpages(cd.members(), 'list', 'members', page_message=page_message,
|
||||
groupKey=group, roles=listRoles, fields=listFields, maxResults=GC_Values[GC_MEMBER_MAX_RESULTS])
|
||||
groupKey=group, roles=listRoles, fields=listFields)
|
||||
users = []
|
||||
for member in members:
|
||||
if ((not groupUserMembersOnly) or (member['type'] == 'USER')) and _checkMemberRoleIsSuspended(member, validRoles, checkSuspended):
|
||||
@@ -13008,7 +13146,7 @@ def getUsersToModify(entity_type=None, entity=None, silent=False, member_type=No
|
||||
page_message = 'Got %%total_items%% Users...'
|
||||
members = callGAPIpages(cd.users(), 'list', 'users', page_message=page_message,
|
||||
customer=GC_Values[GC_CUSTOMER_ID], fields='nextPageToken,users(primaryEmail,orgUnitPath)',
|
||||
query=query, maxResults=GC_Values[GC_USER_MAX_RESULTS])
|
||||
query=query)
|
||||
ou = ou.lower()
|
||||
for member in members:
|
||||
if ou == member.get('orgUnitPath', '').lower():
|
||||
@@ -13030,7 +13168,7 @@ def getUsersToModify(entity_type=None, entity=None, silent=False, member_type=No
|
||||
page_message = 'Got %%total_items%% Users...'
|
||||
members = callGAPIpages(cd.users(), 'list', 'users', page_message=page_message,
|
||||
customer=GC_Values[GC_CUSTOMER_ID], fields='nextPageToken,users(primaryEmail)',
|
||||
query=query, maxResults=GC_Values[GC_USER_MAX_RESULTS])
|
||||
query=query)
|
||||
for member in members:
|
||||
users.append(member['primaryEmail'])
|
||||
if not silent:
|
||||
@@ -13049,7 +13187,7 @@ def getUsersToModify(entity_type=None, entity=None, silent=False, member_type=No
|
||||
page_message = 'Got %%total_items%% Users...'
|
||||
members = callGAPIpages(cd.users(), 'list', 'users', page_message=page_message,
|
||||
customer=GC_Values[GC_CUSTOMER_ID], fields='nextPageToken,users(primaryEmail,suspended)',
|
||||
query=query, maxResults=GC_Values[GC_USER_MAX_RESULTS])
|
||||
query=query)
|
||||
for member in members:
|
||||
email = member['primaryEmail']
|
||||
if (checkSuspended is None or checkSuspended == member['suspended']) and email not in usersSet:
|
||||
@@ -13115,7 +13253,7 @@ def getUsersToModify(entity_type=None, entity=None, silent=False, member_type=No
|
||||
page_message = 'Got %%total_items%% Users...'
|
||||
all_users = callGAPIpages(cd.users(), 'list', 'users', page_message=page_message,
|
||||
customer=GC_Values[GC_CUSTOMER_ID], query=query,
|
||||
fields='nextPageToken,users(primaryEmail)', maxResults=GC_Values[GC_USER_MAX_RESULTS])
|
||||
fields='nextPageToken,users(primaryEmail)')
|
||||
for member in all_users:
|
||||
users.append(member['primaryEmail'])
|
||||
if not silent:
|
||||
@@ -13125,8 +13263,7 @@ def getUsersToModify(entity_type=None, entity=None, silent=False, member_type=No
|
||||
printGettingAllItems('CrOS Devices', None)
|
||||
page_message = 'Got %%total_items%% CrOS Devices...'
|
||||
all_cros = callGAPIpages(cd.chromeosdevices(), 'list', 'chromeosdevices', page_message=page_message,
|
||||
customerId=GC_Values[GC_CUSTOMER_ID], fields='nextPageToken,chromeosdevices(deviceId)',
|
||||
maxResults=GC_Values[GC_DEVICE_MAX_RESULTS])
|
||||
customerId=GC_Values[GC_CUSTOMER_ID], fields='nextPageToken,chromeosdevices(deviceId)')
|
||||
for member in all_cros:
|
||||
users.append(member['deviceId'])
|
||||
if not silent:
|
||||
@@ -13151,7 +13288,7 @@ def getUsersToModify(entity_type=None, entity=None, silent=False, member_type=No
|
||||
page_message = 'Got %%total_items%% CrOS Devices...'
|
||||
members = callGAPIpages(cd.chromeosdevices(), 'list', 'chromeosdevices', page_message=page_message,
|
||||
customerId=GC_Values[GC_CUSTOMER_ID], fields='nextPageToken,chromeosdevices(deviceId)',
|
||||
query=query, maxResults=GC_Values[GC_DEVICE_MAX_RESULTS])
|
||||
query=query)
|
||||
for member in members:
|
||||
deviceId = member['deviceId']
|
||||
if deviceId not in usersSet:
|
||||
|
||||
@@ -114,7 +114,6 @@ else
|
||||
fi
|
||||
|
||||
echo "Upgrading pip packages..."
|
||||
$pip freeze > upgrades.txt
|
||||
$pip install --upgrade -r upgrades.txt
|
||||
$pip list --outdated --format=freeze | grep -v '^\-e' | cut -d = -f 1 | xargs -n1 $pip install -U
|
||||
$pip install --upgrade -r src/requirements.txt
|
||||
$pip install --upgrade pyinstaller
|
||||
|
||||
@@ -65,7 +65,6 @@ cd $whereibelong
|
||||
|
||||
export PATH=/usr/local/opt/python/libexec/bin:$PATH
|
||||
$pip install --upgrade pip
|
||||
$pip freeze > upgrades.txt
|
||||
$pip install --upgrade -r upgrades.txt
|
||||
$pip list --outdated --format=freeze | grep -v '^\-e' | cut -d = -f 1 | xargs -n1 $pip install -U
|
||||
$pip install --upgrade -r src/requirements.txt
|
||||
$pip install --upgrade pyinstaller
|
||||
|
||||
@@ -21,8 +21,7 @@ until cinst -y wixtoolset; do echo "trying again..."; done
|
||||
export PATH=$PATH:/c/Python37/scripts
|
||||
cd $mypath
|
||||
pip install --upgrade pip
|
||||
pip freeze > upgrades.txt
|
||||
pip install --upgrade -r upgrades.txt
|
||||
pip list --outdated --format=freeze | grep -v '^\-e' | cut -d = -f 1 | xargs -n1 pip install -U
|
||||
pip install --upgrade -r src/requirements.txt
|
||||
|
||||
#pip install --upgrade pyinstaller
|
||||
|
||||
@@ -21,7 +21,23 @@ until cinst -y wixtoolset; do echo "trying again..."; done
|
||||
export PATH=$PATH:/c/Python37/scripts
|
||||
cd $mypath
|
||||
pip install --upgrade pip
|
||||
pip freeze > upgrades.txt
|
||||
pip install --upgrade -r upgrades.txt
|
||||
pip list --outdated --format=freeze | grep -v '^\-e' | cut -d = -f 1 | xargs -n1 pip install -U
|
||||
pip install --upgrade -r src/requirements.txt
|
||||
pip install --upgrade pyinstaller
|
||||
#pip install --upgrade pyinstaller
|
||||
# Install PyInstaller from source and build bootloader
|
||||
# to try and avoid getting flagged as malware since
|
||||
# lots of malware uses PyInstaller default bootloader
|
||||
# https://stackoverflow.com/questions/53584395/how-to-recompile-the-bootloader-of-pyinstaller
|
||||
echo "Downloading PyInstaller..."
|
||||
wget --quiet https://github.com/pyinstaller/pyinstaller/releases/download/v$PYINSTALLER_VERSION/PyInstaller-$PYINSTALLER_VERSION.tar.gz
|
||||
tar xf PyInstaller-$PYINSTALLER_VERSION.tar.gz
|
||||
cd PyInstaller-$PYINSTALLER_VERSION/bootloader
|
||||
echo "bootloader before:"
|
||||
md5sum ../PyInstaller/bootloader/Windows-64bit/*
|
||||
/c/python37/python ./waf all --target-arch=64bit
|
||||
echo "bootloader after:"
|
||||
md5sum ../PyInstaller/bootloader/Windows-64bit/*
|
||||
echo "PATH: $PATH"
|
||||
cd ..
|
||||
/c/python37/python setup.py install
|
||||
cd $mypath
|
||||
|
||||
168
src/var.py
168
src/var.py
@@ -6,15 +6,15 @@ import platform
|
||||
import re
|
||||
|
||||
gam_author = 'Jay Lee <jay0lee@gmail.com>'
|
||||
gam_version = '4.90'
|
||||
gam_version = '4.94'
|
||||
gam_license = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
||||
|
||||
GAM_URL = 'https://git.io/gam'
|
||||
GAM_INFO = 'GAM {0} - {1} / {2} / Python {3}.{4}.{5} {6} / {7} {8} /'.format(gam_version, GAM_URL,
|
||||
gam_author,
|
||||
sys.version_info[0], sys.version_info[1],
|
||||
sys.version_info[2], sys.version_info[3],
|
||||
platform.platform(), platform.machine())
|
||||
gam_author,
|
||||
sys.version_info[0], sys.version_info[1],
|
||||
sys.version_info[2], sys.version_info[3],
|
||||
platform.platform(), platform.machine())
|
||||
|
||||
GAM_RELEASES = 'https://github.com/jay0lee/GAM/releases'
|
||||
GAM_WIKI = 'https://github.com/jay0lee/GAM/wiki'
|
||||
@@ -39,6 +39,8 @@ FN_LAST_UPDATE_CHECK_TXT = 'lastupdatecheck.txt'
|
||||
MY_CUSTOMER = 'my_customer'
|
||||
# See https://support.google.com/drive/answer/37603
|
||||
MAX_GOOGLE_SHEET_CELLS = 5000000
|
||||
MAX_LOCAL_GOOGLE_TIME_OFFSET = 30
|
||||
|
||||
SKUS = {
|
||||
'1010010001': {
|
||||
'product': '101001', 'aliases': ['identity', 'cloudidentity'], 'displayName': 'Cloud Identity'},
|
||||
@@ -154,16 +156,18 @@ API_VER_MAPPING = {
|
||||
'vault': 'v1',
|
||||
}
|
||||
|
||||
USERINFO_EMAIL_SCOPE = 'https://www.googleapis.com/auth/userinfo.email'
|
||||
|
||||
API_SCOPE_MAPPING = {
|
||||
'alertcenter': ['https://www.googleapis.com/auth/apps.alerts',],
|
||||
'appsactivity': ['https://www.googleapis.com/auth/activity',
|
||||
'https://www.googleapis.com/auth/drive',],
|
||||
'https://www.googleapis.com/auth/drive',],
|
||||
'calendar': ['https://www.googleapis.com/auth/calendar',],
|
||||
'drive': ['https://www.googleapis.com/auth/drive',],
|
||||
'drive3': ['https://www.googleapis.com/auth/drive',],
|
||||
'gmail': ['https://mail.google.com/',
|
||||
'https://www.googleapis.com/auth/gmail.settings.basic',
|
||||
'https://www.googleapis.com/auth/gmail.settings.sharing',],
|
||||
'https://www.googleapis.com/auth/gmail.settings.basic',
|
||||
'https://www.googleapis.com/auth/gmail.settings.sharing',],
|
||||
'sheets': ['https://www.googleapis.com/auth/spreadsheets',],
|
||||
}
|
||||
|
||||
@@ -403,7 +407,7 @@ DOCUMENT_FORMATS_MAP = {
|
||||
'mht': [{'mime': 'message/rfc822', 'ext': 'mht'}],
|
||||
'odp': [{'mime': 'application/vnd.oasis.opendocument.presentation', 'ext': '.odp'}],
|
||||
'ods': [{'mime': 'application/x-vnd.oasis.opendocument.spreadsheet', 'ext': '.ods'},
|
||||
{'mime': 'application/vnd.oasis.opendocument.spreadsheet', 'ext': '.ods'}],
|
||||
{'mime': 'application/vnd.oasis.opendocument.spreadsheet', 'ext': '.ods'}],
|
||||
'odt': [{'mime': 'application/vnd.oasis.opendocument.text', 'ext': '.odt'}],
|
||||
'pdf': [{'mime': 'application/pdf', 'ext': '.pdf'}],
|
||||
'png': [{'mime': 'image/png', 'ext': '.png'}],
|
||||
@@ -414,7 +418,7 @@ DOCUMENT_FORMATS_MAP = {
|
||||
'rtf': [{'mime': 'application/rtf', 'ext': '.rtf'}],
|
||||
'svg': [{'mime': 'image/svg+xml', 'ext': '.svg'}],
|
||||
'tsv': [{'mime': 'text/tab-separated-values', 'ext': '.tsv'},
|
||||
{'mime': 'text/tsv', 'ext': '.tsv'}],
|
||||
{'mime': 'text/tsv', 'ext': '.tsv'}],
|
||||
'txt': [{'mime': 'text/plain', 'ext': '.txt'}],
|
||||
'xls': [{'mime': 'application/vnd.ms-excel', 'ext': '.xls'}],
|
||||
'xlt': [{'mime': 'application/vnd.ms-excel', 'ext': '.xlt'}],
|
||||
@@ -425,9 +429,9 @@ DOCUMENT_FORMATS_MAP = {
|
||||
'microsoft': _MICROSOFT_FORMATS_LIST,
|
||||
'micro$oft': _MICROSOFT_FORMATS_LIST,
|
||||
'openoffice': [{'mime': 'application/vnd.oasis.opendocument.presentation', 'ext': '.odp'},
|
||||
{'mime': 'application/x-vnd.oasis.opendocument.spreadsheet', 'ext': '.ods'},
|
||||
{'mime': 'application/vnd.oasis.opendocument.spreadsheet', 'ext': '.ods'},
|
||||
{'mime': 'application/vnd.oasis.opendocument.text', 'ext': '.odt'}],
|
||||
{'mime': 'application/x-vnd.oasis.opendocument.spreadsheet', 'ext': '.ods'},
|
||||
{'mime': 'application/vnd.oasis.opendocument.spreadsheet', 'ext': '.ods'},
|
||||
{'mime': 'application/vnd.oasis.opendocument.text', 'ext': '.odt'}],
|
||||
}
|
||||
|
||||
DNS_ERROR_CODES_MAP = {
|
||||
@@ -534,17 +538,21 @@ CROS_ARGUMENT_TO_PROPERTY_MAP = {
|
||||
'annotateduser': ['annotatedUser',],
|
||||
'asset': ['annotatedAssetId',],
|
||||
'assetid': ['annotatedAssetId',],
|
||||
'autoupdateexpiration': ['autoUpdateExpiration',],
|
||||
'bootmode': ['bootMode',],
|
||||
'cpustatusreports': ['cpuStatusReports',],
|
||||
'devicefiles': ['deviceFiles',],
|
||||
'deviceid': ['deviceId',],
|
||||
'dockmacaddress': ['dockMacAddress',],
|
||||
'diskvolumereports': ['diskVolumeReports',],
|
||||
'ethernetmacaddress': ['ethernetMacAddress',],
|
||||
'ethernetmacaddress0': ['ethernetMacAddress0',],
|
||||
'firmwareversion': ['firmwareVersion',],
|
||||
'lastenrollmenttime': ['lastEnrollmentTime',],
|
||||
'lastsync': ['lastSync',],
|
||||
'location': ['annotatedLocation',],
|
||||
'macaddress': ['macAddress',],
|
||||
'manufacturedate': ['manufactureDate',],
|
||||
'meid': ['meid',],
|
||||
'model': ['model',],
|
||||
'notes': ['notes',],
|
||||
@@ -586,12 +594,16 @@ CROS_SCALAR_PROPERTY_PRINT_ORDER = [
|
||||
'osVersion',
|
||||
'bootMode',
|
||||
'meid',
|
||||
'dockMacAddress',
|
||||
'ethernetMacAddress',
|
||||
'ethernetMacAddress0',
|
||||
'macAddress',
|
||||
'systemRamTotal',
|
||||
'lastEnrollmentTime',
|
||||
'orderNumber',
|
||||
'manufactureDate',
|
||||
'supportEndDate',
|
||||
'autoUpdateExpiration',
|
||||
'guessedAUEDate',
|
||||
'guessedAUEModel',
|
||||
'tpmVersionInfo',
|
||||
@@ -612,28 +624,76 @@ CROS_END_ARGUMENTS = ['end', 'enddate']
|
||||
CROS_TPM_VULN_VERSIONS = ['41f', '420', '628', '8520',]
|
||||
CROS_TPM_FIXED_VERSIONS = ['422', '62b', '8521',]
|
||||
|
||||
COLLABORATIVE_ACL_CHOICES = {
|
||||
'members': 'ALL_MEMBERS',
|
||||
'managersonly': 'MANAGERS_ONLY',
|
||||
'managers': 'OWNERS_AND_MANAGERS',
|
||||
'owners': 'OWNERS_ONLY',
|
||||
'none': 'NONE',
|
||||
}
|
||||
COLLABORATIVE_INBOX_ATTRIBUTES = [
|
||||
'whoCanAddReferences',
|
||||
'whoCanAssignTopics',
|
||||
'whoCanEnterFreeFormTags',
|
||||
'whoCanMarkDuplicate',
|
||||
'whoCanMarkFavoriteReplyOnAnyTopic',
|
||||
'whoCanMarkFavoriteReplyOnOwnTopic',
|
||||
'whoCanMarkNoResponseNeeded',
|
||||
'whoCanModifyTagsAndCategories',
|
||||
'whoCanTakeTopics',
|
||||
'whoCanUnassignTopic',
|
||||
'whoCanUnmarkFavoriteReplyOnAnyTopic',
|
||||
'favoriteRepliesOnTop',
|
||||
]
|
||||
|
||||
COLLABORATIVE_INBOX_ATTRIBUTES = {
|
||||
'whoCanAddReferences': 'acl',
|
||||
'whoCanAssignTopics': 'acl',
|
||||
'whoCanEnterFreeFormTags': 'acl',
|
||||
'whoCanMarkDuplicate': 'acl',
|
||||
'whoCanMarkFavoriteReplyOnAnyTopic': 'acl',
|
||||
'whoCanMarkFavoriteReplyOnOwnTopic': 'acl',
|
||||
'whoCanMarkNoResponseNeeded': 'acl',
|
||||
'whoCanModifyTagsAndCategories': 'acl',
|
||||
'whoCanTakeTopics': 'acl',
|
||||
'whoCanUnassignTopic': 'acl',
|
||||
'whoCanUnmarkFavoriteReplyOnAnyTopic': 'acl',
|
||||
'favoriteRepliesOnTop': True,
|
||||
}
|
||||
GROUP_SETTINGS_LIST_ATTRIBUTES = set([
|
||||
# ACL choices
|
||||
'whoCanAdd',
|
||||
'whoCanApproveMembers',
|
||||
'whoCanApproveMessages',
|
||||
'whoCanAssignTopics',
|
||||
'whoCanAssistContent',
|
||||
'whoCanBanUsers',
|
||||
'whoCanContactOwner',
|
||||
'whoCanDeleteAnyPost',
|
||||
'whoCanDeleteTopics',
|
||||
'whoCanDiscoverGroup',
|
||||
'whoCanEnterFreeFormTags',
|
||||
'whoCanHideAbuse',
|
||||
'whoCanInvite',
|
||||
'whoCanJoin',
|
||||
'whoCanLeaveGroup',
|
||||
'whoCanLockTopics',
|
||||
'whoCanMakeTopicsSticky',
|
||||
'whoCanMarkDuplicate',
|
||||
'whoCanMarkFavoriteReplyOnAnyTopic',
|
||||
'whoCanMarkFavoriteReplyOnOwnTopic',
|
||||
'whoCanMarkNoResponseNeeded',
|
||||
'whoCanModerateContent',
|
||||
'whoCanModerateMembers',
|
||||
'whoCanModifyMembers',
|
||||
'whoCanModifyTagsAndCategories',
|
||||
'whoCanMoveTopicsIn',
|
||||
'whoCanMoveTopicsOut',
|
||||
'whoCanPostAnnouncements',
|
||||
'whoCanPostMessage',
|
||||
'whoCanTakeTopics',
|
||||
'whoCanUnassignTopic',
|
||||
'whoCanUnmarkFavoriteReplyOnAnyTopic',
|
||||
'whoCanViewGroup',
|
||||
'whoCanViewMembership',
|
||||
# Miscellaneous hoices
|
||||
'messageModerationLevel',
|
||||
'replyTo',
|
||||
'spamModerationLevel',
|
||||
])
|
||||
GROUP_SETTINGS_BOOLEAN_ATTRIBUTES = set([
|
||||
'allowExternalMembers',
|
||||
'allowGoogleCommunication',
|
||||
'allowWebPosting',
|
||||
'archiveOnly',
|
||||
'enableCollaborativeInbox',
|
||||
'favoriteRepliesOnTop',
|
||||
'includeCustomFooter',
|
||||
'includeInGlobalAddressList',
|
||||
'isArchived',
|
||||
'membersCanPostAsTheGroup',
|
||||
'sendMessageDenyNotification',
|
||||
'showInGroupDirectory',
|
||||
])
|
||||
|
||||
#
|
||||
# Global variables
|
||||
@@ -709,8 +769,6 @@ GM_Globals = {
|
||||
#
|
||||
# Global variables defined by environment variables/signal files
|
||||
#
|
||||
# When retrieving lists of Google Drive activities from API, how many should be retrieved in each chunk
|
||||
GC_ACTIVITY_MAX_RESULTS = 'activity_max_results'
|
||||
# Automatically generate gam batch command if number of users specified in gam users xxx command exceeds this number
|
||||
# Default: 0, don't automatically generate gam batch commands
|
||||
GC_AUTO_BATCH_MIN = 'auto_batch_min'
|
||||
@@ -732,19 +790,15 @@ GC_CUSTOMER_ID = 'customer_id'
|
||||
GC_DEBUG_LEVEL = 'debug_level'
|
||||
# ID Token decoded from OAuth 2.0 refresh token response. Includes hd (domain) and email of authorized user
|
||||
GC_DECODED_ID_TOKEN = 'decoded_id_token'
|
||||
# When retrieving lists of ChromeOS/Mobile devices from API, how many should be retrieved in each chunk
|
||||
GC_DEVICE_MAX_RESULTS = 'device_max_results'
|
||||
# Domain obtained from gam.cfg or oauth2.txt
|
||||
GC_DOMAIN = 'domain'
|
||||
# Google Drive download directory
|
||||
GC_DRIVE_DIR = 'drive_dir'
|
||||
# When retrieving lists of Drive files/folders from API, how many should be retrieved in each chunk
|
||||
GC_DRIVE_MAX_RESULTS = 'drive_max_results'
|
||||
# When retrieving lists of Google Group members from API, how many should be retrieved in each chunk
|
||||
GC_MEMBER_MAX_RESULTS = 'member_max_results'
|
||||
# If no_browser is False, writeCSVfile won't open a browser when todrive is set
|
||||
# and doRequestOAuth prints a link and waits for the verification code when oauth2.txt is being created
|
||||
GC_NO_BROWSER = 'no_browser'
|
||||
# oauth_browser forces usage of web server OAuth flow that proved problematic.
|
||||
GC_OAUTH_BROWSER = 'oauth_browser'
|
||||
# Disable GAM API caching
|
||||
GC_NO_CACHE = 'no_cache'
|
||||
# Disable GAM update check
|
||||
@@ -763,8 +817,6 @@ GC_SHOW_COUNTS_MIN = 'show_counts_min'
|
||||
GC_SHOW_GETTINGS = 'show_gettings'
|
||||
# GAM config directory containing json discovery files
|
||||
GC_SITE_DIR = 'site_dir'
|
||||
# When retrieving lists of Users from API, how many should be retrieved in each chunk
|
||||
GC_USER_MAX_RESULTS = 'user_max_results'
|
||||
# CSV Columns GAM should show on CSV output
|
||||
GC_CSV_HEADER_FILTER = 'csv_header_filter'
|
||||
# CSV Rows GAM should filter
|
||||
@@ -778,7 +830,6 @@ GC_CA_FILE = 'ca_file'
|
||||
|
||||
tls_min = "TLSv1_2" if hasattr(ssl.SSLContext(), "minimum_version") else None
|
||||
GC_Defaults = {
|
||||
GC_ACTIVITY_MAX_RESULTS: 100,
|
||||
GC_AUTO_BATCH_MIN: 0,
|
||||
GC_BATCH_SIZE: 50,
|
||||
GC_CACHE_DIR: '',
|
||||
@@ -789,22 +840,19 @@ GC_Defaults = {
|
||||
GC_CUSTOMER_ID: MY_CUSTOMER,
|
||||
GC_DEBUG_LEVEL: 0,
|
||||
GC_DECODED_ID_TOKEN: '',
|
||||
GC_DEVICE_MAX_RESULTS: 500,
|
||||
GC_DOMAIN: '',
|
||||
GC_DRIVE_DIR: '',
|
||||
GC_DRIVE_MAX_RESULTS: 1000,
|
||||
GC_MEMBER_MAX_RESULTS: 200,
|
||||
GC_NO_BROWSER: False,
|
||||
GC_NO_CACHE: False,
|
||||
GC_NO_UPDATE_CHECK: False,
|
||||
GC_NUM_THREADS: 25,
|
||||
GC_OAUTH_BROWSER: False,
|
||||
GC_OAUTH2_TXT: _FN_OAUTH2_TXT,
|
||||
GC_OAUTH2SERVICE_JSON: _FN_OAUTH2SERVICE_JSON,
|
||||
GC_SECTION: '',
|
||||
GC_SHOW_COUNTS_MIN: 0,
|
||||
GC_SHOW_GETTINGS: True,
|
||||
GC_SITE_DIR: '',
|
||||
GC_USER_MAX_RESULTS: 500,
|
||||
GC_CSV_HEADER_FILTER: '',
|
||||
GC_CSV_ROW_FILTER: '',
|
||||
GC_TLS_MIN_VERSION: tls_min,
|
||||
@@ -829,7 +877,6 @@ GC_VAR_TYPE = 'type'
|
||||
GC_VAR_LIMITS = 'lmit'
|
||||
|
||||
GC_VAR_INFO = {
|
||||
GC_ACTIVITY_MAX_RESULTS: {GC_VAR_TYPE: GC_TYPE_INTEGER, GC_VAR_LIMITS: (1, 500)},
|
||||
GC_AUTO_BATCH_MIN: {GC_VAR_TYPE: GC_TYPE_INTEGER, GC_VAR_LIMITS: (0, None)},
|
||||
GC_BATCH_SIZE: {GC_VAR_TYPE: GC_TYPE_INTEGER, GC_VAR_LIMITS: (1, 1000)},
|
||||
GC_CACHE_DIR: {GC_VAR_TYPE: GC_TYPE_DIRECTORY},
|
||||
@@ -840,22 +887,19 @@ GC_VAR_INFO = {
|
||||
GC_CUSTOMER_ID: {GC_VAR_TYPE: GC_TYPE_STRING},
|
||||
GC_DEBUG_LEVEL: {GC_VAR_TYPE: GC_TYPE_INTEGER, GC_VAR_LIMITS: (0, None)},
|
||||
GC_DECODED_ID_TOKEN: {GC_VAR_TYPE: GC_TYPE_STRING},
|
||||
GC_DEVICE_MAX_RESULTS: {GC_VAR_TYPE: GC_TYPE_INTEGER, GC_VAR_LIMITS: (1, 1000)},
|
||||
GC_DOMAIN: {GC_VAR_TYPE: GC_TYPE_STRING},
|
||||
GC_DRIVE_DIR: {GC_VAR_TYPE: GC_TYPE_DIRECTORY},
|
||||
GC_DRIVE_MAX_RESULTS: {GC_VAR_TYPE: GC_TYPE_INTEGER, GC_VAR_LIMITS: (1, 1000)},
|
||||
GC_MEMBER_MAX_RESULTS: {GC_VAR_TYPE: GC_TYPE_INTEGER, GC_VAR_LIMITS: (1, 10000)},
|
||||
GC_NO_BROWSER: {GC_VAR_TYPE: GC_TYPE_BOOLEAN},
|
||||
GC_NO_CACHE: {GC_VAR_TYPE: GC_TYPE_BOOLEAN},
|
||||
GC_NO_UPDATE_CHECK: {GC_VAR_TYPE: GC_TYPE_BOOLEAN},
|
||||
GC_NUM_THREADS: {GC_VAR_TYPE: GC_TYPE_INTEGER, GC_VAR_LIMITS: (1, None)},
|
||||
GC_OAUTH_BROWSER: {GC_VAR_TYPE: GC_TYPE_BOOLEAN},
|
||||
GC_OAUTH2_TXT: {GC_VAR_TYPE: GC_TYPE_FILE},
|
||||
GC_OAUTH2SERVICE_JSON: {GC_VAR_TYPE: GC_TYPE_FILE},
|
||||
GC_SECTION: {GC_VAR_TYPE: GC_TYPE_STRING},
|
||||
GC_SHOW_COUNTS_MIN: {GC_VAR_TYPE: GC_TYPE_INTEGER, GC_VAR_LIMITS: (0, None)},
|
||||
GC_SHOW_GETTINGS: {GC_VAR_TYPE: GC_TYPE_BOOLEAN},
|
||||
GC_SITE_DIR: {GC_VAR_TYPE: GC_TYPE_DIRECTORY},
|
||||
GC_USER_MAX_RESULTS: {GC_VAR_TYPE: GC_TYPE_INTEGER, GC_VAR_LIMITS: (1, 500)},
|
||||
GC_CSV_HEADER_FILTER: {GC_VAR_TYPE: GC_TYPE_HEADERFILTER},
|
||||
GC_CSV_ROW_FILTER: {GC_VAR_TYPE: GC_TYPE_ROWFILTER},
|
||||
GC_TLS_MIN_VERSION: {GC_VAR_TYPE: GC_TYPE_STRING},
|
||||
@@ -1151,3 +1195,19 @@ LANGUAGE_CODES_MAP = {
|
||||
'ur': 'ur', 'uz': 'uz', 'vi': 'vi', 'wo': 'wo', 'xh': 'xh', 'yi': 'yi', 'yo': 'yo', #Urdu, Uzbek, Vietnamese, Wolof, Xhosa, Yiddish, Yoruba
|
||||
'zh-cn': 'zh-CN', 'zh-hk': 'zh-HK', 'zh-tw': 'zh-TW', 'zu': 'zu', #Chinese (Simplified), Chinese (Hong Kong/Traditional), Chinese (Taiwan/Traditional), Zulu
|
||||
}
|
||||
|
||||
# maxResults exception values for API list calls. Should only be listed if:
|
||||
# - discovery doc does not specify maximum value (we use maximum value if it
|
||||
# exists, not this)
|
||||
# - actual max API returns with maxResults=<bigNum> > default API returns
|
||||
# when maxResults isn't specified (we should use default otherwise by not
|
||||
# setting maxResults)
|
||||
|
||||
MAX_RESULTS_API_EXCEPTIONS = {
|
||||
'calendar.acl.list': 250,
|
||||
'calendar.calendarList.list': 250,
|
||||
'calendar.events.list': 2500,
|
||||
'calendar.settings.list': 250,
|
||||
'directory.chromeosdevices.list': 200,
|
||||
'drive.files.list': 1000,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user