Fix group settings (#990)

* Fix group settings

Clean up md5MatchesFile

* Simplify doGetCustomerInfo

Avoid trap when doPrintDomains returned a domain creationTime with no fraction.
Traceback (most recent call last):
  File "gam.py", line 14761, in <module>
  File "gam.py", line 14159, in ProcessGAMCommand
  File "gam.py", line 2053, in doGetDomainInfo
  File "gam.py", line 2088, in doGetCustomerInfo
  File "_strptime.py", line 577, in _strptime_datetime
  File "_strptime.py", line 359, in _strptime
ValueError: time data '2019-04-23 16:14:56' does not match format '%Y-%m-%d %H:%
M:%S.%f'

lansa.co.uk,True,2019-04-23 16:14:13.041000,secondary,
lansa.com.au,True,2019-04-23 16:14:56,secondary
This commit is contained in:
Ross Scroggs
2019-08-10 12:37:44 -07:00
committed by Jay Lee
parent 7f4f785f0b
commit 3fc2aeed4d
3 changed files with 131 additions and 79 deletions

View File

@@ -874,7 +874,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]

View File

@@ -2082,13 +2082,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 +2168,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 +2209,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():
@@ -8431,11 +8430,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 +8458,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 +8538,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 +8863,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 +8889,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))
@@ -9697,7 +9681,7 @@ 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)
else:
@@ -11639,11 +11623,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 +11643,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',

View File

@@ -11,10 +11,10 @@ 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'
@@ -157,13 +157,13 @@ API_VER_MAPPING = {
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 +403,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 +414,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 +425,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 = {
@@ -612,28 +612,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