mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-19 05:31:37 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1dfcc877d | ||
|
|
f0fa7aa25e | ||
|
|
2445b4f92d | ||
|
|
eb2b9f0e4f | ||
|
|
91c0d66a14 | ||
|
|
0dd05e7b93 | ||
|
|
da93cdbc06 | ||
|
|
cf750e0d58 | ||
|
|
ddda0593f2 | ||
|
|
7798504282 |
@@ -3514,13 +3514,6 @@ gam print guardian|guardians [todrive <ToDriveAttribute>*] [accepted|invitations
|
||||
[showstudentemails]
|
||||
[formatjson [quotechar <Character>]]
|
||||
|
||||
# Business Profile Accounts
|
||||
|
||||
gam show businessprofileaccounts
|
||||
[type locationgroup|organization|personal|usergroup]
|
||||
gam print businessprofileaccounts [todrive <ToDriveAttribute>*]
|
||||
[type locationgroup|organization|personal|usergroup]
|
||||
|
||||
# Classroom User Profiles
|
||||
|
||||
gam <UserTypeEntity> print classroomprofile [todrive <ToDriveAttribute>*]
|
||||
@@ -6100,6 +6093,13 @@ gam <UserTypeEntity> show backupcodes|verificationcodes
|
||||
gam <UserTypeEntity> print backupcodes|verificationcodes [todrive <ToDriveAttribute>*]
|
||||
[delimiter <Character>] [countsonly]
|
||||
|
||||
# Users - Business Profile Accounts
|
||||
|
||||
gam <UserTypeEntity> show businessprofileaccounts
|
||||
[type locationgroup|organization|personal|usergroup]
|
||||
gam <UserTypeEntity> print businessprofileaccounts [todrive <ToDriveAttribute>*]
|
||||
[type locationgroup|organization|personal|usergroup]
|
||||
|
||||
# Users - Calendars
|
||||
|
||||
<CalendarACLRole> ::= editor|freebusy|freebusyreader|owner|reader|writer
|
||||
|
||||
@@ -1,3 +1,25 @@
|
||||
7.40.02
|
||||
|
||||
Updated `gam info|print cigroups` and `gam print|show cigroup-members` to handle trap caused
|
||||
by API returning invalid member data; `preferredMemberKey` with no `id`.
|
||||
|
||||
7.40.01
|
||||
|
||||
Updated `gam <UserTypeEntity> print filelist|filecounts` to handle the `permissionDetails` subfield
|
||||
of the `permissions` field for My Drives; this useful when trying to display permission inheritance.
|
||||
An additional API call per file is required to get the `permissionDetails` subfield.
|
||||
```
|
||||
gam user user@domain.com print filelist fields id,name,mimetype,basicpermissions,permissiondetails oneitemperrow
|
||||
gam user user@domain.com print filelist fields id,name,mimetype,basicpermissions,permissiondetails pm inherited false em pmfilter oneitemperrow
|
||||
```
|
||||
|
||||
7.40.00
|
||||
|
||||
Updated `gam print|show businessprofileaccounts` (client access) to
|
||||
`gam <UserTypeEntity> print|show businessprofileaccounts` (service account access).
|
||||
You'll need to run `gam user user@domain.com update serviceaccount` and
|
||||
select `2) Business Account Management API`.
|
||||
|
||||
7.39.08
|
||||
|
||||
Fixed bug in `gam oauth create` that caused a trap when `0) Business Account Management API` was selected.
|
||||
|
||||
@@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki
|
||||
"""
|
||||
|
||||
__author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
|
||||
__version__ = '7.39.08'
|
||||
__version__ = '7.40.02'
|
||||
__license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
||||
|
||||
# pylint: disable=wrong-import-position
|
||||
@@ -6118,9 +6118,12 @@ def _checkCIMemberCategory(member, memberDisplayOptions):
|
||||
return True
|
||||
|
||||
def getCIGroupMemberRoleFixType(member):
|
||||
''' fixes missing type and returns the highest role of member '''
|
||||
''' fixes missing type/id and returns the highest role of member '''
|
||||
if 'type' not in member:
|
||||
if member['preferredMemberKey']['id'] == GC.Values[GC.CUSTOMER_ID]:
|
||||
if 'id' not in member['preferredMemberKey']:
|
||||
member['preferredMemberKey']['id'] = GC.Values[GC.CUSTOMER_ID]
|
||||
member['type'] = Ent.TYPE_CUSTOMER
|
||||
elif member['preferredMemberKey']['id'] == GC.Values[GC.CUSTOMER_ID]:
|
||||
member['type'] = Ent.TYPE_CUSTOMER
|
||||
else:
|
||||
member['type'] = Ent.TYPE_OTHER
|
||||
@@ -37196,14 +37199,14 @@ def doUpdateCIGroups():
|
||||
throwReasons=GAPI.MEMBERS_THROW_REASONS+[GAPI.DUPLICATE, GAPI.MEMBER_NOT_FOUND, GAPI.RESOURCE_NOT_FOUND,
|
||||
GAPI.INVALID_MEMBER, GAPI.CYCLIC_MEMBERSHIPS_NOT_ALLOWED,
|
||||
GAPI.CONDITION_NOT_MET, GAPI.FAILED_PRECONDITION, GAPI.PERMISSION_DENIED,
|
||||
GAPI.ALREADY_EXISTS, GAPI.CONFLICT],
|
||||
GAPI.ALREADY_EXISTS, GAPI.INVALID_ARGUMENT, GAPI.CONFLICT],
|
||||
parent=group, body=body, fields='')
|
||||
_showSuccess(group, member, role, expireTime, j, jcount)
|
||||
except (GAPI.groupNotFound, GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.invalid, GAPI.forbidden):
|
||||
entityUnknownWarning(entityType, group, i, count)
|
||||
except (GAPI.duplicate, GAPI.memberNotFound, GAPI.resourceNotFound,
|
||||
GAPI.invalidMember, GAPI.cyclicMembershipsNotAllowed, GAPI.conditionNotMet,
|
||||
GAPI.failedPrecondition, GAPI.permissionDenied, GAPI.alreadyExists) as e:
|
||||
GAPI.failedPrecondition, GAPI.permissionDenied, GAPI.alreadyExists, GAPI.invalidArgument) as e:
|
||||
_showFailure(group, member, role, str(e), j, jcount)
|
||||
except GAPI.conflict:
|
||||
_showSuccess(group, member, role, expireTime, j, jcount, Msg.ACTION_MAY_BE_DELAYED)
|
||||
@@ -49336,13 +49339,12 @@ PROFILE_ACCOUNT_TYPE_MAP = {
|
||||
'usergroup': 'USER_GROUP',
|
||||
}
|
||||
|
||||
# gam show businessprofileaccounts
|
||||
# gam <UserTypeEntity> show businessprofileaccounts
|
||||
# [type locationgroup|organization|personal|usergroup]
|
||||
# gam print businessprofileaccounts [todrive <ToDriveAttribute>*]
|
||||
# gam <UserTypeEntity> print businessprofileaccounts [todrive <ToDriveAttribute>*]
|
||||
# [type locationgroup|organization|personal|usergroup]
|
||||
def doPrintShowBusinessProfileAccounts():
|
||||
bp = buildGAPIObject(API.BUSINESSACCOUNTMANAGEMENT)
|
||||
csvPF = CSVPrintFile(['name', 'accountName']) if Act.csvFormat() else None
|
||||
def printShowBusinessProfileAccounts(users):
|
||||
csvPF = CSVPrintFile(['User', 'name', 'accountName']) if Act.csvFormat() else None
|
||||
kwargs = {}
|
||||
while Cmd.ArgumentsRemaining():
|
||||
myarg = getArgument()
|
||||
@@ -49352,25 +49354,40 @@ def doPrintShowBusinessProfileAccounts():
|
||||
kwargs['filter'] = f'type={getChoice(PROFILE_ACCOUNT_TYPE_MAP, mapChoice=True)}'
|
||||
else:
|
||||
unknownArgumentExit()
|
||||
try:
|
||||
accounts = callGAPIpages(bp.accounts(), 'list', 'accounts',
|
||||
throwReasons=[GAPI.PERMISSION_DENIED],
|
||||
**kwargs)
|
||||
except GAPI.permissionDenied as e:
|
||||
accessErrorExitNonDirectory(API.BUSINESSACCOUNTMANAGEMENT, str(e))
|
||||
if not csvPF:
|
||||
count = len(accounts)
|
||||
i = 0
|
||||
for account in sorted(accounts, key=lambda k: k['name']):
|
||||
i += 1
|
||||
printKeyValueListWithCount(['Account', account['name']], i, count)
|
||||
i, count, users = getEntityArgument(users)
|
||||
for user in users:
|
||||
i += 1
|
||||
user, bp = buildGAPIServiceObject(API.BUSINESSACCOUNTMANAGEMENT, user, i, count)
|
||||
if not bp:
|
||||
continue
|
||||
if csvPF:
|
||||
printGettingAllEntityItemsForWhom(Ent.BUSINESS_PROFILE_ACCOUNT, user, i, count, query=kwargs.get('filter'))
|
||||
pageMessage = getPageMessageForWhom()
|
||||
else:
|
||||
pageMessage = None
|
||||
try:
|
||||
accounts = callGAPIpages(bp.accounts(), 'list', 'accounts',
|
||||
pageMessage=pageMessage,
|
||||
throwReasons=[GAPI.PERMISSION_DENIED],
|
||||
**kwargs)
|
||||
except GAPI.permissionDenied as e:
|
||||
accessErrorExitNonDirectory(API.BUSINESSACCOUNTMANAGEMENT, str(e))
|
||||
if not csvPF:
|
||||
jcount = len(accounts)
|
||||
entityPerformActionNumItems([Ent.USER, user], jcount, Ent.BUSINESS_PROFILE_ACCOUNT, i, count)
|
||||
Ind.Increment()
|
||||
showJSON(None, account)
|
||||
j = 0
|
||||
for account in sorted(accounts, key=lambda k: k['name']):
|
||||
j += 1
|
||||
printKeyValueListWithCount(['Account', account['name']], j, jcount)
|
||||
Ind.Increment()
|
||||
showJSON(None, account)
|
||||
Ind.Decrement()
|
||||
Ind.Decrement()
|
||||
else:
|
||||
for account in accounts:
|
||||
row = flattenJSON(account, flattened={'name': account['name'], 'accountName': account['accountName']})
|
||||
csvPF.WriteRowTitles(row)
|
||||
else:
|
||||
for account in accounts:
|
||||
row = flattenJSON(account, flattened={'User': user, 'name': account['name'], 'accountName': account['accountName']})
|
||||
csvPF.WriteRowTitles(row)
|
||||
if csvPF:
|
||||
csvPF.writeCSVfile('Business Profile Accounts')
|
||||
|
||||
@@ -57508,7 +57525,8 @@ def _mapDriveInfo(f_file, parentsSubFields, showParentsIdsAsList):
|
||||
|
||||
DRIVEFILE_BASIC_PERMISSION_FIELDS = [
|
||||
'displayName', 'id', 'emailAddress', 'domain', 'role', 'type',
|
||||
'allowFileDiscovery', 'expirationTime', 'deleted', 'permissionDetails' #permissionDetails must be last
|
||||
'allowFileDiscovery', 'expirationTime', 'deleted', 'inheritedPermissionsDisabled',
|
||||
'permissionDetails' #permissionDetails must be last
|
||||
]
|
||||
|
||||
DRIVE_FIELDS_CHOICE_MAP = {
|
||||
@@ -57517,7 +57535,7 @@ DRIVE_FIELDS_CHOICE_MAP = {
|
||||
'appproperties': 'appProperties',
|
||||
'basicpermissions': ['permissions.displayName', 'permissions.id', 'permissions.emailAddress', 'permissions.domain',
|
||||
'permissions.role', 'permissions.type', 'permissions.allowFileDiscovery',
|
||||
'permissions.expirationTime', 'permissions.deleted'],
|
||||
'permissions.expirationTime', 'permissions.deleted', 'permissions.inheritedPermissionsDisabled'],
|
||||
'cancomment': 'capabilities.canComment',
|
||||
'canreadrevisions': 'capabilities.canReadRevisions',
|
||||
'capabilities': 'capabilities',
|
||||
@@ -57881,19 +57899,37 @@ def _setSkipObjects(skipObjects, skipTitles, fieldsList):
|
||||
skipObjects.add(field)
|
||||
fieldsList.append('parents')
|
||||
|
||||
def _setGetPermissionsForSharedDrives(fieldsList):
|
||||
getPermissionsForSharedDrives = False
|
||||
permissionsFieldsList = []
|
||||
def _setGetPermissionsForMyDriveSharedDrives(fieldsList, permissionsFieldsList):
|
||||
getPermissionDetailsForMyDrive = getPermissionsForSharedDrives = False
|
||||
permissionsFields = None
|
||||
for field in fieldsList:
|
||||
if field.startswith('permissions'):
|
||||
getPermissionsForSharedDrives = True
|
||||
if field.find('.') != -1:
|
||||
field, subField = field.split('.', 1)
|
||||
permissionsFieldsList.append(subField)
|
||||
if getPermissionsForSharedDrives:
|
||||
else:
|
||||
permissionsFieldsList.append('*')
|
||||
if permissionsFieldsList:
|
||||
if 'permissionDetails' in permissionsFieldsList:
|
||||
getPermissionDetailsForMyDrive = True
|
||||
getPermissionsForSharedDrives = True
|
||||
permissionsFields = getItemFieldsFromFieldsList('permissions', permissionsFieldsList, True)
|
||||
return (getPermissionsForSharedDrives, permissionsFields)
|
||||
return (getPermissionDetailsForMyDrive, getPermissionsForSharedDrives, permissionsFields)
|
||||
|
||||
# Do file permissions have to be gotten by API call?
|
||||
def _setGetCheckFilePermissions(fileinfo, getPermissionDetailsForMyDrive, getPermissionsForSharedDrives, driveId, DLP):
|
||||
filePerms = fileinfo.get('permissions')
|
||||
if getPermissionsForSharedDrives and driveId and not filePerms:
|
||||
return True
|
||||
if getPermissionDetailsForMyDrive:
|
||||
return True
|
||||
if DLP.PM.checkDetails:
|
||||
if not filePerms:
|
||||
return True
|
||||
permDetails = filePerms[0].get('permissionDetails')
|
||||
if (not permDetails) or 'inherited' not in permDetails[0]:
|
||||
return True
|
||||
return False
|
||||
|
||||
SHOWLABELS_CHOICES = {'details', 'ids'}
|
||||
|
||||
@@ -57939,6 +57975,7 @@ def showFileInfo(users):
|
||||
returnIdOnly = showParentsIdsAsList = showNoParents = stripCRsFromName = False
|
||||
pathDelimiter = '/'
|
||||
showLabels = None
|
||||
permissionsFieldsList = []
|
||||
simpleLists = []
|
||||
skipObjects = set()
|
||||
fileIdEntity = getDriveFileEntity()
|
||||
@@ -57962,8 +57999,7 @@ def showFileInfo(users):
|
||||
elif myarg == 'showlabels':
|
||||
showLabels = getChoice(SHOWLABELS_CHOICES)
|
||||
elif myarg == 'showshareddrivepermissions':
|
||||
getPermissionsForSharedDrives = True
|
||||
permissionsFields = f'nextPageToken,permissions({",".join(DRIVEFILE_BASIC_PERMISSION_FIELDS)})'
|
||||
permissionsFieldsList = DRIVEFILE_BASIC_PERMISSION_FIELDS.copy()
|
||||
elif myarg == 'returnidonly':
|
||||
returnIdOnly = True
|
||||
elif myarg == 'followshortcuts':
|
||||
@@ -57972,9 +58008,8 @@ def showFileInfo(users):
|
||||
pass
|
||||
else:
|
||||
FJQC.GetFormatJSON(myarg)
|
||||
_, getPermissionsForSharedDrives, permissionsFields = _setGetPermissionsForMyDriveSharedDrives(DFF.fieldsList, permissionsFieldsList)
|
||||
if DFF.fieldsList:
|
||||
if not getPermissionsForSharedDrives:
|
||||
getPermissionsForSharedDrives, permissionsFields = _setGetPermissionsForSharedDrives(DFF.fieldsList)
|
||||
_setSelectionFields()
|
||||
if followShortcuts:
|
||||
DFF.fieldsList.extend(['mimeType', 'shortcutDetails'])
|
||||
@@ -58087,7 +58122,7 @@ def showFileInfo(users):
|
||||
if not FJQC.formatJSON:
|
||||
showJSON(None, result, skipObjects=skipObjects, timeObjects=DRIVE_TIME_OBJECTS, simpleLists=simpleLists,
|
||||
dictObjectsKey={'owners': 'displayName', 'fields': 'id', 'labels': 'id', 'user': 'emailAddress', 'parents': 'id',
|
||||
'permissions': 'displayName'})
|
||||
'permissions': 'displayName', 'permissionDetails': 'inherited'})
|
||||
Ind.Decrement()
|
||||
else:
|
||||
printLine(json.dumps(cleanJSON(result, skipObjects=skipObjects, timeObjects=DRIVE_TIME_OBJECTS), ensure_ascii=False, sort_keys=True))
|
||||
@@ -58836,6 +58871,7 @@ class PermissionMatch():
|
||||
self.permissionMatchKeep = self.permissionMatchOr = True
|
||||
self.permissionFields = set()
|
||||
self.clearDefaultMatch = False
|
||||
self.checkDetails = False
|
||||
|
||||
def GetMatch(self):
|
||||
startEndTime = StartEndTime('expirationstart', 'expirationend')
|
||||
@@ -58917,9 +58953,11 @@ class PermissionMatch():
|
||||
elif myarg == 'inherited':
|
||||
body[myarg] = getBoolean()
|
||||
self.permissionFields.add('permissionDetails')
|
||||
self.checkDetails = True
|
||||
elif myarg == 'permtype':
|
||||
body['permissionType'] = getChoice(DRIVEFILE_ACL_PERMISSION_DETAILS_TYPES)
|
||||
self.permissionFields.add('permissionDetails')
|
||||
self.checkDetails = True
|
||||
elif myarg in {'em', 'endmatch'}:
|
||||
break
|
||||
else:
|
||||
@@ -58967,16 +59005,25 @@ class PermissionMatch():
|
||||
elif field in {'allowFileDiscovery', 'deleted'}:
|
||||
if value != permission.get(field, False):
|
||||
break
|
||||
elif field == 'inherited':
|
||||
if 'permissionDetails' in permission:
|
||||
if value != permission['permissionDetails'][0].get(field, False):
|
||||
break
|
||||
else:
|
||||
break
|
||||
elif field == 'permissionType':
|
||||
if 'permissionDetails' in permission:
|
||||
if value != permission['permissionDetails'][0].get(field, ''):
|
||||
break
|
||||
elif field in {'inherited', 'permissionType'}:
|
||||
permDetails = permission.pop('permissionDetails', None)
|
||||
if permDetails:
|
||||
dfltValue = False if field == 'inherited' else ''
|
||||
permission['permissionDetails'] = []
|
||||
if permission.get('inheritedPermissionsDisabled', False):
|
||||
obreak = False
|
||||
for permissionDetail in permDetails:
|
||||
if permissionDetail.get('inherited', False):
|
||||
continue
|
||||
permission['permissionDetails'].append(permissionDetail)
|
||||
if value != permissionDetail.get(field, dfltValue):
|
||||
obreak = True
|
||||
if obreak:
|
||||
break
|
||||
else:
|
||||
permission['permissionDetails'].append(permDetails[len(permDetails)-1])
|
||||
if value != permDetails[0].get(field, dfltValue):
|
||||
break
|
||||
else:
|
||||
break
|
||||
elif field in {'expirationstart', 'expirationend'}:
|
||||
@@ -59418,7 +59465,8 @@ def printFileList(users):
|
||||
def _printFileInfo(drive, user, f_file, cleanFileName):
|
||||
nonlocal getSharedDriveACLsCount
|
||||
driveId = f_file.get('driveId')
|
||||
checkSharedDrivePermissions = getPermissionsForSharedDrives and driveId and 'permissions' not in f_file
|
||||
getCheckFilePermissions = _setGetCheckFilePermissions(f_file, getPermissionDetailsForMyDrive, getPermissionsForSharedDrives,
|
||||
driveId, DLP)
|
||||
if (f_file.get('noDisplay', False) or
|
||||
not DLP.CheckShowOwnedBy(f_file) or
|
||||
not DLP.CheckShowSharedByMe(f_file) or
|
||||
@@ -59426,10 +59474,10 @@ def printFileList(users):
|
||||
not DLP.CheckMimeType(f_file) or
|
||||
not DLP.CheckFileSize(f_file, sizeField) or
|
||||
not DLP.CheckFilenameMatch(f_file) or
|
||||
(not checkSharedDrivePermissions and not DLP.CheckFilePermissionMatches(f_file)) or
|
||||
(not getCheckFilePermissions and not DLP.CheckFilePermissionMatches(f_file)) or
|
||||
(DLP.onlySharedDrives and not driveId)):
|
||||
return
|
||||
if checkSharedDrivePermissions:
|
||||
if getCheckFilePermissions:
|
||||
if not incrementalPrint:
|
||||
getSharedDriveACLsCount += 1
|
||||
if getSharedDriveACLsCount % 100 == 0:
|
||||
@@ -59596,7 +59644,7 @@ def printFileList(users):
|
||||
csvPFco = None
|
||||
FJQC = FormatJSONQuoteChar(csvPF)
|
||||
addPathsToJSON = continueOnInvalidQuery = countsRowFilter = buildTree = countsOnly = filepath = fullpath = folderPathOnly = \
|
||||
getPermissionsForSharedDrives = mimeTypeInQuery = noRecursion = oneItemPerRow = stripCRsFromName = \
|
||||
getPermissionDetailsForMyDrive = getPermissionsForSharedDrives = mimeTypeInQuery = noRecursion = oneItemPerRow = stripCRsFromName = \
|
||||
showParentsIdsAsList = showDepth = showParent = showSize = showSizeUnits = showMimeTypeSize = showSource = False
|
||||
sizeField = 'quotaBytesUsed'
|
||||
pathDelimiter = '/'
|
||||
@@ -59607,6 +59655,7 @@ def printFileList(users):
|
||||
maxdepth = -1
|
||||
nodataFields = []
|
||||
simpleLists = ['permissionIds', 'spaces']
|
||||
permissionsFieldsList = []
|
||||
skipObjects = set()
|
||||
fileIdEntity = {}
|
||||
selectSubQuery = ''
|
||||
@@ -59690,8 +59739,7 @@ def printFileList(users):
|
||||
elif myarg == 'showlabels':
|
||||
showLabels = getChoice(SHOWLABELS_CHOICES)
|
||||
elif myarg == 'showshareddrivepermissions':
|
||||
getPermissionsForSharedDrives = True
|
||||
permissionsFields = f'nextPageToken,permissions({",".join(DRIVEFILE_BASIC_PERMISSION_FIELDS)})'
|
||||
permissionsFieldsList = DRIVEFILE_BASIC_PERMISSION_FIELDS.copy()
|
||||
elif myarg == 'pmfilter':
|
||||
pmselect = False
|
||||
elif myarg == 'oneitemperrow':
|
||||
@@ -59745,11 +59793,9 @@ def printFileList(users):
|
||||
btkwargs = fileIdEntity['shareddrive']
|
||||
DLP.Finalize(fileIdEntity)
|
||||
if DLP.PM.permissionMatches:
|
||||
getPermissionsForSharedDrives = True
|
||||
permissionsFields = f'nextPageToken,permissions({",".join(DRIVEFILE_BASIC_PERMISSION_FIELDS)})'
|
||||
elif DFF.fieldsList:
|
||||
if not getPermissionsForSharedDrives:
|
||||
getPermissionsForSharedDrives, permissionsFields = _setGetPermissionsForSharedDrives(DFF.fieldsList)
|
||||
permissionsFieldsList += list(DLP.PM.permissionFields)
|
||||
getPermissionDetailsForMyDrive, getPermissionsForSharedDrives, permissionsFields = \
|
||||
_setGetPermissionsForMyDriveSharedDrives(DFF.fieldsList, permissionsFieldsList)
|
||||
if DFF.fieldsList:
|
||||
_setSelectionFields()
|
||||
fields = getFieldsFromFieldsList(DFF.fieldsList)
|
||||
@@ -60636,10 +60682,11 @@ def printShowFileCounts(users):
|
||||
fieldsList.append('driveId')
|
||||
DLP.Finalize(fileIdEntity)
|
||||
if DLP.PM.permissionMatches:
|
||||
getPermissionDetailsForMyDrive = DLP.PM.checkDetails
|
||||
getPermissionsForSharedDrives = True
|
||||
permissionsFields = 'nextPageToken,permissions'
|
||||
permissionsFields = f'nextPageToken,permissions({",".join(DLP.PM.permissionFields)})'
|
||||
else:
|
||||
getPermissionsForSharedDrives = False
|
||||
getPermissionDetailsForMyDrive = getPermissionsForSharedDrives = False
|
||||
_setSelectionFields()
|
||||
if csvPF:
|
||||
sortTitles = ['User', 'id', 'name', 'Total', 'Item cap'] if fileIdEntity.get('shareddrive') else ['User', 'Total']
|
||||
@@ -60680,16 +60727,17 @@ def printShowFileCounts(users):
|
||||
for files in feed:
|
||||
for f_file in files:
|
||||
driveId = f_file.get('driveId')
|
||||
checkSharedDrivePermissions = getPermissionsForSharedDrives and driveId and 'permissions' not in f_file
|
||||
getCheckFilePermissions = _setGetCheckFilePermissions(f_file, getPermissionDetailsForMyDrive, getPermissionsForSharedDrives,
|
||||
driveId, DLP)
|
||||
if (not DLP.CheckShowOwnedBy(f_file) or
|
||||
not DLP.CheckShowSharedByMe(f_file) or
|
||||
not DLP.CheckExcludeTrashed(f_file) or
|
||||
not DLP.CheckFileSize(f_file, sizeField) or
|
||||
not DLP.CheckFilenameMatch(f_file) or
|
||||
(not checkSharedDrivePermissions and not DLP.CheckFilePermissionMatches(f_file)) or
|
||||
(not getCheckFilePermissions and not DLP.CheckFilePermissionMatches(f_file)) or
|
||||
(DLP.onlySharedDrives and not driveId)):
|
||||
continue
|
||||
if checkSharedDrivePermissions:
|
||||
if getCheckFilePermissions:
|
||||
try:
|
||||
f_file['permissions'] = callGAPIpages(drive.permissions(), 'list', 'permissions',
|
||||
throwReasons=GAPI.DRIVE3_GET_ACL_REASONS+[GAPI.BAD_REQUEST],
|
||||
@@ -67080,7 +67128,7 @@ def emptyDriveTrash(users):
|
||||
userDriveServiceNotEnabledWarning(user, str(e), i, count)
|
||||
|
||||
def _getDriveFileACLPrintKeysTimeObjects():
|
||||
printKeys = ['id', 'type', 'emailAddress', 'domain', 'role', 'permissionDetails',
|
||||
printKeys = ['id', 'role', 'type', 'emailAddress', 'domain', 'permissionDetails',
|
||||
'expirationTime', 'photoLink', 'allowFileDiscovery', 'deleted',
|
||||
'pendingOwner', 'view']
|
||||
timeObjects = {'expirationTime'}
|
||||
@@ -67129,14 +67177,14 @@ def _showDriveFilePermission(permission, printKeys, timeObjects, i=0, count=0):
|
||||
printKeyValueList([key, ''])
|
||||
Ind.Increment()
|
||||
for detail in value:
|
||||
printKeyValueList(['role', detail['role']])
|
||||
Ind.Increment()
|
||||
printKeyValueList(['type', detail['permissionType']])
|
||||
if 'additionalRoles' in detail:
|
||||
printKeyValueList(['additionalRoles', ','.join(detail['additionalRoles'])])
|
||||
printKeyValueList(['inherited', detail['inherited']])
|
||||
Ind.Increment()
|
||||
if detail['inherited']:
|
||||
printKeyValueList(['inheritedFrom', detail.get('inheritedFrom', UNKNOWN)])
|
||||
printKeyValueList(['permissionType', detail['permissionType']])
|
||||
printKeyValueList(['role', detail['role']])
|
||||
if 'additionalRoles' in detail:
|
||||
printKeyValueList(['additionalRoles', ','.join(detail['additionalRoles'])])
|
||||
Ind.Decrement()
|
||||
Ind.Decrement()
|
||||
elif key not in timeObjects:
|
||||
@@ -80340,7 +80388,6 @@ MAIN_COMMANDS_WITH_OBJECTS = {
|
||||
Cmd.ARG_BROWSER: doPrintShowBrowsers,
|
||||
Cmd.ARG_BROWSERTOKEN: doPrintShowBrowserTokens,
|
||||
Cmd.ARG_BUILDING: doPrintShowBuildings,
|
||||
Cmd.ARG_BUSINESSPROFILEACCOUNT: doPrintShowBusinessProfileAccounts,
|
||||
Cmd.ARG_CAALEVEL: doPrintShowCAALevels,
|
||||
Cmd.ARG_CHANNELCUSTOMER: doPrintShowChannelCustomers,
|
||||
Cmd.ARG_CHANNELCUSTOMERENTITLEMENT: doPrintShowChannelCustomerEntitlements,
|
||||
@@ -80479,7 +80526,6 @@ MAIN_COMMANDS_WITH_OBJECTS = {
|
||||
Cmd.ARG_BROWSER: doPrintShowBrowsers,
|
||||
Cmd.ARG_BROWSERTOKEN: doPrintShowBrowserTokens,
|
||||
Cmd.ARG_BUILDING: doPrintShowBuildings,
|
||||
Cmd.ARG_BUSINESSPROFILEACCOUNT: doPrintShowBusinessProfileAccounts,
|
||||
Cmd.ARG_CAALEVEL: doPrintShowCAALevels,
|
||||
Cmd.ARG_CHANNELCUSTOMER: doPrintShowChannelCustomers,
|
||||
Cmd.ARG_CHANNELCUSTOMERENTITLEMENT: doPrintShowChannelCustomerEntitlements,
|
||||
@@ -80671,7 +80717,6 @@ MAIN_COMMANDS_OBJ_ALIASES = {
|
||||
Cmd.ARG_BUCKET: Cmd.ARG_STORAGEBUCKET,
|
||||
Cmd.ARG_BUCKETS: Cmd.ARG_STORAGEBUCKET,
|
||||
Cmd.ARG_BUILDINGS: Cmd.ARG_BUILDING,
|
||||
Cmd.ARG_BUSINESSPROFILEACCOUNTS: Cmd.ARG_BUSINESSPROFILEACCOUNT,
|
||||
Cmd.ARG_CAALEVELS: Cmd.ARG_CAALEVEL,
|
||||
Cmd.ARG_CHATMEMBERS: Cmd.ARG_CHATMEMBER,
|
||||
Cmd.ARG_CHANNELCUSTOMERS: Cmd.ARG_CHANNELCUSTOMER,
|
||||
@@ -81415,6 +81460,7 @@ USER_COMMANDS_WITH_OBJECTS = {
|
||||
Cmd.ARG_ANALYTICPROPERTY: printShowAnalyticProperties,
|
||||
Cmd.ARG_ASP: printShowASPs,
|
||||
Cmd.ARG_BACKUPCODE: printShowBackupCodes,
|
||||
Cmd.ARG_BUSINESSPROFILEACCOUNT: printShowBusinessProfileAccounts,
|
||||
Cmd.ARG_CALENDAR: printShowCalendars,
|
||||
Cmd.ARG_CALENDARACL: printShowCalendarACLs,
|
||||
Cmd.ARG_CALSETTINGS: printShowCalSettings,
|
||||
@@ -81532,6 +81578,7 @@ USER_COMMANDS_WITH_OBJECTS = {
|
||||
Cmd.ARG_ANALYTICPROPERTY: printShowAnalyticProperties,
|
||||
Cmd.ARG_ASP: printShowASPs,
|
||||
Cmd.ARG_BACKUPCODE: printShowBackupCodes,
|
||||
Cmd.ARG_BUSINESSPROFILEACCOUNT: printShowBusinessProfileAccounts,
|
||||
Cmd.ARG_CALENDAR: printShowCalendars,
|
||||
Cmd.ARG_CALENDARACL: printShowCalendarACLs,
|
||||
Cmd.ARG_CALSETTINGS: printShowCalSettings,
|
||||
@@ -81755,6 +81802,7 @@ USER_COMMANDS_OBJ_ALIASES = {
|
||||
Cmd.ARG_ANALYTICPROPERTIES: Cmd.ARG_ANALYTICPROPERTY,
|
||||
Cmd.ARG_ASPS: Cmd.ARG_ASP,
|
||||
Cmd.ARG_BACKUPCODES: Cmd.ARG_BACKUPCODE,
|
||||
Cmd.ARG_BUSINESSPROFILEACCOUNTS: Cmd.ARG_BUSINESSPROFILEACCOUNT,
|
||||
Cmd.ARG_CALENDARS: Cmd.ARG_CALENDAR,
|
||||
Cmd.ARG_CALENDARACLS: Cmd.ARG_CALENDARACL,
|
||||
Cmd.ARG_CLASSIFICATIONLABEL: Cmd.ARG_DRIVELABEL,
|
||||
|
||||
@@ -140,7 +140,6 @@ SCOPELESS_APIS = {
|
||||
|
||||
# Scopes not in the discovery doc that are still valid for the API.
|
||||
EXTRA_SCOPES = {
|
||||
BUSINESSACCOUNTMANAGEMENT: ['https://www.googleapis.com/auth/business.manage'],
|
||||
CLOUDRESOURCEMANAGER: ['https://www.googleapis.com/auth/cloudplatformfolders',
|
||||
'https://www.googleapis.com/auth/cloudplatformfolders.readonly',
|
||||
'https://www.googleapis.com/auth/cloudplatformprojects',
|
||||
@@ -318,10 +317,6 @@ _INFO = {
|
||||
READONLY = ['readonly',]
|
||||
|
||||
_CLIENT_SCOPES = [
|
||||
{'name': 'Business Account Management API',
|
||||
'api': BUSINESSACCOUNTMANAGEMENT,
|
||||
'offByDefault': True,
|
||||
'scope': EXTRA_SCOPES[BUSINESSACCOUNTMANAGEMENT]},
|
||||
{'name': 'Calendar API',
|
||||
'api': CALENDAR,
|
||||
'subscopes': READONLY,
|
||||
@@ -568,6 +563,10 @@ _SVCACCT_SCOPES = [
|
||||
{'name': 'Analytics Admin API - readonly',
|
||||
'api': ANALYTICS_ADMIN,
|
||||
'scope': 'https://www.googleapis.com/auth/analytics.readonly'},
|
||||
{'name': 'Business Account Management API',
|
||||
'api': BUSINESSACCOUNTMANAGEMENT,
|
||||
'offByDefault': True,
|
||||
'scope': 'https://www.googleapis.com/auth/business.manage'},
|
||||
{'name': 'Calendar API',
|
||||
'api': CALENDAR,
|
||||
'subscopes': READONLY,
|
||||
|
||||
@@ -428,7 +428,7 @@ See https://support.google.com/chrome/a/answer/3523633 for full details.
|
||||
Thanks to Jay for most of the following.
|
||||
|
||||
Send a remote command to the managed Chrome OS device. It's important to note that the device must be in a proper state to accept the command or an error may be returned.
|
||||
For example, the `reboot`, `set_volume` and `take_a_screenshot` commands only work if the device is configured in auto-start kiosk app mode.
|
||||
For example, the `set_volume` and `take_a_screenshot` commands only work if the device is configured in auto-start kiosk app mode.
|
||||
|
||||
The `wipe_users` and `remote_powerwash` commands will erase all user data on the device and the `remote_powerwash` command will require that the device is physically reconnected to the
|
||||
WiFi network and re-enrolled before it can be managed again. These commands require the `doit` argument so that the admin confirms the potential loss of user data and management.
|
||||
|
||||
@@ -10,9 +10,28 @@ Add the `-s` option to the end of the above commands to suppress creating the `g
|
||||
|
||||
See [Downloads-Installs-GAM7](https://github.com/GAM-team/GAM/wiki/Downloads-Installs) for Windows or other options, including manual installation
|
||||
|
||||
### 7.40.01
|
||||
|
||||
Updated `gam <UserTypeEntity> print filelist|filecounts` to handle the `permissionDetails` subfield
|
||||
of the `permissions` field for My Drives; this useful when trying to display permission inheritance.
|
||||
An additional API call per file is required to get the `permissionDetails` subfield.
|
||||
```
|
||||
gam user user@domain.com print filelist fields id,name,mimetype,basicpermissions,permissiondetails oneitemperrow
|
||||
gam user user@domain.com print filelist fields id,name,mimetype,basicpermissions,permissiondetails pm inherited false em pmfilter oneitemperrow
|
||||
```
|
||||
|
||||
### 7.40.00
|
||||
|
||||
Updated `gam print|show businessprofileaccounts` (client access) to
|
||||
`gam <UserTypeEntity> print|show businessprofileaccounts` (service account access).
|
||||
You'll need to run `gam user user@domain.com update serviceaccount` and
|
||||
select `2) Business Account Management API`.
|
||||
|
||||
### 7.39.08
|
||||
|
||||
Upgraded to Python 3.14.4.
|
||||
Fixed bug in `gam oauth create` that caused a trap when `0) Business Account Management API` was selected.
|
||||
|
||||
Upgraded to Python 3.14.4 on macOS and Windows; Linux is still 3.14.3.
|
||||
|
||||
### 7.39.07
|
||||
|
||||
|
||||
@@ -251,10 +251,10 @@ writes the credentials into the file oauth2.txt.
|
||||
```
|
||||
gamteam@server:/Users/gamteam$ rm -f /Users/gamteam/GAMConfig/oauth2.txt
|
||||
gamteam@server:/Users/gamteam$ gam version
|
||||
GAM 7.39.08 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM 7.40.01 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.14.4 64-bit final
|
||||
macOS Tahoe 26.4 arm64
|
||||
macOS Tahoe 26.4.1 arm64
|
||||
Path: /Users/gamteam/bin/gam7
|
||||
Config File: /Users/gamteam/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
|
||||
@@ -1034,7 +1034,7 @@ writes the credentials into the file oauth2.txt.
|
||||
```
|
||||
C:\>del C:\GAMConfig\oauth2.txt
|
||||
C:\>gam version
|
||||
GAM 7.39.08 - https://github.com/GAM-team/GAM - pythonsource
|
||||
GAM 7.40.01 - https://github.com/GAM-team/GAM - pythonsource
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.14.4 64-bit final
|
||||
Windows 11 10.0.26200 AMD64
|
||||
|
||||
@@ -9,12 +9,13 @@
|
||||
|
||||
|
||||
## Introduction
|
||||
To use these commands you add the 'Business Account Management API' to your project and update client authorization.
|
||||
To use these commands you must add the 'Business Account Management API' to your project and update
|
||||
service account authorization.
|
||||
```
|
||||
gam update project
|
||||
gam oauth create
|
||||
gam user user@domain.com update serviceaccount
|
||||
...
|
||||
[*] 0) Business Account Management API
|
||||
[*] 2) Business Account Management API
|
||||
|
||||
```
|
||||
## Definitions
|
||||
@@ -22,13 +23,13 @@ gam oauth create
|
||||
|
||||
## Display Business Profile Accounts
|
||||
```
|
||||
gam <UserItem> show businessprofileaccounts
|
||||
gam <UserTypeEntity> show businessprofileaccounts
|
||||
[type locationgroup|organization|personal|usergroup]
|
||||
```
|
||||
Gam displays the information as an indented list of keys and values.
|
||||
|
||||
```
|
||||
gam <UserItem> print businessprofileaccounts [todrive <ToDriveAttribute>*]
|
||||
gam <UserTypeEntity> print businessprofileaccounts [todrive <ToDriveAttribute>*]
|
||||
[type locationgroup|organization|personal|usergroup]
|
||||
```
|
||||
Gam displays the information as columns of fields.
|
||||
@@ -3,10 +3,10 @@
|
||||
Print the current version of Gam with details
|
||||
```
|
||||
gam version
|
||||
GAM 7.39.08 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM 7.40.01 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.14.4 64-bit final
|
||||
macOS Tahoe 26.4 arm64
|
||||
macOS Tahoe 26.4.1 arm64
|
||||
Path: /Users/gamteam/bin/gam7
|
||||
Config File: /Users/gamteam/GamConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
Time: 2026-02-15T07:51:00-08:00
|
||||
@@ -15,10 +15,10 @@ Time: 2026-02-15T07:51:00-08:00
|
||||
Print the current version of Gam with details and time offset information
|
||||
```
|
||||
gam version timeoffset
|
||||
GAM 7.39.08 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM 7.40.01 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.14.4 64-bit final
|
||||
macOS Tahoe 26.4 arm64
|
||||
macOS Tahoe 26.4.1 arm64
|
||||
Path: /Users/gamteam/bin/gam7
|
||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
Your system time differs from www.googleapis.com by less than 1 second
|
||||
@@ -27,10 +27,10 @@ Your system time differs from www.googleapis.com by less than 1 second
|
||||
Print the current version of Gam with extended details and SSL information
|
||||
```
|
||||
gam version extended
|
||||
GAM 7.39.08 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM 7.40.01 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.14.4 64-bit final
|
||||
macOS Tahoe 26.4 arm64
|
||||
macOS Tahoe 26.4.1 arm64
|
||||
Path: /Users/gamteam/bin/gam7
|
||||
Config File: /Users/gamteam/GamConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
Time: 2026-02-15T07:51:00-08:00
|
||||
@@ -68,7 +68,7 @@ MacOS High Sierra 10.13.6 x86_64
|
||||
Path: /Users/gamteam/bin/gam7
|
||||
Version Check:
|
||||
Current: 5.35.08
|
||||
Latest: 7.39.08
|
||||
Latest: 7.40.01
|
||||
echo $?
|
||||
1
|
||||
```
|
||||
@@ -76,7 +76,7 @@ echo $?
|
||||
Print the current version number without details
|
||||
```
|
||||
gam version simple
|
||||
7.39.08
|
||||
7.40.01
|
||||
```
|
||||
In Linux/MacOS you can do:
|
||||
```
|
||||
@@ -86,10 +86,10 @@ echo $VER
|
||||
Print the current version of Gam and address of this Wiki
|
||||
```
|
||||
gam help
|
||||
GAM 7.39.08 - https://github.com/GAM-team/GAM
|
||||
GAM 7.40.01 - https://github.com/GAM-team/GAM
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.14.4 64-bit final
|
||||
macOS Tahoe 26.4 arm64
|
||||
macOS Tahoe 26.4.1 arm64
|
||||
Path: /Users/gamteam/bin/gam7
|
||||
Config File: /Users/gamteam/GamConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
Time: 2026-02-15T07:51:00-08:00
|
||||
|
||||
@@ -70,7 +70,6 @@ Client Access
|
||||
* [Administrators](Administrators)
|
||||
* [Alert Center](Alert-Center)
|
||||
* [Aliases](Aliases)
|
||||
* [Business Account Management](Business-Account-Management)
|
||||
* [Calendars](Calendars)
|
||||
* [Calendars - Access](Calendars-Access)
|
||||
* [Calendars - Events](Calendars-Events)
|
||||
@@ -140,6 +139,7 @@ Special Service Account Access
|
||||
|
||||
Service Account Access
|
||||
* [Users - Analytics Admin](Users-Analytics-Admin)
|
||||
* [Users - Business Account Management](Users-Business-Account-Management)
|
||||
* [Users - Calendars](Users-Calendars)
|
||||
* [Users - Calendars - Access](Users-Calendars-Access)
|
||||
* [Users - Calendars - Events](Users-Calendars-Events)
|
||||
|
||||
Reference in New Issue
Block a user