Compare commits

..

10 Commits

Author SHA1 Message Date
Ross Scroggs
f1dfcc877d Updated gam info|print cigroups and gam print|show cigroup-members 2026-04-11 17:38:46 -07:00
Ross Scroggs
f0fa7aa25e Update GamUpdates.md
Some checks failed
Build and test GAM / build (false, build, 1, Build Intel Ubuntu Jammy, ubuntu-22.04) (push) Has been cancelled
Build and test GAM / build (false, build, 10, Build x86_64 macOS 15, macos-15-intel) (push) Has been cancelled
Build and test GAM / build (false, build, 11, Build x86_64 macOS 26, macos-26-intel) (push) Has been cancelled
Build and test GAM / build (false, build, 12, Build Arm MacOS 26, macos-26) (push) Has been cancelled
Build and test GAM / build (false, build, 13, Build Intel Windows, windows-2025-vs2026) (push) Has been cancelled
Build and test GAM / build (false, build, 14, Build Arm Windows, windows-11-arm) (push) Has been cancelled
Build and test GAM / build (false, build, 2, Build Intel Ubuntu Noble, ubuntu-24.04) (push) Has been cancelled
Build and test GAM / build (false, build, 3, Build Arm Ubuntu Noble, ubuntu-24.04-arm) (push) Has been cancelled
Build and test GAM / build (false, build, 4, Build Arm Ubuntu Jammy, ubuntu-22.04-arm) (push) Has been cancelled
Build and test GAM / build (false, build, 5, Build Intel StaticX Legacy, ubuntu-22.04, yes) (push) Has been cancelled
Build and test GAM / build (false, build, 6, Build Arm StaticX Legacy, ubuntu-22.04-arm, yes) (push) Has been cancelled
Build and test GAM / build (false, build, 8, Build Arm MacOS 14, macos-14) (push) Has been cancelled
Build and test GAM / build (false, build, 9, Build Arm MacOS 15, macos-15) (push) Has been cancelled
Build and test GAM / build (false, test, 15, Test Python 3.10, ubuntu-24.04, 3.10) (push) Has been cancelled
Build and test GAM / build (false, test, 16, Test Python 3.11, ubuntu-24.04, 3.11) (push) Has been cancelled
Build and test GAM / build (false, test, 17, Test Python 3.12, ubuntu-24.04, 3.12) (push) Has been cancelled
Build and test GAM / build (false, test, 18, Test Python 3.13, ubuntu-24.04, 3.13) (push) Has been cancelled
Build and test GAM / build (false, test, 19, Test Python 3.15-dev, ubuntu-24.04, 3.15-dev) (push) Has been cancelled
Build and test GAM / build (true, test, 20, Test Python 3.14 freethread, ubuntu-24.04, 3.14) (push) Has been cancelled
Build and test GAM / publish (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Push wiki / pushwiki (push) Has been cancelled
Check for Google Root CA Updates / check-certs (push) Has been cancelled
2026-04-11 11:04:40 -07:00
Ross Scroggs
2445b4f92d Update GamUpdates.md 2026-04-11 11:02:39 -07:00
Ross Scroggs
eb2b9f0e4f Updated gam <UserTypeEntity> print filelist|filecounts 2026-04-11 10:43:50 -07:00
Ross Scroggs
91c0d66a14 Updated gam <UserTypeEntity> print filelist|filecounts to handle permissionDetails 2026-04-11 09:53:20 -07:00
Ross Scroggs
0dd05e7b93 Update ChromeOS-Devices.md
Some checks failed
Push wiki / pushwiki (push) Has been cancelled
Build and test GAM / build (false, build, 1, Build Intel Ubuntu Jammy, ubuntu-22.04) (push) Has been cancelled
Build and test GAM / build (false, build, 10, Build x86_64 macOS 15, macos-15-intel) (push) Has been cancelled
Build and test GAM / build (false, build, 11, Build x86_64 macOS 26, macos-26-intel) (push) Has been cancelled
Build and test GAM / build (false, build, 12, Build Arm MacOS 26, macos-26) (push) Has been cancelled
Build and test GAM / build (false, build, 13, Build Intel Windows, windows-2025-vs2026) (push) Has been cancelled
Build and test GAM / build (false, build, 14, Build Arm Windows, windows-11-arm) (push) Has been cancelled
Build and test GAM / build (false, build, 2, Build Intel Ubuntu Noble, ubuntu-24.04) (push) Has been cancelled
Build and test GAM / build (false, build, 3, Build Arm Ubuntu Noble, ubuntu-24.04-arm) (push) Has been cancelled
Build and test GAM / build (false, build, 4, Build Arm Ubuntu Jammy, ubuntu-22.04-arm) (push) Has been cancelled
Build and test GAM / build (false, build, 5, Build Intel StaticX Legacy, ubuntu-22.04, yes) (push) Has been cancelled
Build and test GAM / build (false, build, 6, Build Arm StaticX Legacy, ubuntu-22.04-arm, yes) (push) Has been cancelled
Build and test GAM / build (false, build, 8, Build Arm MacOS 14, macos-14) (push) Has been cancelled
Build and test GAM / build (false, build, 9, Build Arm MacOS 15, macos-15) (push) Has been cancelled
Build and test GAM / build (false, test, 15, Test Python 3.10, ubuntu-24.04, 3.10) (push) Has been cancelled
Build and test GAM / build (false, test, 16, Test Python 3.11, ubuntu-24.04, 3.11) (push) Has been cancelled
Build and test GAM / build (false, test, 17, Test Python 3.12, ubuntu-24.04, 3.12) (push) Has been cancelled
Build and test GAM / build (false, test, 18, Test Python 3.13, ubuntu-24.04, 3.13) (push) Has been cancelled
Build and test GAM / build (false, test, 19, Test Python 3.15-dev, ubuntu-24.04, 3.15-dev) (push) Has been cancelled
Build and test GAM / build (true, test, 20, Test Python 3.14 freethread, ubuntu-24.04, 3.14) (push) Has been cancelled
Build and test GAM / publish (push) Has been cancelled
Check for Google Root CA Updates / check-certs (push) Has been cancelled
2026-04-09 16:20:05 -07:00
Ross Scroggs
da93cdbc06 Update Business Account Management
Some checks failed
Push wiki / pushwiki (push) Has been cancelled
Build and test GAM / build (false, build, 1, Build Intel Ubuntu Jammy, ubuntu-22.04) (push) Has been cancelled
Build and test GAM / build (false, build, 10, Build x86_64 macOS 15, macos-15-intel) (push) Has been cancelled
Build and test GAM / build (false, build, 11, Build x86_64 macOS 26, macos-26-intel) (push) Has been cancelled
Build and test GAM / build (false, build, 12, Build Arm MacOS 26, macos-26) (push) Has been cancelled
Build and test GAM / build (false, build, 13, Build Intel Windows, windows-2025-vs2026) (push) Has been cancelled
Build and test GAM / build (false, build, 14, Build Arm Windows, windows-11-arm) (push) Has been cancelled
Build and test GAM / build (false, build, 2, Build Intel Ubuntu Noble, ubuntu-24.04) (push) Has been cancelled
Build and test GAM / build (false, build, 3, Build Arm Ubuntu Noble, ubuntu-24.04-arm) (push) Has been cancelled
Build and test GAM / build (false, build, 4, Build Arm Ubuntu Jammy, ubuntu-22.04-arm) (push) Has been cancelled
Build and test GAM / build (false, build, 5, Build Intel StaticX Legacy, ubuntu-22.04, yes) (push) Has been cancelled
Build and test GAM / build (false, build, 6, Build Arm StaticX Legacy, ubuntu-22.04-arm, yes) (push) Has been cancelled
Build and test GAM / build (false, build, 8, Build Arm MacOS 14, macos-14) (push) Has been cancelled
Build and test GAM / build (false, build, 9, Build Arm MacOS 15, macos-15) (push) Has been cancelled
Build and test GAM / build (false, test, 15, Test Python 3.10, ubuntu-24.04, 3.10) (push) Has been cancelled
Build and test GAM / build (false, test, 16, Test Python 3.11, ubuntu-24.04, 3.11) (push) Has been cancelled
Build and test GAM / build (false, test, 17, Test Python 3.12, ubuntu-24.04, 3.12) (push) Has been cancelled
Build and test GAM / build (false, test, 18, Test Python 3.13, ubuntu-24.04, 3.13) (push) Has been cancelled
Build and test GAM / build (false, test, 19, Test Python 3.15-dev, ubuntu-24.04, 3.15-dev) (push) Has been cancelled
Build and test GAM / build (true, test, 20, Test Python 3.14 freethread, ubuntu-24.04, 3.14) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Build and test GAM / publish (push) Has been cancelled
Check for Google Root CA Updates / check-certs (push) Has been cancelled
2026-04-08 21:46:25 -07:00
Ross Scroggs
cf750e0d58 Updated gam print|show businessprofileaccounts to svcacct access 2026-04-08 21:39:16 -07:00
Ross Scroggs
ddda0593f2 Updated gam print|show businessprofileaccounts to svcacct access 2026-04-08 20:52:08 -07:00
Ross Scroggs
7798504282 Fixed bug in gam oauth create
Some checks failed
Build and test GAM / build (false, build, 1, Build Intel Ubuntu Jammy, ubuntu-22.04) (push) Has been cancelled
Build and test GAM / build (false, build, 10, Build x86_64 macOS 15, macos-15-intel) (push) Has been cancelled
Build and test GAM / build (false, build, 11, Build x86_64 macOS 26, macos-26-intel) (push) Has been cancelled
Build and test GAM / build (false, build, 12, Build Arm MacOS 26, macos-26) (push) Has been cancelled
Build and test GAM / build (false, build, 13, Build Intel Windows, windows-2025-vs2026) (push) Has been cancelled
Build and test GAM / build (false, build, 14, Build Arm Windows, windows-11-arm) (push) Has been cancelled
Build and test GAM / build (false, build, 2, Build Intel Ubuntu Noble, ubuntu-24.04) (push) Has been cancelled
Build and test GAM / build (false, build, 3, Build Arm Ubuntu Noble, ubuntu-24.04-arm) (push) Has been cancelled
Build and test GAM / build (false, build, 4, Build Arm Ubuntu Jammy, ubuntu-22.04-arm) (push) Has been cancelled
Build and test GAM / build (false, build, 5, Build Intel StaticX Legacy, ubuntu-22.04, yes) (push) Has been cancelled
Build and test GAM / build (false, build, 6, Build Arm StaticX Legacy, ubuntu-22.04-arm, yes) (push) Has been cancelled
Build and test GAM / build (false, build, 8, Build Arm MacOS 14, macos-14) (push) Has been cancelled
Build and test GAM / build (false, build, 9, Build Arm MacOS 15, macos-15) (push) Has been cancelled
Build and test GAM / build (false, test, 15, Test Python 3.10, ubuntu-24.04, 3.10) (push) Has been cancelled
Build and test GAM / build (false, test, 16, Test Python 3.11, ubuntu-24.04, 3.11) (push) Has been cancelled
Build and test GAM / build (false, test, 17, Test Python 3.12, ubuntu-24.04, 3.12) (push) Has been cancelled
Build and test GAM / build (false, test, 18, Test Python 3.13, ubuntu-24.04, 3.13) (push) Has been cancelled
Build and test GAM / build (false, test, 19, Test Python 3.15-dev, ubuntu-24.04, 3.15-dev) (push) Has been cancelled
Build and test GAM / build (true, test, 20, Test Python 3.14 freethread, ubuntu-24.04, 3.14) (push) Has been cancelled
Build and test GAM / publish (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Push wiki / pushwiki (push) Has been cancelled
2026-04-08 18:55:58 -07:00
10 changed files with 197 additions and 108 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -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,

View File

@@ -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,

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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)