Compare commits

..

2 Commits

Author SHA1 Message Date
Ross Scroggs
09be8b08f7 Updated commands that delete drive ACLs
Some checks failed
Build and test GAM / build (Win64, build, 10, VC-WIN64A, windows-2022) (push) Has been cancelled
Build and test GAM / build (aarch64, build, 3, linux-aarch64, ubuntu-24.04-arm) (push) Has been cancelled
Build and test GAM / build (aarch64, build, 4, linux-aarch64, ubuntu-22.04-arm) (push) Has been cancelled
Build and test GAM / build (aarch64, build, 6, linux-aarch64, ubuntu-22.04-arm, yes) (push) Has been cancelled
Build and test GAM / build (aarch64, build, 8, darwin64-arm64, macos-14) (push) Has been cancelled
Build and test GAM / build (aarch64, build, 9, darwin64-arm64, macos-15) (push) Has been cancelled
Build and test GAM / build (x86_64, build, 1, linux-x86_64, ubuntu-22.04) (push) Has been cancelled
Build and test GAM / build (x86_64, build, 2, linux-x86_64, ubuntu-24.04) (push) Has been cancelled
Build and test GAM / build (x86_64, build, 5, linux-x86_64, ubuntu-22.04, yes) (push) Has been cancelled
Build and test GAM / build (x86_64, build, 7, darwin64-x86_64, macos-13) (push) Has been cancelled
Build and test GAM / build (x86_64, test, 11, ubuntu-24.04, 3.10) (push) Has been cancelled
Build and test GAM / build (x86_64, test, 12, ubuntu-24.04, 3.11) (push) Has been cancelled
Build and test GAM / build (x86_64, test, 13, ubuntu-24.04, 3.12) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Check for Google Root CA Updates / check-apis (push) Has been cancelled
Build and test GAM / merge (push) Has been cancelled
Build and test GAM / publish (push) Has been cancelled
2025-03-28 10:44:42 -07:00
Ross Scroggs
10a91091f2 Added option transpose [<Boolean>] to redirect csv
Some checks are pending
Build and test GAM / build (Win64, build, 10, VC-WIN64A, windows-2022) (push) Waiting to run
Build and test GAM / build (aarch64, build, 3, linux-aarch64, ubuntu-24.04-arm) (push) Waiting to run
Build and test GAM / build (aarch64, build, 4, linux-aarch64, ubuntu-22.04-arm) (push) Waiting to run
Build and test GAM / build (aarch64, build, 6, linux-aarch64, ubuntu-22.04-arm, yes) (push) Waiting to run
Build and test GAM / build (aarch64, build, 8, darwin64-arm64, macos-14) (push) Waiting to run
Build and test GAM / build (aarch64, build, 9, darwin64-arm64, macos-15) (push) Waiting to run
Build and test GAM / build (x86_64, build, 1, linux-x86_64, ubuntu-22.04) (push) Waiting to run
Build and test GAM / build (x86_64, build, 2, linux-x86_64, ubuntu-24.04) (push) Waiting to run
Build and test GAM / build (x86_64, build, 5, linux-x86_64, ubuntu-22.04, yes) (push) Waiting to run
Build and test GAM / build (x86_64, build, 7, darwin64-x86_64, macos-13) (push) Waiting to run
Build and test GAM / build (x86_64, test, 11, ubuntu-24.04, 3.10) (push) Waiting to run
Build and test GAM / build (x86_64, test, 12, ubuntu-24.04, 3.11) (push) Waiting to run
Build and test GAM / build (x86_64, test, 13, ubuntu-24.04, 3.12) (push) Waiting to run
Build and test GAM / merge (push) Blocked by required conditions
Build and test GAM / publish (push) Blocked by required conditions
CodeQL / Analyze (python) (push) Waiting to run
Check for Google Root CA Updates / check-apis (push) Waiting to run
2025-03-27 20:42:27 -07:00
5 changed files with 55 additions and 16 deletions

View File

