Improved create drivefileacl error handling

This commit is contained in:
Ross Scroggs
2026-02-17 16:15:22 -08:00
parent 51fafdb735
commit 991b1aa33b
2 changed files with 106 additions and 50 deletions

View File

@@ -1,3 +1,37 @@
7.34.04
Updated `gam <UserTypeEntity> create drivefileacl <DriveFileEntity> user <UserItem> role owner` to better
handle the case where the current owner of a file is suspended. Previously, the command was displayed as an error
even though the ownership was changed.
```
gam user currentowner@domain.com add drivefileacl <DriveFileID> user newowner@domain.com role owner
User: currentowner@domain.com, Add 1 Drive File/Folder ACL
User: currentowner@domain.com, Drive File/Folder ID: <DriveFileID>, Permission ID: newowner@domain.com, Add Failed: Sorry, the items were successfully shared but emails could not be sent to newowner@domain.com.
```
Now the command is displayed as a success with a note indicating that the ownership change email was not sent.
```
gam user currentowner@domain.com add drivefileacl <DriveFileID> user newowner@domain.com role owner
User: currentowner@domain.com, Add 1 Drive File/Folder ACL
User: currentowner@domain.com, Drive File/Folder ID: <DriveFileID>, Permission ID: newowner@domain.com, Added: Sorry, the items were successfully shared but emails could not be sent to newowner@domain.com.
New Owner
id: 10834698115409747890
type: user
emailAddress: newowner@domain.com
domain: domain.com
role: owner
permissionDetails:
role: writer
type: file
inherited: True
inheritedFrom: Unknown
role: owner
type: file
inherited: False
deleted: False
pendingOwner: False
```
7.34.03 7.34.03
Updated to Python 3.14.3 Updated to Python 3.14.3

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.34.03' __version__ = '7.34.04'
__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
@@ -66781,6 +66781,29 @@ def _checkFileIdEntityDomainAccess(fileIdEntity, useDomainAdminAccess):
# [sendemail|sendnotification] [emailmessage <String>] # [sendemail|sendnotification] [emailmessage <String>]
# [showtitles] [nodetails|(csv [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]])] # [showtitles] [nodetails|(csv [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]])]
def createDriveFileACL(users, useDomainAdminAccess=False): def createDriveFileACL(users, useDomainAdminAccess=False):
def _showResult(permission, showAction):
if updateSheetProtectedRanges and mimeType == MIMETYPE_GA_SPREADSHEET:
_updateSheetProtectedRangesACLchange(sheet, user, i, count, j, jcount, fileId, fileName, True, permission)
if csvPF:
baserow = {'Owner': user, 'id': fileId}
if showTitles:
baserow['name'] = fileName
row = baserow.copy()
_mapDrivePermissionNames(permission)
flattenJSON({'permission': permission}, flattened=row, timeObjects=timeObjects)
if not FJQC.formatJSON:
csvPF.WriteRowTitles(row)
elif csvPF.CheckRowTitles(row):
row = baserow.copy()
row['JSON'] = json.dumps(cleanJSON({'permission': permission}, timeObjects=timeObjects),
ensure_ascii=False, sort_keys=True)
csvPF.WriteRowNoFilter(row)
else:
if showAction:
entityActionPerformed([Ent.USER, user, entityType, fileName, Ent.PERMISSION_ID, permissionId], j, jcount)
if showDetails:
_showDriveFilePermission(permission, printKeys, timeObjects)
moveToNewOwnersRoot = False moveToNewOwnersRoot = False
sendNotificationEmail = showTitles = _transferOwnership = updateSheetProtectedRanges = False sendNotificationEmail = showTitles = _transferOwnership = updateSheetProtectedRanges = False
roleLocation = withLinkLocation = expirationLocation = None roleLocation = withLinkLocation = expirationLocation = None
@@ -66890,35 +66913,30 @@ def createDriveFileACL(users, useDomainAdminAccess=False):
_, sheet = buildGAPIServiceObject(API.SHEETS, user, i, count) _, sheet = buildGAPIServiceObject(API.SHEETS, user, i, count)
if not sheet: if not sheet:
break break
permission = callGAPI(drive.permissions(), 'create', try:
bailOnInternalError=True, permission = callGAPI(drive.permissions(), 'create',
throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+GAPI.DRIVE3_CREATE_ACL_THROW_REASONS+[GAPI.FILE_NEVER_WRITABLE], bailOnInternalError=True,
moveToNewOwnersRoot=moveToNewOwnersRoot, throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+GAPI.DRIVE3_CREATE_ACL_THROW_REASONS+[GAPI.FILE_NEVER_WRITABLE],
useDomainAdminAccess=useDomainAdminAccess, moveToNewOwnersRoot=moveToNewOwnersRoot,
fileId=fileId, sendNotificationEmail=sendNotificationEmail, emailMessage=emailMessage, useDomainAdminAccess=useDomainAdminAccess,
transferOwnership=_transferOwnership, body=body, fields='*', supportsAllDrives=True) fileId=fileId, sendNotificationEmail=sendNotificationEmail, emailMessage=emailMessage,
if updateSheetProtectedRanges and mimeType == MIMETYPE_GA_SPREADSHEET: transferOwnership=_transferOwnership, body=body, fields='*', supportsAllDrives=True)
_updateSheetProtectedRangesACLchange(sheet, user, i, count, j, jcount, fileId, fileName, True, permission) _showResult(permission, True)
if csvPF: except GAPI.invalidSharingRequest as e:
baserow = {'Owner': user, 'id': fileId} errMsg = str(e)
if showTitles: if ('successfully shared but emails could not be sent' not in errMsg) or ('emailAddress' not in body):
baserow['name'] = fileName entityActionFailedWarning([Ent.USER, user, entityType, fileName, Ent.PERMISSION_ID, permissionId], errMsg, j, jcount)
row = baserow.copy() else:
_mapDrivePermissionNames(permission) if not csvPF:
flattenJSON({'permission': permission}, flattened=row, timeObjects=timeObjects) entityActionPerformedMessage([Ent.USER, user, entityType, fileName, Ent.PERMISSION_ID, permissionId], errMsg, j, jcount)
if not FJQC.formatJSON: tempPermId = getPermissionIdForEmail(user, i, count, body['emailAddress'])
csvPF.WriteRowTitles(row) permission = callGAPI(drive.permissions(), 'get',
elif csvPF.CheckRowTitles(row): throwReasons=GAPI.DRIVE_ACCESS_THROW_REASONS+[GAPI.BAD_REQUEST, GAPI.PERMISSION_NOT_FOUND, GAPI.INSUFFICIENT_ADMINISTRATOR_PRIVILEGES],
row = baserow.copy() useDomainAdminAccess=useDomainAdminAccess,
row['JSON'] = json.dumps(cleanJSON({'permission': permission}, timeObjects=timeObjects), fileId=fileId, permissionId=tempPermId, fields='*', supportsAllDrives=True)
ensure_ascii=False, sort_keys=True) _showResult(permission, False)
csvPF.WriteRowNoFilter(row)
else:
entityActionPerformed([Ent.USER, user, entityType, fileName, Ent.PERMISSION_ID, permissionId], j, jcount)
if showDetails:
_showDriveFilePermission(permission, printKeys, timeObjects)
except (GAPI.badRequest, GAPI.invalid, GAPI.fileNotFound, GAPI.forbidden, GAPI.internalError, except (GAPI.badRequest, GAPI.invalid, GAPI.fileNotFound, GAPI.forbidden, GAPI.internalError,
GAPI.cannotSetExpiration, GAPI.cannotSetExpirationOnAnyoneOrDomain, GAPI.permissionNotFound, GAPI.cannotSetExpiration, GAPI.cannotSetExpirationOnAnyoneOrDomain,
GAPI.expirationDateNotAllowedForSharedDriveMembers, GAPI.expirationDatesMustBeInTheFuture, GAPI.expirationDateNotAllowedForSharedDriveMembers, GAPI.expirationDatesMustBeInTheFuture,
GAPI.insufficientFilePermissions, GAPI.unknownError, GAPI.ownershipChangeAcrossDomainNotPermitted, GAPI.insufficientFilePermissions, GAPI.unknownError, GAPI.ownershipChangeAcrossDomainNotPermitted,
GAPI.teamDriveDomainUsersOnlyRestriction, GAPI.teamDriveTeamMembersOnlyRestriction, GAPI.teamDriveDomainUsersOnlyRestriction, GAPI.teamDriveTeamMembersOnlyRestriction,
@@ -66932,7 +66950,7 @@ def createDriveFileACL(users, useDomainAdminAccess=False):
GAPI.fileOrganizerOnNonTeamDriveNotSupported, GAPI.fileOrganizerOnNonTeamDriveNotSupported,
GAPI.cannotModifyInheritedPermission, GAPI.cannotModifyInheritedPermission,
GAPI.teamDrivesFolderSharingNotSupported, GAPI.invalidLinkVisibility, GAPI.teamDrivesFolderSharingNotSupported, GAPI.invalidLinkVisibility,
GAPI.invalidSharingRequest, GAPI.fileNeverWritable, GAPI.abusiveContentRestriction) as e: GAPI.fileNeverWritable, GAPI.abusiveContentRestriction) as e:
entityActionFailedWarning([Ent.USER, user, entityType, fileName, Ent.PERMISSION_ID, permissionId], str(e), j, jcount) entityActionFailedWarning([Ent.USER, user, entityType, fileName, Ent.PERMISSION_ID, permissionId], str(e), j, jcount)
except GAPI.notFound as e: except GAPI.notFound as e:
entityActionFailedWarning([Ent.USER, user, Ent.SHAREDDRIVE, fileName], str(e), j, jcount) entityActionFailedWarning([Ent.USER, user, Ent.SHAREDDRIVE, fileName], str(e), j, jcount)
@@ -66951,6 +66969,29 @@ def doCreateDriveFileACL():
# [updatesheetprotectedranges [<Boolean>]] [enforceexpansiveaccess [<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):
def _showResult(permission, showAction):
if updateSheetProtectedRanges and mimeType == MIMETYPE_GA_SPREADSHEET:
_updateSheetProtectedRangesACLchange(sheet, user, i, count, j, jcount, fileId, fileName, True, permission)
if csvPF:
baserow = {'Owner': user, 'id': fileId}
if showTitles:
baserow['name'] = fileName
row = baserow.copy()
_mapDrivePermissionNames(permission)
flattenJSON({'permission': permission}, flattened=row, timeObjects=timeObjects)
if not FJQC.formatJSON:
csvPF.WriteRowTitles(row)
elif csvPF.CheckRowTitles(row):
row = baserow.copy()
row['JSON'] = json.dumps(cleanJSON({'permission': permission}, timeObjects=timeObjects),
ensure_ascii=False, sort_keys=True)
csvPF.WriteRowNoFilter(row)
else:
if showAction:
entityActionPerformed([Ent.USER, user, entityType, fileName, Ent.PERMISSION_ID, permissionId], j, jcount)
if showDetails:
_showDriveFilePermission(permission, printKeys, timeObjects)
fileIdEntity = getDriveFileEntity() fileIdEntity = getDriveFileEntity()
isEmail, permissionId = getPermissionId() isEmail, permissionId = getPermissionId()
enforceExpansiveAccess = GC.Values[GC.ENFORCE_EXPANSIVE_ACCESS] enforceExpansiveAccess = GC.Values[GC.ENFORCE_EXPANSIVE_ACCESS]
@@ -67030,26 +67071,7 @@ def updateDriveFileACLs(users, useDomainAdminAccess=False):
useDomainAdminAccess=useDomainAdminAccess, enforceExpansiveAccess=enforceExpansiveAccess, useDomainAdminAccess=useDomainAdminAccess, enforceExpansiveAccess=enforceExpansiveAccess,
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: _showResult(permission, True)
_updateSheetProtectedRangesACLchange(sheet, user, i, count, j, jcount, fileId, fileName, True, permission)
if csvPF:
baserow = {'Owner': user, 'id': fileId}
if showTitles:
baserow['name'] = fileName
row = baserow.copy()
_mapDrivePermissionNames(permission)
flattenJSON({'permission': permission}, flattened=row, timeObjects=timeObjects)
if not FJQC.formatJSON:
csvPF.WriteRowTitles(row)
elif csvPF.CheckRowTitles(row):
row = baserow.copy()
row['JSON'] = json.dumps(cleanJSON({'permission': permission}, timeObjects=timeObjects),
ensure_ascii=False, sort_keys=True)
csvPF.WriteRowNoFilter(row)
else:
entityActionPerformed([Ent.USER, user, entityType, fileName, Ent.PERMISSION_ID, permissionId], j, jcount)
if showDetails:
_showDriveFilePermission(permission, printKeys, timeObjects)
except (GAPI.fileNotFound, GAPI.forbidden, GAPI.internalError, GAPI.insufficientFilePermissions, GAPI.unknownError, except (GAPI.fileNotFound, GAPI.forbidden, GAPI.internalError, GAPI.insufficientFilePermissions, GAPI.unknownError,
GAPI.cannotSetExpiration, GAPI.cannotSetExpirationOnAnyoneOrDomain, GAPI.cannotSetExpiration, GAPI.cannotSetExpirationOnAnyoneOrDomain,
GAPI.expirationDateNotAllowedForSharedDriveMembers, GAPI.expirationDatesMustBeInTheFuture, GAPI.expirationDateNotAllowedForSharedDriveMembers, GAPI.expirationDatesMustBeInTheFuture,