mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-28 18:01:36 +00:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35c1e44568 | ||
|
|
5cc68247a3 | ||
|
|
906ee82417 | ||
|
|
3d13d4afd8 | ||
|
|
9d68ce1b46 | ||
|
|
bd0ba995e5 | ||
|
|
0ab4b6d5cd | ||
|
|
163433f15a | ||
|
|
3d6219b551 | ||
|
|
99e363b5d6 | ||
|
|
ed03da815f | ||
|
|
ef1a40afa8 | ||
|
|
cd56f353d8 | ||
|
|
3924722f1c | ||
|
|
3ce48a95c9 | ||
|
|
2dafbfbcfc | ||
|
|
e03086866a | ||
|
|
0422bf22ea | ||
|
|
f3d9f3d518 | ||
|
|
ea9fd3f363 | ||
|
|
bed9db37ad | ||
|
|
072dc4809a | ||
|
|
6db2309fc4 | ||
|
|
cbb0c81652 | ||
|
|
f68aca8361 | ||
|
|
d63fdb4ed9 | ||
|
|
226781766b | ||
|
|
434e30d57c | ||
|
|
2ab059926b | ||
|
|
5ae25495f7 | ||
|
|
20e226e57d | ||
|
|
b4677585bb | ||
|
|
3a1437872c | ||
|
|
602dce2f5a | ||
|
|
8ce930f01b |
10
.github/workflows/build.yml
vendored
10
.github/workflows/build.yml
vendored
@@ -126,16 +126,16 @@ jobs:
|
|||||||
name: Test Python 3.12
|
name: Test Python 3.12
|
||||||
- os: ubuntu-24.04
|
- os: ubuntu-24.04
|
||||||
goal: test
|
goal: test
|
||||||
python: "3.14-dev"
|
python: "3.15-dev"
|
||||||
freethreaded: false
|
freethreaded: false
|
||||||
jid: 17
|
jid: 17
|
||||||
name: Test Python 3.14-dev
|
name: Test Python 3.15-dev
|
||||||
- os: ubuntu-24.04
|
- os: ubuntu-24.04
|
||||||
goal: test
|
goal: test
|
||||||
python: "3.14-dev"
|
python: "3.14"
|
||||||
freethreaded: true
|
freethreaded: true
|
||||||
jid: 18
|
jid: 18
|
||||||
name: Test Python 3.14-dev freethread
|
name: Test Python 3.14 freethread
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
@@ -158,7 +158,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
cache.tar.xz
|
cache.tar.xz
|
||||||
key: gam-${{ matrix.jid }}-20251002
|
key: gam-${{ matrix.jid }}-20251007
|
||||||
|
|
||||||
- name: Untar Cache archive
|
- name: Untar Cache archive
|
||||||
if: matrix.goal == 'build' && steps.cache-python-ssl.outputs.cache-hit == 'true'
|
if: matrix.goal == 'build' && steps.cache-python-ssl.outputs.cache-hit == 'true'
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
description = "CLI tool to manage Google Workspace"
|
description = "CLI tool to manage Google Workspace"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.9"
|
requires-python = ">=3.10"
|
||||||
classifiers = [
|
classifiers = [
|
||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
"Programming Language :: Python :: 3 :: Only",
|
"Programming Language :: Python :: 3 :: Only",
|
||||||
|
|||||||
@@ -368,6 +368,7 @@ If an item contains spaces, it should be surrounded by ".
|
|||||||
## Named items
|
## Named items
|
||||||
|
|
||||||
<AccessToken> ::= <String>
|
<AccessToken> ::= <String>
|
||||||
|
<AdminAssigneeType> ::= group|user|serviceaccount|unknown
|
||||||
<AlertID> ::= <String>
|
<AlertID> ::= <String>
|
||||||
<APIScopeURL> ::= <String>
|
<APIScopeURL> ::= <String>
|
||||||
<APPID> ::= <String>
|
<APPID> ::= <String>
|
||||||
@@ -691,6 +692,7 @@ If an item contains spaces, it should be surrounded by ".
|
|||||||
|
|
||||||
## Lists of basic items
|
## Lists of basic items
|
||||||
|
|
||||||
|
<AdminAssigneeTypeList> ::= "<AdminAssigneeType>(,<AdminAssigneeType>)*"
|
||||||
<APIScopeURLList> ::= "<APIScopeURL>(,<APIScopeURL>)*"
|
<APIScopeURLList> ::= "<APIScopeURL>(,<APIScopeURL>)*"
|
||||||
<ASPIDList> ::= "<ASPID>(,<ASPID>)*"
|
<ASPIDList> ::= "<ASPID>(,<ASPID>)*"
|
||||||
<AssetTagList> ::= "<AssetTag>(,<AssetTag>)*"
|
<AssetTagList> ::= "<AssetTag>(,<AssetTag>)*"
|
||||||
@@ -1551,11 +1553,16 @@ gam create|add admin <EmailAddress>|<UniqueID> <RoleItem> customer|(org_unit <Or
|
|||||||
[condition securitygroup|nonsecuritygroup]
|
[condition securitygroup|nonsecuritygroup]
|
||||||
gam delete admin <RoleAssignmentId>
|
gam delete admin <RoleAssignmentId>
|
||||||
|
|
||||||
|
<AdminAssigneeType> ::= group|user|serviceaccount|unknown
|
||||||
|
<AdminAssigneeTypeList> ::= "<AdminAssigneeType>(,<AdminAssigneeType>)*"
|
||||||
|
|
||||||
gam print admins [todrive <ToDriveAttribute>*]
|
gam print admins [todrive <ToDriveAttribute>*]
|
||||||
[user|group <EmailAddress>|<UniqueID>] [role <RoleItem>]
|
[user|group <EmailAddress>|<UniqueID>] [role <RoleItem>]
|
||||||
|
[types <AdminAssigneeTypeList>]
|
||||||
[recursive] [condition] [privileges] [oneitemperrow]
|
[recursive] [condition] [privileges] [oneitemperrow]
|
||||||
gam show admins
|
gam show admins
|
||||||
[user|group <EmailAddress>|<UniqueID>] [role <RoleItem>]
|
[user|group <EmailAddress>|<UniqueID>] [role <RoleItem>]
|
||||||
|
[types <AdminAssigneeTypeList>]
|
||||||
[recursive] [condition] [privileges]
|
[recursive] [condition] [privileges]
|
||||||
|
|
||||||
# Alert Center
|
# Alert Center
|
||||||
|
|||||||
@@ -1,7 +1,76 @@
|
|||||||
|
7.26.00
|
||||||
|
|
||||||
|
Added `debug_redaction` Boolean variable to `gam.cfg`. When True, the default,
|
||||||
|
sensitive data like access/refresh tokens, client secret and authorization codes
|
||||||
|
are redacted from debug output. This allows you to post debug output without
|
||||||
|
compromising your account information. Even with debug redaction,
|
||||||
|
anything shared publicly should be double-checked for sensitive content.
|
||||||
|
|
||||||
|
7.25.01
|
||||||
|
|
||||||
|
Fixed bug in `gam config timezone <String>` to handle timezone abbreviations correctly;
|
||||||
|
they were incorrectly shifted to lowercase.
|
||||||
|
|
||||||
|
7.25.00
|
||||||
|
|
||||||
|
Removed a capabilty added in 7.24.00 that allowed reading command data from Google Docs and Sheets
|
||||||
|
when a user's service account access to Drive and Sheets had been disabled. Jay was concerned
|
||||||
|
that this change could be exploited to give access to all user's files.
|
||||||
|
|
||||||
|
This capability has been replaced by issuing the following commands. The admin specified in `gam oauth create`
|
||||||
|
can read command data from Docs and Sheets to which it has access.
|
||||||
|
```
|
||||||
|
gam config commanddata_clientaccess true save
|
||||||
|
gam oauth create
|
||||||
|
Enable the following and proceed to authorization.
|
||||||
|
|
||||||
|
[*] 42) Drive API - commanddata_clientaccess
|
||||||
|
[*] 54) Sheets API - commanddata_clientaccess
|
||||||
|
```
|
||||||
|
|
||||||
|
Fixed in bug in `gam report` that caused a trap with either of the `thismonth` or `previousmonths` options were used.
|
||||||
|
|
||||||
|
Upgraded to Python 3.14.0.
|
||||||
|
|
||||||
|
7.24.01
|
||||||
|
|
||||||
|
Updated GAM to handle the following error that occurs when GAM tries to authenticate
|
||||||
|
as a user that has been disabled by Google.
|
||||||
|
```
|
||||||
|
ERROR: Authentication Token Error - invalid_account: Forbidden
|
||||||
|
```
|
||||||
|
|
||||||
|
7.24.00
|
||||||
|
|
||||||
|
If you want to disable a user's service account access to Drive and Sheets but still allow reading command data from Google Docs and Sheets,
|
||||||
|
issue the following command and make these settings:
|
||||||
|
```
|
||||||
|
gam user user@domain.com update serviceaccount
|
||||||
|
|
||||||
|
[ ] 20) Drive API (supports readonly)
|
||||||
|
[*] 21) Drive API - read command data
|
||||||
|
[ ] 42) Sheets API (supports readonly)
|
||||||
|
[*] 43) Sheets API - read command data
|
||||||
|
```
|
||||||
|
|
||||||
|
7.23.07
|
||||||
|
|
||||||
|
Fixed bug in `gam print|show admins` where all admin assignments were not displayed when
|
||||||
|
`types <AdminAssigneeTypeList>` was not specified, i.e., all assignments should be displayed.
|
||||||
|
|
||||||
|
7.23.06
|
||||||
|
|
||||||
|
Added option `types <AdminAssigneeTypeList>` to `gam print|show admins` that allows filtering
|
||||||
|
of admin assignments by the type of the assignee; by default, all assignee types are displayed.
|
||||||
|
```
|
||||||
|
<AdminAssigneeType> ::= group|user|serviceaccount|unknown
|
||||||
|
<AdminAssigneeTypeList> ::= "<AdminAssigneeType>(,<AdminAssigneeType>)*"
|
||||||
|
```
|
||||||
|
|
||||||
7.23.05
|
7.23.05
|
||||||
|
|
||||||
Added option `recursive` to `gam print|show admins` that will display assignments to the members
|
Added option `recursive` that will display assignments to the members
|
||||||
of security groups assigned to roles; the security group membershop is recursively expanded.
|
of security groups assigned to roles; the security group membership is recursively expanded.
|
||||||
|
|
||||||
7.23.04
|
7.23.04
|
||||||
|
|
||||||
|
|||||||
@@ -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.23.05'
|
__version__ = '7.26.00'
|
||||||
__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
|
||||||
@@ -49,7 +49,7 @@ from email.policy import SMTP as policySMTP
|
|||||||
import hashlib
|
import hashlib
|
||||||
from html.entities import name2codepoint
|
from html.entities import name2codepoint
|
||||||
from html.parser import HTMLParser
|
from html.parser import HTMLParser
|
||||||
import http.client as http_client
|
import http.client
|
||||||
import importlib
|
import importlib
|
||||||
from importlib.metadata import version as lib_version
|
from importlib.metadata import version as lib_version
|
||||||
import io
|
import io
|
||||||
@@ -375,6 +375,37 @@ YUBIKEY_VALUE_ERROR_RC = 85
|
|||||||
YUBIKEY_MULTIPLE_CONNECTED_RC = 86
|
YUBIKEY_MULTIPLE_CONNECTED_RC = 86
|
||||||
YUBIKEY_NOT_FOUND_RC = 87
|
YUBIKEY_NOT_FOUND_RC = 87
|
||||||
|
|
||||||
|
DEBUG_REDACTION_PATTERNS = [
|
||||||
|
# Positional patterns that redact sensitive credentials based on their location
|
||||||
|
(r'(Bearer\s+)\S+', r'\1*****'), # access tokens and JWTs in auth header
|
||||||
|
(r'([?&]refresh_token=)[^&]*', r'\1*****'), # refresh token URL parameter
|
||||||
|
(r'([?&]client_secret=)[^&]*', r'\1*****'), # client secret URL parameter
|
||||||
|
(r'([?&]key=)[^&]*', r'\1*****'), # API key URL parameter
|
||||||
|
(r'([?&]code=)[^&]*', r'\1*****'), # auth code URL parameter
|
||||||
|
|
||||||
|
# Pattern match patterns that redact sensitive credentials based on known credential pattern
|
||||||
|
(r'ya29.[0-9A-Za-z-_]+', '*****'), # Access token
|
||||||
|
(r'1%2F%2F[0-9A-Za-z-_]{100}|1%2F%2F[0-9A-Za-z-_]{64}|1%2F%2F[0-9A-Za-z-_]{43}', '*****'), # Refresh token
|
||||||
|
(r'4/[0-9A-Za-z-_]+', '*****'), # Auth code
|
||||||
|
(r'GOCSPX-[0-9a-zA-Z-_]{28}', '*****'), # Client secret
|
||||||
|
(r'AIza[0-9A-Za-z-_]{35}', '*****'), # API key
|
||||||
|
(r'eyJ[a-zA-Z0-9\-_]+\.eyJ[a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]*', '*****'), # JWT
|
||||||
|
]
|
||||||
|
|
||||||
|
def redactable_debug_print(*args):
|
||||||
|
processed_args = []
|
||||||
|
for arg in args:
|
||||||
|
if arg.startswith('b\''):
|
||||||
|
sbytes = arg[2:-1]
|
||||||
|
sbytes = bytes(sbytes, 'utf-8')
|
||||||
|
arg = sbytes.decode()
|
||||||
|
arg = arg.replace('\\r\\n', "\n ")
|
||||||
|
if GC.Values[GC.DEBUG_REDACTION]:
|
||||||
|
for pattern, replace in DEBUG_REDACTION_PATTERNS:
|
||||||
|
arg = re.sub(pattern, replace, arg)
|
||||||
|
processed_args.append(arg)
|
||||||
|
print(*processed_args)
|
||||||
|
|
||||||
# Multiprocessing lock
|
# Multiprocessing lock
|
||||||
mplock = None
|
mplock = None
|
||||||
|
|
||||||
@@ -2113,12 +2144,12 @@ class StartEndTime():
|
|||||||
else:
|
else:
|
||||||
firstMonth = getInteger(minVal=1, maxVal=6)
|
firstMonth = getInteger(minVal=1, maxVal=6)
|
||||||
currDate = todaysDate()
|
currDate = todaysDate()
|
||||||
self.startDateTime = currDate.shift(months=-firstMonth, day=1, hour=0, minute=0, second=0, microsecond=0)
|
self.startDateTime = currDate.replace(day=1, hour=0, minute=0, second=0, microsecond=0).shift(months=-firstMonth)
|
||||||
self.startTime = ISOformatTimeStamp(self.startDateTime)
|
self.startTime = ISOformatTimeStamp(self.startDateTime)
|
||||||
if myarg == 'thismonth':
|
if myarg == 'thismonth':
|
||||||
self.endDateTime = todaysTime()
|
self.endDateTime = todaysTime()
|
||||||
else:
|
else:
|
||||||
self.endDateTime = currDate.shift(day=1, hour=23, minute=59, second=59, microsecond=0).shift(days=-1)
|
self.endDateTime = currDate.replace(day=1, hour=23, minute=59, second=59, microsecond=0).shift(days=-1)
|
||||||
self.endTime = ISOformatTimeStamp(self.endDateTime)
|
self.endTime = ISOformatTimeStamp(self.endDateTime)
|
||||||
if self.startDateTime and self.endDateTime and self.endDateTime < self.startDateTime:
|
if self.startDateTime and self.endDateTime and self.endDateTime < self.startDateTime:
|
||||||
Cmd.Backup()
|
Cmd.Backup()
|
||||||
@@ -3049,9 +3080,13 @@ def getGDocData(gformat):
|
|||||||
mimeType = GDOC_FORMAT_MIME_TYPES[gformat]
|
mimeType = GDOC_FORMAT_MIME_TYPES[gformat]
|
||||||
user = getEmailAddress()
|
user = getEmailAddress()
|
||||||
fileIdEntity = getDriveFileEntity(queryShortcutsOK=False)
|
fileIdEntity = getDriveFileEntity(queryShortcutsOK=False)
|
||||||
user, drive, jcount = _validateUserGetFileIDs(user, 0, 0, fileIdEntity)
|
if not GC.Values[GC.COMMANDDATA_CLIENTACCESS]:
|
||||||
|
_, drive = buildGAPIServiceObject(API.DRIVE3, user)
|
||||||
|
else:
|
||||||
|
drive = buildGAPIObject(API.DRIVE3)
|
||||||
if not drive:
|
if not drive:
|
||||||
sys.exit(GM.Globals[GM.SYSEXITRC])
|
sys.exit(GM.Globals[GM.SYSEXITRC])
|
||||||
|
_, _, jcount = _validateUserGetFileIDs(user, 0, 0, fileIdEntity, drive=drive)
|
||||||
if jcount == 0:
|
if jcount == 0:
|
||||||
getGDocSheetDataFailedExit([Ent.USER, user], Msg.NO_ENTITIES_FOUND.format(Ent.Singular(Ent.DRIVE_FILE)))
|
getGDocSheetDataFailedExit([Ent.USER, user], Msg.NO_ENTITIES_FOUND.format(Ent.Singular(Ent.DRIVE_FILE)))
|
||||||
if jcount > 1:
|
if jcount > 1:
|
||||||
@@ -3105,14 +3140,21 @@ def getGSheetData():
|
|||||||
user = getEmailAddress()
|
user = getEmailAddress()
|
||||||
fileIdEntity = getDriveFileEntity(queryShortcutsOK=False)
|
fileIdEntity = getDriveFileEntity(queryShortcutsOK=False)
|
||||||
sheetEntity = getSheetEntity(False)
|
sheetEntity = getSheetEntity(False)
|
||||||
user, drive, jcount = _validateUserGetFileIDs(user, 0, 0, fileIdEntity)
|
if not GC.Values[GC.COMMANDDATA_CLIENTACCESS]:
|
||||||
|
user, drive = buildGAPIServiceObject(API.DRIVE3, user)
|
||||||
|
else:
|
||||||
|
drive = buildGAPIObject(API.DRIVE3)
|
||||||
if not drive:
|
if not drive:
|
||||||
sys.exit(GM.Globals[GM.SYSEXITRC])
|
sys.exit(GM.Globals[GM.SYSEXITRC])
|
||||||
|
_, _, jcount = _validateUserGetFileIDs(user, 0, 0, fileIdEntity, drive=drive)
|
||||||
if jcount == 0:
|
if jcount == 0:
|
||||||
getGDocSheetDataFailedExit([Ent.USER, user], Msg.NO_ENTITIES_FOUND.format(Ent.Singular(Ent.DRIVE_FILE)))
|
getGDocSheetDataFailedExit([Ent.USER, user], Msg.NO_ENTITIES_FOUND.format(Ent.Singular(Ent.DRIVE_FILE)))
|
||||||
if jcount > 1:
|
if jcount > 1:
|
||||||
getGDocSheetDataFailedExit([Ent.USER, user], Msg.MULTIPLE_ENTITIES_FOUND.format(Ent.Plural(Ent.DRIVE_FILE), jcount, ','.join(fileIdEntity['list'])))
|
getGDocSheetDataFailedExit([Ent.USER, user], Msg.MULTIPLE_ENTITIES_FOUND.format(Ent.Plural(Ent.DRIVE_FILE), jcount, ','.join(fileIdEntity['list'])))
|
||||||
_, sheet = buildGAPIServiceObject(API.SHEETS, user)
|
if not GC.Values[GC.COMMANDDATA_CLIENTACCESS]:
|
||||||
|
_, sheet = buildGAPIServiceObject(API.SHEETS, user)
|
||||||
|
else:
|
||||||
|
sheet = buildGAPIObject(API.SHEETS)
|
||||||
if not sheet:
|
if not sheet:
|
||||||
sys.exit(GM.Globals[GM.SYSEXITRC])
|
sys.exit(GM.Globals[GM.SYSEXITRC])
|
||||||
fileId = fileIdEntity['list'][0]
|
fileId = fileIdEntity['list'][0]
|
||||||
@@ -3705,12 +3747,12 @@ def SetGlobalVariables():
|
|||||||
return stringlist
|
return stringlist
|
||||||
|
|
||||||
def _getCfgTimezone(sectionName, itemName):
|
def _getCfgTimezone(sectionName, itemName):
|
||||||
value = _stripStringQuotes(GM.Globals[GM.PARSER].get(sectionName, itemName).lower())
|
value = _stripStringQuotes(GM.Globals[GM.PARSER].get(sectionName, itemName))
|
||||||
if value in {'utc', 'z'}:
|
if value.lower() in {'utc', 'z'}:
|
||||||
GM.Globals[GM.CONVERT_TO_LOCAL_TIME] = False
|
GM.Globals[GM.CONVERT_TO_LOCAL_TIME] = False
|
||||||
return arrow.now('utc').tzinfo
|
return arrow.now('utc').tzinfo
|
||||||
GM.Globals[GM.CONVERT_TO_LOCAL_TIME] = True
|
GM.Globals[GM.CONVERT_TO_LOCAL_TIME] = True
|
||||||
if value == 'local':
|
if value.lower() == 'local':
|
||||||
return arrow.now(value).tzinfo
|
return arrow.now(value).tzinfo
|
||||||
try:
|
try:
|
||||||
return arrow.now(value).tzinfo
|
return arrow.now(value).tzinfo
|
||||||
@@ -4113,6 +4155,8 @@ def SetGlobalVariables():
|
|||||||
GM.Globals[GM.OAUTH2_TXT_LOCK] = f'{GC.Values[GC.OAUTH2_TXT]}.lock'
|
GM.Globals[GM.OAUTH2_TXT_LOCK] = f'{GC.Values[GC.OAUTH2_TXT]}.lock'
|
||||||
# Override httplib2 settings
|
# Override httplib2 settings
|
||||||
httplib2.debuglevel = GC.Values[GC.DEBUG_LEVEL]
|
httplib2.debuglevel = GC.Values[GC.DEBUG_LEVEL]
|
||||||
|
# Use our own print function for http.client so we can redact and cleanup
|
||||||
|
http.client.print = redactable_debug_print
|
||||||
# Reset global variables if required
|
# Reset global variables if required
|
||||||
if prevExtraArgsTxt != GC.Values[GC.EXTRA_ARGS]:
|
if prevExtraArgsTxt != GC.Values[GC.EXTRA_ARGS]:
|
||||||
GM.Globals[GM.EXTRA_ARGS_LIST] = [('prettyPrint', GC.Values[GC.DEBUG_LEVEL] > 0)]
|
GM.Globals[GM.EXTRA_ARGS_LIST] = [('prettyPrint', GC.Values[GC.DEBUG_LEVEL] > 0)]
|
||||||
@@ -4764,7 +4808,7 @@ def getService(api, httpObj):
|
|||||||
waitOnFailure(n, triesLimit, INVALID_JSON_RC, str(e))
|
waitOnFailure(n, triesLimit, INVALID_JSON_RC, str(e))
|
||||||
continue
|
continue
|
||||||
systemErrorExit(INVALID_JSON_RC, str(e))
|
systemErrorExit(INVALID_JSON_RC, str(e))
|
||||||
except (http_client.ResponseNotReady, OSError, googleapiclient.errors.HttpError) as e:
|
except (http.client.ResponseNotReady, OSError, googleapiclient.errors.HttpError) as e:
|
||||||
errMsg = f'Connection error: {str(e) or repr(e)}'
|
errMsg = f'Connection error: {str(e) or repr(e)}'
|
||||||
if n != triesLimit:
|
if n != triesLimit:
|
||||||
waitOnFailure(n, triesLimit, SOCKET_ERROR_RC, errMsg)
|
waitOnFailure(n, triesLimit, SOCKET_ERROR_RC, errMsg)
|
||||||
@@ -4799,10 +4843,7 @@ def defaultSvcAcctScopes():
|
|||||||
saScopes[scope['api']].append(scope['scope'])
|
saScopes[scope['api']].append(scope['scope'])
|
||||||
else:
|
else:
|
||||||
saScopes[scope['api']].extend(scope['scope'])
|
saScopes[scope['api']].extend(scope['scope'])
|
||||||
saScopes[API.DRIVEACTIVITY].append(API.DRIVE_SCOPE)
|
|
||||||
saScopes[API.DRIVE2] = saScopes[API.DRIVE3]
|
saScopes[API.DRIVE2] = saScopes[API.DRIVE3]
|
||||||
saScopes[API.DRIVETD] = saScopes[API.DRIVE3]
|
|
||||||
saScopes[API.SHEETSTD] = saScopes[API.SHEETS]
|
|
||||||
return saScopes
|
return saScopes
|
||||||
|
|
||||||
def _getSvcAcctData():
|
def _getSvcAcctData():
|
||||||
@@ -5080,7 +5121,7 @@ def callGData(service, function,
|
|||||||
e = e.args[0]
|
e = e.args[0]
|
||||||
handleOAuthTokenError(e, GDATA.SERVICE_NOT_APPLICABLE in throwErrors)
|
handleOAuthTokenError(e, GDATA.SERVICE_NOT_APPLICABLE in throwErrors)
|
||||||
raise GDATA.ERROR_CODE_EXCEPTION_MAP[GDATA.SERVICE_NOT_APPLICABLE](str(e))
|
raise GDATA.ERROR_CODE_EXCEPTION_MAP[GDATA.SERVICE_NOT_APPLICABLE](str(e))
|
||||||
except (http_client.ResponseNotReady, OSError) as e:
|
except (http.client.ResponseNotReady, OSError) as e:
|
||||||
errMsg = f'Connection error: {str(e) or repr(e)}'
|
errMsg = f'Connection error: {str(e) or repr(e)}'
|
||||||
if n != triesLimit:
|
if n != triesLimit:
|
||||||
waitOnFailure(n, triesLimit, SOCKET_ERROR_RC, errMsg)
|
waitOnFailure(n, triesLimit, SOCKET_ERROR_RC, errMsg)
|
||||||
@@ -5390,7 +5431,7 @@ def callGAPI(service, function,
|
|||||||
e = e.args[0]
|
e = e.args[0]
|
||||||
handleOAuthTokenError(e, GAPI.SERVICE_NOT_AVAILABLE in throwReasons)
|
handleOAuthTokenError(e, GAPI.SERVICE_NOT_AVAILABLE in throwReasons)
|
||||||
raise GAPI.REASON_EXCEPTION_MAP[GAPI.SERVICE_NOT_AVAILABLE](str(e))
|
raise GAPI.REASON_EXCEPTION_MAP[GAPI.SERVICE_NOT_AVAILABLE](str(e))
|
||||||
except (http_client.ResponseNotReady, OSError) as e:
|
except (http.client.ResponseNotReady, OSError) as e:
|
||||||
errMsg = f'Connection error: {str(e) or repr(e)}'
|
errMsg = f'Connection error: {str(e) or repr(e)}'
|
||||||
if n != triesLimit:
|
if n != triesLimit:
|
||||||
waitOnFailure(n, triesLimit, SOCKET_ERROR_RC, errMsg)
|
waitOnFailure(n, triesLimit, SOCKET_ERROR_RC, errMsg)
|
||||||
@@ -5609,6 +5650,12 @@ def getSaUser(user):
|
|||||||
GM.Globals[GM.CURRENT_CLIENT_API_SCOPES] = currentClientAPIScopes
|
GM.Globals[GM.CURRENT_CLIENT_API_SCOPES] = currentClientAPIScopes
|
||||||
return userEmail
|
return userEmail
|
||||||
|
|
||||||
|
def chooseSaAPI(api1, api2):
|
||||||
|
_getSvcAcctData()
|
||||||
|
if api1 in GM.Globals[GM.SVCACCT_SCOPES]:
|
||||||
|
return api1
|
||||||
|
return api2
|
||||||
|
|
||||||
def buildGAPIServiceObject(api, user, i=0, count=0, displayError=True):
|
def buildGAPIServiceObject(api, user, i=0, count=0, displayError=True):
|
||||||
userEmail = getSaUser(user)
|
userEmail = getSaUser(user)
|
||||||
httpObj = getHttpObj(cache=GM.Globals[GM.CACHE_DIR])
|
httpObj = getHttpObj(cache=GM.Globals[GM.CACHE_DIR])
|
||||||
@@ -8030,7 +8077,7 @@ class CSVPrintFile():
|
|||||||
|
|
||||||
def getDriveObject():
|
def getDriveObject():
|
||||||
if not GC.Values[GC.TODRIVE_CLIENTACCESS]:
|
if not GC.Values[GC.TODRIVE_CLIENTACCESS]:
|
||||||
_, drive = buildGAPIServiceObject(API.DRIVETD, self.todrive['user'])
|
_, drive = buildGAPIServiceObject(chooseSaAPI(API.DRIVETD, API.DRIVE3), self.todrive['user'])
|
||||||
if not drive:
|
if not drive:
|
||||||
invalidTodriveUserExit(Ent.USER, Msg.NOT_FOUND)
|
invalidTodriveUserExit(Ent.USER, Msg.NOT_FOUND)
|
||||||
else:
|
else:
|
||||||
@@ -8183,7 +8230,7 @@ class CSVPrintFile():
|
|||||||
if result['mimeType'] != MIMETYPE_GA_SPREADSHEET:
|
if result['mimeType'] != MIMETYPE_GA_SPREADSHEET:
|
||||||
invalidTodriveFileIdExit([], f'{Msg.NOT_A} {Ent.Singular(Ent.SPREADSHEET)}', tdfileidLocation)
|
invalidTodriveFileIdExit([], f'{Msg.NOT_A} {Ent.Singular(Ent.SPREADSHEET)}', tdfileidLocation)
|
||||||
if not GC.Values[GC.TODRIVE_CLIENTACCESS]:
|
if not GC.Values[GC.TODRIVE_CLIENTACCESS]:
|
||||||
_, sheet = buildGAPIServiceObject(API.SHEETSTD, self.todrive['user'])
|
_, sheet = buildGAPIServiceObject(chooseSaAPI(API.SHEETSTD, API.SHEETS), self.todrive['user'])
|
||||||
if sheet is None:
|
if sheet is None:
|
||||||
invalidTodriveUserExit(Ent.USER, Msg.NOT_FOUND)
|
invalidTodriveUserExit(Ent.USER, Msg.NOT_FOUND)
|
||||||
else:
|
else:
|
||||||
@@ -8696,7 +8743,7 @@ class CSVPrintFile():
|
|||||||
sheetTitle += tdtime.strftime(self.todrive['sheettimeformat'])
|
sheetTitle += tdtime.strftime(self.todrive['sheettimeformat'])
|
||||||
action = Act.Get()
|
action = Act.Get()
|
||||||
if not GC.Values[GC.TODRIVE_CLIENTACCESS]:
|
if not GC.Values[GC.TODRIVE_CLIENTACCESS]:
|
||||||
user, drive = buildGAPIServiceObject(API.DRIVETD, self.todrive['user'])
|
user, drive = buildGAPIServiceObject(chooseSaAPI(API.DRIVETD, API.DRIVE3), self.todrive['user'])
|
||||||
if not drive:
|
if not drive:
|
||||||
closeFile(csvFile)
|
closeFile(csvFile)
|
||||||
return
|
return
|
||||||
@@ -8729,7 +8776,7 @@ class CSVPrintFile():
|
|||||||
if result['mimeType'] != MIMETYPE_GA_SPREADSHEET:
|
if result['mimeType'] != MIMETYPE_GA_SPREADSHEET:
|
||||||
todriveCSVErrorExit(entityValueList, f'{Msg.NOT_A} {Ent.Singular(Ent.SPREADSHEET)}')
|
todriveCSVErrorExit(entityValueList, f'{Msg.NOT_A} {Ent.Singular(Ent.SPREADSHEET)}')
|
||||||
if not GC.Values[GC.TODRIVE_CLIENTACCESS]:
|
if not GC.Values[GC.TODRIVE_CLIENTACCESS]:
|
||||||
_, sheet = buildGAPIServiceObject(API.SHEETSTD, user)
|
_, sheet = buildGAPIServiceObject(chooseSaAPI(API.SHEETSTD, API.SHEETS), user)
|
||||||
if sheet is None:
|
if sheet is None:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
@@ -8877,7 +8924,7 @@ class CSVPrintFile():
|
|||||||
(self.todrive['sheetEntity'] or self.todrive['locale'] or self.todrive['timeZone'] or
|
(self.todrive['sheetEntity'] or self.todrive['locale'] or self.todrive['timeZone'] or
|
||||||
self.todrive['sheettitle'] or self.todrive['cellwrap'] or self.todrive['cellnumberformat'])):
|
self.todrive['sheettitle'] or self.todrive['cellwrap'] or self.todrive['cellnumberformat'])):
|
||||||
if not GC.Values[GC.TODRIVE_CLIENTACCESS]:
|
if not GC.Values[GC.TODRIVE_CLIENTACCESS]:
|
||||||
_, sheet = buildGAPIServiceObject(API.SHEETSTD, user)
|
_, sheet = buildGAPIServiceObject(chooseSaAPI(API.SHEETSTD, API.SHEETS), user)
|
||||||
if sheet is None:
|
if sheet is None:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
@@ -9590,7 +9637,7 @@ def CSVFileQueueHandler(mpQueue, mpQueueStdout, mpQueueStderr, csvPF, datetimeNo
|
|||||||
clearRowFilters = False
|
clearRowFilters = False
|
||||||
# if sys.platform.startswith('win'):
|
# if sys.platform.startswith('win'):
|
||||||
# signal.signal(signal.SIGINT, signal.SIG_IGN)
|
# signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||||
if multiprocessing.get_start_method() == 'spawn':
|
if multiprocessing.get_start_method() != 'fork':
|
||||||
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||||
Cmd = glclargs.GamCLArgs()
|
Cmd = glclargs.GamCLArgs()
|
||||||
else:
|
else:
|
||||||
@@ -9632,7 +9679,7 @@ def CSVFileQueueHandler(mpQueue, mpQueueStdout, mpQueueStderr, csvPF, datetimeNo
|
|||||||
Cmd.InitializeArguments(dataItem)
|
Cmd.InitializeArguments(dataItem)
|
||||||
elif dataType == GM.REDIRECT_QUEUE_GLOBALS:
|
elif dataType == GM.REDIRECT_QUEUE_GLOBALS:
|
||||||
GM.Globals = dataItem
|
GM.Globals = dataItem
|
||||||
if multiprocessing.get_start_method() == 'spawn':
|
if multiprocessing.get_start_method() != 'fork':
|
||||||
reopenSTDFile(GM.STDOUT)
|
reopenSTDFile(GM.STDOUT)
|
||||||
reopenSTDFile(GM.STDERR)
|
reopenSTDFile(GM.STDERR)
|
||||||
elif dataType == GM.REDIRECT_QUEUE_VALUES:
|
elif dataType == GM.REDIRECT_QUEUE_VALUES:
|
||||||
@@ -9678,7 +9725,7 @@ def initializeCSVFileQueueHandler(mpManager, mpQueueStdout, mpQueueStderr):
|
|||||||
def terminateCSVFileQueueHandler(mpQueue, mpQueueHandler):
|
def terminateCSVFileQueueHandler(mpQueue, mpQueueHandler):
|
||||||
GM.Globals[GM.PARSER] = None
|
GM.Globals[GM.PARSER] = None
|
||||||
GM.Globals[GM.CSVFILE][GM.REDIRECT_QUEUE] = None
|
GM.Globals[GM.CSVFILE][GM.REDIRECT_QUEUE] = None
|
||||||
if multiprocessing.get_start_method() == 'spawn':
|
if multiprocessing.get_start_method() != 'fork':
|
||||||
mpQueue.put((GM.REDIRECT_QUEUE_ARGS, Cmd.AllArguments()))
|
mpQueue.put((GM.REDIRECT_QUEUE_ARGS, Cmd.AllArguments()))
|
||||||
savedValues = saveNonPickleableValues()
|
savedValues = saveNonPickleableValues()
|
||||||
mpQueue.put((GM.REDIRECT_QUEUE_GLOBALS, GM.Globals))
|
mpQueue.put((GM.REDIRECT_QUEUE_GLOBALS, GM.Globals))
|
||||||
@@ -9708,13 +9755,13 @@ def StdQueueHandler(mpQueue, stdtype, gmGlobals, gcValues):
|
|||||||
|
|
||||||
# if sys.platform.startswith('win'):
|
# if sys.platform.startswith('win'):
|
||||||
# signal.signal(signal.SIGINT, signal.SIG_IGN)
|
# signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||||
if multiprocessing.get_start_method() == 'spawn':
|
if multiprocessing.get_start_method() != 'fork':
|
||||||
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||||
GM.Globals = gmGlobals.copy()
|
GM.Globals = gmGlobals.copy()
|
||||||
GC.Values = gcValues.copy()
|
GC.Values = gcValues.copy()
|
||||||
pid0DataItem = [KEYBOARD_INTERRUPT_RC, None]
|
pid0DataItem = [KEYBOARD_INTERRUPT_RC, None]
|
||||||
pidData = {}
|
pidData = {}
|
||||||
if multiprocessing.get_start_method() == 'spawn':
|
if multiprocessing.get_start_method() != 'fork':
|
||||||
if GM.Globals[stdtype][GM.REDIRECT_NAME] == 'null':
|
if GM.Globals[stdtype][GM.REDIRECT_NAME] == 'null':
|
||||||
fd = open(os.devnull, GM.Globals[stdtype][GM.REDIRECT_MODE], encoding=UTF8)
|
fd = open(os.devnull, GM.Globals[stdtype][GM.REDIRECT_MODE], encoding=UTF8)
|
||||||
elif GM.Globals[stdtype][GM.REDIRECT_NAME] == '-':
|
elif GM.Globals[stdtype][GM.REDIRECT_NAME] == '-':
|
||||||
@@ -9802,7 +9849,7 @@ def ProcessGAMCommandMulti(pid, numItems, logCmd, mpQueueCSVFile, mpQueueStdout,
|
|||||||
with mplock:
|
with mplock:
|
||||||
initializeLogging()
|
initializeLogging()
|
||||||
# if sys.platform.startswith('win'):
|
# if sys.platform.startswith('win'):
|
||||||
if multiprocessing.get_start_method() == 'spawn':
|
if multiprocessing.get_start_method() != 'fork':
|
||||||
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||||
GM.Globals[GM.API_CALLS_RETRY_DATA] = {}
|
GM.Globals[GM.API_CALLS_RETRY_DATA] = {}
|
||||||
GM.Globals[GM.CMDLOG_LOGGER] = None
|
GM.Globals[GM.CMDLOG_LOGGER] = None
|
||||||
@@ -9943,7 +9990,7 @@ def MultiprocessGAMCommands(items, showCmds):
|
|||||||
mpManager = multiprocessing.Manager()
|
mpManager = multiprocessing.Manager()
|
||||||
l = mpManager.Lock()
|
l = mpManager.Lock()
|
||||||
try:
|
try:
|
||||||
if multiprocessing.get_start_method() == 'spawn':
|
if multiprocessing.get_start_method() != 'fork':
|
||||||
pool = mpManager.Pool(processes=numPoolProcesses, initializer=initGamWorker, initargs=(l,), maxtasksperchild=200)
|
pool = mpManager.Pool(processes=numPoolProcesses, initializer=initGamWorker, initargs=(l,), maxtasksperchild=200)
|
||||||
else:
|
else:
|
||||||
pool = multiprocessing.Pool(processes=numPoolProcesses, initializer=initGamWorker, initargs=(l,), maxtasksperchild=200)
|
pool = multiprocessing.Pool(processes=numPoolProcesses, initializer=initGamWorker, initargs=(l,), maxtasksperchild=200)
|
||||||
@@ -9952,7 +9999,7 @@ def MultiprocessGAMCommands(items, showCmds):
|
|||||||
except AssertionError as e:
|
except AssertionError as e:
|
||||||
Cmd.SetLocation(0)
|
Cmd.SetLocation(0)
|
||||||
usageErrorExit(str(e))
|
usageErrorExit(str(e))
|
||||||
if multiprocessing.get_start_method() == 'spawn':
|
if multiprocessing.get_start_method() != 'fork':
|
||||||
savedValues = saveNonPickleableValues()
|
savedValues = saveNonPickleableValues()
|
||||||
if GM.Globals[GM.STDOUT][GM.REDIRECT_MULTIPROCESS]:
|
if GM.Globals[GM.STDOUT][GM.REDIRECT_MULTIPROCESS]:
|
||||||
mpQueueStdout, mpQueueHandlerStdout = initializeStdQueueHandler(mpManager, GM.STDOUT, GM.Globals, GC.Values)
|
mpQueueStdout, mpQueueHandlerStdout = initializeStdQueueHandler(mpManager, GM.STDOUT, GM.Globals, GC.Values)
|
||||||
@@ -9967,7 +10014,7 @@ def MultiprocessGAMCommands(items, showCmds):
|
|||||||
mpQueueStderr = mpQueueStdout
|
mpQueueStderr = mpQueueStdout
|
||||||
else:
|
else:
|
||||||
mpQueueStderr = None
|
mpQueueStderr = None
|
||||||
if multiprocessing.get_start_method() == 'spawn':
|
if multiprocessing.get_start_method() != 'fork':
|
||||||
restoreNonPickleableValues(savedValues)
|
restoreNonPickleableValues(savedValues)
|
||||||
if mpQueueStdout:
|
if mpQueueStdout:
|
||||||
mpQueueStdout.put((0, GM.REDIRECT_QUEUE_DATA, GM.Globals[GM.STDOUT][GM.REDIRECT_MULTI_FD].getvalue()))
|
mpQueueStdout.put((0, GM.REDIRECT_QUEUE_DATA, GM.Globals[GM.STDOUT][GM.REDIRECT_MULTI_FD].getvalue()))
|
||||||
@@ -11131,7 +11178,7 @@ class Credentials(google.oauth2.credentials.Credentials):
|
|||||||
|
|
||||||
def doOAuthRequest(currentScopes, login_hint, verifyScopes=False):
|
def doOAuthRequest(currentScopes, login_hint, verifyScopes=False):
|
||||||
client_id, client_secret = getOAuthClientIDAndSecret()
|
client_id, client_secret = getOAuthClientIDAndSecret()
|
||||||
scopesList = API.getClientScopesList(GC.Values[GC.TODRIVE_CLIENTACCESS])
|
scopesList = API.getClientScopesList(GC.Values[GC.COMMANDDATA_CLIENTACCESS], GC.Values[GC.TODRIVE_CLIENTACCESS])
|
||||||
if not currentScopes or verifyScopes:
|
if not currentScopes or verifyScopes:
|
||||||
selectedScopes = getScopesFromUser(scopesList, True, currentScopes)
|
selectedScopes = getScopesFromUser(scopesList, True, currentScopes)
|
||||||
if selectedScopes is None:
|
if selectedScopes is None:
|
||||||
@@ -11177,7 +11224,7 @@ def doOAuthCreate():
|
|||||||
else:
|
else:
|
||||||
login_hint = None
|
login_hint = None
|
||||||
scopes = []
|
scopes = []
|
||||||
scopesList = API.getClientScopesList(GC.Values[GC.TODRIVE_CLIENTACCESS])
|
scopesList = API.getClientScopesList(GC.Values[GC.COMMANDDATA_CLIENTACCESS], GC.Values[GC.TODRIVE_CLIENTACCESS])
|
||||||
while Cmd.ArgumentsRemaining():
|
while Cmd.ArgumentsRemaining():
|
||||||
myarg = getArgument()
|
myarg = getArgument()
|
||||||
if myarg == 'admin':
|
if myarg == 'admin':
|
||||||
@@ -11193,7 +11240,9 @@ def doOAuthCreate():
|
|||||||
scopes.append(uscope)
|
scopes.append(uscope)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
invalidChoiceExit(uscope, API.getClientScopesURLs(GC.Values[GC.TODRIVE_CLIENTACCESS]), True)
|
invalidChoiceExit(uscope,
|
||||||
|
API.getClientScopesURLs(GC.Values[GC.COMMANDDATA_CLIENTACCESS], GC.Values[GC.TODRIVE_CLIENTACCESS]),
|
||||||
|
True)
|
||||||
else:
|
else:
|
||||||
unknownArgumentExit()
|
unknownArgumentExit()
|
||||||
if len(scopes) == 0:
|
if len(scopes) == 0:
|
||||||
@@ -11304,7 +11353,7 @@ def doOAuthUpdate():
|
|||||||
if 'scopes' in jsonDict:
|
if 'scopes' in jsonDict:
|
||||||
currentScopes = jsonDict['scopes']
|
currentScopes = jsonDict['scopes']
|
||||||
else:
|
else:
|
||||||
currentScopes = API.getClientScopesURLs(GC.Values[GC.TODRIVE_CLIENTACCESS])
|
currentScopes = API.getClientScopesURLs(GC.Values[GC.COMMANDDATA_CLIENTACCESS], GC.Values[GC.TODRIVE_CLIENTACCESS])
|
||||||
else:
|
else:
|
||||||
currentScopes = []
|
currentScopes = []
|
||||||
except (AttributeError, IndexError, KeyError, SyntaxError, TypeError, ValueError) as e:
|
except (AttributeError, IndexError, KeyError, SyntaxError, TypeError, ValueError) as e:
|
||||||
@@ -12334,8 +12383,6 @@ def checkServiceAccount(users):
|
|||||||
saScopes[scope['api']].append(scope['roscope'])
|
saScopes[scope['api']].append(scope['roscope'])
|
||||||
checkScopesSet.add(scope['roscope'])
|
checkScopesSet.add(scope['roscope'])
|
||||||
i += 1
|
i += 1
|
||||||
if API.DRIVEACTIVITY in saScopes and API.DRIVE3 in saScopes:
|
|
||||||
saScopes[API.DRIVEACTIVITY].append(API.DRIVE_SCOPE)
|
|
||||||
if API.DRIVE3 in saScopes:
|
if API.DRIVE3 in saScopes:
|
||||||
saScopes[API.DRIVE2] = saScopes[API.DRIVE3]
|
saScopes[API.DRIVE2] = saScopes[API.DRIVE3]
|
||||||
GM.Globals[GM.OAUTH2SERVICE_JSON_DATA][API.OAUTH2SA_SCOPES] = saScopes
|
GM.Globals[GM.OAUTH2SERVICE_JSON_DATA][API.OAUTH2SA_SCOPES] = saScopes
|
||||||
@@ -16263,7 +16310,7 @@ def _showCustomerLicenseInfo(customerInfo, FJQC):
|
|||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
result = callGAPI(rep.customerUsageReports(), 'get',
|
result = callGAPI(rep.customerUsageReports(), 'get',
|
||||||
throwReasons=[GAPI.INVALID, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
throwReasons=[GAPI.INVALID, GAPI.FAILED_PRECONDITION, GAPI.FORBIDDEN, GAPI.PERMISSION_DENIED],
|
||||||
date=tryDate, customerId=customerInfo['id'],
|
date=tryDate, customerId=customerInfo['id'],
|
||||||
fields='warnings,usageReports', parameters=parameters)
|
fields='warnings,usageReports', parameters=parameters)
|
||||||
usageReports = numUsersAvailable(result)
|
usageReports = numUsersAvailable(result)
|
||||||
@@ -16276,7 +16323,7 @@ def _showCustomerLicenseInfo(customerInfo, FJQC):
|
|||||||
if fullData == 0:
|
if fullData == 0:
|
||||||
continue
|
continue
|
||||||
break
|
break
|
||||||
except GAPI.invalid as e:
|
except (GAPI.invalid, GAPI.failedPrecondition) as e:
|
||||||
tryDate = _adjustTryDate(str(e), 0, -1, tryDate)
|
tryDate = _adjustTryDate(str(e), 0, -1, tryDate)
|
||||||
if not tryDate:
|
if not tryDate:
|
||||||
return
|
return
|
||||||
@@ -16990,23 +17037,39 @@ def doDeleteAdmin():
|
|||||||
except (GAPI.forbidden, GAPI.permissionDenied) as e:
|
except (GAPI.forbidden, GAPI.permissionDenied) as e:
|
||||||
ClientAPIAccessDeniedExit(str(e))
|
ClientAPIAccessDeniedExit(str(e))
|
||||||
|
|
||||||
ASSIGNEE_EMAILTYPE_TOFIELD_MAP = {
|
ADMIN_ASSIGNEE_TYPE_TO_ASSIGNEDTO_FIELD_MAP = {
|
||||||
'user': 'assignedToUser',
|
'user': 'assignedToUser',
|
||||||
'group': 'assignedToGroup',
|
'group': 'assignedToGroup',
|
||||||
'serviceaccount': 'assignedToServiceAccount',
|
'serviceaccount': 'assignedToServiceAccount',
|
||||||
|
'unknown': 'assignedToUnknown',
|
||||||
}
|
}
|
||||||
PRINT_ADMIN_FIELDS = ['roleAssignmentId', 'roleId', 'assignedTo', 'scopeType', 'orgUnitId', 'assigneeType']
|
ALL_ASSIGNEE_TYPES = ['user', 'group', 'serviceaccount']
|
||||||
|
|
||||||
|
PRINT_ADMIN_FIELDS = ['roleAssignmentId', 'roleId', 'assignedTo', 'scopeType', 'orgUnitId']
|
||||||
PRINT_ADMIN_TITLES = ['roleAssignmentId', 'roleId', 'role',
|
PRINT_ADMIN_TITLES = ['roleAssignmentId', 'roleId', 'role',
|
||||||
'assignedTo', 'assignedToUser', 'assignedToGroup', 'assignedToServiceAccount', 'assignedToUnknown',
|
'assignedTo', 'assignedToUser', 'assignedToGroup', 'assignedToServiceAccount', 'assignedToUnknown',
|
||||||
'scopeType', 'orgUnitId', 'orgUnit']
|
'scopeType', 'orgUnitId', 'orgUnit']
|
||||||
|
|
||||||
# gam print admins [todrive <ToDriveAttribute>*]
|
# gam print admins [todrive <ToDriveAttribute>*]
|
||||||
# [user|group <EmailAddress>|<UniqueID>] [role <RoleItem>]
|
# [user|group <EmailAddress>|<UniqueID>] [role <RoleItem>]
|
||||||
|
# [types <AdminAssigneeTypeList>]
|
||||||
# [recursive] [condition] [privileges] [oneitemperrow]
|
# [recursive] [condition] [privileges] [oneitemperrow]
|
||||||
# gam show admins
|
# gam show admins
|
||||||
# [user|group <EmailAddress>|<UniqueID>] [role <RoleItem>]
|
# [user|group <EmailAddress>|<UniqueID>] [role <RoleItem>]
|
||||||
|
# [types <AdminAssigneeTypeList>]
|
||||||
# [recursive] [condition] [privileges]
|
# [recursive] [condition] [privileges]
|
||||||
def doPrintShowAdmins():
|
def doPrintShowAdmins():
|
||||||
|
def _getAssigneeTypes(myarg):
|
||||||
|
if myarg in {'type', 'types'}:
|
||||||
|
for gtype in getString(Cmd.OB_ADMIN_ASSIGNEE_TYPE_LIST).lower().replace(',', ' ').split():
|
||||||
|
if gtype in ADMIN_ASSIGNEE_TYPE_TO_ASSIGNEDTO_FIELD_MAP:
|
||||||
|
typesSet.add(ADMIN_ASSIGNEE_TYPE_TO_ASSIGNEDTO_FIELD_MAP[gtype])
|
||||||
|
else:
|
||||||
|
invalidChoiceExit(gtype, ADMIN_ASSIGNEE_TYPE_TO_ASSIGNEDTO_FIELD_MAP, True)
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
def _getPrivileges(admin):
|
def _getPrivileges(admin):
|
||||||
if showPrivileges:
|
if showPrivileges:
|
||||||
roleId = admin['roleId']
|
roleId = admin['roleId']
|
||||||
@@ -17032,16 +17095,16 @@ def doPrintShowAdmins():
|
|||||||
def _setNamesFromIds(admin, privileges):
|
def _setNamesFromIds(admin, privileges):
|
||||||
admin['role'] = role_from_roleid(admin['roleId'])
|
admin['role'] = role_from_roleid(admin['roleId'])
|
||||||
assignedTo = admin['assignedTo']
|
assignedTo = admin['assignedTo']
|
||||||
|
admin['assignedToUnknown'] = False
|
||||||
if assignedTo not in assignedToIdEmailMap:
|
if assignedTo not in assignedToIdEmailMap:
|
||||||
assigneeEmail, assigneeType = convertUIDtoEmailAddressWithType(f'uid:{assignedTo}', cd, sal,
|
emailTypes = ALL_ASSIGNEE_TYPES if admin.get('assigneeType', '') != 'group' else ['group']
|
||||||
emailTypes=allAssigneeTypes if admin.get('assigneeType') != 'group' else ['group'])
|
assigneeEmail, assigneeType = convertUIDtoEmailAddressWithType(f'uid:{assignedTo}', cd, sal, emailTypes=emailTypes)
|
||||||
if assigneeType in ASSIGNEE_EMAILTYPE_TOFIELD_MAP:
|
assignedToField = ADMIN_ASSIGNEE_TYPE_TO_ASSIGNEDTO_FIELD_MAP.get(assigneeType, 'assignedToUnknown')
|
||||||
assignedToField = ASSIGNEE_EMAILTYPE_TOFIELD_MAP[assigneeType]
|
if assignedToField == 'assignedToUnknown':
|
||||||
else:
|
|
||||||
assignedToField = 'assignedToUnknown'
|
|
||||||
assigneeEmail = True
|
assigneeEmail = True
|
||||||
assignedToIdEmailMap[assignedTo] = {'assignedToField': assignedToField, 'assigneeEmail': assigneeEmail}
|
assignedToIdEmailMap[assignedTo] = {'assignedToField': assignedToField, 'assigneeEmail': assigneeEmail}
|
||||||
admin[assignedToIdEmailMap[assignedTo]['assignedToField']] = assignedToIdEmailMap[assignedTo]['assigneeEmail']
|
admin[assignedToIdEmailMap[assignedTo]['assignedToField']] = assignedToIdEmailMap[assignedTo]['assigneeEmail']
|
||||||
|
admin['assignedToField'] = assignedToIdEmailMap[assignedTo]['assignedToField']
|
||||||
if privileges is not None:
|
if privileges is not None:
|
||||||
admin.update(privileges)
|
admin.update(privileges)
|
||||||
if 'orgUnitId' in admin:
|
if 'orgUnitId' in admin:
|
||||||
@@ -17051,18 +17114,23 @@ def doPrintShowAdmins():
|
|||||||
admin['condition'] = 'securitygroup'
|
admin['condition'] = 'securitygroup'
|
||||||
elif admin['condition'] == NONSECURITY_GROUP_CONDITION:
|
elif admin['condition'] == NONSECURITY_GROUP_CONDITION:
|
||||||
admin['condition'] = 'nonsecuritygroup'
|
admin['condition'] = 'nonsecuritygroup'
|
||||||
|
if debug:
|
||||||
|
print('******', admin['assignedTo'], admin.get('assigneeType', 'no type'),
|
||||||
|
admin['assignedToField'], not typesSet or admin['assignedToField'] in typesSet)
|
||||||
|
return not typesSet or admin['assignedToField'] in typesSet
|
||||||
|
|
||||||
cd = buildGAPIObject(API.DIRECTORY)
|
cd = buildGAPIObject(API.DIRECTORY)
|
||||||
sal = buildGAPIObject(API.SERVICEACCOUNTLOOKUP)
|
sal = buildGAPIObject(API.SERVICEACCOUNTLOOKUP)
|
||||||
csvPF = CSVPrintFile(PRINT_ADMIN_TITLES) if Act.csvFormat() else None
|
csvPF = CSVPrintFile(PRINT_ADMIN_TITLES) if Act.csvFormat() else None
|
||||||
roleId = None
|
roleId = None
|
||||||
userKey = None
|
userKey = None
|
||||||
oneItemPerRow = recursive = showPrivileges = False
|
debug = oneItemPerRow = recursive = showPrivileges = False
|
||||||
|
typesSet = set()
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
rolePrivileges = {}
|
rolePrivileges = {}
|
||||||
fieldsList = PRINT_ADMIN_FIELDS
|
allGroupRoles = ','.join(sorted(ALL_GROUP_ROLES))
|
||||||
|
fieldsList = PRINT_ADMIN_FIELDS+['assigneeType']
|
||||||
assignedToIdEmailMap = {}
|
assignedToIdEmailMap = {}
|
||||||
allAssigneeTypes = list(ASSIGNEE_EMAILTYPE_TOFIELD_MAP.keys())
|
|
||||||
while Cmd.ArgumentsRemaining():
|
while Cmd.ArgumentsRemaining():
|
||||||
myarg = getArgument()
|
myarg = getArgument()
|
||||||
if csvPF and myarg == 'todrive':
|
if csvPF and myarg == 'todrive':
|
||||||
@@ -17071,9 +17139,10 @@ def doPrintShowAdmins():
|
|||||||
userKey = kwargs['userKey'] = getEmailAddress()
|
userKey = kwargs['userKey'] = getEmailAddress()
|
||||||
elif myarg == 'role':
|
elif myarg == 'role':
|
||||||
_, roleId = getRoleId()
|
_, roleId = getRoleId()
|
||||||
|
elif _getAssigneeTypes(myarg):
|
||||||
|
pass
|
||||||
elif myarg == 'recursive':
|
elif myarg == 'recursive':
|
||||||
recursive = True
|
recursive = True
|
||||||
allGroupRoles = ','.join(sorted(ALL_GROUP_ROLES))
|
|
||||||
memberOptions = initMemberOptions()
|
memberOptions = initMemberOptions()
|
||||||
memberOptions[MEMBEROPTION_INCLUDEDERIVEDMEMBERSHIP] = True
|
memberOptions[MEMBEROPTION_INCLUDEDERIVEDMEMBERSHIP] = True
|
||||||
memberOptions[MEMBEROPTION_DISPLAYMATCH] = False
|
memberOptions[MEMBEROPTION_DISPLAYMATCH] = False
|
||||||
@@ -17088,6 +17157,8 @@ def doPrintShowAdmins():
|
|||||||
showPrivileges = True
|
showPrivileges = True
|
||||||
elif myarg == 'oneitemperrow':
|
elif myarg == 'oneitemperrow':
|
||||||
oneItemPerRow = True
|
oneItemPerRow = True
|
||||||
|
elif myarg == 'debug':
|
||||||
|
debug = True
|
||||||
else:
|
else:
|
||||||
unknownArgumentExit()
|
unknownArgumentExit()
|
||||||
if roleId and not kwargs:
|
if roleId and not kwargs:
|
||||||
@@ -17109,7 +17180,7 @@ def doPrintShowAdmins():
|
|||||||
return
|
return
|
||||||
except GAPI.notFound as e:
|
except GAPI.notFound as e:
|
||||||
entityActionFailedExit([Ent.ADMIN_ROLE, kwargs['roleId']], str(e))
|
entityActionFailedExit([Ent.ADMIN_ROLE, kwargs['roleId']], str(e))
|
||||||
except (GAPI.forbidden, GAPI.serviceNotAvailable) as e:
|
except GAPI.serviceNotAvailable as e:
|
||||||
entityActionFailedExit([Ent.ADMINISTRATOR, userKey], str(e))
|
entityActionFailedExit([Ent.ADMINISTRATOR, userKey], str(e))
|
||||||
except (GAPI.badRequest, GAPI.customerNotFound):
|
except (GAPI.badRequest, GAPI.customerNotFound):
|
||||||
accessErrorExit(cd)
|
accessErrorExit(cd)
|
||||||
@@ -17123,11 +17194,11 @@ def doPrintShowAdmins():
|
|||||||
i += 1
|
i += 1
|
||||||
if roleId and roleId != admin['roleId']:
|
if roleId and roleId != admin['roleId']:
|
||||||
continue
|
continue
|
||||||
if admin['assigneeType'] != 'group' or not recursive:
|
|
||||||
_setNamesFromIds(admin, _getPrivileges(admin))
|
|
||||||
expandedAdmins.append(admin)
|
|
||||||
continue
|
|
||||||
assignedTo = admin['assignedTo']
|
assignedTo = admin['assignedTo']
|
||||||
|
if admin['assigneeType'] != 'group' or not recursive:
|
||||||
|
if _setNamesFromIds(admin, _getPrivileges(admin)):
|
||||||
|
expandedAdmins.append(admin)
|
||||||
|
continue
|
||||||
if assignedTo not in groupMembers:
|
if assignedTo not in groupMembers:
|
||||||
membersList = []
|
membersList = []
|
||||||
membersSet = set()
|
membersSet = set()
|
||||||
@@ -17135,7 +17206,8 @@ def doPrintShowAdmins():
|
|||||||
getGroupMembers(cd, assignedTo, allGroupRoles, membersList, membersSet, i, count,
|
getGroupMembers(cd, assignedTo, allGroupRoles, membersList, membersSet, i, count,
|
||||||
memberOptions, memberDisplayOptions, level, {Ent.TYPE_USER})
|
memberOptions, memberDisplayOptions, level, {Ent.TYPE_USER})
|
||||||
groupMembers[assignedTo] = membersList[:]
|
groupMembers[assignedTo] = membersList[:]
|
||||||
_setNamesFromIds(admin, _getPrivileges(admin))
|
if not _setNamesFromIds(admin, _getPrivileges(admin)):
|
||||||
|
continue
|
||||||
if not groupMembers[assignedTo]:
|
if not groupMembers[assignedTo]:
|
||||||
expandedAdmins.append(admin)
|
expandedAdmins.append(admin)
|
||||||
continue
|
continue
|
||||||
@@ -17158,7 +17230,7 @@ def doPrintShowAdmins():
|
|||||||
Ind.Increment()
|
Ind.Increment()
|
||||||
for field in PRINT_ADMIN_TITLES:
|
for field in PRINT_ADMIN_TITLES:
|
||||||
if field in admin:
|
if field in admin:
|
||||||
if field == 'roleAssignmentId':
|
if (field == 'roleAssignmentId') or (field == 'assignedToUnknown' and not admin[field]):
|
||||||
continue
|
continue
|
||||||
printKeyValueList([field, admin[field]])
|
printKeyValueList([field, admin[field]])
|
||||||
if showPrivileges:
|
if showPrivileges:
|
||||||
@@ -17173,6 +17245,8 @@ def doPrintShowAdmins():
|
|||||||
Ind.Decrement()
|
Ind.Decrement()
|
||||||
else:
|
else:
|
||||||
for admin in expandedAdmins:
|
for admin in expandedAdmins:
|
||||||
|
admin.pop('assigneeType', None)
|
||||||
|
admin.pop('assignedToField', None)
|
||||||
if not oneItemPerRow or 'rolePrivileges' not in admin:
|
if not oneItemPerRow or 'rolePrivileges' not in admin:
|
||||||
csvPF.WriteRowTitles(flattenJSON(admin))
|
csvPF.WriteRowTitles(flattenJSON(admin))
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -34,7 +34,12 @@ def main():
|
|||||||
|
|
||||||
# Run from command line
|
# Run from command line
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
if platform.system() != 'Linux':
|
if getattr(sys, 'frozen', False): # we're frozen:
|
||||||
multiprocessing.freeze_support()
|
multiprocessing.freeze_support()
|
||||||
|
if platform.system() == 'Linux':
|
||||||
|
# set explictly since it's not default in Python < 3.14, forkserver should
|
||||||
|
# be safer than fork and less likely to see bulk command hangs.
|
||||||
|
multiprocessing.set_start_method('forkserver')
|
||||||
|
else:
|
||||||
multiprocessing.set_start_method('spawn')
|
multiprocessing.set_start_method('spawn')
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -105,6 +105,8 @@ YOUTUBE = 'youtube'
|
|||||||
BUSINESSACCOUNTMANAGEMENT_SCOPE = 'https://www.googleapis.com/auth/business.manage'
|
BUSINESSACCOUNTMANAGEMENT_SCOPE = 'https://www.googleapis.com/auth/business.manage'
|
||||||
CHROMEVERSIONHISTORY_URL = 'https://versionhistory.googleapis.com/v1/chrome/platforms'
|
CHROMEVERSIONHISTORY_URL = 'https://versionhistory.googleapis.com/v1/chrome/platforms'
|
||||||
DRIVE_SCOPE = 'https://www.googleapis.com/auth/drive'
|
DRIVE_SCOPE = 'https://www.googleapis.com/auth/drive'
|
||||||
|
DRIVE_FILE_SCOPE = 'https://www.googleapis.com/auth/drive.file'
|
||||||
|
DRIVE_READONLY_SCOPE = 'https://www.googleapis.com/auth/drive.readonly'
|
||||||
GMAIL_SEND_SCOPE = 'https://www.googleapis.com/auth/gmail.send'
|
GMAIL_SEND_SCOPE = 'https://www.googleapis.com/auth/gmail.send'
|
||||||
GOOGLE_AUTH_PROVIDER_X509_CERT_URL = 'https://www.googleapis.com/oauth2/v1/certs'
|
GOOGLE_AUTH_PROVIDER_X509_CERT_URL = 'https://www.googleapis.com/oauth2/v1/certs'
|
||||||
GOOGLE_OAUTH2_ENDPOINT = 'https://accounts.google.com/o/oauth2/v2/auth'
|
GOOGLE_OAUTH2_ENDPOINT = 'https://accounts.google.com/o/oauth2/v2/auth'
|
||||||
@@ -156,6 +158,7 @@ OAUTH2_TOKEN_ERRORS = [
|
|||||||
'access_denied: Account restricted',
|
'access_denied: Account restricted',
|
||||||
'internal_failure: Backend Error',
|
'internal_failure: Backend Error',
|
||||||
'internal_failure: None',
|
'internal_failure: None',
|
||||||
|
'invalid_account: Forbidden',
|
||||||
'invalid_grant',
|
'invalid_grant',
|
||||||
'invalid_grant: Bad Request',
|
'invalid_grant: Bad Request',
|
||||||
'invalid_grant: Invalid email or User ID',
|
'invalid_grant: Invalid email or User ID',
|
||||||
@@ -253,7 +256,7 @@ _INFO = {
|
|||||||
DOCS: {'name': 'Docs API', 'version': 'v1', 'v2discovery': True},
|
DOCS: {'name': 'Docs API', 'version': 'v1', 'v2discovery': True},
|
||||||
DRIVE2: {'name': 'Drive API v2', 'version': 'v2', 'v2discovery': False, 'mappedAPI': 'drive'},
|
DRIVE2: {'name': 'Drive API v2', 'version': 'v2', 'v2discovery': False, 'mappedAPI': 'drive'},
|
||||||
DRIVE3: {'name': 'Drive API v3', 'version': 'v3', 'v2discovery': False, 'mappedAPI': 'drive'},
|
DRIVE3: {'name': 'Drive API v3', 'version': 'v3', 'v2discovery': False, 'mappedAPI': 'drive'},
|
||||||
DRIVETD: {'name': 'Drive API v3 - todrive', 'version': 'v3', 'v2discovery': False, 'mappedAPI': 'drive'},
|
DRIVETD: {'name': 'Drive API v3 - write todrive data', 'version': 'v3', 'v2discovery': False, 'mappedAPI': 'drive'},
|
||||||
DRIVEACTIVITY: {'name': 'Drive Activity API v2', 'version': 'v2', 'v2discovery': True},
|
DRIVEACTIVITY: {'name': 'Drive Activity API v2', 'version': 'v2', 'v2discovery': True},
|
||||||
DRIVELABELS_ADMIN: {'name': 'Drive Labels API - Admin', 'version': 'v2', 'v2discovery': True, 'mappedAPI': DRIVELABELS},
|
DRIVELABELS_ADMIN: {'name': 'Drive Labels API - Admin', 'version': 'v2', 'v2discovery': True, 'mappedAPI': DRIVELABELS},
|
||||||
DRIVELABELS_USER: {'name': 'Drive Labels API - User', 'version': 'v2', 'v2discovery': True, 'mappedAPI': DRIVELABELS},
|
DRIVELABELS_USER: {'name': 'Drive Labels API - User', 'version': 'v2', 'v2discovery': True, 'mappedAPI': DRIVELABELS},
|
||||||
@@ -283,7 +286,7 @@ _INFO = {
|
|||||||
SERVICEMANAGEMENT: {'name': 'Service Management API', 'version': 'v1', 'v2discovery': True},
|
SERVICEMANAGEMENT: {'name': 'Service Management API', 'version': 'v1', 'v2discovery': True},
|
||||||
SERVICEUSAGE: {'name': 'Service Usage API', 'version': 'v1', 'v2discovery': True},
|
SERVICEUSAGE: {'name': 'Service Usage API', 'version': 'v1', 'v2discovery': True},
|
||||||
SHEETS: {'name': 'Sheets API', 'version': 'v4', 'v2discovery': True},
|
SHEETS: {'name': 'Sheets API', 'version': 'v4', 'v2discovery': True},
|
||||||
SHEETSTD: {'name': 'Sheets API - todrive', 'version': 'v4', 'v2discovery': True, 'mappedAPI': SHEETS},
|
SHEETSTD: {'name': 'Sheets API - write todrive data', 'version': 'v4', 'v2discovery': True, 'mappedAPI': SHEETS},
|
||||||
SITEVERIFICATION: {'name': 'Site Verification API', 'version': 'v1', 'v2discovery': True},
|
SITEVERIFICATION: {'name': 'Site Verification API', 'version': 'v1', 'v2discovery': True},
|
||||||
STORAGE: {'name': 'Cloud Storage API', 'version': 'v1', 'v2discovery': True},
|
STORAGE: {'name': 'Cloud Storage API', 'version': 'v1', 'v2discovery': True},
|
||||||
STORAGEREAD: {'name': 'Cloud Storage API - Read', 'version': 'v1', 'v2discovery': True, 'mappedAPI': STORAGE},
|
STORAGEREAD: {'name': 'Cloud Storage API - Read', 'version': 'v1', 'v2discovery': True, 'mappedAPI': STORAGE},
|
||||||
@@ -530,6 +533,17 @@ _CLIENT_SCOPES = [
|
|||||||
'scope': 'https://www.googleapis.com/auth/ediscovery'},
|
'scope': 'https://www.googleapis.com/auth/ediscovery'},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
_COMMANDDATA_CLIENT_SCOPES = [
|
||||||
|
{'name': 'Drive API - commanddata_clientaccess',
|
||||||
|
'api': DRIVE3,
|
||||||
|
'subscopes': [],
|
||||||
|
'scope': DRIVE_READONLY_SCOPE},
|
||||||
|
{'name': 'Sheets API - commanddata_clientaccess',
|
||||||
|
'api': SHEETS,
|
||||||
|
'subscopes': [],
|
||||||
|
'scope': 'https://www.googleapis.com/auth/spreadsheets.readonly'},
|
||||||
|
]
|
||||||
|
|
||||||
_TODRIVE_CLIENT_SCOPES = [
|
_TODRIVE_CLIENT_SCOPES = [
|
||||||
{'name': 'Drive API - todrive_clientaccess',
|
{'name': 'Drive API - todrive_clientaccess',
|
||||||
'api': DRIVE3,
|
'api': DRIVE3,
|
||||||
@@ -538,7 +552,7 @@ _TODRIVE_CLIENT_SCOPES = [
|
|||||||
{'name': 'Drive File API - todrive_clientaccess',
|
{'name': 'Drive File API - todrive_clientaccess',
|
||||||
'api': DRIVE3,
|
'api': DRIVE3,
|
||||||
'subscopes': [],
|
'subscopes': [],
|
||||||
'scope': 'https://www.googleapis.com/auth/drive.file'},
|
'scope': DRIVE_FILE_SCOPE},
|
||||||
{'name': 'Gmail API - todrive_clientaccess',
|
{'name': 'Gmail API - todrive_clientaccess',
|
||||||
'api': GMAIL,
|
'api': GMAIL,
|
||||||
'subscopes': [],
|
'subscopes': [],
|
||||||
@@ -643,7 +657,8 @@ _SVCACCT_SCOPES = [
|
|||||||
{'name': 'Drive Activity API v2 - must pair with Drive API',
|
{'name': 'Drive Activity API v2 - must pair with Drive API',
|
||||||
'api': DRIVEACTIVITY,
|
'api': DRIVEACTIVITY,
|
||||||
'subscopes': [],
|
'subscopes': [],
|
||||||
'scope': 'https://www.googleapis.com/auth/drive.activity'},
|
'scope': [DRIVE_READONLY_SCOPE,
|
||||||
|
'https://www.googleapis.com/auth/drive.activity']},
|
||||||
{'name': 'Drive Labels API - Admin',
|
{'name': 'Drive Labels API - Admin',
|
||||||
'api': DRIVELABELS_ADMIN,
|
'api': DRIVELABELS_ADMIN,
|
||||||
'subscopes': READONLY,
|
'subscopes': READONLY,
|
||||||
@@ -656,10 +671,12 @@ _SVCACCT_SCOPES = [
|
|||||||
'api': DOCS,
|
'api': DOCS,
|
||||||
'subscopes': READONLY,
|
'subscopes': READONLY,
|
||||||
'scope': 'https://www.googleapis.com/auth/documents'},
|
'scope': 'https://www.googleapis.com/auth/documents'},
|
||||||
{'name': 'Forms API',
|
{'name': 'Forms API - must pair with Drive API',
|
||||||
'api': FORMS,
|
'api': FORMS,
|
||||||
'subscopes': [],
|
'subscopes': [],
|
||||||
'scope': DRIVE_SCOPE},
|
'scope': [DRIVE_READONLY_SCOPE,
|
||||||
|
'https://www.googleapis.com/auth/forms.body',
|
||||||
|
'https://www.googleapis.com/auth/forms.responses.readonly']},
|
||||||
{'name': 'Gmail API - Full Access (Labels, Messages)',
|
{'name': 'Gmail API - Full Access (Labels, Messages)',
|
||||||
'api': GMAIL,
|
'api': GMAIL,
|
||||||
'subscopes': [],
|
'subscopes': [],
|
||||||
@@ -750,9 +767,10 @@ _SVCACCT_SCOPES = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
_SVCACCT_SPECIAL_SCOPES = [
|
_SVCACCT_SPECIAL_SCOPES = [
|
||||||
{'name': 'Drive API - todrive',
|
{'name': 'Drive API - write todrive data - has access to all Drive',
|
||||||
'api': DRIVETD,
|
'api': DRIVETD,
|
||||||
'subscopes': [],
|
'subscopes': [],
|
||||||
|
'offByDefault': True,
|
||||||
'scope': DRIVE_SCOPE},
|
'scope': DRIVE_SCOPE},
|
||||||
{'name': 'Gmail API - Full Access - read only',
|
{'name': 'Gmail API - Full Access - read only',
|
||||||
'api': GMAIL,
|
'api': GMAIL,
|
||||||
@@ -764,8 +782,9 @@ _SVCACCT_SPECIAL_SCOPES = [
|
|||||||
'subscopes': [],
|
'subscopes': [],
|
||||||
'offByDefault': True,
|
'offByDefault': True,
|
||||||
'scope': GMAIL_SEND_SCOPE},
|
'scope': GMAIL_SEND_SCOPE},
|
||||||
{'name': 'Sheets API - todrive',
|
{'name': 'Sheets API - write todrive data - has access to all Sheets',
|
||||||
'api': SHEETSTD,
|
'api': SHEETSTD,
|
||||||
|
'offByDefault': True,
|
||||||
'subscopes': [],
|
'subscopes': [],
|
||||||
'scope': 'https://www.googleapis.com/auth/spreadsheets'},
|
'scope': 'https://www.googleapis.com/auth/spreadsheets'},
|
||||||
]
|
]
|
||||||
@@ -789,14 +808,18 @@ def getVersion(api):
|
|||||||
def getClientScopesSet(api):
|
def getClientScopesSet(api):
|
||||||
return {scope['scope'] for scope in _CLIENT_SCOPES if scope['api'] == api}
|
return {scope['scope'] for scope in _CLIENT_SCOPES if scope['api'] == api}
|
||||||
|
|
||||||
def getClientScopesList(todriveClientAccess):
|
def getClientScopesList(commanddataClientAccess, todriveClientAccess):
|
||||||
caScopes = _CLIENT_SCOPES[:]
|
caScopes = _CLIENT_SCOPES[:]
|
||||||
|
if commanddataClientAccess:
|
||||||
|
caScopes.extend(_COMMANDDATA_CLIENT_SCOPES)
|
||||||
if todriveClientAccess:
|
if todriveClientAccess:
|
||||||
caScopes.extend(_TODRIVE_CLIENT_SCOPES)
|
caScopes.extend(_TODRIVE_CLIENT_SCOPES)
|
||||||
return sorted(caScopes, key=lambda k: k['name'])
|
return sorted(caScopes, key=lambda k: k['name'])
|
||||||
|
|
||||||
def getClientScopesURLs(todriveClientAccess):
|
def getClientScopesURLs(commanddataClientAccess, todriveClientAccess):
|
||||||
caScopes = _CLIENT_SCOPES[:]
|
caScopes = _CLIENT_SCOPES[:]
|
||||||
|
if commanddataClientAccess:
|
||||||
|
caScopes.extend(_COMMANDDATA_CLIENT_SCOPES)
|
||||||
if todriveClientAccess:
|
if todriveClientAccess:
|
||||||
caScopes.extend(_TODRIVE_CLIENT_SCOPES)
|
caScopes.extend(_TODRIVE_CLIENT_SCOPES)
|
||||||
return sorted({scope['scope'] for scope in _CLIENT_SCOPES})
|
return sorted({scope['scope'] for scope in _CLIENT_SCOPES})
|
||||||
|
|||||||
@@ -85,6 +85,8 @@ CMDLOG_MAX__BACKUPS = 'cmdlog_max__backups'
|
|||||||
CMDLOG_MAX_BACKUPS = 'cmdlog_max_backups'
|
CMDLOG_MAX_BACKUPS = 'cmdlog_max_backups'
|
||||||
# Command logging max kilo bytes per log file
|
# Command logging max kilo bytes per log file
|
||||||
CMDLOG_MAX_KILO_BYTES = 'cmdlog_max_kilo_bytes'
|
CMDLOG_MAX_KILO_BYTES = 'cmdlog_max_kilo_bytes'
|
||||||
|
# Use client access for command data from Google Docs/Sheets
|
||||||
|
COMMANDDATA_CLIENTACCESS = 'commanddata_clientaccess'
|
||||||
# GAM config directory containing client_secrets.json, oauth2.txt, oauth2service.json, extra_args.txt
|
# GAM config directory containing client_secrets.json, oauth2.txt, oauth2service.json, extra_args.txt
|
||||||
CONFIG_DIR = 'config_dir'
|
CONFIG_DIR = 'config_dir'
|
||||||
# When retrieving lists of Google Contacts from API, how many should be retrieved in each chunk
|
# When retrieving lists of Google Contacts from API, how many should be retrieved in each chunk
|
||||||
@@ -147,6 +149,8 @@ CSV_OUTPUT_USERS_AUDIT = 'csv_output_users_audit'
|
|||||||
CUSTOMER_ID = 'customer_id'
|
CUSTOMER_ID = 'customer_id'
|
||||||
# If debug_level > 0: extra_args['prettyPrint'] = True, httplib2.debuglevel = gam_debug_level, appsObj.debug = True
|
# If debug_level > 0: extra_args['prettyPrint'] = True, httplib2.debuglevel = gam_debug_level, appsObj.debug = True
|
||||||
DEBUG_LEVEL = 'debug_level'
|
DEBUG_LEVEL = 'debug_level'
|
||||||
|
# redact sensitive credentials from debug output
|
||||||
|
DEBUG_REDACTION = 'debug_redaction'
|
||||||
# Developer Preview API Key
|
# Developer Preview API Key
|
||||||
DEVELOPER_PREVIEW_API_KEY = 'developer_preview_api_key'
|
DEVELOPER_PREVIEW_API_KEY = 'developer_preview_api_key'
|
||||||
# When retrieving lists of ChromeOS devices from API, how many should be retrieved in each chunk
|
# When retrieving lists of ChromeOS devices from API, how many should be retrieved in each chunk
|
||||||
@@ -344,6 +348,7 @@ Defaults = {
|
|||||||
CMDLOG: '',
|
CMDLOG: '',
|
||||||
CMDLOG_MAX_BACKUPS: 5,
|
CMDLOG_MAX_BACKUPS: 5,
|
||||||
CMDLOG_MAX_KILO_BYTES: 1000,
|
CMDLOG_MAX_KILO_BYTES: 1000,
|
||||||
|
COMMANDDATA_CLIENTACCESS: FALSE,
|
||||||
CONFIG_DIR: '',
|
CONFIG_DIR: '',
|
||||||
CONTACT_MAX_RESULTS: '100',
|
CONTACT_MAX_RESULTS: '100',
|
||||||
CSV_INPUT_COLUMN_DELIMITER: ',',
|
CSV_INPUT_COLUMN_DELIMITER: ',',
|
||||||
@@ -375,6 +380,7 @@ Defaults = {
|
|||||||
CSV_OUTPUT_USERS_AUDIT: FALSE,
|
CSV_OUTPUT_USERS_AUDIT: FALSE,
|
||||||
CUSTOMER_ID: MY_CUSTOMER,
|
CUSTOMER_ID: MY_CUSTOMER,
|
||||||
DEBUG_LEVEL: '0',
|
DEBUG_LEVEL: '0',
|
||||||
|
DEBUG_REDACTION: TRUE,
|
||||||
DEVELOPER_PREVIEW_API_KEY: '',
|
DEVELOPER_PREVIEW_API_KEY: '',
|
||||||
DEVICE_MAX_RESULTS: '200',
|
DEVICE_MAX_RESULTS: '200',
|
||||||
DOMAIN: '',
|
DOMAIN: '',
|
||||||
@@ -512,6 +518,7 @@ VAR_INFO = {
|
|||||||
CMDLOG: {VAR_TYPE: TYPE_FILE, VAR_ACCESS: os.W_OK},
|
CMDLOG: {VAR_TYPE: TYPE_FILE, VAR_ACCESS: os.W_OK},
|
||||||
CMDLOG_MAX_BACKUPS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 10)},
|
CMDLOG_MAX_BACKUPS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 10)},
|
||||||
CMDLOG_MAX_KILO_BYTES: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (100, 10000)},
|
CMDLOG_MAX_KILO_BYTES: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (100, 10000)},
|
||||||
|
COMMANDDATA_CLIENTACCESS: {VAR_TYPE: TYPE_BOOLEAN},
|
||||||
CONFIG_DIR: {VAR_TYPE: TYPE_DIRECTORY, VAR_ENVVAR: 'GAMUSERCONFIGDIR'},
|
CONFIG_DIR: {VAR_TYPE: TYPE_DIRECTORY, VAR_ENVVAR: 'GAMUSERCONFIGDIR'},
|
||||||
CONTACT_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 10000)},
|
CONTACT_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 10000)},
|
||||||
CSV_INPUT_COLUMN_DELIMITER: {VAR_TYPE: TYPE_CHARACTER},
|
CSV_INPUT_COLUMN_DELIMITER: {VAR_TYPE: TYPE_CHARACTER},
|
||||||
@@ -543,6 +550,7 @@ VAR_INFO = {
|
|||||||
CSV_OUTPUT_USERS_AUDIT: {VAR_TYPE: TYPE_BOOLEAN},
|
CSV_OUTPUT_USERS_AUDIT: {VAR_TYPE: TYPE_BOOLEAN},
|
||||||
CUSTOMER_ID: {VAR_TYPE: TYPE_STRING, VAR_ENVVAR: 'CUSTOMER_ID', VAR_LIMITS: (0, None)},
|
CUSTOMER_ID: {VAR_TYPE: TYPE_STRING, VAR_ENVVAR: 'CUSTOMER_ID', VAR_LIMITS: (0, None)},
|
||||||
DEBUG_LEVEL: {VAR_TYPE: TYPE_INTEGER, VAR_SIGFILE: 'debug.gam', VAR_LIMITS: (0, None), VAR_SFFT: ('0', '4')},
|
DEBUG_LEVEL: {VAR_TYPE: TYPE_INTEGER, VAR_SIGFILE: 'debug.gam', VAR_LIMITS: (0, None), VAR_SFFT: ('0', '4')},
|
||||||
|
DEBUG_REDACTION: {VAR_TYPE: TYPE_BOOLEAN},
|
||||||
DEVELOPER_PREVIEW_API_KEY: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
|
DEVELOPER_PREVIEW_API_KEY: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
|
||||||
DEVICE_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 200)},
|
DEVICE_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 200)},
|
||||||
DOMAIN: {VAR_TYPE: TYPE_STRING, VAR_ENVVAR: 'GA_DOMAIN', VAR_LIMITS: (0, None)},
|
DOMAIN: {VAR_TYPE: TYPE_STRING, VAR_ENVVAR: 'GA_DOMAIN', VAR_LIMITS: (0, None)},
|
||||||
|
|||||||
@@ -1138,6 +1138,7 @@ class GamCLArgs():
|
|||||||
OB_ARGUMENT = 'argument'
|
OB_ARGUMENT = 'argument'
|
||||||
OB_ASP_ID_LIST = 'ASPIDList'
|
OB_ASP_ID_LIST = 'ASPIDList'
|
||||||
OB_ASSET_ID = 'AssetID'
|
OB_ASSET_ID = 'AssetID'
|
||||||
|
OB_ADMIN_ASSIGNEE_TYPE_LIST = 'AdminAssigneeTypeList'
|
||||||
OB_BROWSER_ENROLLEMNT_TOKEN_ID = 'BrowserEnrollmentTokenID'
|
OB_BROWSER_ENROLLEMNT_TOKEN_ID = 'BrowserEnrollmentTokenID'
|
||||||
OB_BROWSER_ENTITY = 'BrowserEntity'
|
OB_BROWSER_ENTITY = 'BrowserEntity'
|
||||||
OB_BUILDING_ID = 'BuildingID'
|
OB_BUILDING_ID = 'BuildingID'
|
||||||
|
|||||||
@@ -107,6 +107,8 @@ CURRENT_SVCACCT_USER = 'csa'
|
|||||||
DATETIME_NOW = 'dtno'
|
DATETIME_NOW = 'dtno'
|
||||||
# If debug_level > 0: extra_args['prettyPrint'] = True, httplib2.debuglevel = gam_debug_level, appsObj.debug = True
|
# If debug_level > 0: extra_args['prettyPrint'] = True, httplib2.debuglevel = gam_debug_level, appsObj.debug = True
|
||||||
DEBUG_LEVEL = 'dbgl'
|
DEBUG_LEVEL = 'dbgl'
|
||||||
|
# Whether debug output should redact sensitive credentials
|
||||||
|
DEBUG_REDACTION = 'dbrd'
|
||||||
# Decoded ID token
|
# Decoded ID token
|
||||||
DECODED_ID_TOKEN = 'didt'
|
DECODED_ID_TOKEN = 'didt'
|
||||||
# Index of start of <UserTypeEntity> in command line
|
# Index of start of <UserTypeEntity> in command line
|
||||||
@@ -263,6 +265,7 @@ Globals = {
|
|||||||
CURRENT_SVCACCT_USER: None,
|
CURRENT_SVCACCT_USER: None,
|
||||||
DATETIME_NOW: None,
|
DATETIME_NOW: None,
|
||||||
DEBUG_LEVEL: 0,
|
DEBUG_LEVEL: 0,
|
||||||
|
DEBUG_REDACTION: True,
|
||||||
DECODED_ID_TOKEN: None,
|
DECODED_ID_TOKEN: None,
|
||||||
ENTITY_CL_DELAY_START: 1,
|
ENTITY_CL_DELAY_START: 1,
|
||||||
ENTITY_CL_START: 1,
|
ENTITY_CL_START: 1,
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
## Definitions
|
## Definitions
|
||||||
```
|
```
|
||||||
|
<AdminAssigneeType> ::= group|user|serviceaccount|unknown
|
||||||
|
<AdminAssigneeTypeList> ::= "<AdminAssigneeType>(,<AdminAssigneeType>)*"
|
||||||
<DomainName> ::= <String>(.<String>)+
|
<DomainName> ::= <String>(.<String>)+
|
||||||
<EmailAddress> ::= <String>@<DomainName>
|
<EmailAddress> ::= <String>@<DomainName>
|
||||||
<GroupItem> ::= <EmailAddress>|<UniqueID>|<String>
|
<GroupItem> ::= <EmailAddress>|<UniqueID>|<String>
|
||||||
@@ -1475,16 +1477,25 @@ gam delete admin <RoleAssignmentId>
|
|||||||
## Display administrators
|
## Display administrators
|
||||||
```
|
```
|
||||||
gam print admins [todrive <ToDriveAttribute>*]
|
gam print admins [todrive <ToDriveAttribute>*]
|
||||||
[user|group <EmailAddress>|<UniqueID>] [role <RoleItem>] [condition]
|
[user|group <EmailAddress>|<UniqueID>] [role <RoleItem>]
|
||||||
[privileges] [oneitemperrow]
|
[types <AdminAssigneeTypeList>]
|
||||||
|
[recursive] [condition] [privileges] [oneitemperrow]
|
||||||
gam show admins
|
gam show admins
|
||||||
[user|group <EmailAddress>|<UniqueID>] [role <RoleItem>] [condition] [privileges]
|
[user|group <EmailAddress>|<UniqueID>] [role <RoleItem>]
|
||||||
|
[types <AdminAssigneeTypeList>]
|
||||||
|
[recursive] [condition] [privileges]
|
||||||
```
|
```
|
||||||
By default, all administrators and roles are displayed; choose from the following
|
By default, all administrators and roles are displayed; choose from the following
|
||||||
options to limit the display:
|
options to limit the display:
|
||||||
* `user <UserItem>` - Display only this administrator
|
* `user|group <EmailAddress>|<UniqueID>` - Display assignments to this administrator
|
||||||
* `role <RoleItem>` - Display only administrators with this role
|
* `role <RoleItem>` - Display only administrators with this role
|
||||||
|
|
||||||
|
By default, all admin assignee types are displayed. use `types <AdminAssigneeTypeList>` to filter
|
||||||
|
admin assignments by the type of the assignee.
|
||||||
|
|
||||||
|
By default, assignments to security groups are displayed as a single item; use `recursive`
|
||||||
|
to display assignments to the members of the security groups; the security group membershop is recursively expanded.
|
||||||
|
|
||||||
* `condition` - Display any conditions associated with a role assignment
|
* `condition` - Display any conditions associated with a role assignment
|
||||||
* `privileges` - Display privileges associated with each role assignment
|
* `privileges` - Display privileges associated with each role assignment
|
||||||
|
|
||||||
|
|||||||
@@ -265,6 +265,7 @@
|
|||||||
## Named items
|
## Named items
|
||||||
```
|
```
|
||||||
<AccessToken> ::= <String>
|
<AccessToken> ::= <String>
|
||||||
|
<AdminAssigneeType> ::= group|user|serviceaccount|unknown
|
||||||
<AlertID> ::= <String>
|
<AlertID> ::= <String>
|
||||||
<APIScopeURL> ::= <String>
|
<APIScopeURL> ::= <String>
|
||||||
<APPID> ::= <String>
|
<APPID> ::= <String>
|
||||||
|
|||||||
@@ -53,286 +53,7 @@ You must enable access to policies in the GCP cloud console.
|
|||||||
These are the supported policies GAM can show today.
|
These are the supported policies GAM can show today.
|
||||||
|
|
||||||
See: https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings
|
See: https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings
|
||||||
```
|
|
||||||
user_takeout_status (is takeout enabled for service)
|
|
||||||
blogger.user_takeout
|
|
||||||
books.user_takeout
|
|
||||||
location_history.user_takeout
|
|
||||||
maps.user_takeout
|
|
||||||
pay.user_takeout
|
|
||||||
photos.user_takeout
|
|
||||||
play.user_takeout
|
|
||||||
play_console.user_takeout
|
|
||||||
youtube.user_takeout
|
|
||||||
service_status (is service enabled)
|
|
||||||
ad_manager
|
|
||||||
ads
|
|
||||||
adsense
|
|
||||||
alerts
|
|
||||||
analytics
|
|
||||||
applied_digital_skills
|
|
||||||
appsheet
|
|
||||||
arts_and_culture
|
|
||||||
beyondcorp_enterprise
|
|
||||||
blogger
|
|
||||||
bookmarks
|
|
||||||
books
|
|
||||||
calendar
|
|
||||||
campaign_manager
|
|
||||||
chat
|
|
||||||
chrome_canvas
|
|
||||||
chrome_remote_desktop
|
|
||||||
chrome_sync
|
|
||||||
chrome_web_store
|
|
||||||
classroom
|
|
||||||
cloud
|
|
||||||
cloud_search
|
|
||||||
colab
|
|
||||||
cs_first
|
|
||||||
data_studio
|
|
||||||
developers
|
|
||||||
domains
|
|
||||||
drive_and_docs
|
|
||||||
earth
|
|
||||||
enterprise_service_restrictions
|
|
||||||
experimental_apps
|
|
||||||
feedburner
|
|
||||||
fi
|
|
||||||
gmail
|
|
||||||
groups
|
|
||||||
groups_for_business
|
|
||||||
jamboard
|
|
||||||
keep
|
|
||||||
location_history
|
|
||||||
managed_play
|
|
||||||
maps
|
|
||||||
material_gallery
|
|
||||||
meet
|
|
||||||
merchant_center
|
|
||||||
messages
|
|
||||||
migrate
|
|
||||||
my_business
|
|
||||||
my_maps
|
|
||||||
news
|
|
||||||
partner_dash
|
|
||||||
pay
|
|
||||||
pay_for_business
|
|
||||||
photos
|
|
||||||
pinpoint
|
|
||||||
play
|
|
||||||
play_books_partner_center
|
|
||||||
play_console
|
|
||||||
public_data
|
|
||||||
question_hub
|
|
||||||
scholar_profiles
|
|
||||||
search_ads_360
|
|
||||||
search_and_assistant
|
|
||||||
search_console
|
|
||||||
sites
|
|
||||||
socratic
|
|
||||||
takeout
|
|
||||||
tasks
|
|
||||||
third_party_app_backups
|
|
||||||
translate
|
|
||||||
trips
|
|
||||||
vault
|
|
||||||
voice
|
|
||||||
work_insights
|
|
||||||
youtube
|
|
||||||
calendar.appointment_schedules
|
|
||||||
enablePayments
|
|
||||||
chat.chat_apps_access
|
|
||||||
enableApps
|
|
||||||
enableWebhooks
|
|
||||||
chat.chat_file_sharing
|
|
||||||
externalFileSharing
|
|
||||||
internalFileSharing
|
|
||||||
chat.chat_history
|
|
||||||
enableChatHistory
|
|
||||||
historyOnByDefault
|
|
||||||
allowUserModification
|
|
||||||
chat.external_chat_restriction
|
|
||||||
allowExternalChat
|
|
||||||
chat.space_history
|
|
||||||
historyState
|
|
||||||
classroom.api_data_access
|
|
||||||
enableApiAccess
|
|
||||||
classroom.class_membership
|
|
||||||
whoCanJoinClasses
|
|
||||||
whichClassesCanUsersJoin
|
|
||||||
classroom.guardian_access
|
|
||||||
allowAccess
|
|
||||||
whoCanManageGuardianAccess
|
|
||||||
classroom.originality_reports
|
|
||||||
enableOriginalityReportsSchoolMatches
|
|
||||||
classroom.roster_import
|
|
||||||
rosterImportOption
|
|
||||||
classroom.student_unenrollment
|
|
||||||
whoCanUnenrollStudents
|
|
||||||
classroom.teacher_permissions
|
|
||||||
whoCanCreateClasses
|
|
||||||
cloud_sharing_options.cloud_data_sharing
|
|
||||||
sharingOptions
|
|
||||||
detector.regular_expression
|
|
||||||
displayName
|
|
||||||
regularExpression
|
|
||||||
createTime
|
|
||||||
updateTime
|
|
||||||
detector.word_list
|
|
||||||
displayName
|
|
||||||
wordList
|
|
||||||
createTime
|
|
||||||
updateTime
|
|
||||||
description
|
|
||||||
drive_and_docs.drive_for_desktop
|
|
||||||
allowDriveForDesktop
|
|
||||||
restrictToAuthorizedDevices
|
|
||||||
showDownloadLink
|
|
||||||
allowRealTimePresence
|
|
||||||
drive_and_docs.external_sharing
|
|
||||||
externalSharingMode
|
|
||||||
allowReceivingExternalFiles
|
|
||||||
warnForSharingOutsideAllowlistedDomains
|
|
||||||
allowReceivingFilesOutsideAllowlistedDomains
|
|
||||||
allowNonGoogleInvitesInAllowlistedDomains
|
|
||||||
warnForExternalSharing
|
|
||||||
allowNonGoogleInvites
|
|
||||||
allowPublishingFiles
|
|
||||||
accessCheckerSuggestions
|
|
||||||
allowedPartiesForDistributingContent
|
|
||||||
drive_and_docs.file_security_update
|
|
||||||
securityUpdate
|
|
||||||
allowUsersToManageUpdate
|
|
||||||
drive_and_docs.shared_drive_creation
|
|
||||||
allowSharedDriveCreation
|
|
||||||
orgUnitForNewSharedDrives
|
|
||||||
customOrgUnit
|
|
||||||
allowManagersToOverrideSettings
|
|
||||||
allowExternalUserAccess
|
|
||||||
allowNonMemberAccess
|
|
||||||
allowedPartiesForDownloadPrintCopy
|
|
||||||
allowContentManagersToShareFolders
|
|
||||||
gmail.auto_forwarding
|
|
||||||
enableAutoForwarding
|
|
||||||
gmail.confidential_mode
|
|
||||||
enableConfidentialMode
|
|
||||||
gmail.email_attachment_safety
|
|
||||||
enableEncryptedAttachmentProtection
|
|
||||||
encryptedAttachmentProtectionConsequence
|
|
||||||
enableAttachmentWithScriptsProtection
|
|
||||||
attachmentWithScriptsProtectionConsequence
|
|
||||||
enableAnomalousAttachmentProtection
|
|
||||||
anomalousAttachmentProtectionConsequence
|
|
||||||
allowedAnomalousAttachmentFiletypes
|
|
||||||
applyFutureRecommendedSettingsAutomatically
|
|
||||||
encryptedAttachmentProtectionQuarantineId
|
|
||||||
attachmentWithScriptsProtectionQuarantineId
|
|
||||||
anomalousAttachmentProtectionQuarantineId
|
|
||||||
gmail.email_image_proxy_bypass
|
|
||||||
imageProxyBypassPattern
|
|
||||||
enableImageProxy
|
|
||||||
gmail.enhanced_pre_delivery_message_scanning
|
|
||||||
enableImprovedSuspiciousContentDetection
|
|
||||||
gmail.enhanced_smime_encryption
|
|
||||||
enableSmimeEncryption
|
|
||||||
allowUserToUploadCertificates
|
|
||||||
gmail.gmail_name_format
|
|
||||||
allowCustomDisplayNames
|
|
||||||
defaultDisplayNameFormat
|
|
||||||
gmail.imap_access
|
|
||||||
enableImapAccess
|
|
||||||
gmail.links_and_external_images
|
|
||||||
enableShortenerScanning
|
|
||||||
enableExternalImageScanning
|
|
||||||
enableAggressiveWarningsOnUntrustedLinks
|
|
||||||
applyFutureSettingsAutomatically
|
|
||||||
gmail.per_user_outbound_gateway
|
|
||||||
allowUsersToUseExternalSmtpServers
|
|
||||||
gmail.pop_access
|
|
||||||
enablePopAccess
|
|
||||||
gmail.spoofing_and_authentication
|
|
||||||
detectDomainNameSpoofing
|
|
||||||
detectEmployeeNameSpoofing
|
|
||||||
detectDomainSpoofingFromUnauthenticatedSenders
|
|
||||||
detectUnauthenticatedEmails
|
|
||||||
domainNameSpoofingConsequence
|
|
||||||
employeeNameSpoofingConsequence
|
|
||||||
domainSpoofingConsequence
|
|
||||||
unauthenticatedEmailConsequence
|
|
||||||
detectGroupsSpoofing
|
|
||||||
groupsSpoofingVisibilityType
|
|
||||||
groupsSpoofingConsequence
|
|
||||||
applyFutureSettingsAutomatically
|
|
||||||
domainNameSpoofingQuarantineId
|
|
||||||
employeeNameSpoofingQuarantineId
|
|
||||||
domainSpoofingQuarantineId
|
|
||||||
unauthenticatedEmailQuarantineId
|
|
||||||
groupsSpoofingQuarantineId
|
|
||||||
gmail.user_email_uploads
|
|
||||||
enableMailAndContactsImport
|
|
||||||
gmail.workspace_sync_for_outlook
|
|
||||||
enableGoogleWorkspaceSyncForMicrosoftOutlook
|
|
||||||
groups_for_business.groups_sharing
|
|
||||||
ownersCanAllowIncomingMailFromPublic
|
|
||||||
collaborationCapability
|
|
||||||
createGroupsAccessLevel
|
|
||||||
ownersCanAllowExternalMembers
|
|
||||||
ownersCanHideGroups
|
|
||||||
newGroupsAreHidden
|
|
||||||
viewTopicsDefaultAccessLevel
|
|
||||||
meet.safety_access
|
|
||||||
meetingsAllowedToJoin
|
|
||||||
meet.safety_domain
|
|
||||||
usersAllowedToJoin
|
|
||||||
meet.safety_external_participants
|
|
||||||
enableExternalLabel
|
|
||||||
meet.safety_host_management
|
|
||||||
enableHostManagement
|
|
||||||
meet.video_recording
|
|
||||||
enableRecording
|
|
||||||
rule.dlp
|
|
||||||
displayName
|
|
||||||
description
|
|
||||||
triggers
|
|
||||||
condition
|
|
||||||
action
|
|
||||||
state
|
|
||||||
createTime
|
|
||||||
updateTime
|
|
||||||
ruleTypeMetadata
|
|
||||||
rule.system_defined_alerts
|
|
||||||
displayName
|
|
||||||
description
|
|
||||||
action
|
|
||||||
state
|
|
||||||
createTime
|
|
||||||
updateTime
|
|
||||||
security.advanced_protection_program
|
|
||||||
enableAdvancedProtectionSelfEnrollment
|
|
||||||
securityCodeOption
|
|
||||||
security.less_secure_apps
|
|
||||||
allowLessSecureApps
|
|
||||||
security.login_challenges
|
|
||||||
enableEmployeeIdChallenge
|
|
||||||
security.password
|
|
||||||
allowedStrength
|
|
||||||
minimumLength
|
|
||||||
maximumLength
|
|
||||||
enforceRequirementsAtLogin
|
|
||||||
allowReuse
|
|
||||||
expirationDuration
|
|
||||||
security.session_controls
|
|
||||||
webSessionDuration
|
|
||||||
security.super_admin_account_recovery
|
|
||||||
enableAccountRecovery
|
|
||||||
security.user_account_recovery
|
|
||||||
enableAccountRecovery
|
|
||||||
sites.sites_creation_and_modification
|
|
||||||
allowSitesCreation
|
|
||||||
allowSitesModification
|
|
||||||
workspace_marketplace.apps_allowlist
|
|
||||||
apps
|
|
||||||
```
|
|
||||||
## Display Cloud Identity Policies
|
## Display Cloud Identity Policies
|
||||||
Display selected policies.
|
Display selected policies.
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
- [Plain Text](#plain-text)
|
- [Plain Text](#plain-text)
|
||||||
- [HTML](#html)
|
- [HTML](#html)
|
||||||
- [Read data from a Google Sheet](#read-data-from-a-google-sheet)
|
- [Read data from a Google Sheet](#read-data-from-a-google-sheet)
|
||||||
|
- [Limited Service Account Access](#limited-service-account-access)
|
||||||
- [Read data from a Google Cloud Storage File](#read-data-from-a-google-cloud-storage-file)
|
- [Read data from a Google Cloud Storage File](#read-data-from-a-google-cloud-storage-file)
|
||||||
- [Plain Text](#plain-text)
|
- [Plain Text](#plain-text)
|
||||||
- [CSV](#csv)
|
- [CSV](#csv)
|
||||||
@@ -79,6 +80,25 @@ Example:
|
|||||||
```
|
```
|
||||||
gam csv gsheet you@exmaple.com <DriveFileIDEntity> "Sheet 1" gam create user firstname "~FirstName" lastname "~lastName" email "~email"
|
gam csv gsheet you@exmaple.com <DriveFileIDEntity> "Sheet 1" gam create user firstname "~FirstName" lastname "~lastName" email "~email"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Limited Service Account Access
|
||||||
|
If you want to disable a user's service account access to Drive and Sheets but still allow reading command data from Google Docs and Sheets,
|
||||||
|
issue the following commands. The admin specified in `gam oauth create` can read command data from Docs and Sheets to which it has access.
|
||||||
|
```
|
||||||
|
gam config commanddata_clientaccess true save
|
||||||
|
gam oauth create
|
||||||
|
Enable the following and proceed to authorization.
|
||||||
|
|
||||||
|
[*] 42) Drive API - commanddata_clientaccess
|
||||||
|
[*] 54) Sheets API - commanddata_clientaccess
|
||||||
|
```
|
||||||
|
In these options, the `<EmailAddress> is not used, but for clarity you may want to specify the
|
||||||
|
email address of the admin specified in `gam oauth create`.
|
||||||
|
```
|
||||||
|
gdoc <EmailAddress> <DriveFileIDEntity>|<DriveFileNameEntity>|(<SharedDriveEntity> <SharedDriveFileNameEntity>)
|
||||||
|
gsheet <EmailAddress> <DriveFileIDEntity>|<DriveFileNameEntity>|(<SharedDriveEntity> <SharedDriveFileNameEntity>) <SheetEntity>
|
||||||
|
```
|
||||||
|
|
||||||
## Read data from a Google Cloud Storage File
|
## Read data from a Google Cloud Storage File
|
||||||
```
|
```
|
||||||
<StorageBucketName> ::= <String>
|
<StorageBucketName> ::= <String>
|
||||||
|
|||||||
@@ -10,6 +10,74 @@ 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
|
See [Downloads-Installs-GAM7](https://github.com/GAM-team/GAM/wiki/Downloads-Installs) for Windows or other options, including manual installation
|
||||||
|
|
||||||
|
### 7.25.01
|
||||||
|
|
||||||
|
Fixed bug in `gam config timezone <String>` to handle timezone abbreviations correctly;
|
||||||
|
they were incorrectly shifted to lowercase.
|
||||||
|
|
||||||
|
### 7.25.00
|
||||||
|
|
||||||
|
Removed a capabilty added in 7.24.00 that allowed reading command data from Google Docs and Sheets
|
||||||
|
when a user's service account access to Drive and Sheets had been disabled. Jay was concerned
|
||||||
|
that this change could be exploited to give access to all user's files.
|
||||||
|
|
||||||
|
This capability has been replaced by issuing the following commands. The admin specified in `gam oauth create`
|
||||||
|
can read command data from Docs and Sheets to which it has access.
|
||||||
|
```
|
||||||
|
gam config commanddata_clientaccess true save
|
||||||
|
gam oauth create
|
||||||
|
Enable the following and proceed to authorization.
|
||||||
|
|
||||||
|
[*] 42) Drive API - commanddata_clientaccess
|
||||||
|
[*] 54) Sheets API - commanddata_clientaccess
|
||||||
|
```
|
||||||
|
|
||||||
|
* See: https://github.com/GAM-team/GAM/wiki/Command-Data-From-Google-Docs-Sheets-Storage#limited-service-account-access
|
||||||
|
|
||||||
|
Fixed in bug in `gam report` that caused a trap with either of the `thismonth` or `previousmonths` options were used.
|
||||||
|
|
||||||
|
Upgraded to Python 3.14.0.
|
||||||
|
|
||||||
|
### 7.24.01
|
||||||
|
|
||||||
|
Updated GAM to handle the following error that occurs when GAM tries to authenticate
|
||||||
|
as a user that has been disabled by Google.
|
||||||
|
```
|
||||||
|
ERROR: Authentication Token Error - invalid_account: Forbidden
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.24.00
|
||||||
|
|
||||||
|
If you want to disable a user's service account access to Drive and Sheets but still allow reading command data from Google Docs and Sheets,
|
||||||
|
issue the following command and make these settings:
|
||||||
|
```
|
||||||
|
gam user user@domain.com update serviceaccount
|
||||||
|
|
||||||
|
[ ] 20) Drive API (supports readonly)
|
||||||
|
[*] 21) Drive API - read command data
|
||||||
|
[ ] 42) Sheets API (supports readonly)
|
||||||
|
[*] 43) Sheets API - read command data
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.23.07
|
||||||
|
|
||||||
|
Fixed bug in `gam print|show admins` where all admin assignments were not displayed when
|
||||||
|
`types <AdminAssigneeTypeList>` was not specified, i.e., all assignments should be displayed.
|
||||||
|
|
||||||
|
### 7.23.06
|
||||||
|
|
||||||
|
Added option `types <AdminAssigneeTypeList>` to `gam print|show admins` that allows filtering
|
||||||
|
of admin assignments by the type of the assignee; by default, all assignee types are displayed.
|
||||||
|
```
|
||||||
|
<AdminAssigneeType> ::= group|user|serviceaccount|unknown
|
||||||
|
<AdminAssigneeTypeList> ::= "<AdminAssigneeType>(,<AdminAssigneeType>)*"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.23.05
|
||||||
|
|
||||||
|
Added option `recursive` to `gam print|show admins` that will display assignments to the members
|
||||||
|
of security groups assigned to roles; the security group membership is recursively expanded.
|
||||||
|
|
||||||
### 7.23.04
|
### 7.23.04
|
||||||
|
|
||||||
Added option `addcsvdata <FieldName> <String>` to `gam <UserTypeEntity> print events`
|
Added option `addcsvdata <FieldName> <String>` to `gam <UserTypeEntity> print events`
|
||||||
|
|||||||
@@ -252,9 +252,9 @@ writes the credentials into the file oauth2.txt.
|
|||||||
admin@server:/Users/admin$ rm -f /Users/admin/GAMConfig/oauth2.txt
|
admin@server:/Users/admin$ rm -f /Users/admin/GAMConfig/oauth2.txt
|
||||||
admin@server:/Users/admin$ gam version
|
admin@server:/Users/admin$ gam version
|
||||||
WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/admin/GAMConfig/oauth2.txt, Not Found
|
WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/admin/GAMConfig/oauth2.txt, Not Found
|
||||||
GAM 7.23.04 - https://github.com/GAM-team/GAM - pyinstaller
|
GAM 7.25.01 - https://github.com/GAM-team/GAM - pyinstaller
|
||||||
GAM Team <google-apps-manager@googlegroups.com>
|
GAM Team <google-apps-manager@googlegroups.com>
|
||||||
Python 3.13.7 64-bit final
|
Python 3.14.0 64-bit final
|
||||||
macOS Tahoe 26.0.1 x86_64
|
macOS Tahoe 26.0.1 x86_64
|
||||||
Path: /Users/admin/bin/gam7
|
Path: /Users/admin/bin/gam7
|
||||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||||
@@ -990,9 +990,9 @@ writes the credentials into the file oauth2.txt.
|
|||||||
C:\>del C:\GAMConfig\oauth2.txt
|
C:\>del C:\GAMConfig\oauth2.txt
|
||||||
C:\>gam version
|
C:\>gam version
|
||||||
WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found
|
WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found
|
||||||
GAM 7.22.00 - https://github.com/GAM-team/GAM - pythonsource
|
GAM 7.25.01 - https://github.com/GAM-team/GAM - pythonsource
|
||||||
GAM Team <google-apps-manager@googlegroups.com>
|
GAM Team <google-apps-manager@googlegroups.com>
|
||||||
Python 3.13.7 64-bit final
|
Python 3.14.0 64-bit final
|
||||||
Windows-10-10.0.17134 AMD64
|
Windows-10-10.0.17134 AMD64
|
||||||
Path: C:\GAM7
|
Path: C:\GAM7
|
||||||
Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
## Lists of basic items
|
## Lists of basic items
|
||||||
```
|
```
|
||||||
|
<AdminAssigneeTypeList> ::= "<AdminAssigneeType>(,<AdminAssigneeType>)*"
|
||||||
<APIScopeURLList> ::= "<APIScopeURL>(,<APIScopeURL>)*"
|
<APIScopeURLList> ::= "<APIScopeURL>(,<APIScopeURL>)*"
|
||||||
<ASPIDList> ::= "<ASPID>(,<ASPID>)*"
|
<ASPIDList> ::= "<ASPID>(,<ASPID>)*"
|
||||||
<AssetTagList> ::= "<AssetTag>(,<AssetTag>)*"
|
<AssetTagList> ::= "<AssetTag>(,<AssetTag>)*"
|
||||||
|
|||||||
@@ -330,16 +330,16 @@ you want the updated data copied to `Latest` so you don't have to remember what
|
|||||||
gam redirect csv - todrive tdfileid <DriveFileID> tdupdatesheet tdsheet Tuesday tdbackupsheet "Backup Tuesday" tdcopysheet "Latest" ...
|
gam redirect csv - todrive tdfileid <DriveFileID> tdupdatesheet tdsheet Tuesday tdbackupsheet "Backup Tuesday" tdcopysheet "Latest" ...
|
||||||
```
|
```
|
||||||
## Limited Service Account Access
|
## Limited Service Account Access
|
||||||
If you want to limit a user's service account access but still allow `todrive',
|
If you want to limit a user's service account access to Drive, Gmail and Sheets but still allow `todrive`,
|
||||||
issue the following command and authorize the additional service account APIs:
|
issue the following command and make these settings:
|
||||||
```
|
```
|
||||||
gam user user@domain.com update serviceaccount`
|
gam user user@domain.com update serviceaccount
|
||||||
|
|
||||||
Authorize these APIs:
|
[ ] 20) Drive API (supports readonly)
|
||||||
|
[*] 22) Drive API - write todrive data - has access to all Drive
|
||||||
Drive API - todrive
|
[*] 31) Gmail API - Send Messages - including todrive
|
||||||
Gmail API - Send Messages - including todrive
|
[ ] 42) Sheets API (supports readonly)
|
||||||
Sheets API - todrive
|
[*] 44) Sheets API - write todrive data - has access to all Sheets
|
||||||
```
|
```
|
||||||
|
|
||||||
## No Service Account Access
|
## No Service Account Access
|
||||||
|
|||||||
@@ -327,7 +327,7 @@ gam <UserTypeEntity> show chatspaces
|
|||||||
By default, chat spaces of all types are displayed.
|
By default, chat spaces of all types are displayed.
|
||||||
* `types <ChatSpaceTypeList>` - Display specific types of spaces.
|
* `types <ChatSpaceTypeList>` - Display specific types of spaces.
|
||||||
|
|
||||||
When listing Chat Spaces, the Chat API does not return the `accessSettings` field; if you need to see this fieldf,
|
When listing Chat Spaces, the Chat API does not return the `accessSettings` field; if you need to see this field,
|
||||||
add `showaccesssettings` to the command. This requires an additional Chat API call per chat space of type `SPACE`
|
add `showaccesssettings` to the command. This requires an additional Chat API call per chat space of type `SPACE`
|
||||||
to get the `accessSettings` field.
|
to get the `accessSettings` field.
|
||||||
|
|
||||||
@@ -343,7 +343,7 @@ gam <UserTypeEntity> print chatspaces [todrive <ToDriveAttribute>*]
|
|||||||
By default, chat spaces of all types are displayed.
|
By default, chat spaces of all types are displayed.
|
||||||
* `types <ChatSpaceTypeList>` - Display specific types of spaces.
|
* `types <ChatSpaceTypeList>` - Display specific types of spaces.
|
||||||
|
|
||||||
When listing Chat Spaces, the Chat API does not return the `accessSettings` field; if you need to see this fieldf,
|
When listing Chat Spaces, the Chat API does not return the `accessSettings` field; if you need to see this field,
|
||||||
add `showaccesssettings` to the command. This requires an additional Chat API call per chat space of type `SPACE`
|
add `showaccesssettings` to the command. This requires an additional Chat API call per chat space of type `SPACE`
|
||||||
to get the `accessSettings` field.
|
to get the `accessSettings` field.
|
||||||
|
|
||||||
@@ -401,7 +401,7 @@ By default, all chat spaces of type SPACE are displayed.
|
|||||||
* `query <String> [querytime<String> <Time>]` - Display selected chat spaces
|
* `query <String> [querytime<String> <Time>]` - Display selected chat spaces
|
||||||
* See: https://developers.google.com/workspace/chat/api/reference/rest/v1/spaces/search
|
* See: https://developers.google.com/workspace/chat/api/reference/rest/v1/spaces/search
|
||||||
|
|
||||||
When listing Chat Spaces, the Chat API does not return the `accessSettings` field; if you need to see this fieldf,
|
When listing Chat Spaces, the Chat API does not return the `accessSettings` field; if you need to see this field,
|
||||||
add `showaccesssettings` to the command. This requires an additional Chat API call per chat space of type `SPACE`
|
add `showaccesssettings` to the command. This requires an additional Chat API call per chat space of type `SPACE`
|
||||||
to get the `accessSettings` field.
|
to get the `accessSettings` field.
|
||||||
|
|
||||||
@@ -419,7 +419,7 @@ By default, all chat spaces of type SPACE are displayed.
|
|||||||
* `query <String> [querytime<String> <Time>]` - Display selected chat spaces
|
* `query <String> [querytime<String> <Time>]` - Display selected chat spaces
|
||||||
* See: https://developers.google.com/workspace/chat/api/reference/rest/v1/spaces/search
|
* See: https://developers.google.com/workspace/chat/api/reference/rest/v1/spaces/search
|
||||||
|
|
||||||
When listing Chat Spaces, the Chat API does not return the `accessSettings` field; if you need to see this fieldf,
|
When listing Chat Spaces, the Chat API does not return the `accessSettings` field; if you need to see this field,
|
||||||
add `showaccesssettings` to the command. This requires an additional Chat API call per chat space of type `SPACE`
|
add `showaccesssettings` to the command. This requires an additional Chat API call per chat space of type `SPACE`
|
||||||
to get the `accessSettings` field.
|
to get the `accessSettings` field.
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
- [Delete Vault Saved Queries](#delete-vault-saved-queries)
|
- [Delete Vault Saved Queries](#delete-vault-saved-queries)
|
||||||
- [Display Vault Saved Queries](#display-vault-saved-queries)
|
- [Display Vault Saved Queries](#display-vault-saved-queries)
|
||||||
- [Takeout](#takeout)
|
- [Takeout](#takeout)
|
||||||
- [Copy a Takeout Bucket](#copy-a-takeoutbucket)
|
- [Copy a Takeout Bucket](#copy-a-takeout-bucket)
|
||||||
- [Download a Takeout Bucket](#download-a-takeout-bucket)
|
- [Download a Takeout Bucket](#download-a-takeout-bucket)
|
||||||
|
|
||||||
## API documentation
|
## API documentation
|
||||||
@@ -848,7 +848,7 @@ gam create vaultquery <MatterItem> [name <String>]
|
|||||||
[<JSONData>]
|
[<JSONData>]
|
||||||
[shownames]
|
[shownames]
|
||||||
[showdetails|returnidonly|formatjson]
|
[showdetails|returnidonly|formatjson]
|
||||||
``
|
```
|
||||||
|
|
||||||
If `name <String>` is omitted, the query is named `GAM <corpus> Query - <Time>`
|
If `name <String>` is omitted, the query is named `GAM <corpus> Query - <Time>`
|
||||||
|
|
||||||
@@ -928,7 +928,7 @@ Select fields to display:
|
|||||||
|
|
||||||
The `shownames` argument controls whether org unit and shared drive names are displayed in queries; additional API calls are required to get the names.
|
The `shownames` argument controls whether org unit and shared drive names are displayed in queries; additional API calls are required to get the names.
|
||||||
|
|
||||||
# Takeout
|
## Takeout
|
||||||
Many thanks to Jay for these commands and documentation.
|
Many thanks to Jay for these commands and documentation.
|
||||||
|
|
||||||
GAM 6.42.00 and newer support copying and downloading Google Cloud Storage (GCS) buckets generated by [organization-wide Takeout](https://support.google.com/a/answer/100458?hl=en).
|
GAM 6.42.00 and newer support copying and downloading Google Cloud Storage (GCS) buckets generated by [organization-wide Takeout](https://support.google.com/a/answer/100458?hl=en).
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
Print the current version of Gam with details
|
Print the current version of Gam with details
|
||||||
```
|
```
|
||||||
gam version
|
gam version
|
||||||
GAM 7.23.04 - https://github.com/GAM-team/GAM - pyinstaller
|
GAM 7.25.01 - https://github.com/GAM-team/GAM - pyinstaller
|
||||||
GAM Team <google-apps-manager@googlegroups.com>
|
GAM Team <google-apps-manager@googlegroups.com>
|
||||||
Python 3.13.7 64-bit final
|
Python 3.14.0 64-bit final
|
||||||
macOS Tahoe 26.0.1 x86_64
|
macOS Tahoe 26.0.1 x86_64
|
||||||
Path: /Users/Admin/bin/gam7
|
Path: /Users/Admin/bin/gam7
|
||||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||||
@@ -15,9 +15,9 @@ Time: 2023-06-02T21:10:00-07:00
|
|||||||
Print the current version of Gam with details and time offset information
|
Print the current version of Gam with details and time offset information
|
||||||
```
|
```
|
||||||
gam version timeoffset
|
gam version timeoffset
|
||||||
GAM 7.23.04 - https://github.com/GAM-team/GAM - pyinstaller
|
GAM 7.25.01 - https://github.com/GAM-team/GAM - pyinstaller
|
||||||
GAM Team <google-apps-manager@googlegroups.com>
|
GAM Team <google-apps-manager@googlegroups.com>
|
||||||
Python 3.13.7 64-bit final
|
Python 3.14.0 64-bit final
|
||||||
macOS Tahoe 26.0.1 x86_64
|
macOS Tahoe 26.0.1 x86_64
|
||||||
Path: /Users/Admin/bin/gam7
|
Path: /Users/Admin/bin/gam7
|
||||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||||
@@ -27,9 +27,9 @@ 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
|
Print the current version of Gam with extended details and SSL information
|
||||||
```
|
```
|
||||||
gam version extended
|
gam version extended
|
||||||
GAM 7.23.04 - https://github.com/GAM-team/GAM - pyinstaller
|
GAM 7.25.01 - https://github.com/GAM-team/GAM - pyinstaller
|
||||||
GAM Team <google-apps-manager@googlegroups.com>
|
GAM Team <google-apps-manager@googlegroups.com>
|
||||||
Python 3.13.7 64-bit final
|
Python 3.14.0 64-bit final
|
||||||
macOS Tahoe 26.0.1 x86_64
|
macOS Tahoe 26.0.1 x86_64
|
||||||
Path: /Users/Admin/bin/gam7
|
Path: /Users/Admin/bin/gam7
|
||||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||||
@@ -68,7 +68,7 @@ MacOS High Sierra 10.13.6 x86_64
|
|||||||
Path: /Users/Admin/bin/gam7
|
Path: /Users/Admin/bin/gam7
|
||||||
Version Check:
|
Version Check:
|
||||||
Current: 5.35.08
|
Current: 5.35.08
|
||||||
Latest: 7.22.00
|
Latest: 7.25.01
|
||||||
echo $?
|
echo $?
|
||||||
1
|
1
|
||||||
```
|
```
|
||||||
@@ -76,7 +76,7 @@ echo $?
|
|||||||
Print the current version number without details
|
Print the current version number without details
|
||||||
```
|
```
|
||||||
gam version simple
|
gam version simple
|
||||||
7.22.00
|
7.25.01
|
||||||
```
|
```
|
||||||
In Linux/MacOS you can do:
|
In Linux/MacOS you can do:
|
||||||
```
|
```
|
||||||
@@ -86,9 +86,9 @@ echo $VER
|
|||||||
Print the current version of Gam and address of this Wiki
|
Print the current version of Gam and address of this Wiki
|
||||||
```
|
```
|
||||||
gam help
|
gam help
|
||||||
GAM 7.22.00 - https://github.com/GAM-team/GAM
|
GAM 7.25.01 - https://github.com/GAM-team/GAM
|
||||||
GAM Team <google-apps-manager@googlegroups.com>
|
GAM Team <google-apps-manager@googlegroups.com>
|
||||||
Python 3.13.7 64-bit final
|
Python 3.14.0 64-bit final
|
||||||
macOS Tahoe 26.0.1 x86_64
|
macOS Tahoe 26.0.1 x86_64
|
||||||
Path: /Users/Admin/bin/gam7
|
Path: /Users/Admin/bin/gam7
|
||||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||||
|
|||||||
@@ -140,6 +140,11 @@ cmdlog_max_kilo_bytes
|
|||||||
Maximum kilobytes per log file
|
Maximum kilobytes per log file
|
||||||
Default: 1000
|
Default: 1000
|
||||||
Range: 100 - 10000
|
Range: 100 - 10000
|
||||||
|
commanddata_clientaccess
|
||||||
|
Enable/disable use of client access rather than service account access for the
|
||||||
|
admin specified in `gam oauth create` when reading command data from Docs and Sheets
|
||||||
|
to which it has access.
|
||||||
|
Default: False
|
||||||
config_dir
|
config_dir
|
||||||
GAM config directory containing client_secrets.json, oauth2.txt, oauth2service.json
|
GAM config directory containing client_secrets.json, oauth2.txt, oauth2service.json
|
||||||
and extra_args.txt
|
and extra_args.txt
|
||||||
|
|||||||
Reference in New Issue
Block a user