Enabled support for Limited Access

This commit is contained in:
Ross Scroggs
2025-02-19 18:59:37 -08:00
parent 03148a6ae8
commit 80933755c4
3 changed files with 135 additions and 45 deletions

View File

@@ -3846,6 +3846,7 @@ gam info group|groups <GroupEntity>
[formatjson] [formatjson]
gam print groups [todrive <ToDriveAttribute>*] gam print groups [todrive <ToDriveAttribute>*]
[([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryGroupList>)]))| [([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryGroupList>)]))|
(group|group_ns|group_susp <GroupItem>)|
(select <GroupEntity>)] (select <GroupEntity>)]
[emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>] [emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
[descriptionmatchpattern [not] <RegularExpression>] (matchsetting [not] <GroupAttribute>)* [descriptionmatchpattern [not] <RegularExpression>] (matchsetting [not] <GroupAttribute>)*
@@ -4963,6 +4964,7 @@ gam create|add permissions <SharedDriveEntityAdmin> <DriveFilePermissionEntity>
<PermissionMatch>* [<PermissionMatchAction>] <PermissionMatch>* [<PermissionMatchAction>]
gam delete permissions <SharedDriveEntityAdmin> <DriveFilePermissionIDEntity> gam delete permissions <SharedDriveEntityAdmin> <DriveFilePermissionIDEntity>
<PermissionMatch>* [<PermissionMatchAction>] <PermissionMatch>* [<PermissionMatchAction>]
[enforceexpansiveaccess [<Boolean>]]
In these commands, you specify an administrator and then indicate that you want domain administrator access with the adminaccess option. In these commands, you specify an administrator and then indicate that you want domain administrator access with the adminaccess option.
@@ -4976,9 +4978,11 @@ gam <UserTypeEntity> create|add drivefileacl <SharedDriveEntityAdmin>
adminaccess adminaccess
gam <UserTypeEntity> update drivefileacl <SharedDriveEntityAdmin> <DriveFilePermissionIDorEmail> gam <UserTypeEntity> update drivefileacl <SharedDriveEntityAdmin> <DriveFilePermissionIDorEmail>
(role <DriveFileACLRole>) [expires|expiration <Time>] [removeexpiration [<Boolean>]] (role <DriveFileACLRole>) [expires|expiration <Time>] [removeexpiration [<Boolean>]]
[enforceexpansiveaccess [<Boolean>]]
[showtitles] [nodetails|(csv [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]])] [showtitles] [nodetails|(csv [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]])]
adminaccess adminaccess
gam <UserTypeEntity> delete drivefileacl <SharedDriveEntityAdmin> <DriveFilePermissionIDorEmail> gam <UserTypeEntity> delete drivefileacl <SharedDriveEntityAdmin> <DriveFilePermissionIDorEmail>
[enforceexpansiveaccess [<Boolean>]]
[showtitles] adminaccess [showtitles] adminaccess
gam <UserTypeEntity> info drivefileacl <SharedDriveEntityAdmin> <DriveFilePermissionIDorEmail> adminaccess gam <UserTypeEntity> info drivefileacl <SharedDriveEntityAdmin> <DriveFilePermissionIDorEmail> adminaccess
[showtitles] [showtitles]
@@ -5004,6 +5008,7 @@ gam <UserTypeEntity> create|add permissions <SharedDriveEntityAdmin> <DriveFileP
<PermissionMatch>* [<PermissionMatchAction>] <PermissionMatch>* [<PermissionMatchAction>]
gam <UserTypeEntity> delete permissions <SharedDriveEntityAdmin> <DriveFilePermissionIDEntity> adminaccess gam <UserTypeEntity> delete permissions <SharedDriveEntityAdmin> <DriveFilePermissionIDEntity> adminaccess
<PermissionMatch>* [<PermissionMatchAction>] <PermissionMatch>* [<PermissionMatchAction>]
[enforceexpansiveaccess [<Boolean>]]
In these commands, the Google administrator named in oauth2.txt is used. In these commands, the Google administrator named in oauth2.txt is used.
@@ -6566,6 +6571,7 @@ gam <UserTypeEntity> copy drivefile <DriveFileEntity>
[sendemailifrequired [<Boolean>]] [sendemailifrequired [<Boolean>]]
[suppressnotselectedmessages [<Boolean>]] [suppressnotselectedmessages [<Boolean>]]
[verifyorganizer [<Boolean>]] [verifyorganizer [<Boolean>]]
[enforceexpansiveaccess [<Boolean>]]
gam <UserTypeEntity> move drivefile <DriveFileEntity> [newfilename <DriveFileName>] gam <UserTypeEntity> move drivefile <DriveFileEntity> [newfilename <DriveFileName>]
[summary [<Boolean>]] [showpermissionmessages [<Boolean>]] [summary [<Boolean>]] [showpermissionmessages [<Boolean>]]
@@ -6589,6 +6595,7 @@ gam <UserTypeEntity> move drivefile <DriveFileEntity> [newfilename <DriveFileNam
[retainsourcefolders [<Boolean>]] [retainsourcefolders [<Boolean>]]
[sendemailifrequired [<Boolean>]] [sendemailifrequired [<Boolean>]]
[verifyorganizer [<Boolean>]] [verifyorganizer [<Boolean>]]
[enforceexpansiveaccess [<Boolean>]]
gam <UserTypeEntity> get document <DriveFileEntity> gam <UserTypeEntity> get document <DriveFileEntity>
[viewmode default|suggestions_inline|preview_suggestions_accepted|preview_without_suggestions] [viewmode default|suggestions_inline|preview_suggestions_accepted|preview_without_suggestions]
@@ -6694,10 +6701,10 @@ gam <UserTypeEntity> create|add drivefileacl <DriveFileEntity> [adminaccess|asad
[showtitles] [nodetails|(csv [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]])] [showtitles] [nodetails|(csv [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]])]
gam <UserTypeEntity> update drivefileacl <DriveFileEntity> <DriveFilePermissionIDorEmail> gam <UserTypeEntity> update drivefileacl <DriveFileEntity> <DriveFilePermissionIDorEmail>
(role <DriveFileACLRole>) [expires|expiration <Time>] [removeexpiration [<Boolean>]] (role <DriveFileACLRole>) [expires|expiration <Time>] [removeexpiration [<Boolean>]]
[updatesheetprotectedranges [<Boolean>]] [updatesheetprotectedranges [<Boolean>]] [enforceexpansiveaccess [<Boolean>]]
[showtitles] [nodetails|(csv [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]])] [showtitles] [nodetails|(csv [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]])]
gam <UserTypeEntity> delete drivefileacl <DriveFileEntity> <DriveFilePermissionIDorEmail> gam <UserTypeEntity> delete drivefileacl <DriveFileEntity> <DriveFilePermissionIDorEmail>
[updatesheetprotectedranges [<Boolean>]] [updatesheetprotectedranges [<Boolean>]] [enforceexpansiveaccess [<Boolean>]]
[showtitles] [showtitles]
gam <UserTypeEntity> info drivefileacl <DriveFileEntity> <DriveFilePermissionIDorEmail> gam <UserTypeEntity> info drivefileacl <DriveFileEntity> <DriveFilePermissionIDorEmail>
[showtitles] [showtitles]
@@ -6821,6 +6828,7 @@ gam <UserTypeEntity> print filerevisions <DriveFileEntity> [todrive <ToDriveAttr
gam <UserTypeEntity> transfer ownership <DriveFileEntity> <UserItem> gam <UserTypeEntity> transfer ownership <DriveFileEntity> <UserItem>
[<DriveFileParentAttribute>] [includetrashed] [norecursion [<Boolean>]] [<DriveFileParentAttribute>] [includetrashed] [norecursion [<Boolean>]]
[enforceexpansiveaccess [<Boolean>]]
(orderby <DriveFileOrderByFieldName> [ascending|descending])* (orderby <DriveFileOrderByFieldName> [ascending|descending])*
[preview] [filepath] [pathdelimiter <Character>] [buildtree] [preview] [filepath] [pathdelimiter <Character>] [buildtree]
[todrive <ToDriveAttribute>*] [todrive <ToDriveAttribute>*]
@@ -6829,6 +6837,7 @@ gam <UserTypeEntity> claim ownership <DriveFileEntity>
[skipids <DriveFileEntity>] [onlyusers|skipusers <UserTypeEntity>] [subdomains <DomainNameEntity>] [skipids <DriveFileEntity>] [onlyusers|skipusers <UserTypeEntity>] [subdomains <DomainNameEntity>]
[restricted [<Boolean>]] [writerscanshare|writerscantshare [<Boolean>]] [restricted [<Boolean>]] [writerscanshare|writerscantshare [<Boolean>]]
[keepuser | (retainrole commenter|reader|writer|editor|fileorganizer|none)] [noretentionmessages] [keepuser | (retainrole commenter|reader|writer|editor|fileorganizer|none)] [noretentionmessages]
[enforceexpansiveaccess [<Boolean>]]
(orderby <DriveFileOrderByFieldName> [ascending|descending])* (orderby <DriveFileOrderByFieldName> [ascending|descending])*
[preview] [filepath] [pathdelimiter <Character>] [buildtree] [preview] [filepath] [pathdelimiter <Character>] [buildtree]
[todrive <ToDriveAttribute>*] [todrive <ToDriveAttribute>*]
@@ -6836,6 +6845,7 @@ gam <UserTypeEntity> claim ownership <DriveFileEntity>
gam <UserTypeEntity> transfer drive <UserItem> [select <DriveFileEntity>] gam <UserTypeEntity> transfer drive <UserItem> [select <DriveFileEntity>]
[(targetfolderid <DriveFolderID>)|(targetfoldername <DriveFolderName>)] [(targetfolderid <DriveFolderID>)|(targetfoldername <DriveFolderName>)]
[targetuserfoldername <DriveFolderName>] [targetuserorphansfoldername <DriveFolderName>] [targetuserfoldername <DriveFolderName>] [targetuserorphansfoldername <DriveFolderName>]
[enforceexpansiveaccess [<Boolean>]]
[mergewithtarget [<Boolean>]] [mergewithtarget [<Boolean>]]
[skipids <DriveFileEntity>] [skipids <DriveFileEntity>]
[keepuser | (retainrole reader|commenter|writer|editor|fileorganizer|none)] [noretentionmessages] [keepuser | (retainrole reader|commenter|writer|editor|fileorganizer|none)] [noretentionmessages]
@@ -7888,7 +7898,6 @@ gam <UserTypeEntity> show lookerstudiopermissions
reactionrestriction hostsonly|norestriction | reactionrestriction hostsonly|norestriction |
presentrestriction hostsonly|norestriction | presentrestriction hostsonly|norestriction |
defaultjoinasviewer <Boolean> | defaultjoinasviewer <Boolean> |
firstjoiner hostsonly|anyone |
recording <Boolean> | recording <Boolean> |
transcription <Boolean> | transcription <Boolean> |
smartnotes <Boolean> smartnotes <Boolean>

View File

@@ -1,3 +1,31 @@
7.05.00
Enabled support for Limited Access as described here:
* https://workspaceupdates.googleblog.com/2025/02/updating-access-experience-in-google-drive.html
Note that the rollout may take 15 days.
Added option `inheritedpermissionsdisabled [<Boolean>]` to `<DriveFileAttribute>`; this
attribute can be set on folders.
Added `inheritedpermissionsdisabled` to `<DriveFieldName>`.
Added `capabilities.candisableinheritedpermissions` and `capabilities.canenableinheritedpermissions`
to `<DriveCapabilitiesSubfieldName>`.
Added option `enforceexpansiveaccess [<Boolean>]` to all commands that delete or update
drive file ACLs/permissions.
```
gam <UserTypeEntity> delete permissions
gam <UserTypeEntity> delete drivefileacl
gam <UserTypeEntity> update drivefileacl
gam <UserTypeEntity> copy drivefile
gam <UserTypeEntity> move drivefile
gam <UserTypeEntity> transfer ownership
gam <UserTypeEntity> claim ownership
gam <UserTypeEntity> transfer drive
```
7.04.05 7.04.05
Added initial support for Meet API v2beta; you must be in the Developer Preview program Added initial support for Meet API v2beta; you must be in the Developer Preview program
@@ -12,7 +40,6 @@ the following options are added to `<MeetSpaceOptions>` used in `gam <UserTypeEn
reactionrestriction hostsonly|norestriction | reactionrestriction hostsonly|norestriction |
presentrestriction hostsonly|norestriction | presentrestriction hostsonly|norestriction |
defaultjoinasviewer <Boolean> | defaultjoinasviewer <Boolean> |
firstjoiner hostsonly|anyone |
recording <Boolean> | recording <Boolean> |
transcription <Boolean> | transcription <Boolean> |
smartnotes <Boolean> smartnotes <Boolean>

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.04.05' __version__ = '7.05.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
@@ -4710,7 +4710,7 @@ def getAPIService(api, httpObj):
def getService(api, httpObj): def getService(api, httpObj):
### Drive v3beta ### Drive v3beta
mapDriveURL = api == API.DRIVE3 and GC.Values[GC.DRIVE_V3_BETA] # mapDriveURL = api == API.DRIVE3 and GC.Values[GC.DRIVE_V3_BETA]
hasLocalJSON = API.hasLocalJSON(api) hasLocalJSON = API.hasLocalJSON(api)
api, version, v2discovery = API.getVersion(api) api, version, v2discovery = API.getVersion(api)
if api in GM.Globals[GM.CURRENT_API_SERVICES] and version in GM.Globals[GM.CURRENT_API_SERVICES][api]: if api in GM.Globals[GM.CURRENT_API_SERVICES] and version in GM.Globals[GM.CURRENT_API_SERVICES][api]:
@@ -4727,8 +4727,8 @@ def getService(api, httpObj):
GM.Globals[GM.CURRENT_API_SERVICES].setdefault(api, {}) GM.Globals[GM.CURRENT_API_SERVICES].setdefault(api, {})
GM.Globals[GM.CURRENT_API_SERVICES][api][version] = service._rootDesc.copy() GM.Globals[GM.CURRENT_API_SERVICES][api][version] = service._rootDesc.copy()
### Drive v3beta ### Drive v3beta
if mapDriveURL: # if mapDriveURL:
setattr(service, '_baseUrl', getattr(service, '_baseUrl').replace('/v3/', '/v3beta/')) # setattr(service, '_baseUrl', getattr(service, '_baseUrl').replace('/v3/', '/v3beta/'))
if GM.Globals[GM.CACHE_DISCOVERY_ONLY]: if GM.Globals[GM.CACHE_DISCOVERY_ONLY]:
clearServiceCache(service) clearServiceCache(service)
return service return service
@@ -9842,7 +9842,7 @@ def MultiprocessGAMCommands(items, showCmds):
if GM.Globals[GM.MULTIPROCESS_EXIT_CONDITION] is not None and checkChildProcessRC(result[1]): if GM.Globals[GM.MULTIPROCESS_EXIT_CONDITION] is not None and checkChildProcessRC(result[1]):
GM.Globals[GM.MULTIPROCESS_EXIT_PROCESSING] = True GM.Globals[GM.MULTIPROCESS_EXIT_PROCESSING] = True
def signal_handler(sig, frame): def signal_handler(_, _):
nonlocal controlC nonlocal controlC
controlC = True controlC = True
@@ -17256,7 +17256,7 @@ def checkOrgUnitPathExists(cd, orgUnitPath, i=0, count=0, showError=False):
return (False, orgUnitPath, orgUnitPath) return (False, orgUnitPath, orgUnitPath)
def _batchMoveCrOSesToOrgUnit(cd, orgUnitPath, orgUnitId, i, count, items, quickCrOSMove, fromOrgUnitPath=None): def _batchMoveCrOSesToOrgUnit(cd, orgUnitPath, orgUnitId, i, count, items, quickCrOSMove, fromOrgUnitPath=None):
def _callbackMoveCrOSesToOrgUnit(request_id, response, exception): def _callbackMoveCrOSesToOrgUnit(request_id, _, exception):
ri = request_id.splitlines() ri = request_id.splitlines()
if exception is None: if exception is None:
if not fromOrgUnitPath: if not fromOrgUnitPath:
@@ -17337,7 +17337,7 @@ def _batchMoveCrOSesToOrgUnit(cd, orgUnitPath, orgUnitId, i, count, items, quick
def _batchMoveUsersToOrgUnit(cd, orgUnitPath, i, count, items, fromOrgUnitPath=None): def _batchMoveUsersToOrgUnit(cd, orgUnitPath, i, count, items, fromOrgUnitPath=None):
_MOVE_USER_REASON_TO_MESSAGE_MAP = {GAPI.USER_NOT_FOUND: Msg.DOES_NOT_EXIST, GAPI.DOMAIN_NOT_FOUND: Msg.SERVICE_NOT_APPLICABLE, GAPI.FORBIDDEN: Msg.SERVICE_NOT_APPLICABLE} _MOVE_USER_REASON_TO_MESSAGE_MAP = {GAPI.USER_NOT_FOUND: Msg.DOES_NOT_EXIST, GAPI.DOMAIN_NOT_FOUND: Msg.SERVICE_NOT_APPLICABLE, GAPI.FORBIDDEN: Msg.SERVICE_NOT_APPLICABLE}
def _callbackMoveUsersToOrgUnit(request_id, response, exception): def _callbackMoveUsersToOrgUnit(request_id, _, exception):
ri = request_id.splitlines() ri = request_id.splitlines()
if exception is None: if exception is None:
if not fromOrgUnitPath: if not fromOrgUnitPath:
@@ -27532,10 +27532,10 @@ MEET_SPACE_RESTRICTIONS_CHOICES_MAP = {
'norestriction': 'NO_RESTRICTION' 'norestriction': 'NO_RESTRICTION'
} }
MEET_SPACE_FIRSTJOINERTYPE_CHOICES_MAP = { #MEET_SPACE_FIRSTJOINERTYPE_CHOICES_MAP = {
'hostsonly': 'HOSTS_ONLY', # 'hostsonly': 'HOSTS_ONLY',
'anyone': 'ANYONE' # 'anyone': 'ANYONE'
} # }
MEET_SPACE_ARTIFACT_SUB_OPTIONS = { MEET_SPACE_ARTIFACT_SUB_OPTIONS = {
'recordingConfig': 'autoRecordingGeneration', 'recordingConfig': 'autoRecordingGeneration',
@@ -27569,8 +27569,8 @@ def _getMeetSpaceParameters(myarg, body):
body['config']['moderationRestrictions'][option] = getChoice(MEET_SPACE_RESTRICTIONS_CHOICES_MAP, mapChoice=True) body['config']['moderationRestrictions'][option] = getChoice(MEET_SPACE_RESTRICTIONS_CHOICES_MAP, mapChoice=True)
elif option == 'defaultJoinAsViewerType': elif option == 'defaultJoinAsViewerType':
body['config'][option] = 'ON' if getBoolean() else 'OFF' body['config'][option] = 'ON' if getBoolean() else 'OFF'
elif option == 'firstJoinerType': # elif option == 'firstJoinerType':
body['config'][option] = getChoice(MEET_SPACE_FIRSTJOINERTYPE_CHOICES_MAP, mapChoice=True) # body['config'][option] = getChoice(MEET_SPACE_FIRSTJOINERTYPE_CHOICES_MAP, mapChoice=True)
elif option in {'recordingConfig', 'transcriptionConfig', 'smartNotesConfig'}: elif option in {'recordingConfig', 'transcriptionConfig', 'smartNotesConfig'}:
body['config'].setdefault('artifactConfig', {}) body['config'].setdefault('artifactConfig', {})
body['config']['artifactConfig'].setdefault(option, {}) body['config']['artifactConfig'].setdefault(option, {})
@@ -32023,7 +32023,7 @@ def doUpdateGroups():
GAPI.INVALID_MEMBER: Msg.INVALID_MEMBER, GAPI.INVALID_MEMBER: Msg.INVALID_MEMBER,
GAPI.CYCLIC_MEMBERSHIPS_NOT_ALLOWED: Msg.WOULD_MAKE_MEMBERSHIP_CYCLE} GAPI.CYCLIC_MEMBERSHIPS_NOT_ALLOWED: Msg.WOULD_MAKE_MEMBERSHIP_CYCLE}
def _callbackAddGroupMembers(request_id, response, exception): def _callbackAddGroupMembers(request_id, _, exception):
ri = request_id.splitlines() ri = request_id.splitlines()
if exception is None: if exception is None:
_showSuccess(ri[RI_ENTITY], ri[RI_ITEM], ri[RI_ROLE], ri[RI_OPTION], int(ri[RI_J]), int(ri[RI_JCOUNT])) _showSuccess(ri[RI_ENTITY], ri[RI_ITEM], ri[RI_ROLE], ri[RI_OPTION], int(ri[RI_J]), int(ri[RI_JCOUNT]))
@@ -32112,7 +32112,7 @@ def doUpdateGroups():
GAPI.CONDITION_NOT_MET: f'{Msg.NOT_A} {Ent.Singular(Ent.MEMBER)}', GAPI.CONDITION_NOT_MET: f'{Msg.NOT_A} {Ent.Singular(Ent.MEMBER)}',
GAPI.INVALID_MEMBER: Msg.DOES_NOT_EXIST} GAPI.INVALID_MEMBER: Msg.DOES_NOT_EXIST}
def _callbackRemoveGroupMembers(request_id, response, exception): def _callbackRemoveGroupMembers(request_id, _, exception):
ri = request_id.splitlines() ri = request_id.splitlines()
if exception is None: if exception is None:
_showSuccess(ri[RI_ENTITY], ri[RI_ITEM], ri[RI_ROLE], DELIVERY_SETTINGS_UNDEFINED, int(ri[RI_J]), int(ri[RI_JCOUNT])) _showSuccess(ri[RI_ENTITY], ri[RI_ITEM], ri[RI_ROLE], DELIVERY_SETTINGS_UNDEFINED, int(ri[RI_J]), int(ri[RI_JCOUNT]))
@@ -32211,7 +32211,7 @@ def doUpdateGroups():
except (GAPI.invalidMember, GAPI.resourceNotFound, GAPI.serviceNotAvailable) as e: except (GAPI.invalidMember, GAPI.resourceNotFound, GAPI.serviceNotAvailable) as e:
_showFailure(group, member, role, str(e), j, jcount) _showFailure(group, member, role, str(e), j, jcount)
def _callbackUpdateGroupMembers(request_id, response, exception): def _callbackUpdateGroupMembers(request_id, _, exception):
ri = request_id.splitlines() ri = request_id.splitlines()
if exception is None: if exception is None:
_showSuccess(ri[RI_ENTITY], ri[RI_ITEM], ri[RI_ROLE], ri[RI_OPTION], int(ri[RI_J]), int(ri[RI_JCOUNT])) _showSuccess(ri[RI_ENTITY], ri[RI_ITEM], ri[RI_ROLE], ri[RI_OPTION], int(ri[RI_J]), int(ri[RI_JCOUNT]))
@@ -33448,6 +33448,7 @@ PRINT_GROUPS_JSON_TITLES = ['email', 'JSON']
# gam print groups [todrive <ToDriveAttribute>*] # gam print groups [todrive <ToDriveAttribute>*]
# [([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryUserList>)]))| # [([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryUserList>)]))|
# (group|group_ns|group_susp <GroupItem>)|
# (select <GroupEntity>)] # (select <GroupEntity>)]
# [emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>] # [emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
# [descriptionmatchpattern [not] <RegularExpression>] (matchsetting [not] <GroupAttribute>)* # [descriptionmatchpattern [not] <RegularExpression>] (matchsetting [not] <GroupAttribute>)*
@@ -33660,6 +33661,12 @@ def doPrintGroups():
pass pass
elif getGroupMatchPatterns(myarg, matchPatterns, False): elif getGroupMatchPatterns(myarg, matchPatterns, False):
pass pass
elif myarg in {'group', 'groupns', 'groupsusp'}:
entitySelection = [getString(Cmd.OB_EMAIL_ADDRESS)]
if myarg == 'groupns':
isSuspended = False
elif myarg == 'groupsusp':
isSuspended = True
elif myarg == 'select': elif myarg == 'select':
entitySelection = getEntityList(Cmd.OB_GROUP_ENTITY) entitySelection = getEntityList(Cmd.OB_GROUP_ENTITY)
elif myarg in SUSPENDED_ARGUMENTS: elif myarg in SUSPENDED_ARGUMENTS:
@@ -42347,7 +42354,7 @@ def doPrintVaultCounts():
# gam [<UserTypeEntity>] print siteacls <SiteEntity> [todrive <ToDriveAttribute>*] # gam [<UserTypeEntity>] print siteacls <SiteEntity> [todrive <ToDriveAttribute>*]
# gam [<UserTypeEntity>] print siteactivity <SiteEntity> [todrive <ToDriveAttribute>*] # gam [<UserTypeEntity>] print siteactivity <SiteEntity> [todrive <ToDriveAttribute>*]
# [startindex <Number>] [maxresults <Number>] [updated_min <Date>] [updated_max <Date>] # [startindex <Number>] [maxresults <Number>] [updated_min <Date>] [updated_max <Date>]
def deprecatedUserSites(users): def deprecatedUserSites(_):
deprecatedCommandExit() deprecatedCommandExit()
def deprecatedDomainSites(): def deprecatedDomainSites():
@@ -43669,7 +43676,7 @@ def waitForMailbox(entityList):
Ind.Decrement() Ind.Decrement()
def getUserLicenses(lic, user, skus): def getUserLicenses(lic, user, skus):
def _callbackGetLicense(request_id, response, exception): def _callbackGetLicense(_, response, exception):
if exception is None: if exception is None:
if response and 'skuId' in response: if response and 'skuId' in response:
licenses.append(response['skuId']) licenses.append(response['skuId'])
@@ -48175,7 +48182,7 @@ def _batchAddItemsToCourse(croom, courseId, i, count, addParticipants, role):
_ADD_PART_REASON_TO_MESSAGE_MAP = {GAPI.NOT_FOUND: Msg.DOES_NOT_EXIST, _ADD_PART_REASON_TO_MESSAGE_MAP = {GAPI.NOT_FOUND: Msg.DOES_NOT_EXIST,
GAPI.ALREADY_EXISTS: Msg.DUPLICATE, GAPI.ALREADY_EXISTS: Msg.DUPLICATE,
GAPI.FAILED_PRECONDITION: Msg.NOT_ALLOWED} GAPI.FAILED_PRECONDITION: Msg.NOT_ALLOWED}
def _callbackAddItemsToCourse(request_id, response, exception): def _callbackAddItemsToCourse(request_id, _, exception):
ri = request_id.splitlines() ri = request_id.splitlines()
if exception is None: if exception is None:
entityActionPerformed([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], int(ri[RI_J]), int(ri[RI_JCOUNT])) entityActionPerformed([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], int(ri[RI_J]), int(ri[RI_JCOUNT]))
@@ -48255,7 +48262,7 @@ def _batchRemoveItemsFromCourse(croom, courseId, i, count, removeParticipants, r
_REMOVE_PART_REASON_TO_MESSAGE_MAP = {GAPI.NOT_FOUND: Msg.DOES_NOT_EXIST, _REMOVE_PART_REASON_TO_MESSAGE_MAP = {GAPI.NOT_FOUND: Msg.DOES_NOT_EXIST,
GAPI.FORBIDDEN: Msg.FORBIDDEN, GAPI.FORBIDDEN: Msg.FORBIDDEN,
GAPI.PERMISSION_DENIED: Msg.PERMISSION_DENIED} GAPI.PERMISSION_DENIED: Msg.PERMISSION_DENIED}
def _callbackRemoveItemsFromCourse(request_id, response, exception): def _callbackRemoveItemsFromCourse(request_id, _, exception):
ri = request_id.splitlines() ri = request_id.splitlines()
if exception is None: if exception is None:
entityActionPerformed([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], int(ri[RI_J]), int(ri[RI_JCOUNT])) entityActionPerformed([Ent.COURSE, ri[RI_ENTITY], ri[RI_ROLE], ri[RI_ITEM]], int(ri[RI_J]), int(ri[RI_JCOUNT]))
@@ -48467,7 +48474,7 @@ def doCourseRemoveItems(courseIdList, getEntityListArg):
# gam courses <CourseEntity> clear teachers|students # gam courses <CourseEntity> clear teachers|students
# gam course <CourseID> clear teacher|student # gam course <CourseID> clear teacher|student
def doCourseClearParticipants(courseIdList, getEntityListArg): def doCourseClearParticipants(courseIdList, _):
croom = buildGAPIObject(API.CLASSROOM) croom = buildGAPIObject(API.CLASSROOM)
role = getChoice(CLEAR_SYNC_PARTICIPANT_TYPES_MAP, mapChoice=True) role = getChoice(CLEAR_SYNC_PARTICIPANT_TYPES_MAP, mapChoice=True)
checkForExtraneousArguments() checkForExtraneousArguments()
@@ -48483,7 +48490,7 @@ def doCourseClearParticipants(courseIdList, getEntityListArg):
# gam course <CourseID> sync students [addonly|removeonly] <UserTypeEntity> # gam course <CourseID> sync students [addonly|removeonly] <UserTypeEntity>
# gam courses <CourseEntity> sync teachers [addonly|removeonly] [makefirstteacherowner] <UserTypeEntity> # gam courses <CourseEntity> sync teachers [addonly|removeonly] [makefirstteacherowner] <UserTypeEntity>
# gam course <CourseID> sync teachers [addonly|removeonly] [makefirstteacherowner] <UserTypeEntity> # gam course <CourseID> sync teachers [addonly|removeonly] [makefirstteacherowner] <UserTypeEntity>
def doCourseSyncParticipants(courseIdList, getEntityListArg): def doCourseSyncParticipants(courseIdList, _):
croom = buildGAPIObject(API.CLASSROOM) croom = buildGAPIObject(API.CLASSROOM)
role = getChoice(CLEAR_SYNC_PARTICIPANT_TYPES_MAP, mapChoice=True) role = getChoice(CLEAR_SYNC_PARTICIPANT_TYPES_MAP, mapChoice=True)
if role == Ent.TEACHER: if role == Ent.TEACHER:
@@ -58219,6 +58226,7 @@ def initCopyMoveOptions(copyCmd):
'showPermissionMessages': False, 'showPermissionMessages': False,
'sendEmailIfRequired': False, 'sendEmailIfRequired': False,
'useDomainAdminAccess': False, 'useDomainAdminAccess': False,
'enforceExpansiveAccess': False,
'copiedShortcutsPointToCopiedFiles': True, 'copiedShortcutsPointToCopiedFiles': True,
'createShortcutsForNonmovableFiles': False, 'createShortcutsForNonmovableFiles': False,
'duplicateFiles': DUPLICATE_FILE_OVERWRITE_OLDER, 'duplicateFiles': DUPLICATE_FILE_OVERWRITE_OLDER,
@@ -58317,6 +58325,8 @@ def getCopyMoveOptions(myarg, copyMoveOptions):
elif myarg == 'mappermissionsdomain': elif myarg == 'mappermissionsdomain':
oldDomain = getString(Cmd.OB_DOMAIN_NAME).lower() oldDomain = getString(Cmd.OB_DOMAIN_NAME).lower()
copyMoveOptions['mapPermissionsDomains'][oldDomain] = getString(Cmd.OB_DOMAIN_NAME).lower() copyMoveOptions['mapPermissionsDomains'][oldDomain] = getString(Cmd.OB_DOMAIN_NAME).lower()
elif myarg == 'enforceexpansiveaccess':
copyMoveOptions['enforceExpansiveAccess'] = getBoolean()
else: else:
# Move arguments # Move arguments
if not copyMoveOptions['copyCmd']: if not copyMoveOptions['copyCmd']:
@@ -58588,6 +58598,9 @@ def _copyPermissions(drive, user, i, count, j, jcount,
updateTargetPerms[permissionId].update(updatePerm) updateTargetPerms[permissionId].update(updatePerm)
updateTargetPerms[permissionId]['updates'] = updatePerm updateTargetPerms[permissionId]['updates'] = updatePerm
copySourcePerms.pop(permissionId) copySourcePerms.pop(permissionId)
deleteUpdateKwargs = {'useDomainAdminAccess': copyMoveOptions['useDomainAdminAccess']}
if entityType != Ent.SHAREDDRIVE:
deleteUpdateKwargs['enforceExpansiveAccess'] = copyMoveOptions['enforceExpansiveAccess']
Ind.Increment() Ind.Increment()
action = Act.Get() action = Act.Get()
Act.Set(Act.COPY) Act.Set(Act.COPY)
@@ -58606,8 +58619,9 @@ def _copyPermissions(drive, user, i, count, j, jcount,
callGAPI(drive.permissions(), 'create', callGAPI(drive.permissions(), 'create',
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+GAPI.DRIVE3_CREATE_ACL_THROW_REASONS, throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+GAPI.DRIVE3_CREATE_ACL_THROW_REASONS,
# retryReasons=[GAPI.INVALID_SHARING_REQUEST], # retryReasons=[GAPI.INVALID_SHARING_REQUEST],
useDomainAdminAccess=copyMoveOptions['useDomainAdminAccess'],
fileId=newFileId, sendNotificationEmail=sendNotificationEmail, emailMessage=None, fileId=newFileId, sendNotificationEmail=sendNotificationEmail, emailMessage=None,
body=permission, fields='', useDomainAdminAccess=copyMoveOptions['useDomainAdminAccess'], supportsAllDrives=True) body=permission, fields='', supportsAllDrives=True)
if copyMoveOptions['showPermissionMessages']: if copyMoveOptions['showPermissionMessages']:
entityActionPerformed(kvList, k, kcount) entityActionPerformed(kvList, k, kcount)
break break
@@ -58645,7 +58659,8 @@ def _copyPermissions(drive, user, i, count, j, jcount,
try: try:
callGAPI(drive.permissions(), 'delete', callGAPI(drive.permissions(), 'delete',
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+GAPI.DRIVE3_DELETE_ACL_THROW_REASONS+[GAPI.FILE_NEVER_WRITABLE], throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+GAPI.DRIVE3_DELETE_ACL_THROW_REASONS+[GAPI.FILE_NEVER_WRITABLE],
fileId=newFileId, permissionId=permissionId, useDomainAdminAccess=copyMoveOptions['useDomainAdminAccess'], supportsAllDrives=True) **deleteUpdateKwargs,
fileId=newFileId, permissionId=permissionId, supportsAllDrives=True)
if copyMoveOptions['showPermissionMessages']: if copyMoveOptions['showPermissionMessages']:
entityActionPerformed(kvList, k, kcount) entityActionPerformed(kvList, k, kcount)
except (GAPI.notFound, GAPI.permissionNotFound, except (GAPI.notFound, GAPI.permissionNotFound,
@@ -58670,8 +58685,9 @@ def _copyPermissions(drive, user, i, count, j, jcount,
callGAPI(drive.permissions(), 'update', callGAPI(drive.permissions(), 'update',
bailOnInternalError=True, bailOnInternalError=True,
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+GAPI.DRIVE3_UPDATE_ACL_THROW_REASONS+[GAPI.FILE_NEVER_WRITABLE], throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+GAPI.DRIVE3_UPDATE_ACL_THROW_REASONS+[GAPI.FILE_NEVER_WRITABLE],
fileId=newFileId, permissionId=permissionId, removeExpiration=removeExpiration, removeExpiration=removeExpiration,
body=permission['updates'], useDomainAdminAccess=copyMoveOptions['useDomainAdminAccess'], supportsAllDrives=True) **deleteUpdateKwargs,
fileId=newFileId, permissionId=permissionId, body=permission['updates'], supportsAllDrives=True)
if copyMoveOptions['showPermissionMessages']: if copyMoveOptions['showPermissionMessages']:
entityActionPerformed(kvList, k, kcount) entityActionPerformed(kvList, k, kcount)
except (GAPI.notFound, GAPI.permissionNotFound, except (GAPI.notFound, GAPI.permissionNotFound,
@@ -58945,6 +58961,7 @@ copyReturnItemMap = {
# [sendemailifrequired [<Boolean>]] # [sendemailifrequired [<Boolean>]]
# [suppressnotselectedmessages [<Boolean>]] # [suppressnotselectedmessages [<Boolean>]]
# [verifyorganizer [<Boolean>]] # [verifyorganizer [<Boolean>]]
# [enforceexpansiveaccess [<Boolean>]]
def copyDriveFile(users): def copyDriveFile(users):
def _writeCSVData(user, oldName, oldId, newName, newId, mimeType): def _writeCSVData(user, oldName, oldId, newName, newId, mimeType):
row = {'User': user, fileNameTitle: oldName, 'id': oldId, row = {'User': user, fileNameTitle: oldName, 'id': oldId,
@@ -59672,7 +59689,9 @@ def _updateMoveFilePermissions(drive, user, i, count,
try: try:
callGAPI(drive.permissions(), 'delete', callGAPI(drive.permissions(), 'delete',
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+GAPI.DRIVE3_DELETE_ACL_THROW_REASONS+[GAPI.FILE_NEVER_WRITABLE], throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+GAPI.DRIVE3_DELETE_ACL_THROW_REASONS+[GAPI.FILE_NEVER_WRITABLE],
fileId=fileId, permissionId=permissionId, useDomainAdminAccess=copyMoveOptions['useDomainAdminAccess'], supportsAllDrives=True) useDomainAdminAccess=copyMoveOptions['useDomainAdminAccess'],
enforceExpansiveAccess=copyMoveOptions['enforceExpansiveAccess'],
fileId=fileId, permissionId=permissionId, supportsAllDrives=True)
if copyMoveOptions['showPermissionMessages']: if copyMoveOptions['showPermissionMessages']:
entityActionPerformed(kvList, k, kcount) entityActionPerformed(kvList, k, kcount)
except (GAPI.notFound, GAPI.permissionNotFound, except (GAPI.notFound, GAPI.permissionNotFound,
@@ -59760,6 +59779,7 @@ def _updateMoveFilePermissions(drive, user, i, count,
# [retainsourcefolders [<Boolean>]] # [retainsourcefolders [<Boolean>]]
# [sendemailifrequired [<Boolean>]] # [sendemailifrequired [<Boolean>]]
# [verifyorganizer [<Boolean>]] # [verifyorganizer [<Boolean>]]
# [enforceexpansiveaccess [<Boolean>]]
def moveDriveFile(users): def moveDriveFile(users):
def _cloneFolderMove(drive, user, i, count, j, jcount, def _cloneFolderMove(drive, user, i, count, j, jcount,
source, targetChildren, newFolderName, newParentId, newParentName, mergeParentModifiedTime, source, targetChildren, newFolderName, newParentId, newParentName, mergeParentModifiedTime,
@@ -60103,9 +60123,8 @@ def moveDriveFile(users):
parentBody = {} parentBody = {}
parentParms = initDriveFileAttributes() parentParms = initDriveFileAttributes()
copyMoveOptions = initCopyMoveOptions(False) copyMoveOptions = initCopyMoveOptions(False)
newParentsSpecified = False newParentsSpecified = updateFilePermissions = False
movedFiles = {} movedFiles = {}
updateFilePermissions = False
verifyOrganizer = True verifyOrganizer = True
while Cmd.ArgumentsRemaining(): while Cmd.ArgumentsRemaining():
myarg = getArgument() myarg = getArgument()
@@ -60994,6 +61013,7 @@ TRANSFER_DRIVEFILE_ACL_ROLES_MAP = {
# [nonowner_retainrole reader|commenter|writer|editor|fileorganizer|current|none] # [nonowner_retainrole reader|commenter|writer|editor|fileorganizer|current|none]
# [nonowner_targetrole reader|commenter|writer|editor|fileorganizer|current|none|source] # [nonowner_targetrole reader|commenter|writer|editor|fileorganizer|current|none|source]
# (orderby <DriveFileOrderByFieldName> [ascending|descending])* # (orderby <DriveFileOrderByFieldName> [ascending|descending])*
# [enforceexpansiveaccess [<Boolean>]]
# [preview] [todrive <ToDriveAttribute>*] # [preview] [todrive <ToDriveAttribute>*]
def transferDrive(users): def transferDrive(users):
@@ -61180,6 +61200,7 @@ def transferDrive(users):
callGAPI(sourceDrive.permissions(), 'update', callGAPI(sourceDrive.permissions(), 'update',
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.BAD_REQUEST, GAPI.INVALID_OWNERSHIP_TRANSFER, throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.BAD_REQUEST, GAPI.INVALID_OWNERSHIP_TRANSFER,
GAPI.PERMISSION_NOT_FOUND, GAPI.SHARING_RATE_LIMIT_EXCEEDED], GAPI.PERMISSION_NOT_FOUND, GAPI.SHARING_RATE_LIMIT_EXCEEDED],
enforceExpansiveAccess=enforceExpansiveAccess,
fileId=childFileId, permissionId=targetPermissionId, fileId=childFileId, permissionId=targetPermissionId,
transferOwnership=True, body={'role': 'owner'}, fields='') transferOwnership=True, body={'role': 'owner'}, fields='')
if removeSourceParents: if removeSourceParents:
@@ -61368,6 +61389,7 @@ def transferDrive(users):
if ownerRetainRoleBody['role'] != 'writer': if ownerRetainRoleBody['role'] != 'writer':
callGAPI(targetDrive.permissions(), 'update', callGAPI(targetDrive.permissions(), 'update',
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.PERMISSION_NOT_FOUND, GAPI.BAD_REQUEST, GAPI.SHARING_RATE_LIMIT_EXCEEDED], throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.PERMISSION_NOT_FOUND, GAPI.BAD_REQUEST, GAPI.SHARING_RATE_LIMIT_EXCEEDED],
enforceExpansiveAccess=enforceExpansiveAccess,
fileId=childFileId, permissionId=sourcePermissionId, body=ownerRetainRoleBody, fields='') fileId=childFileId, permissionId=sourcePermissionId, body=ownerRetainRoleBody, fields='')
else: else:
callGAPI(targetDrive.permissions(), 'delete', callGAPI(targetDrive.permissions(), 'delete',
@@ -61417,6 +61439,7 @@ def transferDrive(users):
if nonOwnerRetainRoleBody['role'] != 'current': if nonOwnerRetainRoleBody['role'] != 'current':
callGAPI(ownerDrive.permissions(), 'update', callGAPI(ownerDrive.permissions(), 'update',
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.PERMISSION_NOT_FOUND, GAPI.BAD_REQUEST, GAPI.SHARING_RATE_LIMIT_EXCEEDED], throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.PERMISSION_NOT_FOUND, GAPI.BAD_REQUEST, GAPI.SHARING_RATE_LIMIT_EXCEEDED],
enforceExpansiveAccess=enforceExpansiveAccess,
fileId=childFileId, permissionId=sourcePermissionId, body=sourceUpdateRole, fields='') fileId=childFileId, permissionId=sourcePermissionId, body=sourceUpdateRole, fields='')
else: else:
try: try:
@@ -61577,7 +61600,7 @@ def transferDrive(users):
targetUserFolderPattern = '#user# old files' targetUserFolderPattern = '#user# old files'
targetUserOrphansFolderPattern = '#user# orphaned files' targetUserOrphansFolderPattern = '#user# orphaned files'
targetIds = [None, None] targetIds = [None, None]
createShortcutsForNonmovableFiles = False createShortcutsForNonmovableFiles = enforceExpansiveAccess = False
mergeWithTarget = False mergeWithTarget = False
thirdPartyOwners = {} thirdPartyOwners = {}
skipFileIdEntity = initDriveFileEntity() skipFileIdEntity = initDriveFileEntity()
@@ -61595,6 +61618,8 @@ def transferDrive(users):
nonOwnerRetainRoleBody['role'] = 'current' nonOwnerRetainRoleBody['role'] = 'current'
elif myarg == 'nonownertargetrole': elif myarg == 'nonownertargetrole':
nonOwnerTargetRoleBody['role'] = getChoice(TRANSFER_DRIVEFILE_ACL_ROLES_MAP, mapChoice=True) nonOwnerTargetRoleBody['role'] = getChoice(TRANSFER_DRIVEFILE_ACL_ROLES_MAP, mapChoice=True)
elif myarg == 'enforceexpansiveaccess':
enforceExpansiveAccess = getBoolean()
elif myarg == 'noretentionmessages': elif myarg == 'noretentionmessages':
showRetentionMessages = False showRetentionMessages = False
elif myarg == 'orderby': elif myarg == 'orderby':
@@ -61832,6 +61857,7 @@ def getPermissionIdForEmail(user, i, count, email):
# [<DriveFileParentAttribute>] [includetrashed] [norecursion [<Boolean>]] # [<DriveFileParentAttribute>] [includetrashed] [norecursion [<Boolean>]]
# (orderby <DriveFileOrderByFieldName> [ascending|descending])* # (orderby <DriveFileOrderByFieldName> [ascending|descending])*
# [preview] [filepath] [pathdelimiter <Character>] [buildtree] # [preview] [filepath] [pathdelimiter <Character>] [buildtree]
# [enforceexpansiveaccess [<Boolean>]]
# [todrive <ToDriveAttribute>*] # [todrive <ToDriveAttribute>*]
def transferOwnership(users): def transferOwnership(users):
def _identifyFilesToTransfer(fileEntry): def _identifyFilesToTransfer(fileEntry):
@@ -61880,7 +61906,7 @@ def transferOwnership(users):
body = {} body = {}
newOwner = getEmailAddress() newOwner = getEmailAddress()
OBY = OrderBy(DRIVEFILE_ORDERBY_CHOICE_MAP) OBY = OrderBy(DRIVEFILE_ORDERBY_CHOICE_MAP)
changeParents = filepath = includeTrashed = noRecursion = False changeParents = enforceExpansiveAccess = filepath = includeTrashed = noRecursion = False
pathDelimiter = '/' pathDelimiter = '/'
csvPF = fileTree = None csvPF = fileTree = None
addParents = '' addParents = ''
@@ -61907,6 +61933,8 @@ def transferOwnership(users):
csvPF.GetTodriveParameters() csvPF.GetTodriveParameters()
elif getDriveFileParentAttribute(myarg, parentParms): elif getDriveFileParentAttribute(myarg, parentParms):
changeParents = True changeParents = True
elif myarg == 'enforceexpansiveaccess':
enforceExpansiveAccess = getBoolean()
else: else:
unknownArgumentExit() unknownArgumentExit()
Act.Set(Act.TRANSFER_OWNERSHIP) Act.Set(Act.TRANSFER_OWNERSHIP)
@@ -62024,6 +62052,7 @@ def transferOwnership(users):
Act.Set(action) Act.Set(action)
callGAPI(drive.permissions(), 'update', callGAPI(drive.permissions(), 'update',
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.PERMISSION_NOT_FOUND], throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.PERMISSION_NOT_FOUND],
enforceExpansiveAccess=enforceExpansiveAccess,
fileId=xferFileId, permissionId=permissionId, transferOwnership=True, body=body, fields='') fileId=xferFileId, permissionId=permissionId, transferOwnership=True, body=body, fields='')
entityModifierNewValueItemValueListActionPerformed(kvList, Act.MODIFIER_TO, None, [Ent.USER, newOwner], k, kcount) entityModifierNewValueItemValueListActionPerformed(kvList, Act.MODIFIER_TO, None, [Ent.USER, newOwner], k, kcount)
else: else:
@@ -62045,6 +62074,7 @@ def transferOwnership(users):
fileId=xferFileId, sendNotificationEmail=False, body=bodyAdd, fields='') fileId=xferFileId, sendNotificationEmail=False, body=bodyAdd, fields='')
callGAPI(drive.permissions(), 'update', callGAPI(drive.permissions(), 'update',
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.PERMISSION_NOT_FOUND], throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.PERMISSION_NOT_FOUND],
enforceExpansiveAccess=enforceExpansiveAccess,
fileId=xferFileId, permissionId=permissionId, transferOwnership=True, body=body, fields='') fileId=xferFileId, permissionId=permissionId, transferOwnership=True, body=body, fields='')
entityModifierNewValueItemValueListActionPerformed(kvList, Act.MODIFIER_TO, None, [Ent.USER, newOwner], k, kcount) entityModifierNewValueItemValueListActionPerformed(kvList, Act.MODIFIER_TO, None, [Ent.USER, newOwner], k, kcount)
except GAPI.invalidSharingRequest as e: except GAPI.invalidSharingRequest as e:
@@ -62117,6 +62147,7 @@ def transferOwnership(users):
# [keepuser | (retainrole reader|commenter|writer|editor|none)] [noretentionmessages] # [keepuser | (retainrole reader|commenter|writer|editor|none)] [noretentionmessages]
# (orderby <DriveFileOrderByFieldName> [ascending|descending])* # (orderby <DriveFileOrderByFieldName> [ascending|descending])*
# [preview] [filepath] [pathdelimiter <Character>] [buildtree] # [preview] [filepath] [pathdelimiter <Character>] [buildtree]
# [enforceexpansiveaccess [<Boolean>]]
# [todrive <ToDriveAttribute>*] # [todrive <ToDriveAttribute>*]
def claimOwnership(users): def claimOwnership(users):
def _identifyFilesToClaim(fileEntry): def _identifyFilesToClaim(fileEntry):
@@ -62177,6 +62208,7 @@ def claimOwnership(users):
if sourceRetainRoleBody['role'] != 'writer': if sourceRetainRoleBody['role'] != 'writer':
callGAPI(sourceDrive.permissions(), 'update', callGAPI(sourceDrive.permissions(), 'update',
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.PERMISSION_NOT_FOUND, GAPI.BAD_REQUEST], throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.PERMISSION_NOT_FOUND, GAPI.BAD_REQUEST],
enforceExpansiveAccess=enforceExpansiveAccess,
fileId=ofileId, permissionId=oldOwnerPermissionId, body=sourceRetainRoleBody, fields='') fileId=ofileId, permissionId=oldOwnerPermissionId, body=sourceRetainRoleBody, fields='')
else: else:
callGAPI(sourceDrive.permissions(), 'delete', callGAPI(sourceDrive.permissions(), 'delete',
@@ -62200,7 +62232,7 @@ def claimOwnership(users):
onlyOwners = set() onlyOwners = set()
skipOwners = set() skipOwners = set()
subdomains = [] subdomains = []
filepath = includeTrashed = False enforceExpansiveAccess = filepath = includeTrashed = False
pathDelimiter = '/' pathDelimiter = '/'
addParents = '' addParents = ''
parentBody = {} parentBody = {}
@@ -62235,6 +62267,8 @@ def claimOwnership(users):
includeTrashed = True includeTrashed = True
elif myarg == 'orderby': elif myarg == 'orderby':
OBY.GetChoice() OBY.GetChoice()
elif myarg == 'enforceexpansiveaccess':
enforceExpansiveAccess = getBoolean()
elif myarg == 'restricted': elif myarg == 'restricted':
bodyShare['copyRequiresWriterPermission'] = getBoolean() bodyShare['copyRequiresWriterPermission'] = getBoolean()
elif myarg == 'writerscanshare': elif myarg == 'writerscanshare':
@@ -62405,6 +62439,7 @@ def claimOwnership(users):
Act.Set(action) Act.Set(action)
callGAPI(sourceDrive.permissions(), 'update', callGAPI(sourceDrive.permissions(), 'update',
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.PERMISSION_NOT_FOUND], throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.PERMISSION_NOT_FOUND],
enforceExpansiveAccess=enforceExpansiveAccess,
fileId=xferFileId, permissionId=permissionId, transferOwnership=True, body=body, fields='') fileId=xferFileId, permissionId=permissionId, transferOwnership=True, body=body, fields='')
kvList = [Ent.USER, user, entityType, fileDesc] kvList = [Ent.USER, user, entityType, fileDesc]
entityModifierNewValueItemValueListActionPerformed(kvList, Act.MODIFIER_FROM, None, [Ent.USER, oldOwner], l, lcount) entityModifierNewValueItemValueListActionPerformed(kvList, Act.MODIFIER_FROM, None, [Ent.USER, oldOwner], l, lcount)
@@ -62429,6 +62464,7 @@ def claimOwnership(users):
fileId=xferFileId, sendNotificationEmail=False, body=bodyAdd, fields='') fileId=xferFileId, sendNotificationEmail=False, body=bodyAdd, fields='')
callGAPI(sourceDrive.permissions(), 'update', callGAPI(sourceDrive.permissions(), 'update',
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.PERMISSION_NOT_FOUND], throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.PERMISSION_NOT_FOUND],
enforceExpansiveAccess=enforceExpansiveAccess,
fileId=xferFileId, permissionId=permissionId, transferOwnership=True, body=body, fields='') fileId=xferFileId, permissionId=permissionId, transferOwnership=True, body=body, fields='')
entityModifierNewValueItemValueListActionPerformed(kvList, Act.MODIFIER_FROM, None, [Ent.USER, oldOwner], l, lcount) entityModifierNewValueItemValueListActionPerformed(kvList, Act.MODIFIER_FROM, None, [Ent.USER, oldOwner], l, lcount)
_processRetainedRole(user, i, count, oldOwner, entityType, xferFileId, fileDesc, l, lcount) _processRetainedRole(user, i, count, oldOwner, entityType, xferFileId, fileDesc, l, lcount)
@@ -62965,11 +63001,12 @@ def doCreateDriveFileACL():
# gam [<UserTypeEntity>] update drivefileacl <DriveFileEntity> <DriveFilePermissionIDorEmail> [asadmin] # gam [<UserTypeEntity>] update drivefileacl <DriveFileEntity> <DriveFilePermissionIDorEmail> [asadmin]
# (role <DriveFileACLRole>) [expiration <Time>] [removeexpiration [<Boolean>]] # (role <DriveFileACLRole>) [expiration <Time>] [removeexpiration [<Boolean>]]
# [updatesheetprotectedranges [<Boolean>]] # [updatesheetprotectedranges [<Boolean>]] [enforceexpansiveaccess [<Boolean>]]
# [showtitles] [nodetails|(csv [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]])] # [showtitles] [nodetails|(csv [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]])]
def updateDriveFileACLs(users, useDomainAdminAccess=False): def updateDriveFileACLs(users, useDomainAdminAccess=False):
fileIdEntity = getDriveFileEntity() fileIdEntity = getDriveFileEntity()
isEmail, permissionId = getPermissionId() isEmail, permissionId = getPermissionId()
enforceExpansiveAccess = None
removeExpiration = showTitles = updateSheetProtectedRanges = False removeExpiration = showTitles = updateSheetProtectedRanges = False
showDetails = True showDetails = True
csvPF = None csvPF = None
@@ -62988,6 +63025,8 @@ def updateDriveFileACLs(users, useDomainAdminAccess=False):
showTitles = True showTitles = True
elif myarg == 'updatesheetprotectedranges': elif myarg == 'updatesheetprotectedranges':
updateSheetProtectedRanges = getBoolean() updateSheetProtectedRanges = getBoolean()
elif myarg == 'enforceexpansiveaccess':
enforceExpansiveAccess = getBoolean()
elif myarg == 'nodetails': elif myarg == 'nodetails':
showDetails = False showDetails = False
elif myarg == 'csv': elif myarg == 'csv':
@@ -63005,6 +63044,9 @@ def updateDriveFileACLs(users, useDomainAdminAccess=False):
_checkFileIdEntityDomainAccess(fileIdEntity, useDomainAdminAccess) _checkFileIdEntityDomainAccess(fileIdEntity, useDomainAdminAccess)
if 'role' not in body: if 'role' not in body:
missingArgumentExit(f'role {formatChoiceList(DRIVEFILE_ACL_ROLES_MAP)}') missingArgumentExit(f'role {formatChoiceList(DRIVEFILE_ACL_ROLES_MAP)}')
updateKwargs = {'useDomainAdminAccess': useDomainAdminAccess}
if enforceExpansiveAccess is not None:
updateKwargs['enforceExpansiveAccess'] = enforceExpansiveAccess
printKeys, timeObjects = _getDriveFileACLPrintKeysTimeObjects() printKeys, timeObjects = _getDriveFileACLPrintKeysTimeObjects()
if csvPF and showTitles: if csvPF and showTitles:
csvPF.AddTitles(fileNameTitle) csvPF.AddTitles(fileNameTitle)
@@ -63042,7 +63084,7 @@ def updateDriveFileACLs(users, useDomainAdminAccess=False):
permission = callGAPI(drive.permissions(), 'update', permission = callGAPI(drive.permissions(), 'update',
bailOnInternalError=True, bailOnInternalError=True,
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+GAPI.DRIVE3_UPDATE_ACL_THROW_REASONS+[GAPI.FILE_NEVER_WRITABLE], throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+GAPI.DRIVE3_UPDATE_ACL_THROW_REASONS+[GAPI.FILE_NEVER_WRITABLE],
useDomainAdminAccess=useDomainAdminAccess, **updateKwargs,
fileId=fileId, permissionId=permissionId, removeExpiration=removeExpiration, fileId=fileId, permissionId=permissionId, removeExpiration=removeExpiration,
transferOwnership=body.get('role', '') == 'owner', body=body, fields='*', supportsAllDrives=True) transferOwnership=body.get('role', '') == 'owner', body=body, fields='*', supportsAllDrives=True)
if updateSheetProtectedRanges and mimeType == MIMETYPE_GA_SPREADSHEET: if updateSheetProtectedRanges and mimeType == MIMETYPE_GA_SPREADSHEET:
@@ -63138,7 +63180,7 @@ def createDriveFilePermissions(users, useDomainAdminAccess=False):
except ValueError: except ValueError:
return None return None
def _callbackCreatePermission(request_id, response, exception): def _callbackCreatePermission(request_id, _, exception):
ri = request_id.splitlines() ri = request_id.splitlines()
if int(ri[RI_J]) == 1: if int(ri[RI_J]) == 1:
entityPerformActionNumItems([Ent.DRIVE_FILE_OR_FOLDER_ID, ri[RI_ENTITY]], int(ri[RI_JCOUNT]), Ent.PERMITTEE, int(ri[RI_I]), int(ri[RI_COUNT])) entityPerformActionNumItems([Ent.DRIVE_FILE_OR_FOLDER_ID, ri[RI_ENTITY]], int(ri[RI_JCOUNT]), Ent.PERMITTEE, int(ri[RI_I]), int(ri[RI_COUNT]))
@@ -63286,11 +63328,12 @@ def doCreatePermissions():
createDriveFilePermissions([_getAdminEmail()], True) createDriveFilePermissions([_getAdminEmail()], True)
# gam [<UserTypeEntity>] delete drivefileacl <DriveFileEntity> <DriveFilePermissionIDorEmail> [asadmin] # gam [<UserTypeEntity>] delete drivefileacl <DriveFileEntity> <DriveFilePermissionIDorEmail> [asadmin]
# [updatesheetprotectedranges [<Boolean>]] # [updatesheetprotectedranges [<Boolean>]] [enforceexpansiveaccess [<Boolean>]]
# [showtitles] # [showtitles]
def deleteDriveFileACLs(users, useDomainAdminAccess=False): def deleteDriveFileACLs(users, useDomainAdminAccess=False):
fileIdEntity = getDriveFileEntity() fileIdEntity = getDriveFileEntity()
isEmail, permissionId = getPermissionId() isEmail, permissionId = getPermissionId()
enforceExpansiveAccess = None
showTitles = updateSheetProtectedRanges = False showTitles = updateSheetProtectedRanges = False
while Cmd.ArgumentsRemaining(): while Cmd.ArgumentsRemaining():
myarg = getArgument() myarg = getArgument()
@@ -63298,11 +63341,16 @@ def deleteDriveFileACLs(users, useDomainAdminAccess=False):
showTitles = getBoolean() showTitles = getBoolean()
elif myarg == 'updatesheetprotectedranges': elif myarg == 'updatesheetprotectedranges':
updateSheetProtectedRanges = getBoolean() updateSheetProtectedRanges = getBoolean()
elif myarg == 'enforceexpansiveaccess':
enforceExpansiveAccess = getBoolean()
elif myarg in ADMIN_ACCESS_OPTIONS: elif myarg in ADMIN_ACCESS_OPTIONS:
useDomainAdminAccess = True useDomainAdminAccess = True
else: else:
unknownArgumentExit() unknownArgumentExit()
_checkFileIdEntityDomainAccess(fileIdEntity, useDomainAdminAccess) _checkFileIdEntityDomainAccess(fileIdEntity, useDomainAdminAccess)
deleteKwargs = {'useDomainAdminAccess': useDomainAdminAccess}
if enforceExpansiveAccess is not None:
deleteKwargs['enforceExpansiveAccess'] = enforceExpansiveAccess
i, count, users = getEntityArgument(users) i, count, users = getEntityArgument(users)
for user in users: for user in users:
i += 1 i += 1
@@ -63335,7 +63383,8 @@ def deleteDriveFileACLs(users, useDomainAdminAccess=False):
break break
callGAPI(drive.permissions(), 'delete', callGAPI(drive.permissions(), 'delete',
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+GAPI.DRIVE3_DELETE_ACL_THROW_REASONS+[GAPI.FILE_NEVER_WRITABLE], throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+GAPI.DRIVE3_DELETE_ACL_THROW_REASONS+[GAPI.FILE_NEVER_WRITABLE],
useDomainAdminAccess=useDomainAdminAccess, fileId=fileId, permissionId=permissionId, supportsAllDrives=True) **deleteKwargs,
fileId=fileId, permissionId=permissionId, supportsAllDrives=True)
entityActionPerformed([Ent.USER, user, entityType, fileName, Ent.PERMISSION_ID, permissionId], j, jcount) entityActionPerformed([Ent.USER, user, entityType, fileName, Ent.PERMISSION_ID, permissionId], j, jcount)
if updateSheetProtectedRanges and mimeType == MIMETYPE_GA_SPREADSHEET: if updateSheetProtectedRanges and mimeType == MIMETYPE_GA_SPREADSHEET:
_updateSheetProtectedRangesACLchange(sheet, user, i, count, j, jcount, fileId, fileName, False, permission) _updateSheetProtectedRangesACLchange(sheet, user, i, count, j, jcount, fileId, fileName, False, permission)
@@ -63357,6 +63406,7 @@ def doDeleteDriveFileACLs():
# gam [<UserTypeEntity>] delete permissions <DriveFileEntity> <DriveFilePermissionIDEntity> [asadmin] # gam [<UserTypeEntity>] delete permissions <DriveFileEntity> <DriveFilePermissionIDEntity> [asadmin]
# <PermissionMatch>* [<PermissionMatchAction>] # <PermissionMatch>* [<PermissionMatchAction>]
# [enforceexpansiveaccess [<Boolean>]]
def deletePermissions(users, useDomainAdminAccess=False): def deletePermissions(users, useDomainAdminAccess=False):
def convertJSONPermissions(jsonPermissions): def convertJSONPermissions(jsonPermissions):
permissionIds = [] permissionIds = []
@@ -63366,7 +63416,7 @@ def deletePermissions(users, useDomainAdminAccess=False):
permissionIds.append(permission['id']) permissionIds.append(permission['id'])
return permissionIds return permissionIds
def _callbackDeletePermissionId(request_id, response, exception): def _callbackDeletePermissionId(request_id, _, exception):
ri = request_id.splitlines() ri = request_id.splitlines()
if int(ri[RI_J]) == 1: if int(ri[RI_J]) == 1:
entityPerformActionNumItems([Ent.DRIVE_FILE_OR_FOLDER_ID, ri[RI_ENTITY]], int(ri[RI_JCOUNT]), Ent.PERMISSION_ID, int(ri[RI_I]), int(ri[RI_COUNT])) entityPerformActionNumItems([Ent.DRIVE_FILE_OR_FOLDER_ID, ri[RI_ENTITY]], int(ri[RI_JCOUNT]), Ent.PERMISSION_ID, int(ri[RI_I]), int(ri[RI_COUNT]))
@@ -63391,7 +63441,8 @@ def deletePermissions(users, useDomainAdminAccess=False):
callGAPI(drive.permissions(), 'delete', callGAPI(drive.permissions(), 'delete',
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+GAPI.DRIVE3_DELETE_ACL_THROW_REASONS, throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+GAPI.DRIVE3_DELETE_ACL_THROW_REASONS,
retryReasons=[GAPI.SERVICE_LIMIT], retryReasons=[GAPI.SERVICE_LIMIT],
useDomainAdminAccess=useDomainAdminAccess, fileId=ri[RI_ENTITY], permissionId=ri[RI_ITEM], supportsAllDrives=True) useDomainAdminAccess=useDomainAdminAccess, enforceExpansiveAccess=enforceExpansiveAccess,
fileId=ri[RI_ENTITY], permissionId=ri[RI_ITEM], supportsAllDrives=True)
entityActionPerformed([Ent.DRIVE_FILE_OR_FOLDER_ID, ri[RI_ENTITY], Ent.PERMISSION_ID, ri[RI_ITEM]], int(ri[RI_J]), int(ri[RI_JCOUNT])) entityActionPerformed([Ent.DRIVE_FILE_OR_FOLDER_ID, ri[RI_ENTITY], Ent.PERMISSION_ID, ri[RI_ITEM]], int(ri[RI_J]), int(ri[RI_JCOUNT]))
except (GAPI.fileNotFound, GAPI.forbidden, GAPI.internalError, GAPI.insufficientFilePermissions, GAPI.unknownError, except (GAPI.fileNotFound, GAPI.forbidden, GAPI.internalError, GAPI.insufficientFilePermissions, GAPI.unknownError,
GAPI.badRequest, GAPI.cannotRemoveOwner, GAPI.cannotModifyInheritedTeamDrivePermission, GAPI.badRequest, GAPI.cannotRemoveOwner, GAPI.cannotModifyInheritedTeamDrivePermission,
@@ -63411,12 +63462,15 @@ def deletePermissions(users, useDomainAdminAccess=False):
jsonData = getJSON([]) jsonData = getJSON([])
PM = PermissionMatch() PM = PermissionMatch()
PM.SetDefaultMatch(False, {'role': 'owner'}) PM.SetDefaultMatch(False, {'role': 'owner'})
enforceExpansiveAccess = False
while Cmd.ArgumentsRemaining(): while Cmd.ArgumentsRemaining():
myarg = getArgument() myarg = getArgument()
if myarg in ADMIN_ACCESS_OPTIONS: if myarg in ADMIN_ACCESS_OPTIONS:
useDomainAdminAccess = True useDomainAdminAccess = True
elif PM and PM.ProcessArgument(myarg): elif PM and PM.ProcessArgument(myarg):
pass pass
elif myarg == 'enforceexpansiveaccess':
enforceExpansiveAccess = getBoolean()
else: else:
unknownArgumentExit() unknownArgumentExit()
_checkFileIdEntityDomainAccess(fileIdEntity, useDomainAdminAccess) _checkFileIdEntityDomainAccess(fileIdEntity, useDomainAdminAccess)
@@ -68647,7 +68701,7 @@ def deleteLabels(users, labelEntity):
http_status, reason, message = checkGAPIError(exception) http_status, reason, message = checkGAPIError(exception)
entityActionFailedWarning([Ent.USER, ri[RI_ENTITY], Ent.LABEL, labelIdToNameMap[ri[RI_ITEM]]], formatHTTPError(http_status, reason, message), int(ri[RI_J]), int(ri[RI_JCOUNT])) entityActionFailedWarning([Ent.USER, ri[RI_ENTITY], Ent.LABEL, labelIdToNameMap[ri[RI_ITEM]]], formatHTTPError(http_status, reason, message), int(ri[RI_J]), int(ri[RI_JCOUNT]))
def _callbackDeleteLabel(request_id, response, exception): def _callbackDeleteLabel(request_id, _, exception):
ri = request_id.splitlines() ri = request_id.splitlines()
if exception is None: if exception is None:
entityActionPerformed([Ent.USER, ri[RI_ENTITY], Ent.LABEL, labelIdToNameMap[ri[RI_ITEM]]], int(ri[RI_J]), int(ri[RI_JCOUNT])) entityActionPerformed([Ent.USER, ri[RI_ENTITY], Ent.LABEL, labelIdToNameMap[ri[RI_ITEM]]], int(ri[RI_J]), int(ri[RI_JCOUNT]))
@@ -69248,7 +69302,7 @@ def _processMessagesThreads(users, entityType):
GAPI.INVALID_MESSAGE_ID: Msg.INVALID_MESSAGE_ID, GAPI.INVALID_MESSAGE_ID: Msg.INVALID_MESSAGE_ID,
GAPI.FAILED_PRECONDITION: Msg.FAILED_PRECONDITION} GAPI.FAILED_PRECONDITION: Msg.FAILED_PRECONDITION}
def _callbackProcessMessage(request_id, response, exception): def _callbackProcessMessage(request_id, _, exception):
ri = request_id.splitlines() ri = request_id.splitlines()
if exception is None: if exception is None:
if not csvPF: if not csvPF: