Added input_dir directory variable to gam.cfg

Added support for the new resource calendar setting `autoAcceptInvitations`.
# 1865
This commit is contained in:
Ross Scroggs
2025-12-10 17:51:20 -08:00
parent 07b3e89809
commit 185386e5e7
6 changed files with 289 additions and 85 deletions

View File

@@ -1962,6 +1962,7 @@ gam calendar <CalendarEntity> printevents <EventSelectProperty>* <EventDisplayPr
[formatjson [quotechar <Character>]] [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]] [todrive <ToDriveAttribute>*]
<CalendarSettingsField> ::= <CalendarSettingsField> ::=
autoacceptinvitations|
conferenceproperties| conferenceproperties|
description| description|
id| id|
@@ -1971,6 +1972,7 @@ gam calendar <CalendarEntity> printevents <EventSelectProperty>* <EventDisplayPr
<CalendarSettingsFieldList> ::= "<CalendarSettingsField>(,<CalendarSettingsField>)*" <CalendarSettingsFieldList> ::= "<CalendarSettingsField>(,<CalendarSettingsField>)*"
<CalendarSettings> ::= <CalendarSettings> ::=
(autoacceptinvitations [<Boolean>])|
(description <String>)| (description <String>)|
(location <String>)| (location <String>)|
(summary <String>)| (summary <String>)|
@@ -1987,10 +1989,10 @@ gam calendar|calendars <CalendarEntity> modify <CalendarSettings>+
# Chat Bot # Chat Bot
<ChatContent> ::= <ChatContent> ::=
((text <String>)| (text <String>)|
(textfile <FileName> [charset <Charset>])| (textfile <FileName> [charset <Charset>])|
(gdoc <UserGoogleDoc>)| (gdoc <UserGoogleDoc>)|
(gcsdoc <StorageBucketObjectName>)) (gcsdoc <StorageBucketObjectName>)
<ChatMember> ::= spaces/<String>/members/<String> <ChatMember> ::= spaces/<String>/members/<String>
<ChatMessage> ::= spaces/<String>/messages/<String> <ChatMessage> ::= spaces/<String>/messages/<String>
@@ -3201,10 +3203,10 @@ gam course <CourseID> create|add alias <CourseAlias>
gam course <CourseID> delete alias <CourseAlias> gam course <CourseID> delete alias <CourseAlias>
<CourseAnnouncementContent> ::= <CourseAnnouncementContent> ::=
((text <String>)| (text <String>)|
(textfile <FileName> [charset <Charset>])| (textfile <FileName> [charset <Charset>])|
(gdoc <UserGoogleDoc>)| (gdoc <UserGoogleDoc>)|
(gcsdoc <StorageBucketObjectName>)) (gcsdoc <StorageBucketObjectName>)
gam course <CourseID> create announcement gam course <CourseID> create announcement
<CourseAnnouncementContent> [scheduledtime <Time>] [state draft|published] <CourseAnnouncementContent> [scheduledtime <Time>] [state draft|published]
@@ -3575,6 +3577,12 @@ gam show domainaliases|aliasdomains
# Domain - Contacts and Global Address List # Domain - Contacts and Global Address List
<ContactNoteContent> ::=
<String>|
(file|textfile <FileName> [charset <Charset>])|
(gdoc <UserGoogleDoc>)
(gcsdoc <StorageBucketObjectName>)
<ContactAttribute> ::= <ContactAttribute> ::=
(additionalname|middlename <String>)| (additionalname|middlename <String>)|
(address clear|(work|home|other|<String> ((formatted|unstructured <String>)|(streetaddress <String>)|(pobox <String>)| (address clear|(work|home|other|<String> ((formatted|unstructured <String>)|(streetaddress <String>)|(pobox <String>)|
@@ -3600,7 +3608,7 @@ gam show domainaliases|aliasdomains
(mileage <String>)| (mileage <String>)|
(name <String>)| (name <String>)|
(nickname <String>)| (nickname <String>)|
(note <NoteContent>)| (note <ContactNoteContent>)|
(occupation <String>)| (occupation <String>)|
(organization clear|(work|other|<String> <String> ((location <String>)|(department <String>)|(title <String>)|(jobdescription <String>)|(symbol <String>))* notprimary|primary))| (organization clear|(work|other|<String> <String> ((location <String>)|(department <String>)|(title <String>)|(jobdescription <String>)|(symbol <String>))* notprimary|primary))|
(phone clear|(work|home|other|fax|work_fax|home_fax|other_fax|main|company_main|assistant|mobile|work_mobile|pager|work_pager|car|radio|callback|isdn|telex|tty_tdd|<String> <String> notprimary|primary))| (phone clear|(work|home|other|fax|work_fax|home_fax|other_fax|main|company_main|assistant|mobile|work_mobile|pager|work_pager|car|radio|callback|isdn|telex|tty_tdd|<String> <String> notprimary|primary))|
@@ -4728,6 +4736,7 @@ gam print resoldsubscriptions [todrive <ToDriveAttribute>*]
(zipcode|postalcode <String>) (zipcode|postalcode <String>)
<ResourceAttribute> ::= <ResourceAttribute> ::=
(autoacceptinvitations [<Boolean>])|
(addfeatures <FeatureNameList>)| (addfeatures <FeatureNameList>)|
(buildingid <BuildingID>)| (buildingid <BuildingID>)|
(capacity <Number>)| (capacity <Number>)|
@@ -4743,6 +4752,7 @@ gam print resoldsubscriptions [todrive <ToDriveAttribute>*]
<ResourceFieldName> ::= <ResourceFieldName> ::=
acls| acls|
autoacceptinvitations|
buildingid| buildingid|
calendar| calendar|
capacity| capacity|
@@ -5668,6 +5678,11 @@ gam download storagefile <StorageBucketObjectName>
<UserOrderByFieldName> ::= <UserOrderByFieldName> ::=
familyname|lastname|givenname|firstname|email familyname|lastname|givenname|firstname|email
<UserNoteContent> ::=
<String>|
(file|textfile|htmlfile <FileName> [charset <Charset>])|
(gdoc|ghtml <UserGoogleDoc>)
<UserBasicAttribute> ::= <UserBasicAttribute> ::=
(archive|archived <Boolean>)| (archive|archived <Boolean>)|
(changepassword|changepasswordatnextlogin <Boolean>)| (changepassword|changepasswordatnextlogin <Boolean>)|
@@ -5681,9 +5696,7 @@ gam download storagefile <StorageBucketObjectName>
(ipwhitelisted <Boolean>)| (ipwhitelisted <Boolean>)|
(language clear|<LanguageList>)| (language clear|<LanguageList>)|
(lastname|familyname <String>)| (lastname|familyname <String>)|
(note clear|([text_html|text_plain] <String>| (note clear|([text_html|text_plain] <UserNoteContent))|
(file|htmlfile <FileName> [charset <Charset>])|
(gdoc|ghtml <UserGoogleDoc>)))|
(ou|org|orgunitpath <OrgUnitPath>|<OrgUnitID>) (ou|org|orgunitpath <OrgUnitPath>|<OrgUnitID>)
(password (random [<Integer>])|(uniquerandom [<Integer>])| (password (random [<Integer>])|(uniquerandom [<Integer>])|
blocklogin| blocklogin|
@@ -6054,6 +6067,7 @@ gam <UserTypeEntity> print backupcodes|verificationcodes [todrive <ToDriveAttrib
(summary <String>) (summary <String>)
<CalendarSettings> ::= <CalendarSettings> ::=
(autoacceptinvitations [<Boolean>])|
(description <String>)| (description <String>)|
(location <String>)| (location <String>)|
(summary <String>)| (summary <String>)|
@@ -6380,10 +6394,10 @@ gam <UserTypeEntity> print focustime|outofoffice|workinglocation
# Users - Chat # Users - Chat
<ChatContent> ::= <ChatContent> ::=
((text <String>)| (text <String>)|
(textfile <FileName> [charset <Charset>])| (textfile <FileName> [charset <Charset>])|
(gdoc <UserGoogleDoc>)| (gdoc <UserGoogleDoc>)|
(gcsdoc <StorageBucketObjectName>)) (gcsdoc <StorageBucketObjectName>)
<ChatMember> ::= spaces/<String>/members/<String> <ChatMember> ::= spaces/<String>/members/<String>
<ChatMemberList> ::= "<ChatMember>(,<ChatMember>)*" <ChatMemberList> ::= "<ChatMember>(,<ChatMember>)*"
@@ -8071,16 +8085,16 @@ gam <UserTypeEntity> check isinvitable [todrive <ToDriveAttribute>*]
# Users - Keep Notes # Users - Keep Notes
<NoteContent> ::= <KeepNoteContent> ::=
((text <String>)| (text <String>)|
(textfile <FileName> [charset <Charset>])| (textfile <FileName> [charset <Charset>])|
(gdoc <UserGoogleDoc>)| (gdoc <UserGoogleDoc>)|
(gcsdoc <StorageBucketObjectName>)| (gcsdoc <StorageBucketObjectName>)|
<JSONData>) <JSONData>
gam <UserTypeEntity> create note [title <String>] gam <UserTypeEntity> create note [title <String>]
[missingtextvalue <String>] [missingtextvalue <String>]
<NoteContent> <KeepNoteContent>
[copyacls [copyowneraswriter]] [copyacls [copyowneraswriter]]
[compact|formatjson|nodetails] [compact|formatjson|nodetails]
@@ -8251,12 +8265,17 @@ gam <UserItem> print meettranscripts <MeetConferenceName> [todrive <ToDriveAttri
# Users - Contacts and Profiles # Users - Contacts and Profiles
<BiographyContent> ::=
<String>|
(file|textfile <FileName> [charset <Charset>])|
(gdoc <UserGoogleDoc>)
<PeopleContactAttribute> ::= <PeopleContactAttribute> ::=
(additionalname|middlename <String>)| (additionalname|middlename <String>)|
(address clear|(work|home|other|<String> ((formatted|unstructured <String>)|(streetaddress <String>)|(pobox <String>)| (address clear|(work|home|other|<String> ((formatted|unstructured <String>)|(streetaddress <String>)|(pobox <String>)|
(neighborhood <String>)|(locality <String>)|(region <String>)|(postalcode <String>)|(country <String>))* notprimary|primary))| (neighborhood <String>)|(locality <String>)|(region <String>)|(postalcode <String>)|(country <String>))* notprimary|primary))|
(billinginfo <String>)| (billinginfo <String>)|
(biography|biographies <String>|(file <FileName> [charset <Charset>])|(gdoc <UserGoogleDoc>))| (biography|biographies <BiographyContent)|
(birthday <Date>)| (birthday <Date>)|
(calendar clear|(work|home|free-busy|<String> <URL> notprimary|primary))| (calendar clear|(work|home|free-busy|<String> <URL> notprimary|primary))|
(clientdata <String> <String>)| (clientdata <String> <String>)|

View File

@@ -1,3 +1,11 @@
7.30.00
Added `input_dir` directory variable to `gam.cfg` that is used to select a directory for reading files with non-absolute file names.
The default is an empty string that matches the current behavior where these files are read from the current working directory.
This will be most useful in multiple domain situations where each domain will have distinct `drive_dir` and `input_dir` values.
Added support for the new resource calendar setting `autoAcceptInvitations`.
7.29.04 7.29.04
Updated `gam delete chromepolicy chrome.users.apps.InstallType ou <OrgUnitItem> appid <AppID>` Updated `gam delete chromepolicy chrome.users.apps.InstallType ou <OrgUnitItem> appid <AppID>`

View File

@@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki
""" """
__author__ = 'GAM Team <google-apps-manager@googlegroups.com>' __author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
__version__ = '7.29.04' __version__ = '7.30.00'
__license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)' __license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
#pylint: disable=wrong-import-position #pylint: disable=wrong-import-position
@@ -1856,7 +1856,7 @@ def getStringOrFile(myarg, minLen=0, unescapeCRLF=False):
if myarg in {'file', 'textfile', 'htmlfile'}: if myarg in {'file', 'textfile', 'htmlfile'}:
filename = getString(Cmd.OB_FILE_NAME) filename = getString(Cmd.OB_FILE_NAME)
encoding = getCharSet() encoding = getCharSet()
return (readFile(filename, encoding=encoding), encoding, html) return (readFile(setFilePath(filename, GC.INPUT_DIR), encoding=encoding), encoding, html)
if myarg in {'gdoc', 'ghtml'}: if myarg in {'gdoc', 'ghtml'}:
f = getGDocData(myarg) f = getGDocData(myarg)
data = f.read() data = f.read()
@@ -2254,7 +2254,7 @@ def getJSON(deleteFields):
filename = getString(Cmd.OB_FILE_NAME) filename = getString(Cmd.OB_FILE_NAME)
encoding = getCharSet() encoding = getCharSet()
try: try:
jsonData = json.loads(readFile(filename, encoding=encoding)) jsonData = json.loads(readFile(setFilePath(filename, GC.INPUT_DIR), encoding=encoding))
except (IndexError, KeyError, SyntaxError, TypeError, ValueError) as e: except (IndexError, KeyError, SyntaxError, TypeError, ValueError) as e:
Cmd.Backup() Cmd.Backup()
usageErrorExit(Msg.JSON_ERROR.format(str(e), filename)) usageErrorExit(Msg.JSON_ERROR.format(str(e), filename))
@@ -2906,14 +2906,13 @@ def entityModifierNewValueKeyValueActionPerformed(entityValueList, modifier, new
def cleanFilename(filename): def cleanFilename(filename):
return sanitize_filename(filename, '_') return sanitize_filename(filename, '_')
def setFilePath(fileName): def setFilePath(filename, cfgDir):
if fileName.startswith('./') or fileName.startswith('.\\'): if filename.startswith('./') or filename.startswith('.\\'):
fileName = os.path.join(os.getcwd(), fileName[2:]) return os.path.join(os.getcwd(), filename[2:])
else: filename = os.path.expanduser(filename)
fileName = os.path.expanduser(fileName) if os.path.isabs(filename):
if not os.path.isabs(fileName): return filename
fileName = os.path.join(GC.Values[GC.DRIVE_DIR], fileName) return os.path.join(GC.Values[cfgDir], filename)
return fileName
def uniqueFilename(targetFolder, filetitle, overwrite, extension=None): def uniqueFilename(targetFolder, filetitle, overwrite, extension=None):
filename = filetitle filename = filetitle
@@ -3278,6 +3277,7 @@ def openCSVFileReader(filename, fieldnames=None):
getCharSet() getCharSet()
else: else:
encoding = getCharSet() encoding = getCharSet()
filename = setFilePath(filename, GC.INPUT_DIR)
f = openFile(filename, mode=DEFAULT_CSV_READ_MODE, encoding=encoding) f = openFile(filename, mode=DEFAULT_CSV_READ_MODE, encoding=encoding)
if checkArgumentPresent('warnifnodata'): if checkArgumentPresent('warnifnodata'):
loc = f.tell() loc = f.tell()
@@ -3888,7 +3888,7 @@ def SetGlobalVariables():
def _setCSVFile(fileName, mode, encoding, writeHeader, multi): def _setCSVFile(fileName, mode, encoding, writeHeader, multi):
if fileName != '-': if fileName != '-':
fileName = setFilePath(fileName) fileName = setFilePath(fileName, GC.DRIVE_DIR)
GM.Globals[GM.CSVFILE][GM.REDIRECT_NAME] = fileName GM.Globals[GM.CSVFILE][GM.REDIRECT_NAME] = fileName
GM.Globals[GM.CSVFILE][GM.REDIRECT_MODE] = mode GM.Globals[GM.CSVFILE][GM.REDIRECT_MODE] = mode
GM.Globals[GM.CSVFILE][GM.REDIRECT_ENCODING] = encoding GM.Globals[GM.CSVFILE][GM.REDIRECT_ENCODING] = encoding
@@ -3909,7 +3909,7 @@ def SetGlobalVariables():
else: else:
GM.Globals[stdtype][GM.REDIRECT_FD] = os.fdopen(os.dup(sys.stderr.fileno()), mode, encoding=GM.Globals[GM.SYS_ENCODING]) GM.Globals[stdtype][GM.REDIRECT_FD] = os.fdopen(os.dup(sys.stderr.fileno()), mode, encoding=GM.Globals[GM.SYS_ENCODING])
else: else:
fileName = setFilePath(fileName) fileName = setFilePath(fileName, GC.DRIVE_DIR)
if multi and mode == DEFAULT_FILE_WRITE_MODE: if multi and mode == DEFAULT_FILE_WRITE_MODE:
deleteFile(fileName) deleteFile(fileName)
mode = DEFAULT_FILE_APPEND_MODE mode = DEFAULT_FILE_APPEND_MODE
@@ -6815,6 +6815,7 @@ def getEntitiesFromFile(shlexSplit, returnSet=False):
filenameLower = filename.lower() filenameLower = filename.lower()
if filenameLower not in {'gcsv', 'gdoc', 'gcscsv', 'gcsdoc'}: if filenameLower not in {'gcsv', 'gdoc', 'gcscsv', 'gcsdoc'}:
encoding = getCharSet() encoding = getCharSet()
filename = setFilePath(filename, GC.INPUT_DIR)
f = openFile(filename, encoding=encoding, stripUTFBOM=True) f = openFile(filename, encoding=encoding, stripUTFBOM=True)
elif filenameLower in {'gcsv', 'gdoc'}: elif filenameLower in {'gcsv', 'gdoc'}:
f = getGDocData(filenameLower) f = getGDocData(filenameLower)
@@ -7238,7 +7239,7 @@ def checkUserSuspended(cd, user, entityType=Ent.USER, i=0, count=0):
def _addAttachmentsToMessage(message, attachments): def _addAttachmentsToMessage(message, attachments):
for attachment in attachments: for attachment in attachments:
try: try:
attachFilename = attachment[0] attachFilename = setFilePath(attachment[0], GC.INPUT_DIR)
attachContentType, attachEncoding = mimetypes.guess_type(attachFilename) attachContentType, attachEncoding = mimetypes.guess_type(attachFilename)
if attachContentType is None or attachEncoding is not None: if attachContentType is None or attachEncoding is not None:
attachContentType = 'application/octet-stream' attachContentType = 'application/octet-stream'
@@ -7263,7 +7264,7 @@ def _addAttachmentsToMessage(message, attachments):
def _addEmbeddedImagesToMessage(message, embeddedImages): def _addEmbeddedImagesToMessage(message, embeddedImages):
for embeddedImage in embeddedImages: for embeddedImage in embeddedImages:
try: try:
imageFilename = embeddedImage[0] imageFilename = setFilePath(embeddedImage[0], GC.INPUT_DIR)
imageContentType, imageEncoding = mimetypes.guess_type(imageFilename) imageContentType, imageEncoding = mimetypes.guess_type(imageFilename)
if imageContentType is None or imageEncoding is not None: if imageContentType is None or imageEncoding is not None:
imageContentType = 'application/octet-stream' imageContentType = 'application/octet-stream'
@@ -10270,6 +10271,7 @@ def doBatch(threadBatch=False):
filenameLower = filename.lower() filenameLower = filename.lower()
if filenameLower not in {'gdoc', 'gcsdoc'}: if filenameLower not in {'gdoc', 'gcsdoc'}:
encoding = getCharSet() encoding = getCharSet()
filename = setFilePath(filename, GC.INPUT_DIR)
f = openFile(filename, encoding=encoding, stripUTFBOM=True) f = openFile(filename, encoding=encoding, stripUTFBOM=True)
elif filenameLower == 'gdoc': elif filenameLower == 'gdoc':
f = getGDocData(filenameLower) f = getGDocData(filenameLower)
@@ -18500,7 +18502,7 @@ def doCheckOrgUnit():
else: else:
invalidChoiceExit(field, list(ORG_ITEMS_FIELD_MAP), True) invalidChoiceExit(field, list(ORG_ITEMS_FIELD_MAP), True)
elif myarg == 'filename': elif myarg == 'filename':
fileName = setFilePath(getString(Cmd.OB_FILE_NAME)) fileName = setFilePath(getString(Cmd.OB_FILE_NAME), GC.DRIVE_DIR)
elif myarg == 'movetoou': elif myarg == 'movetoou':
movetoouLocation = Cmd.Location() movetoouLocation = Cmd.Location()
status, moveToOrgUnitPath, _ = checkOrgUnitPathExists(cd, getOrgUnitItem()) status, moveToOrgUnitPath, _ = checkOrgUnitPathExists(cd, getOrgUnitItem())
@@ -18517,7 +18519,7 @@ def doCheckOrgUnit():
usageErrorExit(Msg.OU_AND_MOVETOOU_CANNOT_BE_IDENTICAL.format(orgUnitPath, moveToOrgUnitPath)) usageErrorExit(Msg.OU_AND_MOVETOOU_CANNOT_BE_IDENTICAL.format(orgUnitPath, moveToOrgUnitPath))
if 'subous' in fieldsList and moveToOrgUnitPathLower.startswith(orgUnitPathLower): if 'subous' in fieldsList and moveToOrgUnitPathLower.startswith(orgUnitPathLower):
usageErrorExit(Msg.OU_SUBOUS_CANNOT_BE_MOVED_TO_MOVETOOU.format(orgUnitPath, moveToOrgUnitPath)) usageErrorExit(Msg.OU_SUBOUS_CANNOT_BE_MOVED_TO_MOVETOOU.format(orgUnitPath, moveToOrgUnitPath))
fileName = setFilePath(fileName) fileName = setFilePath(fileName, GC.DRIVE_DIR)
f = openFile(fileName, DEFAULT_FILE_WRITE_MODE) f = openFile(fileName, DEFAULT_FILE_WRITE_MODE)
orgUnitItemCounts = {} orgUnitItemCounts = {}
for field in sorted(fieldsList): for field in sorted(fieldsList):
@@ -29136,7 +29138,7 @@ def doDeleteChromePolicy():
policy = result['resolvedPolicies'][0] policy = result['resolvedPolicies'][0]
if request['policyTargetKey']['targetResource'] == policy['addedSourceKey'].get('targetResource', ''): if request['policyTargetKey']['targetResource'] == policy['addedSourceKey'].get('targetResource', ''):
request['policySchema'] = 'chrome.users.apps.*' request['policySchema'] = 'chrome.users.apps.*'
except (GAPI.notFound, GAPI.permissionDenied, GAPI.invalidArgument, GAPI.serviceNotAvailable, GAPI.quotaExceeded) as e: except (GAPI.notFound, GAPI.permissionDenied, GAPI.invalidArgument, GAPI.serviceNotAvailable, GAPI.quotaExceeded):
continue continue
try: try:
callGAPI(service, function, callGAPI(service, function,
@@ -37107,6 +37109,134 @@ def _checkPoliciesWithDASA():
systemErrorExit(USAGE_ERROR_RC, systemErrorExit(USAGE_ERROR_RC,
Msg.COMMAND_NOT_COMPATIBLE_WITH_ENABLE_DASA.format(Act.ToPerform().lower(), Cmd.ARG_CIPOLICIES)) Msg.COMMAND_NOT_COMPATIBLE_WITH_ENABLE_DASA.format(Act.ToPerform().lower(), Cmd.ARG_CIPOLICIES))
def _getCIPolicyOrgUnitTarget(cd, myarg, groupEmail):
if groupEmail:
Cmd.Backup()
usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format(myarg, 'group'))
targetName, targetResource = _getOrgunitsOrgUnitIdPath(cd, getString(Cmd.OB_ORGUNIT_PATH))
return (targetName, targetResource)
def _getCIPolicyGroupTarget(cd, myarg, orgUnit):
if orgUnit:
Cmd.Backup()
usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format(myarg, 'ou|org|orgunit'))
targetName = getEmailAddress(returnUIDprefix='uid:')
targetResource = f"groups/{convertEmailAddressToUID(targetName, cd, emailType='group')}"
return (targetName, targetResource)
# gam create policy
# json <JSONData>
# [(ou|orgunit <OrgUnitItem>)|(group <GroupItem>)]
# gam update policy
# json <JSONData>
# [(ou|orgunit <OrgUnitItem>)|(group <GroupItem>)]
def doCreateUpdateCIPolicy():
_checkPoliciesWithDASA()
ci = buildGAPIObject(API.CLOUDIDENTITY_POLICY_BETA)
cd = buildGAPIObject(API.DIRECTORY)
updateCmd = Act.Get() == Act.UPDATE
groupEmail = orgUnit = None
checkArgumentPresent('json', True)
jsonData = getJSON(['type'])
if updateCmd:
pname = jsonData.pop('name', None)
else:
pname = 'New Policy'
if 'policyQuery' in jsonData:
jsonData['policyQuery'].pop('orgUnitPath', None)
jsonData['policyQuery'].pop('groupEmail', None)
jsonData['policyQuery'].pop('sortOrder', None)
if 'setting' in jsonData and 'value' in jsonData['setting']:
jsonData['setting']['value'].pop('createTime', None)
jsonData['setting']['value'].pop('updateTime', None)
while Cmd.ArgumentsRemaining():
myarg = getArgument()
if myarg in {'ou', 'org', 'orgunit'}:
orgUnit, targetResource = _getCIPolicyOrgUnitTarget(cd, myarg, groupEmail)
jsonData.setdefault('policyQuery', {})
jsonData['policyQuery'].pop('group', None)
jsonData['policyQuery']['orgUnit'] = targetResource
elif myarg == 'group':
groupEmail, targetResource = _getCIPolicyGroupTarget(cd, myarg, orgUnit)
jsonData.setdefault('policyQuery', {})
jsonData['policyQuery'].pop('orgUnit', None)
jsonData['policyQuery']['group'] = targetResource
else:
unknownArgumentExit()
jsonData['customer'] = _getCustomersCustomerIdWithC()
try:
if updateCmd:
result = callGAPI(ci.policies(), 'patch',
bailOnInternalError=True,
throwReasons=[GAPI.INVALID, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.INTERNAL_ERROR],
name=pname, body=jsonData)
else:
result = callGAPI(ci.policies(), 'create',
bailOnInternalError=True,
throwReasons=[GAPI.INVALID, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.INTERNAL_ERROR],
body=jsonData)
if result['done']:
if 'error' not in result:
if not updateCmd:
pname = result['response'].get('id', pname)
entityActionPerformed([Ent.POLICY, pname])
else:
entityActionFailedWarning([Ent.POLICY, pname], result['error']['message'])
else:
entityActionPerformedMessage([Ent.POLICY, pname], Msg.ACTION_IN_PROGRESS.format('delete'))
except (GAPI.invalid, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.internalError) as e:
entityActionFailedWarning([Ent.POLICY, pname], str(e))
# gam delete policies <CIPolicyNameEntity>
def doDeleteCIPolicies():
_checkPoliciesWithDASA()
ci = buildGAPIObject(API.CLOUDIDENTITY_POLICY_BETA)
entityList = getEntityList(Cmd.OB_CIPOLICY_NAME_ENTITY)
checkForExtraneousArguments()
i = 0
count = len(entityList)
for pname in entityList:
i += 1
if pname.startswith('policies/'):
try:
policies = [callGAPI(ci.policies(), 'get',
bailOnInternalError=True,
throwReasons=[GAPI.INVALID, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.INTERNAL_ERROR],
name=pname,
fields='name')]
except (GAPI.invalid, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.internalError) as e:
entityActionFailedWarning([Ent.POLICY, pname], str(e), i, count)
continue
else:
if pname.startswith('settings/'):
pname = pname.split('/')[1]
ifilter = f"setting.type.matches('{pname}')"
printGettingAllAccountEntities(Ent.POLICY, ifilter)
policies = _filterPolicies(ci, getPageMessage(), ifilter)
jcount = len(policies)
performActionNumItems(jcount, Ent.POLICY)
Ind.Increment()
j = 0
for policy in policies:
j += 1
pname = policy['name']
try:
result = callGAPI(ci.policies(), 'delete',
bailOnInternalError=True,
throwReasons=[GAPI.INVALID, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.INTERNAL_ERROR],
name=pname)
if result['done']:
if 'error' not in result:
entityActionPerformed([Ent.POLICY, pname], j, jcount)
else:
entityActionFailedWarning([Ent.POLICY, pname], result['error']['message'], j, jcount)
else:
entityActionPerformedMessage([Ent.POLICY, pname], Msg.ACTION_IN_PROGRESS.format('delete'), j, jcount)
except (GAPI.invalid, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.internalError) as e:
entityActionFailedWarning([Ent.POLICY, pname], str(e), j, jcount)
Ind.Decrement()
# gam info policies <CIPolicyNameEntity> # gam info policies <CIPolicyNameEntity>
# [nowarnings] [noappnames] # [nowarnings] [noappnames]
# [formatjson] # [formatjson]
@@ -38851,6 +38981,7 @@ RESOURCE_CATEGORY_MAP = {
} }
def _getResourceCalendarAttributes(cd, body, updateMode): def _getResourceCalendarAttributes(cd, body, updateMode):
autoAcceptInvitations = None
featureChanges = {'add': set(), 'remove': set()} featureChanges = {'add': set(), 'remove': set()}
while Cmd.ArgumentsRemaining(): while Cmd.ArgumentsRemaining():
myarg = getArgument() myarg = getArgument()
@@ -38887,26 +39018,45 @@ def _getResourceCalendarAttributes(cd, body, updateMode):
body['resourceCategory'] = getChoice(RESOURCE_CATEGORY_MAP, mapChoice=True) body['resourceCategory'] = getChoice(RESOURCE_CATEGORY_MAP, mapChoice=True)
elif myarg in {'userdescription', 'uservisibledescription'}: elif myarg in {'userdescription', 'uservisibledescription'}:
body['userVisibleDescription'] = getString(Cmd.OB_STRING) body['userVisibleDescription'] = getString(Cmd.OB_STRING)
elif myarg == 'autoacceptinvitations':
autoAcceptInvitations = getBoolean()
else: else:
unknownArgumentExit() unknownArgumentExit()
if ('featureInstances' in body and not body['featureInstances'] and if ('featureInstances' in body and not body['featureInstances'] and
not featureChanges['add'] and not featureChanges['remove']): not featureChanges['add'] and not featureChanges['remove']):
body['featureInstances'] = [{}] body['featureInstances'] = [{}]
if not updateMode: if not updateMode:
return body return body, autoAcceptInvitations, None
return body, featureChanges return body, autoAcceptInvitations, featureChanges
def updateAutoAcceptInvitations(cal, calId, autoAcceptInvitations, i=0, count=0):
Ind.Increment()
try:
callGAPI(cal.calendars(), 'patch',
throwReasons=GAPI.CALENDAR_THROW_REASONS+[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.INVALID],
calendarId=calId, body={'autoAcceptInvitations': autoAcceptInvitations})
entityActionPerformed([Ent.CALENDAR, calId], i, count)
except (GAPI.notFound, GAPI.forbidden, GAPI.invalid) as e:
entityActionFailedWarning([Ent.CALENDAR, calId], str(e), i, count)
except GAPI.notACalendarUser:
userCalServiceNotEnabledWarning(calId, i, count)
Ind.Decrement()
# gam create resource <ResourceID> <Name> <ResourceAttribute>* # gam create resource <ResourceID> <Name> <ResourceAttribute>*
def doCreateResourceCalendar(): def doCreateResourceCalendar():
cd = buildGAPIObject(API.DIRECTORY) cd = buildGAPIObject(API.DIRECTORY)
body = _getResourceCalendarAttributes(cd, {'resourceId': getString(Cmd.OB_RESOURCE_ID), 'resourceName': getString(Cmd.OB_NAME)}, False) body, autoAcceptInvitations, _ = _getResourceCalendarAttributes(cd, {'resourceId': getString(Cmd.OB_RESOURCE_ID), 'resourceName': getString(Cmd.OB_NAME)}, False)
if autoAcceptInvitations is not None:
cal = buildGAPIObject(API.CALENDAR)
try: try:
callGAPI(cd.resources().calendars(), 'insert', result = callGAPI(cd.resources().calendars(), 'insert',
throwReasons=[GAPI.INVALID, GAPI.INVALID_INPUT, GAPI.SERVICE_NOT_AVAILABLE, throwReasons=[GAPI.INVALID, GAPI.INVALID_INPUT, GAPI.SERVICE_NOT_AVAILABLE,
GAPI.REQUIRED, GAPI.DUPLICATE, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN], GAPI.REQUIRED, GAPI.DUPLICATE, GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.FORBIDDEN],
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
customer=GC.Values[GC.CUSTOMER_ID], body=body, fields='') customer=GC.Values[GC.CUSTOMER_ID], body=body, fields='resourceEmail')
entityActionPerformed([Ent.RESOURCE_CALENDAR, body['resourceId']]) entityActionPerformed([Ent.RESOURCE_CALENDAR, body['resourceId']])
if autoAcceptInvitations is not None:
updateAutoAcceptInvitations(cal, result['resourceEmail'], autoAcceptInvitations)
except (GAPI.invalid, GAPI.invalidInput, GAPI.serviceNotAvailable) as e: except (GAPI.invalid, GAPI.invalidInput, GAPI.serviceNotAvailable) as e:
entityActionFailedWarning([Ent.RESOURCE_CALENDAR, body['resourceId']], str(e)) entityActionFailedWarning([Ent.RESOURCE_CALENDAR, body['resourceId']], str(e))
except GAPI.required as e: except GAPI.required as e:
@@ -38924,17 +39074,19 @@ def doCreateResourceCalendar():
def _doUpdateResourceCalendars(entityList): def _doUpdateResourceCalendars(entityList):
cd = buildGAPIObject(API.DIRECTORY) cd = buildGAPIObject(API.DIRECTORY)
body, featureChanges = _getResourceCalendarAttributes(cd, {}, True) body, autoAcceptInvitations, featureChanges = _getResourceCalendarAttributes(cd, {}, True)
if autoAcceptInvitations is not None:
cal = buildGAPIObject(API.CALENDAR)
i = 0 i = 0
count = len(entityList) count = len(entityList)
for resourceId in entityList: for resourceId in entityList:
i += 1 i += 1
try: try:
if featureChanges['add'] or featureChanges['remove']: if autoAcceptInvitations is not None or featureChanges['add'] or featureChanges['remove']:
features = callGAPI(cd.resources().calendars(), 'get', result = callGAPI(cd.resources().calendars(), 'get',
throwReasons=[GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE, GAPI.FORBIDDEN], throwReasons=[GAPI.BAD_REQUEST, GAPI.RESOURCE_NOT_FOUND, GAPI.SERVICE_NOT_AVAILABLE, GAPI.FORBIDDEN],
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
customer=GC.Values[GC.CUSTOMER_ID], calendarResourceId=resourceId, fields='featureInstances(feature(name))') customer=GC.Values[GC.CUSTOMER_ID], calendarResourceId=resourceId, fields='resourceEmail,featureInstances(feature(name))')
bodyFeatures = body.pop('featureInstances', []) bodyFeatures = body.pop('featureInstances', [])
body['featureInstances'] = [] body['featureInstances'] = []
featureSet = set() featureSet = set()
@@ -38943,7 +39095,7 @@ def _doUpdateResourceCalendars(entityList):
if featureName not in featureChanges['remove'] and featureName not in featureSet: if featureName not in featureChanges['remove'] and featureName not in featureSet:
body['featureInstances'].append({'feature': {'name': featureName}}) body['featureInstances'].append({'feature': {'name': featureName}})
featureSet.add(featureName) featureSet.add(featureName)
for feature in features.get('featureInstances', []): for feature in result.get('featureInstances', []):
featureName = feature['feature']['name'] featureName = feature['feature']['name']
if featureName not in featureChanges['remove'] and featureName not in featureSet: if featureName not in featureChanges['remove'] and featureName not in featureSet:
body['featureInstances'].append({'feature': {'name': featureName}}) body['featureInstances'].append({'feature': {'name': featureName}})
@@ -38960,6 +39112,8 @@ def _doUpdateResourceCalendars(entityList):
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS, retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
customer=GC.Values[GC.CUSTOMER_ID], calendarResourceId=resourceId, body=body, fields='') customer=GC.Values[GC.CUSTOMER_ID], calendarResourceId=resourceId, body=body, fields='')
entityActionPerformed([Ent.RESOURCE_CALENDAR, resourceId], i, count) entityActionPerformed([Ent.RESOURCE_CALENDAR, resourceId], i, count)
if autoAcceptInvitations is not None:
updateAutoAcceptInvitations(cal, result['resourceEmail'], autoAcceptInvitations, i, count)
except (GAPI.invalid, GAPI.invalidInput, GAPI.serviceNotAvailable, GAPI.required) as e: except (GAPI.invalid, GAPI.invalidInput, GAPI.serviceNotAvailable, GAPI.required) as e:
entityActionFailedWarning([Ent.RESOURCE_CALENDAR, resourceId], str(e), i, count) entityActionFailedWarning([Ent.RESOURCE_CALENDAR, resourceId], str(e), i, count)
except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden): except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden):
@@ -41171,6 +41325,7 @@ def doCalendarsPrintShowEvents(calIds):
# <CalendarSettings> ::== # <CalendarSettings> ::==
# [description <String>] [location <String>] [summary <String>] [timezone <TimeZone>] # [description <String>] [location <String>] [summary <String>] [timezone <TimeZone>]
# [autoacceptinvitations [<Boolean>]]
def _getCalendarSetting(myarg, body): def _getCalendarSetting(myarg, body):
if myarg == 'description': if myarg == 'description':
body['description'] = getStringWithCRsNLs() body['description'] = getStringWithCRsNLs()
@@ -41180,6 +41335,8 @@ def _getCalendarSetting(myarg, body):
body['summary'] = getString(Cmd.OB_STRING) body['summary'] = getString(Cmd.OB_STRING)
elif myarg == 'timezone': elif myarg == 'timezone':
body['timeZone'] = getString(Cmd.OB_STRING) body['timeZone'] = getString(Cmd.OB_STRING)
elif myarg == 'autoacceptinvitations':
body['autoAcceptInvitations'] = getBoolean()
else: else:
return False return False
return True return True
@@ -41232,9 +41389,12 @@ def _showCalendarSettings(calendar, j, jcount):
Ind.Increment() Ind.Increment()
printKeyValueList(['AllowedConferenceSolutionTypes', ','.join(calendar.get('conferenceProperties', {}).get('allowedConferenceSolutionTypes', []))]) printKeyValueList(['AllowedConferenceSolutionTypes', ','.join(calendar.get('conferenceProperties', {}).get('allowedConferenceSolutionTypes', []))])
Ind.Decrement() Ind.Decrement()
if 'autoAcceptInvitations' in calendar:
printKeyValueList(['AutoAcceptInvitations', calendar['autoAcceptInvitations']])
Ind.Decrement() Ind.Decrement()
CALENDAR_SETTINGS_FIELDS_CHOICE_MAP = { CALENDAR_SETTINGS_FIELDS_CHOICE_MAP = {
'autoacceptinvitations': 'autoAcceptInvitations',
'conferenceproperties': 'conferenceProperties', 'conferenceproperties': 'conferenceProperties',
'dataowner': 'dataOwner', 'dataowner': 'dataOwner',
'description': 'description', 'description': 'description',
@@ -41720,6 +41880,26 @@ def _copyStorageObjects(objects, target_bucket, target_prefix):
ClientAPIAccessDeniedExit() ClientAPIAccessDeniedExit()
Act.Set(action) Act.Set(action)
def md5MatchesFile(filename, expected_md5, j=0, jcount=0):
action = Act.Get()
Act.Set(Act.VERIFY)
try:
f = openFile(filename, 'rb')
hash_md5 = hashlib.md5()
for chunk in iter(lambda: f.read(4096), b""):
hash_md5.update(chunk)
closeFile(f)
actual_hash = hash_md5.hexdigest()
if actual_hash == expected_md5:
entityActionPerformed([Ent.FILE, filename, Ent.MD5HASH, expected_md5], j, jcount)
Act.Set(action)
return True
entityActionFailedWarning([Ent.FILE, filename, Ent.MD5HASH, expected_md5], Msg.DOES_NOT_MATCH.format(actual_hash), j, jcount)
Act.Set(action)
return False
except IOError as e:
systemErrorExit(FILE_ERROR_RC, fileErrorMessage(filename, e))
def _getCloudStorageObject(s, bucket, s_object, localFilename, expectedMd5=None, zipToStdout=False, j=0, jcount=0): def _getCloudStorageObject(s, bucket, s_object, localFilename, expectedMd5=None, zipToStdout=False, j=0, jcount=0):
if not zipToStdout: if not zipToStdout:
localFilename = cleanFilepath(localFilename) localFilename = cleanFilepath(localFilename)
@@ -42550,26 +42730,6 @@ def doPrintShowVaultExports():
if csvPF: if csvPF:
csvPF.writeCSVfile('Vault Exports') csvPF.writeCSVfile('Vault Exports')
def md5MatchesFile(filename, expected_md5, j=0, jcount=0):
action = Act.Get()
Act.Set(Act.VERIFY)
try:
f = openFile(filename, 'rb')
hash_md5 = hashlib.md5()
for chunk in iter(lambda: f.read(4096), b""):
hash_md5.update(chunk)
closeFile(f)
actual_hash = hash_md5.hexdigest()
if actual_hash == expected_md5:
entityActionPerformed([Ent.FILE, filename, Ent.MD5HASH, expected_md5], j, jcount)
Act.Set(action)
return True
entityActionFailedWarning([Ent.FILE, filename, Ent.MD5HASH, expected_md5], Msg.DOES_NOT_MATCH.format(actual_hash), j, jcount)
Act.Set(action)
return False
except IOError as e:
systemErrorExit(FILE_ERROR_RC, fileErrorMessage(filename, e))
# gam copy vaultexport|export <ExportItem> matter <MatterItem> # gam copy vaultexport|export <ExportItem> matter <MatterItem>
# [targetbucket <String>] [targetprefix <String>] # [targetbucket <String>] [targetprefix <String>]
# [bucketmatchpattern <REMatchPattern>] [objectmatchpattern <REMatchPattern>] # [bucketmatchpattern <REMatchPattern>] [objectmatchpattern <REMatchPattern>]
@@ -47372,7 +47532,7 @@ def doCreateInboundSSOCredential():
if not profile: if not profile:
return return
elif myarg == 'pemfile': elif myarg == 'pemfile':
pemData = readFile(getString(Cmd.OB_FILE_NAME)) pemData = readFile(setFilePath(getString(Cmd.OB_FILE_NAME), GC.INPUT_DIR))
elif myarg == 'generatekey': elif myarg == 'generatekey':
generateKey = True generateKey = True
elif myarg == 'replaceoldest': elif myarg == 'replaceoldest':
@@ -73211,6 +73371,7 @@ def _draftImportInsertMessage(users, operation):
filename = getString(Cmd.OB_FILE_NAME) filename = getString(Cmd.OB_FILE_NAME)
if checkArgumentPresent('charset'): if checkArgumentPresent('charset'):
emlEncoding = getString(Cmd.OB_CHAR_SET) emlEncoding = getString(Cmd.OB_CHAR_SET)
filename = setFilePath(filename, GC.INPUT_DIR)
msgText = readFile(filename, encoding=emlEncoding) msgText = readFile(filename, encoding=emlEncoding)
emlFile = True emlFile = True
internalDateSource = 'dateHeader' internalDateSource = 'dateHeader'
@@ -76042,8 +76203,7 @@ def createSmime(users):
while Cmd.ArgumentsRemaining(): while Cmd.ArgumentsRemaining():
myarg = getArgument() myarg = getArgument()
if myarg == 'file': if myarg == 'file':
smimefile = getString(Cmd.OB_FILE_NAME) smimeData = readFile(setFilePath(getString(Cmd.OB_FILE_NAME), GC.INPUT_DIR), mode='rb')
smimeData = readFile(smimefile, mode='rb')
body['pkcs12'] = base64.urlsafe_b64encode(smimeData).decode(UTF8) body['pkcs12'] = base64.urlsafe_b64encode(smimeData).decode(UTF8)
elif myarg == 'password': elif myarg == 'password':
body['encryptedKeyPassword'] = getString(Cmd.OB_PASSWORD) body['encryptedKeyPassword'] = getString(Cmd.OB_PASSWORD)
@@ -78124,7 +78284,7 @@ def importTasklist(users):
filename = getString(Cmd.OB_FILE_NAME) filename = getString(Cmd.OB_FILE_NAME)
encoding = getCharSet() encoding = getCharSet()
try: try:
jsonData = json.loads(readFile(filename, encoding=encoding)) jsonData = json.loads(readFile(setFilePath(filename, GC.INPUT_DIR), encoding=encoding))
except (IndexError, KeyError, SyntaxError, TypeError, ValueError) as e: except (IndexError, KeyError, SyntaxError, TypeError, ValueError) as e:
Cmd.Backup() Cmd.Backup()
usageErrorExit(Msg.JSON_ERROR.format(str(e), filename)) usageErrorExit(Msg.JSON_ERROR.format(str(e), filename))
@@ -78648,6 +78808,7 @@ MAIN_ADD_CREATE_FUNCTIONS = {
Cmd.ARG_CHROMEPOLICYIMAGE: doCreateChromePolicyImage, Cmd.ARG_CHROMEPOLICYIMAGE: doCreateChromePolicyImage,
Cmd.ARG_CHROMEPROFILECOMMAND: doCreateChromeProfileCommand, Cmd.ARG_CHROMEPROFILECOMMAND: doCreateChromeProfileCommand,
Cmd.ARG_CIGROUP: doCreateCIGroup, Cmd.ARG_CIGROUP: doCreateCIGroup,
Cmd.ARG_CIPOLICY: doCreateUpdateCIPolicy,
Cmd.ARG_CONTACT: doCreateDomainContact, Cmd.ARG_CONTACT: doCreateDomainContact,
Cmd.ARG_COURSE: doCreateCourse, Cmd.ARG_COURSE: doCreateCourse,
Cmd.ARG_COURSESTUDENTGROUP: doCreateCourseStudentGroups, Cmd.ARG_COURSESTUDENTGROUP: doCreateCourseStudentGroups,
@@ -78768,6 +78929,7 @@ MAIN_COMMANDS_WITH_OBJECTS = {
Cmd.ARG_CHROMEPOLICY: doDeleteChromePolicy, Cmd.ARG_CHROMEPOLICY: doDeleteChromePolicy,
Cmd.ARG_CHROMEPROFILE: doDeleteChromeProfile, Cmd.ARG_CHROMEPROFILE: doDeleteChromeProfile,
Cmd.ARG_CIGROUP: doDeleteCIGroups, Cmd.ARG_CIGROUP: doDeleteCIGroups,
Cmd.ARG_CIPOLICY: doDeleteCIPolicies,
Cmd.ARG_CLASSROOMINVITATION: doDeleteClassroomInvitations, Cmd.ARG_CLASSROOMINVITATION: doDeleteClassroomInvitations,
Cmd.ARG_CONTACT: doDeleteDomainContacts, Cmd.ARG_CONTACT: doDeleteDomainContacts,
Cmd.ARG_CONTACTPHOTO: doDeleteDomainContactPhoto, Cmd.ARG_CONTACTPHOTO: doDeleteDomainContactPhoto,
@@ -79161,6 +79323,7 @@ MAIN_COMMANDS_WITH_OBJECTS = {
Cmd.ARG_CHATMESSAGE: doUpdateChatMessage, Cmd.ARG_CHATMESSAGE: doUpdateChatMessage,
Cmd.ARG_CHROMEPOLICY: doUpdateChromePolicy, Cmd.ARG_CHROMEPOLICY: doUpdateChromePolicy,
Cmd.ARG_CIGROUP: doUpdateCIGroups, Cmd.ARG_CIGROUP: doUpdateCIGroups,
Cmd.ARG_CIPOLICY: doCreateUpdateCIPolicy,
Cmd.ARG_CONTACT: doUpdateDomainContacts, Cmd.ARG_CONTACT: doUpdateDomainContacts,
Cmd.ARG_CONTACTPHOTO: doUpdateDomainContactPhoto, Cmd.ARG_CONTACTPHOTO: doUpdateDomainContactPhoto,
Cmd.ARG_COURSE: doUpdateCourse, Cmd.ARG_COURSE: doUpdateCourse,

View File

@@ -50,6 +50,7 @@ CLOUDIDENTITY_INBOUND_SSO = 'cloudidentityinboundsso'
CLOUDIDENTITY_ORGUNITS = 'cloudidentityorgunits' CLOUDIDENTITY_ORGUNITS = 'cloudidentityorgunits'
CLOUDIDENTITY_ORGUNITS_BETA = 'cloudidentityorgunitsbeta' CLOUDIDENTITY_ORGUNITS_BETA = 'cloudidentityorgunitsbeta'
CLOUDIDENTITY_POLICY = 'cloudidentitypolicy' CLOUDIDENTITY_POLICY = 'cloudidentitypolicy'
CLOUDIDENTITY_POLICY_BETA = 'cloudidentitypolicybeta'
CLOUDIDENTITY_USERINVITATIONS = 'cloudidentityuserinvitations' CLOUDIDENTITY_USERINVITATIONS = 'cloudidentityuserinvitations'
CLOUDRESOURCEMANAGER = 'cloudresourcemanager' CLOUDRESOURCEMANAGER = 'cloudresourcemanager'
CONTACTS = 'contacts' CONTACTS = 'contacts'
@@ -245,6 +246,7 @@ _INFO = {
CLOUDIDENTITY_ORGUNITS: {'name': 'Cloud Identity API - OrgUnits', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'}, CLOUDIDENTITY_ORGUNITS: {'name': 'Cloud Identity API - OrgUnits', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
CLOUDIDENTITY_ORGUNITS_BETA: {'name': 'Cloud Identity API - OrgUnits Beta', 'version': 'v1beta1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'}, CLOUDIDENTITY_ORGUNITS_BETA: {'name': 'Cloud Identity API - OrgUnits Beta', 'version': 'v1beta1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
CLOUDIDENTITY_POLICY: {'name': 'Cloud Identity API - Policy', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'}, CLOUDIDENTITY_POLICY: {'name': 'Cloud Identity API - Policy', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
CLOUDIDENTITY_POLICY_BETA: {'name': 'Cloud Identity API - Policy Beta', 'version': 'v1beta1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
CLOUDIDENTITY_USERINVITATIONS: {'name': 'Cloud Identity API - User Invitations', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'}, CLOUDIDENTITY_USERINVITATIONS: {'name': 'Cloud Identity API - User Invitations', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
CLOUDRESOURCEMANAGER: {'name': 'Cloud Resource Manager API v3', 'version': 'v3', 'v2discovery': True}, CLOUDRESOURCEMANAGER: {'name': 'Cloud Resource Manager API v3', 'version': 'v3', 'v2discovery': True},
CONTACTS: {'name': 'Contacts API', 'version': 'v3', 'v2discovery': False}, CONTACTS: {'name': 'Contacts API', 'version': 'v3', 'v2discovery': False},
@@ -398,6 +400,11 @@ _CLIENT_SCOPES = [
'subscopes': READONLY, 'subscopes': READONLY,
'roByDefault': True, 'roByDefault': True,
'scope': 'https://www.googleapis.com/auth/cloud-identity.policies'}, 'scope': 'https://www.googleapis.com/auth/cloud-identity.policies'},
{'name': 'Cloud Identity API - Policy Beta',
'api': CLOUDIDENTITY_POLICY_BETA,
'subscopes': [],
'offByDefault': True,
'scope': 'https://www.googleapis.com/auth/cloud-identity.policies'},
{'name': 'Cloud Identity API - User Invitations', {'name': 'Cloud Identity API - User Invitations',
'api': CLOUDIDENTITY_USERINVITATIONS, 'api': CLOUDIDENTITY_USERINVITATIONS,
'subscopes': READONLY, 'subscopes': READONLY,

View File

@@ -157,7 +157,7 @@ DEVELOPER_PREVIEW_API_KEY = 'developer_preview_api_key'
DEVICE_MAX_RESULTS = 'device_max_results' DEVICE_MAX_RESULTS = 'device_max_results'
# Domain obtained from gam.cfg or oauth2.txt # Domain obtained from gam.cfg or oauth2.txt
DOMAIN = 'domain' DOMAIN = 'domain'
# Google Drive download directory # directory for file output
DRIVE_DIR = 'drive_dir' DRIVE_DIR = 'drive_dir'
# When retrieving lists of Drive files/folders from API, how many should be retrieved in each chunk # When retrieving lists of Drive files/folders from API, how many should be retrieved in each chunk
DRIVE_MAX_RESULTS = 'drive_max_results' DRIVE_MAX_RESULTS = 'drive_max_results'
@@ -177,6 +177,8 @@ EXTRA_ARGS = 'extra_args'
GMAIL_CSE_INCERT_DIR = 'gmail_cse_incert_dir' GMAIL_CSE_INCERT_DIR = 'gmail_cse_incert_dir'
# Gmail CSE KACL wrapped key files # Gmail CSE KACL wrapped key files
GMAIL_CSE_INKEY_DIR = 'gmail_cse_inkey_dir' GMAIL_CSE_INKEY_DIR = 'gmail_cse_inkey_dir'
# directory for file input
INPUT_DIR = 'input_dir'
# When processing items in batches, how many seconds should GAM wait between batches # When processing items in batches, how many seconds should GAM wait between batches
INTER_BATCH_WAIT = 'inter_batch_wait' INTER_BATCH_WAIT = 'inter_batch_wait'
# When retrieving lists of licenses from API, how many should be retrieved in each chunk # When retrieving lists of licenses from API, how many should be retrieved in each chunk
@@ -394,6 +396,7 @@ Defaults = {
EXTRA_ARGS: '', EXTRA_ARGS: '',
GMAIL_CSE_INCERT_DIR: '', GMAIL_CSE_INCERT_DIR: '',
GMAIL_CSE_INKEY_DIR: '', GMAIL_CSE_INKEY_DIR: '',
INPUT_DIR: '',
INTER_BATCH_WAIT: '0', INTER_BATCH_WAIT: '0',
LICENSE_MAX_RESULTS: '100', LICENSE_MAX_RESULTS: '100',
LICENSE_SKUS: '', LICENSE_SKUS: '',
@@ -564,6 +567,7 @@ VAR_INFO = {
EXTRA_ARGS: {VAR_TYPE: TYPE_FILE, VAR_SIGFILE: FN_EXTRA_ARGS_TXT, VAR_SFFT: ('', FN_EXTRA_ARGS_TXT), VAR_ACCESS: os.R_OK}, EXTRA_ARGS: {VAR_TYPE: TYPE_FILE, VAR_SIGFILE: FN_EXTRA_ARGS_TXT, VAR_SFFT: ('', FN_EXTRA_ARGS_TXT), VAR_ACCESS: os.R_OK},
GMAIL_CSE_INCERT_DIR: {VAR_TYPE: TYPE_DIRECTORY}, GMAIL_CSE_INCERT_DIR: {VAR_TYPE: TYPE_DIRECTORY},
GMAIL_CSE_INKEY_DIR: {VAR_TYPE: TYPE_DIRECTORY}, GMAIL_CSE_INKEY_DIR: {VAR_TYPE: TYPE_DIRECTORY},
INPUT_DIR: {VAR_TYPE: TYPE_DIRECTORY},
INTER_BATCH_WAIT: {VAR_TYPE: TYPE_FLOAT, VAR_LIMITS: (0.0, 60.0)}, INTER_BATCH_WAIT: {VAR_TYPE: TYPE_FLOAT, VAR_LIMITS: (0.0, 60.0)},
LICENSE_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (10, 1000)}, LICENSE_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (10, 1000)},
LICENSE_SKUS: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)}, LICENSE_SKUS: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},

View File

@@ -231,6 +231,11 @@ When specifying a `<UserMultiAttribute>` you have to specify all instances; ther
You can remove all instances of a `<UserMultiAttribute>` with `<UserClearAttribute>`. You can remove all instances of a `<UserMultiAttribute>` with `<UserClearAttribute>`.
``` ```
<UserNoteContent> ::=
<String>|
(file|textfile|htmlfile <FileName> [charset <Charset>])|
(gdoc|ghtml <UserGoogleDoc>)
<UserBasicAttribute> ::= <UserBasicAttribute> ::=
(archive|archived <Boolean>)| (archive|archived <Boolean>)|
(changepassword|changepasswordatnextlogin <Boolean>)| (changepassword|changepasswordatnextlogin <Boolean>)|
@@ -244,9 +249,7 @@ You can remove all instances of a `<UserMultiAttribute>` with `<UserClearAttribu
(ipwhitelisted <Boolean>)| (ipwhitelisted <Boolean>)|
(language clear|<LanguageList>)| (language clear|<LanguageList>)|
(lastname|familyname <String>)| (lastname|familyname <String>)|
(note clear|([text_html|text_plain] <String>| (note clear|([text_html|text_plain] <UserNoteContent>))|
(file|htmlfile <FileName> [charset <Charset>])|
(gdoc|ghtml <UserGoogleDoc>)))|
(org|ou|orgunitpath <OrgUnitPath>|<OrgUnitID>) (org|ou|orgunitpath <OrgUnitPath>|<OrgUnitID>)
(password (random [<Integer>])|(uniquerandom [<Integer>])| (password (random [<Integer>])|(uniquerandom [<Integer>])|
blocklogin| blocklogin|