@@ -102,7 +102,7 @@ If an item contains spaces, it should be surrounded by ".
banana|basil|blueberry|flamingo|graphite|grape|
lavender|peacock|sage|tangerine|tomato
<FileFormat> ::=
csv|doc|dot|docx|dotx|epub|html|jpeg|jpg|mht|odp|ods|odt|
csv|doc|dot|docx|dotx|epub|html|jpeg|jpg|json|mht|odp|ods|odt|
pdf|png|ppt|pot|potx|pptx|rtf|svg|tsv|txt|xls|xlt|xlsx|xltx|zip|
ms|microsoft|openoffice|
<LabelColorHex> ::=
@@ -1276,8 +1276,8 @@ If the pattern {{Section}} appears in <FileName>, it will be replaced with the n
For redirect csv, the optional arguments must appear in the order shown.
<Redirect> ::=
redirect csv <FileName> [multiprocess] [append] [noheader] [charset <Charset>]
[columndelimiter <Character>] [noescapechar <Boolean>] [quotechar <Character>]
[sortheaders <StringList>] [timestampcolumn <String>]
[columndelimiter <Character>] [noescapechar [<Boolean>]] [quotechar <Character>]
[sortheaders <StringList>] [timestampcolumn <String>] [transpose [<Bopolean>]]
[todrive <ToDriveAttribute>*] |
redirect stdout <FileName> [multiprocess] [append] |
redirect stdout null [multiprocess] |

View File

