Fixed bug in gam <UserTypeEntity> move drivefile <DriveFileEntity>

This commit is contained in:
Ross Scroggs
2025-12-02 16:25:56 -08:00
parent 3491bd2286
commit c36a71fe9e
3 changed files with 80 additions and 28 deletions

View File

@@ -6841,7 +6841,6 @@ gam <UserTypeEntity> move drivefile <DriveFileEntity> [newfilename <DriveFileNam
[excludepermissionsfromdomains|includepermissionsfromdomains <DomainNameList>] [excludepermissionsfromdomains|includepermissionsfromdomains <DomainNameList>]
(mappermissionsemail <EmailAddress> <EmailAddress>)* [mappermissionsemailfile <CSVFileInput> endcsv] (mappermissionsemail <EmailAddress> <EmailAddress>)* [mappermissionsemailfile <CSVFileInput> endcsv]
(mappermissionsdomain <DomainName> <DomainName>)* (mappermissionsdomain <DomainName> <DomainName>)*
[updatefilepermissions [<Boolean>]]
[retainsourcefolders [<Boolean>]] [retainsourcefolders [<Boolean>]]
[sendemailifrequired [<Boolean>]] [sendemailifrequired [<Boolean>]]
[verifyorganizer [<Boolean>]] [verifyorganizer [<Boolean>]]

View File

@@ -1,8 +1,25 @@
7.29.02
Fixed bug in `gam <UserTypeEntity> move drivefile <DriveFileEntity>` where the following options
were only applied to top level files or folders re-created in the destination. Now, domain
and email address mappings apply to all moved files/folders.
```
excludepermissionsfromdomains <DomainNameList>
includepermissionsfromdomains <DomainNameList>
mappermissionsdomain <DomainName> <DomainName>
mappermissionsemail <EmailAddress> <EmailAddress>
mappermissionsemailfile <CSVFileInput> endcsv
```
Upgraded to Python 3.14.1.
7.29.01 7.29.01
Added option `oneitemperrow` to `gam <UserTypeEntity> print calendars ... permissions` to have each of a Added option `oneitemperrow` to `gam <UserTypeEntity> print calendars ... permissions` to have each of a
calendar's permissions displayed on a separate row with all of the other calendar fields. calendar's permissions displayed on a separate row with all of the other calendar fields.
Updated `gam yubikey reset_piv` to handle YubiKey firmware updates that caused an error.
7.29.00 7.29.00
Added options `mappermissionsemail <EmailAddress> <EmailAddress>` and ` mappermissionsemailfile <CSVFileInput> endcsv` Added options `mappermissionsemail <EmailAddress> <EmailAddress>` and ` mappermissionsemailfile <CSVFileInput> endcsv`

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.01' __version__ = '7.29.02'
__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
@@ -61468,6 +61468,7 @@ def _copyPermissions(drive, user, i, count, j, jcount,
sourcePerms = getPermissions(fileId) sourcePerms = getPermissions(fileId)
if sourcePerms is None: if sourcePerms is None:
return return
Ind.Increment()
copySourcePerms = {} copySourcePerms = {}
deleteTargetPermIds = set() deleteTargetPermIds = set()
updateTargetPerms = {} updateTargetPerms = {}
@@ -61483,6 +61484,7 @@ def _copyPermissions(drive, user, i, count, j, jcount,
COPY_NONINHERITED_PERMISSIONS_SYNC_UPDATED_FOLDERS}: COPY_NONINHERITED_PERMISSIONS_SYNC_UPDATED_FOLDERS}:
targetPerms = getPermissions(newFileId) targetPerms = getPermissions(newFileId)
if targetPerms is None: if targetPerms is None:
Ind.Decrement()
return return
sourceNonInheritedPermIDs = getNonInheritedPermissions(copySourcePerms) sourceNonInheritedPermIDs = getNonInheritedPermissions(copySourcePerms)
targetNonInheritedPermIDs = getNonInheritedPermissions(targetPerms) targetNonInheritedPermIDs = getNonInheritedPermissions(targetPerms)
@@ -61520,7 +61522,6 @@ def _copyPermissions(drive, user, i, count, j, jcount,
deleteUpdateKwargs = {'useDomainAdminAccess': copyMoveOptions['useDomainAdminAccess']} deleteUpdateKwargs = {'useDomainAdminAccess': copyMoveOptions['useDomainAdminAccess']}
if entityType != Ent.SHAREDDRIVE: if entityType != Ent.SHAREDDRIVE:
deleteUpdateKwargs['enforceExpansiveAccess'] = copyMoveOptions['enforceExpansiveAccess'] deleteUpdateKwargs['enforceExpansiveAccess'] = copyMoveOptions['enforceExpansiveAccess']
Ind.Increment()
action = Act.Get() action = Act.Get()
Act.Set(Act.COPY) Act.Set(Act.COPY)
kcount = len(copySourcePerms) kcount = len(copySourcePerms)
@@ -62604,14 +62605,14 @@ def _updateMoveFilePermissions(drive, user, i, count,
sourcePerms = getPermissions(fileId) sourcePerms = getPermissions(fileId)
if sourcePerms is None: if sourcePerms is None:
return return
Ind.Increment()
deleteSourcePerms = {} deleteSourcePerms = {}
addSourcePerms = {} addSourcePerms = {}
for permissionId, permission in sourcePerms.items(): for permissionId, permission in sourcePerms.items():
kvList = permissionKVList(user, entityType, fileTitle, permission) kvList = permissionKVList(user, entityType, fileTitle, permission)
if isPermissionDeletable(kvList, permission): if permission.get('permissionDetails', {}).get('inherited', False):
pass continue
elif copyMoveOptions['mapPermissionsDomains'] or copyMoveOptions['mapPermissionsEmails']: if (not isPermissionDeletable(kvList, permission) and
(copyMoveOptions['mapPermissionsDomains'] or copyMoveOptions['mapPermissionsEmails'])):
mapPermissionsDomains(kvList, permission) mapPermissionsDomains(kvList, permission)
action = Act.Get() action = Act.Get()
kcount = len(deleteSourcePerms) kcount = len(deleteSourcePerms)
@@ -62638,7 +62639,6 @@ def _updateMoveFilePermissions(drive, user, i, count,
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e: except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
userDriveServiceNotEnabledWarning(user, str(e), i, count) userDriveServiceNotEnabledWarning(user, str(e), i, count)
_incrStatistic(statistics, stat) _incrStatistic(statistics, stat)
Ind.Decrement()
Act.Set(action) Act.Set(action)
return return
kcount = len(addSourcePerms) kcount = len(addSourcePerms)
@@ -62685,12 +62685,42 @@ def _updateMoveFilePermissions(drive, user, i, count,
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e: except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
userDriveServiceNotEnabledWarning(user, str(e), i, count) userDriveServiceNotEnabledWarning(user, str(e), i, count)
_incrStatistic(statistics, stat) _incrStatistic(statistics, stat)
Ind.Decrement()
Act.Set(action) Act.Set(action)
return return
Ind.Decrement()
Act.Set(action) Act.Set(action)
def _recursiveUpdateMovePermissions(drive, user, i, count,
fileId, fileTitle,
statistics, copyMoveOptions, sourceSearchArgs):
print('***recursiveUpdateMovePermissions', fileTitle)
_updateMoveFilePermissions(drive, user, i, count,
Ent.DRIVE_FOLDER, fileId, fileTitle,
statistics, STAT_FOLDER_PERMISSIONS_FAILED, copyMoveOptions)
sourceChildren = callGAPIpages(drive.files(), 'list', 'files',
throwReasons=GAPI.DRIVE_USER_THROW_REASONS,
retryReasons=[GAPI.UNKNOWN_ERROR],
q=WITH_PARENTS.format(fileId),
orderBy='folder desc,name,modifiedTime desc',
fields='nextPageToken,files(id,name,mimeType)',
pageSize=GC.Values[GC.DRIVE_MAX_RESULTS], **sourceSearchArgs)
kcount = len(sourceChildren)
if kcount > 0:
Ind.Increment()
k = 0
for child in sourceChildren:
k += 1
childId = child['id']
childName = child['name']
if child['mimeType'] == MIMETYPE_GA_FOLDER:
_recursiveUpdateMovePermissions(drive, user, i, count,
childId, childName,
statistics, copyMoveOptions, sourceSearchArgs)
else:
_updateMoveFilePermissions(drive, user, i, count,
Ent.DRIVE_FILE, childId, childName,
statistics, STAT_FILE_PERMISSIONS_FAILED, copyMoveOptions)
Ind.Decrement()
# gam <UserTypeEntity> move drivefile <DriveFileEntity> [newfilename <DriveFileName>] # gam <UserTypeEntity> move drivefile <DriveFileEntity> [newfilename <DriveFileName>]
# [summary [<Boolean>]] [showpermissionsmessages [<Boolean>]] # [summary [<Boolean>]] [showpermissionsmessages [<Boolean>]]
# [<DriveFileParentAttribute>] # [<DriveFileParentAttribute>]
@@ -62713,7 +62743,6 @@ def _updateMoveFilePermissions(drive, user, i, count,
# [excludepermissionsfromdomains|includepermissionsfromdomains <DomainNameList>] # [excludepermissionsfromdomains|includepermissionsfromdomains <DomainNameList>]
# (mappermissionsemail <EmailAddress> <EmailAddress>)* [mappermissionsemailfile <CSVFileInput> endcsv] # (mappermissionsemail <EmailAddress> <EmailAddress>)* [mappermissionsemailfile <CSVFileInput> endcsv]
# (mappermissionsdomain <DomainName> <DomainName>)* # (mappermissionsdomain <DomainName> <DomainName>)*
# [updatefilepermissions [<Boolean>]]
# [retainsourcefolders [<Boolean>]] # [retainsourcefolders [<Boolean>]]
# [sendemailifrequired [<Boolean>]] # [sendemailifrequired [<Boolean>]]
# [verifyorganizer [<Boolean>]] # [verifyorganizer [<Boolean>]]
@@ -62749,7 +62778,7 @@ def moveDriveFile(users):
copyFolderNonInheritedPermissions, copyFolderNonInheritedPermissions,
False) False)
source.pop('oldparents', None) source.pop('oldparents', None)
return (newParentId, newParentName, True) return (newParentId, newParentName, True, True)
# Merge parent folders # Merge parent folders
if atTop and copyMoveOptions['sourceIsMyDriveSharedDrive']: if atTop and copyMoveOptions['sourceIsMyDriveSharedDrive']:
pass pass
@@ -62778,11 +62807,11 @@ def moveDriveFile(users):
['copySubFolderInheritedPermissions', 'copyTopFolderInheritedPermissions'][atTop], ['copySubFolderInheritedPermissions', 'copyTopFolderInheritedPermissions'][atTop],
copyFolderNonInheritedPermissions, copyFolderNonInheritedPermissions,
False) False)
return (newFolderId, newFolderName, True) return (newFolderId, newFolderName, True, True)
entityActionFailedWarning(kvList+[Ent.DRIVE_FOLDER, newParentNameId], Msg.NOT_WRITABLE, j, jcount) entityActionFailedWarning(kvList+[Ent.DRIVE_FOLDER, newParentNameId], Msg.NOT_WRITABLE, j, jcount)
_incrStatistic(statistics, STAT_FOLDER_NOT_WRITABLE) _incrStatistic(statistics, STAT_FOLDER_NOT_WRITABLE)
copyMoveOptions['retainSourceFolders'] = True copyMoveOptions['retainSourceFolders'] = True
return (None, None, False) return (None, None, False, False)
elif copyMoveOptions['duplicateFolders'] == DUPLICATE_FOLDER_UNIQUE_NAME: elif copyMoveOptions['duplicateFolders'] == DUPLICATE_FOLDER_UNIQUE_NAME:
newFolderName = _getUniqueFilename(newFolderName, sourceMimeType, targetChildren) newFolderName = _getUniqueFilename(newFolderName, sourceMimeType, targetChildren)
elif copyMoveOptions['duplicateFolders'] == DUPLICATE_FOLDER_SKIP: elif copyMoveOptions['duplicateFolders'] == DUPLICATE_FOLDER_SKIP:
@@ -62793,7 +62822,7 @@ def moveDriveFile(users):
Msg.DUPLICATE, j, jcount) Msg.DUPLICATE, j, jcount)
_incrStatistic(statistics, STAT_FOLDER_DUPLICATE) _incrStatistic(statistics, STAT_FOLDER_DUPLICATE)
copyMoveOptions['retainSourceFolders'] = True copyMoveOptions['retainSourceFolders'] = True
return (None, None, False) return (None, None, False, False)
# Update parents on: not retain and MD->MD, SD->MD, SD->SD # Update parents on: not retain and MD->MD, SD->MD, SD->SD
if atTop and copyMoveOptions['sourceIsMyDriveSharedDrive']: if atTop and copyMoveOptions['sourceIsMyDriveSharedDrive']:
pass pass
@@ -62822,7 +62851,7 @@ def moveDriveFile(users):
[Ent.DRIVE_FOLDER, newParentNameId, Ent.DRIVE_FOLDER, f'{newFolderName}({folderId})'], [Ent.DRIVE_FOLDER, newParentNameId, Ent.DRIVE_FOLDER, f'{newFolderName}({folderId})'],
j, jcount) j, jcount)
_incrStatistic(statistics, STAT_FILE_COPIED_MOVED) _incrStatistic(statistics, STAT_FILE_COPIED_MOVED)
return (None, None, False) return (None, None, False, True)
except (GAPI.badRequest, GAPI.insufficientParentPermissions, GAPI.fileOwnerNotMemberOfTeamDrive, GAPI.fileOwnerNotMemberOfWriterDomain, except (GAPI.badRequest, GAPI.insufficientParentPermissions, GAPI.fileOwnerNotMemberOfTeamDrive, GAPI.fileOwnerNotMemberOfWriterDomain,
GAPI.fileWriterTeamDriveMoveInDisabled, GAPI.targetUserRoleLimitedByLicenseRestriction, GAPI.fileWriterTeamDriveMoveInDisabled, GAPI.targetUserRoleLimitedByLicenseRestriction,
GAPI.cannotMoveTrashedItemIntoTeamDrive, GAPI.cannotMoveTrashedItemOutOfTeamDrive, GAPI.cannotMoveTrashedItemIntoTeamDrive, GAPI.cannotMoveTrashedItemOutOfTeamDrive,
@@ -62832,7 +62861,7 @@ def moveDriveFile(users):
userDriveServiceNotEnabledWarning(user, str(e), i, count) userDriveServiceNotEnabledWarning(user, str(e), i, count)
_incrStatistic(statistics, STAT_FILE_FAILED) _incrStatistic(statistics, STAT_FILE_FAILED)
copyMoveOptions['retainSourceFolders'] = True copyMoveOptions['retainSourceFolders'] = True
return (None, None, False) return (None, None, False, False)
# Create new parent on: retain or MD->SD # Create new parent on: retain or MD->SD
source.pop('oldparents', None) source.pop('oldparents', None)
body = source.copy() body = source.copy()
@@ -62867,7 +62896,7 @@ def moveDriveFile(users):
['copySubFolderInheritedPermissions', 'copyTopFolderInheritedPermissions'][atTop], ['copySubFolderInheritedPermissions', 'copyTopFolderInheritedPermissions'][atTop],
['copySubFolderNonInheritedPermissions', 'copyTopFolderNonInheritedPermissions'][atTop], ['copySubFolderNonInheritedPermissions', 'copyTopFolderNonInheritedPermissions'][atTop],
True) True)
return (newFolderId, newFolderName, False) return (newFolderId, newFolderName, False, True)
except (GAPI.forbidden, GAPI.insufficientFilePermissions, GAPI.insufficientParentPermissions, except (GAPI.forbidden, GAPI.insufficientFilePermissions, GAPI.insufficientParentPermissions,
GAPI.internalError, GAPI.storageQuotaExceeded, GAPI.teamDriveFileLimitExceeded, GAPI.teamDriveHierarchyTooDeep, GAPI.internalError, GAPI.storageQuotaExceeded, GAPI.teamDriveFileLimitExceeded, GAPI.teamDriveHierarchyTooDeep,
GAPI.badRequest, GAPI.targetUserRoleLimitedByLicenseRestriction) as e: GAPI.badRequest, GAPI.targetUserRoleLimitedByLicenseRestriction) as e:
@@ -62876,7 +62905,7 @@ def moveDriveFile(users):
userDriveServiceNotEnabledWarning(user, str(e), i, count) userDriveServiceNotEnabledWarning(user, str(e), i, count)
_incrStatistic(statistics, STAT_FOLDER_FAILED) _incrStatistic(statistics, STAT_FOLDER_FAILED)
copyMoveOptions['retainSourceFolders'] = True copyMoveOptions['retainSourceFolders'] = True
return (None, None, False) return (None, None, False, False)
def _makeMoveShortcut(drive, user, k, kcount, entityType, childId, childName, newParentId, newParentName): def _makeMoveShortcut(drive, user, k, kcount, entityType, childId, childName, newParentId, newParentName):
kvList = [Ent.USER, user, entityType, f'{childName}({childId})'] kvList = [Ent.USER, user, entityType, f'{childName}({childId})']
@@ -62926,7 +62955,7 @@ def moveDriveFile(users):
def _moveFile(drive, user, i, count, k, kcount, entityType, childId, childName, newChildName, newParentId, newParentName, removeParents, body): def _moveFile(drive, user, i, count, k, kcount, entityType, childId, childName, newChildName, newParentId, newParentName, removeParents, body):
kvList = [Ent.USER, user, entityType, f'{childName}({childId})'] kvList = [Ent.USER, user, entityType, f'{childName}({childId})']
newParentNameId = f'{newParentName}({newParentId})' newParentNameId = f'{newParentName}({newParentId})'
if updateFilePermissions: if updateMovePermissions:
_updateMoveFilePermissions(drive, user, i, count, _updateMoveFilePermissions(drive, user, i, count,
entityType, childId, childName, entityType, childId, childName,
statistics, STAT_FILE_PERMISSIONS_FAILED, copyMoveOptions) statistics, STAT_FILE_PERMISSIONS_FAILED, copyMoveOptions)
@@ -62971,11 +63000,17 @@ def moveDriveFile(users):
def _recursiveFolderMove(drive, user, i, count, j, jcount, def _recursiveFolderMove(drive, user, i, count, j, jcount,
source, targetChildren, newFolderName, newParentId, newParentName, mergeParentModifiedTime, atTop): source, targetChildren, newFolderName, newParentId, newParentName, mergeParentModifiedTime, atTop):
folderId = source['id'] folderId = source['id']
newFolderId, newFolderName, existingTargetFolder = _cloneFolderMove(drive, user, i, count, j, jcount, newFolderId, newFolderName, existingTargetFolder, status = _cloneFolderMove(drive, user, i, count, j, jcount,
source, targetChildren, newFolderName, source, targetChildren, newFolderName,
newParentId, newParentName, mergeParentModifiedTime, newParentId, newParentName, mergeParentModifiedTime,
statistics, copyMoveOptions, atTop) statistics, copyMoveOptions, atTop)
if not status:
return
if newFolderId is None: if newFolderId is None:
if updateMovePermissions:
_recursiveUpdateMovePermissions(drive, user, i, count,
folderId, source['name'],
statistics, copyMoveOptions, sourceSearchArgs)
return return
movedFiles[newFolderId] = 1 movedFiles[newFolderId] = 1
sourceChildren = callGAPIpages(drive.files(), 'list', 'files', sourceChildren = callGAPIpages(drive.files(), 'list', 'files',
@@ -63062,7 +63097,7 @@ def moveDriveFile(users):
parentBody = {} parentBody = {}
parentParms = initDriveFileAttributes() parentParms = initDriveFileAttributes()
copyMoveOptions = initCopyMoveOptions(False) copyMoveOptions = initCopyMoveOptions(False)
newParentsSpecified = updateFilePermissions = False newParentsSpecified = False
movedFiles = {} movedFiles = {}
verifyOrganizer = True verifyOrganizer = True
while Cmd.ArgumentsRemaining(): while Cmd.ArgumentsRemaining():
@@ -63071,12 +63106,13 @@ def moveDriveFile(users):
pass pass
elif getDriveFileParentAttribute(myarg, parentParms): elif getDriveFileParentAttribute(myarg, parentParms):
newParentsSpecified = True newParentsSpecified = True
elif myarg == 'updatefilepermissions':
updateFilePermissions = getBoolean()
elif myarg == 'verifyorganizer': elif myarg == 'verifyorganizer':
verifyOrganizer = getBoolean() verifyOrganizer = getBoolean()
else: else:
unknownArgumentExit() unknownArgumentExit()
updateMovePermissions = (copyMoveOptions['excludePermissionsFromDomains'] or copyMoveOptions['includePermissionsFromDomains'] or
copyMoveOptions['mapPermissionsDomains'] or copyMoveOptions['mapPermissionsEmails'])
i, count, users = getEntityArgument(users) i, count, users = getEntityArgument(users)
for user in users: for user in users:
i += 1 i += 1