@@ -1,7 +1,20 @@
7.05.17
Updated commands that delete drive ACLs to handle the following error:
```
ERROR: 403: cannotDeletePermission - The authenticated user does not have the required access to delete the permission.
```
7.05.16
Added option `transpose [<Boolean>]` to `redirect csv` that causes
GAM to transpose CSV output rows and columns. This will most useful
when a `countsonly` option is used in a `print` or `report` command.
7.05.15
Updated `gam <UserTypeEntity> get drivefile` and `gam <UserTypeEntity> create drivefile`
to allow downloading and uploading of Google App Scripts.
to allow downloading and uploading of Google Apps Scripts.
```
$ gam user user1@domain.com get drivefile 1ZY-YkS3E0OKipALra_XzfIh9cvxoILSbb8TRdHBFCpyB_mXI_J8FmjHv format json
User: user1@domain.com, Download 1 Drive File

View File

@@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki
"""
__author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
__version__ = '7.05.15'
__version__ = '7.05.17'
__license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
#pylint: disable=wrong-import-position
@@ -4113,8 +4113,8 @@ def SetGlobalVariables():
if checkArgumentPresent(Cmd.MULTIPROCESSEXIT_CMD):
_setMultiprocessExit()
# redirect csv <FileName> [multiprocess] [append] [noheader] [charset <CharSet>]
# [columndelimiter <Character>] [noescapechar <Boolean>] [quotechar <Character>]]
# [sortheaders <StringList>] [timestampcolumn <String>]
# [columndelimiter <Character>] [noescapechar [<Boolean>]] [quotechar <Character>]]
# [sortheaders <StringList>] [timestampcolumn <String>] [transpose [<Boolean>]]
# [todrive <ToDriveAttribute>*]
# redirect stdout <FileName> [multiprocess] [append]
# redirect stdout null
@@ -4139,6 +4139,8 @@ def SetGlobalVariables():
GM.Globals[GM.CSV_OUTPUT_SORT_HEADERS] = GC.Values[GC.CSV_OUTPUT_SORT_HEADERS] = getString(Cmd.OB_STRING_LIST, minLen=0).replace(',', ' ').split()
if checkArgumentPresent('timestampcolumn'):
GM.Globals[GM.CSV_OUTPUT_TIMESTAMP_COLUMN] = GC.Values[GC.CSV_OUTPUT_TIMESTAMP_COLUMN] = getString(Cmd.OB_STRING, minLen=0)
if checkArgumentPresent('transpose'):
GM.Globals[GM.CSV_OUTPUT_TRANSPOSE] = getBoolean()
_setCSVFile(filename, mode, encoding, writeHeader, multi)
GM.Globals[GM.CSVFILE][GM.REDIRECT_QUEUE_CSVPF] = CSVPrintFile()
if checkArgumentPresent('todrive'):
@@ -7794,6 +7796,7 @@ class CSVPrintFile():
def __init__(self, titles=None, sortTitles=None, indexedTitles=None):
self.rows = []
self.rowCount = 0
self.outputTranspose = GM.Globals[GM.CSV_OUTPUT_TRANSPOSE]
self.todrive = GM.Globals[GM.CSV_TODRIVE]
self.titlesSet = set()
self.titlesList = []
@@ -8993,6 +8996,22 @@ class CSVPrintFile():
self.JSONtitlesList = self.orderHeaders(self.JSONtitlesList)
titlesList = self.JSONtitlesList
normalizeSortHeaders()
if self.outputTranspose:
newRows = []
pivotKey = titlesList[0]
newTitlesList = [pivotKey]
newTitlesSet = set(newTitlesList)
for title in titlesList[1:]:
newRow = {pivotKey: title}
for row in self.rows:
pivotValue = row[pivotKey]
if pivotValue not in newTitlesSet:
newTitlesSet.add(pivotValue)
newTitlesList.append(pivotValue)
newRow[pivotValue] = row.get(title)
newRows.append(newRow)
titlesList = newTitlesList
self.rows = newRows
if (not self.todrive) or self.todrive['localcopy']:
if GM.Globals[GM.CSVFILE][GM.REDIRECT_NAME] == '-':
if GM.Globals[GM.STDOUT][GM.REDIRECT_MULTI_FD]:
@@ -58726,7 +58745,7 @@ def _copyPermissions(drive, user, i, count, j, jcount,
except (GAPI.notFound, GAPI.permissionNotFound,
GAPI.fileNotFound, GAPI.forbidden, GAPI.internalError, GAPI.insufficientFilePermissions, GAPI.unknownError,
GAPI.fileNeverWritable, GAPI.badRequest, GAPI.cannotRemoveOwner, GAPI.cannotModifyInheritedTeamDrivePermission,
GAPI.insufficientAdministratorPrivileges, GAPI.sharingRateLimitExceeded) as e:
GAPI.insufficientAdministratorPrivileges, GAPI.sharingRateLimitExceeded, GAPI.cannotDeletePermission) as e:
entityActionFailedWarning(kvList, str(e), k, kcount)
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
userDriveServiceNotEnabledWarning(user, str(e), i, count)
@@ -59757,7 +59776,7 @@ def _updateMoveFilePermissions(drive, user, i, count,
except (GAPI.notFound, GAPI.permissionNotFound,
GAPI.fileNotFound, GAPI.forbidden, GAPI.internalError, GAPI.insufficientFilePermissions, GAPI.unknownError,
GAPI.fileNeverWritable, GAPI.badRequest, GAPI.cannotRemoveOwner, GAPI.cannotModifyInheritedTeamDrivePermission,
GAPI.insufficientAdministratorPrivileges, GAPI.sharingRateLimitExceeded) as e:
GAPI.insufficientAdministratorPrivileges, GAPI.sharingRateLimitExceeded, GAPI.cannotDeletePermission) as e:
entityActionFailedWarning(kvList, str(e), k, kcount)
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
userDriveServiceNotEnabledWarning(user, str(e), i, count)
@@ -61460,7 +61479,7 @@ def transferDrive(users):
if showRetentionMessages:
entityActionPerformed([Ent.USER, sourceUser, childFileType, childFileName, Ent.ROLE, ownerRetainRoleBody['role']], j, jcount)
except (GAPI.fileNotFound, GAPI.forbidden, GAPI.internalError, GAPI.insufficientFilePermissions, GAPI.unknownError,
GAPI.badRequest, GAPI.sharingRateLimitExceeded, GAPI.cannotRemoveOwner) as e:
GAPI.badRequest, GAPI.sharingRateLimitExceeded, GAPI.cannotRemoveOwner, GAPI.cannotDeletePermission) as e:
entityActionFailedWarning([Ent.USER, sourceUser, childFileType, childFileName], str(e), j, jcount)
except GAPI.permissionNotFound:
entityDoesNotHaveItemWarning([Ent.USER, sourceUser, childFileType, childFileName, Ent.PERMISSION_ID, sourcePermissionId], j, jcount)
@@ -61513,7 +61532,7 @@ def transferDrive(users):
if showRetentionMessages:
entityActionPerformed([Ent.USER, sourceUser, childFileType, childFileName, Ent.ROLE, sourceUpdateRole['role']], j, jcount)
except (GAPI.fileNotFound, GAPI.forbidden, GAPI.internalError, GAPI.insufficientFilePermissions, GAPI.unknownError,
GAPI.badRequest, GAPI.sharingRateLimitExceeded, GAPI.cannotRemoveOwner) as e:
GAPI.badRequest, GAPI.sharingRateLimitExceeded, GAPI.cannotRemoveOwner, GAPI.cannotDeletePermission) as e:
entityActionFailedWarning([Ent.USER, ownerUser, childFileType, childFileName], str(e), j, jcount)
except GAPI.permissionNotFound:
entityDoesNotHaveItemWarning([Ent.USER, ownerUser, childFileType, childFileName, Ent.PERMISSION_ID, sourcePermissionId], j, jcount)
@@ -61537,7 +61556,7 @@ def transferDrive(users):
if showRetentionMessages:
entityActionPerformed([Ent.USER, targetUser, childFileType, childFileName, Ent.ROLE, targetInsertBody['role']], j, jcount)
except (GAPI.fileNotFound, GAPI.forbidden, GAPI.internalError, GAPI.insufficientFilePermissions, GAPI.unknownError,
GAPI.badRequest, GAPI.sharingRateLimitExceeded) as e:
GAPI.badRequest, GAPI.sharingRateLimitExceeded, GAPI.cannotDeletePermission) as e:
entityActionFailedWarning([Ent.USER, ownerUser, childFileType, childFileName], str(e), j, jcount)
except GAPI.invalidSharingRequest as e:
entityActionFailedWarning([Ent.USER, ownerUser, childFileType, childFileName], Ent.TypeNameMessage(Ent.PERMISSION_ID, targetPermissionId, str(e)), j, jcount)
@@ -62280,7 +62299,7 @@ def claimOwnership(users):
entityActionPerformed([Ent.USER, oldOwner, entityType, fileDesc, Ent.ROLE, sourceRetainRoleBody['role']], l, lcount)
except GAPI.permissionNotFound:
entityDoesNotHaveItemWarning([Ent.USER, oldOwner, entityType, fileDesc, Ent.PERMISSION_ID, oldOwnerPermissionId], l, lcount)
except (GAPI.badRequest, GAPI.insufficientFilePermissions) as e:
except (GAPI.badRequest, GAPI.insufficientFilePermissions, GAPI.cannotDeletePermission) as e:
entityActionFailedWarning([Ent.USER, oldOwner, entityType, fileDesc], str(e), l, lcount)
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
userDriveServiceNotEnabledWarning(user, str(e), i, count)
@@ -63452,7 +63471,7 @@ def deleteDriveFileACLs(users, useDomainAdminAccess=False):
_updateSheetProtectedRangesACLchange(sheet, user, i, count, j, jcount, fileId, fileName, False, permission)
except (GAPI.fileNotFound, GAPI.forbidden, GAPI.internalError, GAPI.insufficientFilePermissions, GAPI.unknownError,
GAPI.fileNeverWritable, GAPI.badRequest, GAPI.cannotRemoveOwner, GAPI.cannotModifyInheritedTeamDrivePermission,
GAPI.insufficientAdministratorPrivileges, GAPI.sharingRateLimitExceeded) as e:
GAPI.insufficientAdministratorPrivileges, GAPI.sharingRateLimitExceeded, GAPI.cannotDeletePermission) as e:
entityActionFailedWarning([Ent.USER, user, entityType, fileName], str(e), j, jcount)
except GAPI.notFound as e:
entityActionFailedWarning([Ent.USER, user, Ent.SHAREDDRIVE, fileName], str(e), j, jcount)
@@ -63508,7 +63527,7 @@ def deletePermissions(users, useDomainAdminAccess=False):
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,
GAPI.badRequest, GAPI.cannotRemoveOwner, GAPI.cannotModifyInheritedTeamDrivePermission,
GAPI.insufficientAdministratorPrivileges, GAPI.sharingRateLimitExceeded, GAPI.permissionNotFound,
GAPI.insufficientAdministratorPrivileges, GAPI.sharingRateLimitExceeded, GAPI.permissionNotFound, GAPI.cannotDeletePermission,
GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
entityActionFailedWarning([Ent.DRIVE_FILE_OR_FOLDER_ID, ri[RI_ENTITY], Ent.PERMISSION_ID, ri[RI_ITEM]], str(e), int(ri[RI_J]), int(ri[RI_JCOUNT]))
if int(ri[RI_J]) == int(ri[RI_JCOUNT]):

View File

@@ -37,6 +37,7 @@ CANNOT_CHANGE_OWNER_ACL = 'cannotChangeOwnerAcl'
CANNOT_CHANGE_OWN_PRIMARY_SUBSCRIPTION = 'cannotChangeOwnPrimarySubscription'
CANNOT_COPY_FILE = 'cannotCopyFile'
CANNOT_DELETE_ONLY_REVISION = 'cannotDeleteOnlyRevision'
CANNOT_DELETE_PERMISSION = 'cannotDeletePermission'
CANNOT_DELETE_PRIMARY_CALENDAR = 'cannotDeletePrimaryCalendar'
CANNOT_DELETE_PRIMARY_SENDAS = 'cannotDeletePrimarySendAs'
CANNOT_DELETE_RESOURCE_WITH_CHILDREN = 'cannotDeleteResourceWithChildren'
@@ -246,7 +247,7 @@ DRIVE3_UPDATE_ACL_THROW_REASONS = [BAD_REQUEST, INVALID_OWNERSHIP_TRANSFER, CANN
DRIVE3_DELETE_ACL_THROW_REASONS = [BAD_REQUEST, CANNOT_REMOVE_OWNER,
CANNOT_MODIFY_INHERITED_TEAMDRIVE_PERMISSION,
INSUFFICIENT_ADMINISTRATOR_PRIVILEGES, SHARING_RATE_LIMIT_EXCEEDED,
NOT_FOUND, PERMISSION_NOT_FOUND]
NOT_FOUND, PERMISSION_NOT_FOUND, CANNOT_DELETE_PERMISSION]
DRIVE3_MODIFY_LABEL_THROW_REASONS = DRIVE_USER_THROW_REASONS+[FILE_NOT_FOUND, NOT_FOUND, FORBIDDEN, INTERNAL_ERROR,
FILE_NEVER_WRITABLE, APPLY_LABEL_FORBIDDEN,
INSUFFICIENT_ADMINISTRATOR_PRIVILEGES, INSUFFICIENT_FILE_PERMISSIONS,
@@ -382,6 +383,8 @@ class cannotCopyFile(Exception):
pass
class cannotDeleteOnlyRevision(Exception):
pass
class cannotDeletePermission(Exception):
pass
class cannotDeletePrimaryCalendar(Exception):
pass
class cannotDeletePrimarySendAs(Exception):
@@ -676,6 +679,7 @@ REASON_EXCEPTION_MAP = {
CANNOT_CHANGE_OWN_PRIMARY_SUBSCRIPTION: cannotChangeOwnPrimarySubscription,
CANNOT_COPY_FILE: cannotCopyFile,
CANNOT_DELETE_ONLY_REVISION: cannotDeleteOnlyRevision,
CANNOT_DELETE_PERMISSION: cannotDeletePermission,
CANNOT_DELETE_PRIMARY_CALENDAR: cannotDeletePrimaryCalendar,
CANNOT_DELETE_PRIMARY_SENDAS: cannotDeletePrimarySendAs,
CANNOT_DELETE_RESOURCE_WITH_CHILDREN: cannotDeleteResourceWithChildren,

View File

@@ -85,6 +85,8 @@ CSV_OUTPUT_ROW_FILTER_MODE = 'corm'
CSV_OUTPUT_ROW_LIMIT = 'corl'
# Add timestamp column to CSV output file
CSV_OUTPUT_TIMESTAMP_COLUMN = 'cotc'
# Transpose output rows/columns
CSV_OUTPUT_TRANSPOSE = 'cotr'
# Output sort headers
CSV_OUTPUT_SORT_HEADERS = 'cosh'
# CSV todrive options
@@ -250,6 +252,7 @@ Globals = {
CSV_OUTPUT_ROW_LIMIT: 0,
CSV_OUTPUT_SORT_HEADERS: [],
CSV_OUTPUT_TIMESTAMP_COLUMN: None,
CSV_OUTPUT_TRANSPOSE: False,
CSV_TODRIVE: {},
CURRENT_API_SERVICES: {},
CURRENT_CLIENT_API: None,