Compare commits

..

116 Commits

Author SHA1 Message Date
Ross Scroggs
42d33786a1 Updated gam report <ActivityApplictionName> to retry/handle the following error:
ERROR: 503: serviceNotAvailable - The service is currently unavailable.
2024-05-01 13:33:13 -07:00
Ross Scroggs
683435cfb8 Added option admin <EmailAddress> to gam upload sakey. 2024-04-26 09:16:22 -07:00
Ross Scroggs
6b8170dd2f Improved code in gam upload sakey. 2024-04-26 08:33:50 -07:00
Ross Scroggs
941fe97785 Updated gam create project
to simplify handling the situation when your workspace is configured to disable service account private key uploads.
2024-04-25 22:23:16 -07:00
Ross Scroggs
f87e013ec4 Fixed bug in gam <UserTypeEntity> print shareddrives ... formatjson that caused a trap. 2024-04-25 08:46:14 -07:00
Ross Scroggs
fc792bf454 Updated gam create|update drivefileacl <DriveFileEntity> ... expiration <Time> to handle the following
ERROR: 403: expirationDateNotAllowedForSharedDriveMembers - Expiration dates are not allowed for shared drive members.
2024-04-23 15:18:37 -07:00
Ross Scroggs
b4b9bd2436 Added truncate_client_id Boolean variable to gam.cfg doc cleanup 2024-04-22 18:31:06 -07:00
Ross Scroggs
0e455a2e40 Added truncate_client_id Boolean variable to gam.cfg 2024-04-22 18:26:24 -07:00
Ross Scroggs
b384bdb503 Do not truncate client_id by default 2024-04-22 17:17:47 -07:00
jeffssh
10a6348ddd fixed minor typo not -> no (#1685) 2024-04-19 15:41:30 -04:00
Ross Scroggs
74be07a9ef The Google Chat API has been updated so that chat members can now have their role set to manager. 2024-04-19 10:05:35 -07:00
Ross Scroggs
5607d659fb Updated emailaddressList <EmailAddressList> and domainlist|notdomainlist <DomainNameList> in <PermissionMatch> to perform case-insensitive matches 2024-04-18 15:03:14 -07:00
Ross Scroggs
da1ef497a1 Merge branch 'main' of https://github.com/GAM-team/GAM 2024-04-18 13:03:00 -07:00
Ross Scroggs
ac4fef0e4b Updated all commands that display tasks to display the due date in GMT 2024-04-18 12:43:39 -07:00
Jay Lee
0bc44582af BCE > CEP to match admin console product name change 2024-04-17 14:13:51 -04:00
Ross Scroggs
baf0c7863f Upgraded to Python 3.12.3 where possible. 2024-04-16 19:52:03 -07:00
Ross Scroggs
b00077151b Added the following option to <EventMatchProperty>
```
matchfield attendeesonlydomainlist <DomainNameList>
```
2024-04-16 18:34:13 -07:00
Ross Scroggs
842e46d060 Added the following options to <EventMatchProperty>
```
matchfield attendeesdomainlist <DomainNameList>
matchfield attendeesnotdomainlist <DomainNameList>
```
2024-04-16 15:32:25 -07:00
Ross Scroggs
bad4866bf7 Added option oneitemperrow to 'gam print vaultholds` 2024-04-15 15:59:57 -07:00
Ross Scroggs
3f5d96e13b Added timeofdayrange=<HH:MM>/<HH:MM> and timeofdayrange!=<HH:MM>/<HH:MM> to <RowValueFilter> 2024-04-12 11:55:50 -07:00
Ross Scroggs
a0dc04e7b0 Updated countsonly option of gam <UserTypeEntity> print|show notes to additionally display the total number of notes. 2024-04-04 17:57:55 -07:00
Ross Scroggs
23b0b0f203 Added option countsonly to gam <UserTypeEntity> print|show notes 2024-04-04 11:37:41 -07:00
Ross Scroggs
83d464d167 Updated commands that send emails to not downshift 'First Last<firstlast@domain.com>' 2024-04-02 10:38:35 -07:00
Ross Scroggs
1ba9f73fbd Update sendemail and notify email address lists 2024-04-01 19:36:12 -07:00
Ross Scroggs
0a21f2c959 Cleaned up code for all commands that display Chat objects. 2024-03-30 09:37:43 -07:00
Ross Scroggs
62b7b5d84b Added commands to display Chat events. 2024-03-29 20:06:07 -07:00
Ross Scroggs
7e12a8f0a7 Fixed bug in gam <UserTypeEntity> create chatspace that caused a trap. 2024-03-28 08:00:58 -07:00
Ross Scroggs
d347c65fcb Updated gam delete admin <RoleAssignmentId> 2024-03-25 20:01:38 -07:00
Ross Scroggs
51f109ffa7 Improved commands to display drive file comments. 2024-03-24 08:44:22 -07:00
Ross Scroggs
a5e7d6ff6c Added commands to display drive file comments 2024-03-22 15:52:44 -07:00
Ross Scroggs
2260e7df50 Updated <CrOSFieldName> to include cpuinfo and backlightinfo. 2024-03-21 11:42:12 -07:00
Ross Scroggs
08fc3bdb6f Added depth column to output of gam <UserTypeEntity> print diskusage <DriveFileEntity> 2024-03-17 18:39:39 -07:00
Ross Scroggs
0754a9b176 Updated gam <UserTypeEntity> create|update sendas <EmailAddress> ... replyto <EmailAddress>
to allow uppercase letters in `sendas <EmailAddress>` and `replyto <EmailAddress>`.
2024-03-17 12:05:27 -07:00
Ross Scroggs
448d58f9ba Updated gam create project to handle the following error:
ERROR: 403: permissionDenied - Authentication error: 7; Error Details: User not allowed to access GCP services.
2024-03-13 15:13:46 -07:00
Ross Scroggs
bdc330405e Show new label name on update 2024-03-11 17:04:05 -07:00
Ross Scroggs
abe1d5381d Added a command to update a Gmail label's settings by specifying it's ID rather than it's name. 2024-03-11 16:41:47 -07:00
Ross Scroggs
be0eff7e14 Updated <UserMultiAttribute>.location.buildingid <String> to allow non-validated building IDs 2024-03-09 07:06:45 -08:00
Ross Scroggs
f88a125966 Update WIX toolset version 2024-03-06 18:35:45 -08:00
Ross Scroggs
623ff1fae9 Added option showmimetype category <MimeTypeNameList> to gam <UserTypeEntity> print|show filecounts|filelist|filetree 2024-03-06 17:13:01 -08:00
Ross Scroggs
63d7b5568b Added option addcsvdata <FieldName> <String> to gam print cros 2024-03-05 16:14:18 -08:00
Ross Scroggs
7c8a87673a Reverted change made in 6.71.09 to gam <UserTypeEntity> print filelist 2024-03-04 10:41:43 -08:00
Ross Scroggs
a3b814f758 Improved the performance of gam <UserTypeEntity> print filelist when showmimetype and filepath|fullpath are both specified. 2024-03-03 14:18:17 -08:00
Ross Scroggs
1989d72f4f Added option oneitemperrow to 'gam print admins|adminroles` 2024-03-01 15:10:42 -08:00
Ross Scroggs
63b1ca7e30 Added command to upload changes to Google Docs. 2024-02-29 22:08:17 -08:00
Ross Scroggs
a328ac8ea9 Add licenses, thanks to Jay 2024-02-29 14:15:11 -08:00
Ross Scroggs
2188bfa704 Merge branch 'main' of https://github.com/GAM-team/GAM 2024-02-29 14:03:36 -08:00
Ross Scroggs
0f5adbe211 Added additional error handling to Gmail Client Side Encryption commands. 2024-02-29 14:03:29 -08:00
Jay Lee
d0251182de Education Endpoint SKU 2024-02-29 19:42:30 +00:00
Ross Scroggs
a04345fb10 Include missing gamlib updates 2024-02-29 11:09:35 -08:00
Ross Scroggs
80440255ab Many updates/fixes
Gmail CSE updates

Added todrive options: tdalert, tdfrom, tdsubject

Added CSV output row sorting

Fixed audit monitor create
2024-02-29 10:58:46 -08:00
Ross Scroggs
7b3cc6d819 Update documentation YouTube API off by default 2024-02-26 11:30:12 -08:00
Ross Scroggs
76d3ead61b Updated gam oauth create and gam <UserTypeEntity> update serviceaccount 2024-02-26 10:58:25 -08:00
Ross Scroggs
21ca008a47 Updated gam info users <UserTypeEntity> to not include group tree infornation unless option grouptree is specified. 2024-02-25 10:24:06 -08:00
Ross Scroggs
96aa4f3bd2 Added commands to create|delete|display Drive Label permissions. 2024-02-24 16:56:16 -08:00
Ross Scroggs
883979f5f5 Added options showvalidcolumn and addcsvdata to `gam print use 2024-02-23 14:41:45 -08:00
Ross Scroggs
b03a43777d Update DuetAI licenses to Gemini 2024-02-22 08:33:53 -08:00
Ross Scroggs
a0e4be4b50 Use gam.cfg/use_course_owner_access in course copyfrom. 2024-02-21 14:20:40 -08:00
Ross Scroggs
115caf2486 Added support for Gmail Client Side Encryption 2024-02-21 11:32:00 -08:00
Ross Scroggs
d5255615fd Added use_classroom_owner_access Boolean variable to gam.cfg 2024-02-18 20:59:58 -08:00
Ross Scroggs
d949ca2cad permissionDetails improvements 2024-02-15 18:24:47 -08:00
Ross Scroggs
4b0533ff0e Merge branch 'main' of https://github.com/GAM-team/GAM 2024-02-14 08:34:53 -08:00
Ross Scroggs
d1e87df2df Updated gam info user ... locations formatjson to include the buildingName field in the locations entries. 2024-02-14 08:34:39 -08:00
Jay Lee
dc8f6c3b5e actions: upgrade various action versions 2024-02-13 19:12:46 -05:00
Ross Scroggs
70640c1ddb Bug fix/enhancement copy|more drive file 2024-02-13 13:08:20 -08:00
Ross Scroggs
a72b81f99e Limit testing so jobs complete 2024-02-12 19:58:32 -08:00
Ross Scroggs
89a7c86840 Try multi artifact build - 2 2024-02-12 18:08:19 -08:00
Ross Scroggs
a086c1c2a8 Try multi artifact build 2024-02-12 18:02:57 -08:00
Ross Scroggs
be3c6f10c7 Updated gam print groups ... ciallfields|(cifields <CIGroupFieldNameList>) to account for an API shortcoming that failed to get all of the Cloud Identity fields. 2024-02-12 11:28:09 -08:00
Ross Scroggs
1c9f65f7ca Fix for delete artifacts failing? Try 2 2024-02-11 15:20:46 -08:00
Ross Scroggs
b023ecf8ce Fix for delete artifacts failing? 2024-02-11 15:16:48 -08:00
Ross Scroggs
0a0cb2a18b Back to macos-14 for universal2 build 2024-02-10 19:56:54 -08:00
Ross Scroggs
a02afe76fc Add missing lines
Build universal2 with macos-12, doesn't run with macos-14
2024-02-10 19:27:05 -08:00
Ross Scroggs
0b24beca30 Make artifact names unique with jid 2024-02-10 18:16:39 -08:00
Ross Scroggs
7dfa236bc1 Use v4 actions 2024-02-10 15:29:36 -08:00
Ross Scroggs
b7400b9010 run format cleanup, fix typo line 594 2024-02-09 16:03:11 -08:00
Jay Lee
50c5986c3e actions: fix Windows cache 2024-02-09 15:08:16 -05:00
Ross Scroggs
fff892300b Update glmsgs.py 2024-02-09 10:29:32 -08:00
Ross Scroggs
adbee45073 Added option skiprows <Integer> to gam csv|loop 2024-02-09 10:00:33 -08:00
Ross Scroggs
2d091c8ca0 Fixed bug in gam <UserTypeEntity> create drivefileacl that caused a trap. 2024-02-09 07:26:39 -08:00
Jay Lee
933fc19379 actions: reduce cache sizes by only caching necessary path for OS 2024-02-09 06:23:20 -05:00
Jay Lee
2bb2684165 actions: fix jid numbering 2024-02-08 14:29:33 -05:00
Jay Lee
868e5e1ab6 actions: expire cache to ensure all builds are correct 2024-02-08 14:25:55 -05:00
Jay Lee
d537067908 [no ci] actions: build arm64 and universal2 on github hosted runner 2024-02-08 14:23:17 -05:00
Jay Lee
a9b8a14d8e actions: cleanup brew installs for macOS 2024-02-08 14:18:24 -05:00
Ross Scroggs
f3d654fc76 Upgraded to Python 3.12.2 where possible. 2024-02-08 10:27:41 -08:00
Ross Scroggs
62a01bbcfd Added options restricted|(audience <String>) to gam <UserTypeEntity> create|update chatspace 2024-02-08 10:15:29 -08:00
Jay Lee
e60e1e939b [actions] github hosted Apple silicon (sweet) 2024-02-08 10:55:44 -05:00
Jay Lee
5305f1bda0 actions: rebuild for Python 3.12.2 2024-02-08 08:47:24 -05:00
Ross Scroggs
6126e6ac67 Fixed <PermissionMatch> bug for real. 2024-02-07 12:18:08 -08:00
Ross Scroggs
58e2f74700 Fixed <PermissionMatch> bug introduced in 6.67.35 2024-02-07 09:04:47 -08:00
Ross Scroggs
dcaf892e95 Added option wait <Integer> <Integer> to gam create datatransfer 2024-02-06 18:20:54 -08:00
Ross Scroggs
e8b2dee02d Added option tdnotify [<Boolean>] to <ToDriveAttribute> 2024-02-06 13:46:53 -08:00
Ross Scroggs
267d63fcd6 Fixed bug in gam <UserTypeEntity> show messages ... showattachments to avoid a trap when text/plain attachments in character sets other than UTF-8 are displayed. 2024-02-03 21:14:16 -08:00
Ross Scroggs
566a0c0345 Added sleep <Integer> to batch commands 2024-02-03 17:33:36 -08:00
Ross Scroggs
6ed3f8ebfc Added the following options to <PermissionMatch> that allow more powerful matching.
Added the following options to `<PermissionMatch>` that allow more powerful matching.
```
nottype	<DriveFileACLType>
typelist <DriveFileACLTypeList>
nottypelist <DriveFileACLTypeList>
rolelist <DriveFileACLRoleList>
notrolelist <DriveFileACLRoleList>
```
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Permission-Matches#define-a-match
2024-02-03 12:09:09 -08:00
Ross Scroggs
51c7a542e3 Shared Drive fixes/updates 2024-02-02 16:00:43 -08:00
Ross Scroggs
ee68669652 Fixed bug in gam <UserTypeEntity> print shareddrives where role was improperly displayed as unknown 2024-02-01 15:40:18 -08:00
Ross Scroggs
e7e653d395 Updated <ToDriveAttribute> to allow multiple tdshare <EmailAddress> commenter|reader|writer options. 2024-02-01 13:28:43 -08:00
Ross Scroggs
e6a4eb7fd9 Merge branch 'main' of https://github.com/GAM-team/GAM 2024-01-31 10:41:39 -08:00
Ross Scroggs
25cdf2e544 Multiple updates 2024-01-31 10:41:36 -08:00
Jay Lee
5e1702018c [actions] bump actions for OpenSSL 3.2.1 2024-01-30 13:04:12 -05:00
Ross Scroggs
a404af0582 Fixed bug that caused HTML password notification email messages to be displayed in raw form. 2024-01-22 09:52:26 -08:00
Ross Scroggs
741b69ff2d Update __init__.py 2024-01-20 10:23:17 -08:00
Ross Scroggs
da1f808c06 Use local copy of googleapiclient to remove static discovery documents to improve performance. 2024-01-20 10:01:04 -08:00
Ross Scroggs
39a8bf9485 Two updates
Added `permissionidlist <PermissionIDList>` to `<PermissionMatch>`

Added option `exportlinkeddrivefiles <Boolean>` to `gam create vaultexport`
2024-01-19 14:31:47 -08:00
Ross Scroggs
53d1ce5ddb Updated gam remove aliases <EmailAddress> user|group <EmailAddressEntity> 2024-01-19 08:12:27 -08:00
Ross Scroggs
432ef09129 Added option onelicenseperrow|onelicenceperrow to gam print users ... licenses 2024-01-17 20:14:18 -08:00
Ross Scroggs
647da9f980 Password notification fix
Updated `gam create|update user ... notify` to encode the characters `<>&` in the password
so that they display correctly when the notify message content is HTML.
2024-01-15 09:31:05 -08:00
Ross Scroggs
cc50ae28cd Cleaned up Getting/Got messages for gam print courses|course-participants. 2024-01-13 17:18:45 -08:00
Ross Scroggs
64ed92692a Added option showitemcountonly to various commands 2024-01-12 21:53:12 -08:00
Ross Scroggs
2dd810ba69 Correct editing error 2024-01-12 11:40:25 -08:00
Ross Scroggs
5922d939e2 Improve gam print group-members 2024-01-12 11:23:41 -08:00
Ross Scroggs
14eaa9f32f Updated reseller commands to handle the following error:
ERROR: 400: invalid - Customer domain [domain.com] is linked to one or more email verified customers, please provide a customer id.
2024-01-10 16:46:04 -08:00
Ross Scroggs
f935a6bdfc Updated gam create domain <DomainName> to handle the following error:
ERROR: 409: conflict - Domain in request is in use by an email verified customer.
2024-01-10 14:42:25 -08:00
Ross Scroggs
29ceda7f43 Added option addcsvdata <FieldName> <String> to gam print datatransfers 2024-01-10 11:38:19 -08:00
Ross Scroggs
f950c863f4 Gmail permissions update
Updated various Gmail related commands to handle this error:
```
ERROR: 403: permissionDenied - Insufficient Permission
```
2024-01-10 08:52:57 -08:00
86 changed files with 12927 additions and 1839 deletions

View File

@@ -34,11 +34,13 @@ jobs:
goal: build
arch: x86_64
openssl_archs: linux-x86_64
fullGamTest: yes
- os: [self-hosted, linux, arm64]
jid: 2
goal: build
arch: aarch64
openssl_archs: linux-aarch64
fullGamTest: yes
- os: ubuntu-20.04
jid: 3
goal: build
@@ -56,21 +58,24 @@ jobs:
goal: build
arch: x86_64
openssl_archs: darwin64-x86_64
- os: [self-hosted, macOS, ARM64]
jid: 7
fullGamTest: yes
- os: macos-14
jid: 6
goal: build
arch: aarch64
openssl_archs: darwin64-arm64
- os: [self-hosted, macOS, ARM64]
jid: 12
fullGamTest: yes
- os: macos-14
jid: 7
goal: build
arch: universal2
openssl_archs: darwin64-arm64 darwin64-x86_64
- os: windows-2022
jid: 6
jid: 8
goal: build
arch: Win64
openssl_archs: VC-WIN64A
fullGamTest: yes
- os: ubuntu-22.04
goal: test
python: "3.8"
@@ -94,37 +99,36 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
persist-credentials: false
fetch-depth: 0
- id: auth
name: Authenticate to Google Cloud
uses: google-github-actions/auth@v1
uses: google-github-actions/auth@v2
with:
workload_identity_provider: projects/297925809119/locations/global/workloadIdentityPools/gha-pool/providers/gha-provider
service_account: github-actions-testing-for-gam@gam-project-wyo-lub-ivl.iam.gserviceaccount.com
- name: Cache multiple paths
if: matrix.goal == 'build'
uses: actions/cache@v3
uses: actions/cache@v4
id: cache-python-ssl
with:
path: |
bin.tar.xz
src/cpython
key: gam-${{ matrix.jid }}-20231212
cache.tar.xz
key: gam-${{ matrix.jid }}-20240416
- name: Untar Cache archive
if: matrix.goal == 'build' && steps.cache-python-ssl.outputs.cache-hit == 'true'
working-directory: ${{ github.workspace }}
run: |
tar xvvf bin.tar.xz
tar xvvf cache.tar.xz
- name: Use pre-compiled Python for testing
if: matrix.python != ''
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
allow-prereleases: true
@@ -155,19 +159,30 @@ jobs:
fi
echo "GAMCFGDIR=${GAMCFGDIR}" >> $GITHUB_ENV
echo "GAMCFGDIR is: ${GAMCFGDIR}"
if [[ "${RUNNER_OS}" == "macOS" ]]; then
GAMOS="macos"
elif [[ "${RUNNER_OS}" == "Linux" ]]; then
GAMOS="linux"
elif [[ "${RUNNER_OS}" == "Windows" ]]; then
GAMOS="windows"
else
GAMOS='unknown'
fi
echo "GAMOS=${GAMOS}" >> $GITHUB_ENV
echo "GAMOS is: ${GAMOS}"
- name: Set env variables for test
if: matrix.goal == 'test'
run: |
export PYTHON=$(which python3)
export PIP=$(which pip3)
export gam="${PYTHON} -m gam"
export gampath="$(readlink -e .)"
echo -e "PYTHON: ${PYTHON}\nPIP: ${PIP}\gam: ${gam}\ngampath: ${gampath}"
echo "PYTHON=${PYTHON}" >> $GITHUB_ENV
echo "PIP=${PIP}" >> $GITHUB_ENV
echo "gam=${gam}" >> $GITHUB_ENV
echo "gampath=${gampath}" >> $GITHUB_ENV
export PYTHON=$(which python3)
export PIP=$(which pip3)
export gam="${PYTHON} -m gam"
export gampath="$(readlink -e .)"
echo -e "PYTHON: ${PYTHON}\nPIP: ${PIP}\gam: ${gam}\ngampath: ${gampath}"
echo "PYTHON=${PYTHON}" >> $GITHUB_ENV
echo "PIP=${PIP}" >> $GITHUB_ENV
echo "gam=${gam}" >> $GITHUB_ENV
echo "gampath=${gampath}" >> $GITHUB_ENV
- name: Install necessary Github-hosted Linux packages
if: runner.os == 'Linux' && runner.arch == 'X64'
@@ -177,15 +192,15 @@ jobs:
sudo apt-get -qq --yes install swig libpcsclite-dev libxslt1-dev
- name: MacOS install tools
if: runner.os == 'macOS' && runner.arch == 'x86_64'
if: runner.os == 'macOS'
run: |
# Install latest Rust
curl $curl_retry -fsS -o rust.sh https://sh.rustup.rs
bash ./rust.sh -y
source $HOME/.cargo/env
# needed for Rust to compile cryptography Python package for universal2
# not needed since MacOS ARM and universal will be on self-hosted
#rustup target add aarch64-apple-darwin
# Install needed packages
brew update
brew install gpg swig
- name: Windows Configure VCode
uses: ilammy/msvc-dev-cmd@v1
@@ -215,14 +230,11 @@ jobs:
CHOC_OPS="--forcex86"
fi
if [[ "${RUNNER_OS}" == "macOS" ]]; then
#brew install coreutils
#brew install bash
MAKE=make
MAKEOPT="-j$(sysctl -n hw.logicalcpu)"
PERL=perl
echo "MACOSX_DEPLOYMENT_TARGET=10.15" >> $GITHUB_ENV
echo "PYTHON=${PYTHON_INSTALL_PATH}/bin/python3" >> $GITHUB_ENV
#echo "PIP_ARGS=--no-binary=:all:" >> $GITHUB_ENV
elif [[ "${RUNNER_OS}" == "Linux" ]]; then
MAKE=make
MAKEOPT="-j$(nproc)"
@@ -444,39 +456,39 @@ jobs:
- name: Install pip requirements
run: |
echo "before anything..."
"${PYTHON}" -m pip list
if ([ "${RUNNER_OS}" == "macOS" ] && [ "$arch" == "universal2" ]); then
# cffi is a dep of cryptography and doesn't ship
# a universal2 wheel so we must build one ourself :-/
export CFLAGS="-arch x86_64 -arch arm64"
export ARCHFLAGS="-arch x86_64 -arch arm64"
"${PYTHON}" -m pip install --upgrade --force-reinstall --no-binary :all: \
--no-cache-dir --no-deps --use-pep517 \
--use-feature=no-binary-enable-wheel-cache \
cffi
echo "before cryptography..."
"${PYTHON}" -m pip list
# cryptography has a universal2 wheel but getting it installed
# on x86-64 MacOS is a royal pain in the keester.
"${PYTHON}" -m pip download --only-binary :all: \
--dest . \
--no-cache \
--no-deps \
--platform macosx_10_15_universal2 \
cryptography
"${PYTHON}" -m pip install --force-reinstall --no-deps cryptography*.whl
echo "after cryptography..."
"${PYTHON}" -m pip list
"${PYTHON}" -m pip install --upgrade --no-binary :all: -r requirements.txt
else
"${PYTHON}" -m pip install --upgrade -r requirements.txt
echo "after requirements..."
"${PYTHON}" -m pip list
"${PYTHON}" -m pip install --force-reinstall --no-deps --upgrade cryptography
fi
echo "after everything..."
"${PYTHON}" -m pip list
echo "before anything..."
"${PYTHON}" -m pip list
if ([ "${RUNNER_OS}" == "macOS" ] && [ "$arch" == "universal2" ]); then
# cffi is a dep of cryptography and doesn't ship
# a universal2 wheel so we must build one ourself :-/
export CFLAGS="-arch x86_64 -arch arm64"
export ARCHFLAGS="-arch x86_64 -arch arm64"
"${PYTHON}" -m pip install --upgrade --force-reinstall --no-binary :all: \
--no-cache-dir --no-deps --use-pep517 \
--use-feature=no-binary-enable-wheel-cache \
cffi
echo "before cryptography..."
"${PYTHON}" -m pip list
# cryptography has a universal2 wheel but getting it installed
# on x86-64 MacOS is a royal pain in the keester.
"${PYTHON}" -m pip download --only-binary :all: \
--dest . \
--no-cache \
--no-deps \
--platform macosx_10_15_universal2 \
cryptography
"${PYTHON}" -m pip install --force-reinstall --no-deps cryptography*.whl
echo "after cryptography..."
"${PYTHON}" -m pip list
"${PYTHON}" -m pip install --upgrade --no-binary :all: -r requirements.txt
else
"${PYTHON}" -m pip install --upgrade -r requirements.txt
echo "after requirements..."
"${PYTHON}" -m pip list
"${PYTHON}" -m pip install --force-reinstall --no-deps --upgrade cryptography
fi
echo "after everything..."
"${PYTHON}" -m pip list
- name: Install PyInstaller
if: matrix.goal == 'build'
@@ -514,34 +526,34 @@ jobs:
- name: Build GAM with PyInstaller
if: matrix.goal != 'test'
run: |
if [[ "${staticx}" == "yes" ]]; then
export distpath="./dist/gam"
export gampath="${distpath}"
else
export distpath="./dist"
export gampath="${distpath}/gam"
fi
mkdir -p -v "${gampath}"
if [[ "${RUNNER_OS}" == "macOS" ]]; then
export gampath=$($PYTHON -c "import os; print(os.path.realpath('$gampath'))")
elif [[ "${RUNNER_OS}" == "Windows" ]]; then
# Work around issue where PyInstaller picks up python3.dll from other Python versions
# https://github.com/pyinstaller/pyinstaller/issues/7102
export PATH="/usr/bin"
else
export gampath=$(realpath "${gampath}")
fi
export gam="${gampath}/gam"
echo "gampath=${gampath}" >> $GITHUB_ENV
echo "gam=${gam}" >> $GITHUB_ENV
echo -e "GAM: ${gam}\nGAMPATH: ${gampath}"
# TEMP force everything back to one file.
export PYINSTALLER_BUILD_ONEFILE="yes"
export distpath="./dist/gam"
export gampath="${distpath}"
"${PYTHON}" -m PyInstaller --clean --noconfirm --distpath="${distpath}" gam.spec
cat build/gam/warn-gam.txt
if [[ "${staticx}" == "yes" ]]; then
export distpath="./dist/gam"
export gampath="${distpath}"
else
export distpath="./dist"
export gampath="${distpath}/gam"
fi
mkdir -p -v "${gampath}"
if [[ "${RUNNER_OS}" == "macOS" ]]; then
export gampath=$($PYTHON -c "import os; print(os.path.realpath('$gampath'))")
elif [[ "${RUNNER_OS}" == "Windows" ]]; then
# Work around issue where PyInstaller picks up python3.dll from other Python versions
# https://github.com/pyinstaller/pyinstaller/issues/7102
export PATH="/usr/bin"
else
export gampath=$(realpath "${gampath}")
fi
export gam="${gampath}/gam"
echo "gampath=${gampath}" >> $GITHUB_ENV
echo "gam=${gam}" >> $GITHUB_ENV
echo -e "GAM: ${gam}\nGAMPATH: ${gampath}"
# TEMP force everything back to one file.
export PYINSTALLER_BUILD_ONEFILE="yes"
export distpath="./dist/gam"
export gampath="${distpath}"
"${PYTHON}" -m PyInstaller --clean --noconfirm --distpath="${distpath}" gam.spec
cat build/gam/warn-gam.txt
- name: Copy extra package files
if: matrix.goal == 'build'
run: |
@@ -578,11 +590,11 @@ jobs:
- name: Basic Tests all jobs
id: basictests
run: |
$PYTHON -m unittest discover --start-directory ./ --pattern "*_test.py" --buffer || if [ $? != 5 ]; then exit $?; fi # exit 5 is no tests
$gam version extended nooffseterror
export GAMVERSION=$($gam version simple)
echo "GAM Version ${GAMVERSION}"
echo "GAMVERSION=${GAMVERSION}" >> $GITHUB_ENV
$PYTHON -m unittest discover --start-directory ./ --pattern "*_test.py" --buffer || if [ $? != 5 ]; then exit $?; fi # exit 5 is no tests
$gam version extended nooffseterror
export GAMVERSION=$($gam version simple)
echo "GAM Version ${GAMVERSION}"
echo "GAMVERSION=${GAMVERSION}" >> $GITHUB_ENV
- name: Linux/MacOS package
if: runner.os != 'Windows' && matrix.goal == 'build'
@@ -595,7 +607,7 @@ jobs:
else
libver="glibc$(ldd --version | awk '/ldd/{print $NF}')"
fi
GAM_ARCHIVE="gam-${GAMVERSION}-linux-$(arch)-$libver}.tar.xz"
GAM_ARCHIVE="gam-${GAMVERSION}-linux-$(arch)-${libver}.tar.xz"
fi
tar -C dist/ --create --verbose --exclude-from "${GITHUB_WORKSPACE}/.github/actions/package_exclusions.txt" --file $GAM_ARCHIVE --xz gam
@@ -606,8 +618,8 @@ jobs:
GAM_ARCHIVE="../gam-${GAMVERSION}-windows-${GAM_ARCHIVE_ARCH}.zip"
/c/Program\ Files/7-Zip/7z.exe a -tzip $GAM_ARCHIVE gam "-xr@${GITHUB_WORKSPACE}/.github/actions/package_exclusions.txt" -bb3
cd ..
/c/Program\ Files\ \(x86\)/WiX\ Toolset\ v3.11/bin/candle.exe -arch "${WIX_ARCH}" gam.wxs
/c/Program\ Files\ \(x86\)/WiX\ Toolset\ v3.11/bin/light.exe -ext /c/Program\ Files\ \(x86\)/WiX\ Toolset\ v3.11/bin/WixUIExtension.dll gam.wixobj -o "gam-${GAMVERSION}-windows-${GAM_ARCHIVE_ARCH}.msi" || true;
/c/Program\ Files\ \(x86\)/WiX\ Toolset\ v3.14/bin/candle.exe -arch "${WIX_ARCH}" gam.wxs
/c/Program\ Files\ \(x86\)/WiX\ Toolset\ v3.14/bin/light.exe -ext /c/Program\ Files\ \(x86\)/WiX\ Toolset\ v3.14/bin/WixUIExtension.dll gam.wixobj -o "gam-${GAMVERSION}-windows-${GAM_ARCHIVE_ARCH}.msi" || true;
rm -v -f *.wixpdb
- name: Basic Tests build jobs only
@@ -631,227 +643,248 @@ jobs:
echo "We successfully compiled Python ${this_python} and OpenSSL ${this_openssl}"
- name: Live API tests push only
if: github.event_name == 'push' || github.event_name == 'schedule'
if: (github.event_name == 'push' || github.event_name == 'schedule') && matrix.fullGamTest == 'yes'
env:
PASSCODE: ${{ secrets.PASSCODE }}
run: |
if ([ "${RUNNER_OS}" == "macOS" ] && which gpg > /dev/null); then
brew install gnupg
fi
source ../.github/actions/decrypt.sh ../.github/actions/creds.tar.xz.gpg creds.tar.xz "${GAMCFGDIR}"
mv -v "${GAMCFGDIR}/oauth2.txt-gam-gha-${JID}" "${GAMCFGDIR}/oauth2.txt"
rm -v $GAMCFGDIR/oauth2.txt-gam*
export gam_user="gam-gha-${JID}@pdl.jaylee.us"
echo "gam_user=${gam_user}" >> $GITHUB_ENV
$gam config customer_id "C03uzfv2s" save
$gam config domain "pdl.jaylee.us" save
$gam config admin_email "${gam_user}" save
$gam config enable_dasa false save
$gam oauth info
$gam oauth refresh
$gam config enable_dasa true save
$gam create signjwtserviceaccount
$gam checkconn
$gam user "$gam_user" check serviceaccount
$gam info domain
$gam info user
export tstamp=$($PYTHON -c "import time; print(time.time_ns())")
export newbase="gha_test_${JID}_${tstamp}"
export newuser="${newbase}@pdl.jaylee.us"
export newgroup="${newbase}-group@pdl.jaylee.us"
export newalias="${newbase}-alias@pdl.jaylee.us"
export newbuilding="${newbase}-building"
export newresource="${newbase}-resource"
export newou="aaaGithub Actions/${newbase}"
# cleanup old runs
$gam config enable_dasa false save
$gam config csv_output_row_filter "name:regex:gha_test_${JID}_" print vaultholds || if [ $? != 55 ]; then exit $?; fi | $gam csv - gam delete vaulthold "id:~~holdId~~" matter "id:~~matterId~~"
$gam config enable_dasa true save
$gam config csv_output_row_filter "name:regex:gha_test_${JID}_" print features | $gam csv - gam delete feature ~name
$gam config csv_output_row_filter "name:regex:^gha_test_${JID}_" user $gam_user print shareddrives asadmin | $gam csv - gam user $gam_user delete shareddrive ~id nukefromorbit
$gam print users query "gha.jid=$JID" | $gam csv - gam delete user ~primaryEmail
$gam config csv_output_row_filter "name:regex:^gha_test_${JID}_" print ous fromparent "aaaGithub Actions" | $gam csv - gam delete ou ~orgUnitId
$gam config csv_output_row_filter "email:regex:^gha_test_${JID}_" print cigroups | $gam csv - gam delete cigroup ~email
$gam config csv_output_row_filter "resourceId:regex:^gha_test_${JID}_" print resources | $gam csv - gam delete resource ~resourceId
$gam config csv_output_row_filter "buildingId:regex:^gha_test_${JID}_" print buildings | $gam csv - gam delete building ~buildingId
$gam config csv_output_row_filter "Emails.1.address:regex:^gha_test-${JID}_" print contacts | $gam csv - gam delete contact ~ContactID
source ../.github/actions/decrypt.sh ../.github/actions/creds.tar.xz.gpg creds.tar.xz "${GAMCFGDIR}"
mv -v "${GAMCFGDIR}/oauth2.txt-gam-gha-${JID}" "${GAMCFGDIR}/oauth2.txt"
rm -v $GAMCFGDIR/oauth2.txt-gam*
export gam_user="gam-gha-${JID}@pdl.jaylee.us"
echo "gam_user=${gam_user}" >> $GITHUB_ENV
$gam config customer_id "C03uzfv2s" save
$gam config domain "pdl.jaylee.us" save
$gam config admin_email "${gam_user}" save
$gam config enable_dasa false save
$gam oauth info
$gam oauth refresh
$gam config enable_dasa true save
$gam create signjwtserviceaccount
$gam checkconn
$gam user "$gam_user" check serviceaccount
$gam info domain
$gam info user
export tstamp=$($PYTHON -c "import time; print(time.time_ns())")
export newbase="gha_test_${JID}_${tstamp}"
export newuser="${newbase}@pdl.jaylee.us"
export newgroup="${newbase}-group@pdl.jaylee.us"
export newalias="${newbase}-alias@pdl.jaylee.us"
export newbuilding="${newbase}-building"
export newresource="${newbase}-resource"
export newou="aaaGithub Actions/${newbase}"
# cleanup old runs
$gam config enable_dasa false save
$gam config csv_output_row_filter "name:regex:gha_test_${JID}_" print vaultholds || if [ $? != 55 ]; then exit $?; fi | $gam csv - gam delete vaulthold "id:~~holdId~~" matter "id:~~matterId~~"
$gam config enable_dasa true save
$gam config csv_output_row_filter "name:regex:gha_test_${JID}_" print features | $gam csv - gam delete feature ~name
$gam config csv_output_row_filter "name:regex:^gha_test_${JID}_" user $gam_user print shareddrives asadmin | $gam csv - gam user $gam_user delete shareddrive ~id nukefromorbit
$gam print users query "gha.jid=$JID" | $gam csv - gam delete user ~primaryEmail
$gam config csv_output_row_filter "name:regex:^gha_test_${JID}_" print ous fromparent "aaaGithub Actions" | $gam csv - gam delete ou ~orgUnitId
$gam config csv_output_row_filter "email:regex:^gha_test_${JID}_" print cigroups | $gam csv - gam delete cigroup ~email
$gam config csv_output_row_filter "resourceId:regex:^gha_test_${JID}_" print resources | $gam csv - gam delete resource ~resourceId
$gam config csv_output_row_filter "buildingId:regex:^gha_test_${JID}_" print buildings | $gam csv - gam delete building ~buildingId
$gam config csv_output_row_filter "Emails.1.address:regex:^gha_test-${JID}_" print contacts | $gam csv - gam delete contact ~ContactID
echo "Creating OrgUnit ${newou}"
$gam create ou "${newou}"
export GAM_THREADS=5
echo email > sample.csv;
for i in {1..10}; do
echo "${newbase}-bulkuser-$i" >> sample.csv;
done
driveid=$($gam user $gam_user add shareddrive "${newbase}" returnidonly)
echo "Created shared drive ${driveid}"
$gam create user $newuser firstname GHA lastname $JID displayname "Github Actions ${JID}" password random ou "${newou}" recoveryphone 12125121110 recoveryemail jay0lee@gmail.com gha.jid $JID languages en+,en-GB-
$gam user $newuser update photo https://dummyimage.com/400x600/000/fff
$gam user $newuser get photo
$gam user $newuser delete photo
$gam create alias $newalias user $newuser
$gam create group $newgroup name "GHA $JID group" description "This is a description" isarchived true
$gam user $gam_user sendemail recipient $newuser subject "test message $newbase" message "GHA test message"
$gam user $gam_user sendemail recipient exchange@pdl.jaylee.us subject "test ${tstamp}" message "test message"
$gam config enable_dasa false save
$gam create contact firstname GHA lastname "$JID" email work "${newbase}@example.com" primary
$gam print contacts
$gam user $newuser add license workspaceenterpriseplus
$gam print privileges
$gam config enable_dasa true save
$gam update cigroup $newgroup security memberrestriction 'member.type == 1 || member.customer_id == groupCustomerId()'
$gam info cigroup $newgroup
$gam update group $newgroup add owner $gam_user
$gam update group $newgroup add member $newuser
$gam config enable_dasa false save
$gam create admin $newuser _GROUPS_EDITOR_ROLE CUSTOMER # condition nonsecuritygroup
$gam create admin $newgroup _HELP_DESK_ADMIN_ROLE org_unit "${newou}"
$gam config csv_output_row_filter "assignedToUser:regex:${newuser}" print admins | $gam csv - gam delete admin "~roleAssignmentId"
$gam config csv_output_row_filter "assignedToGroup:regex:${newgroup}" print admins | $gam csv - gam delete admin "~roleAssignmentId"
$gam config enable_dasa false save
$gam csv sample.csv gam create user ~~email~~ firstname "GHA Bulk" lastname ~~email~~ gha.jid $JID ou "${newou}"
$gam csv sample.csv gam update user ~~email~~ recoveryphone 12125121110 recoveryemail jay0lee@gmail.com password random displayname "GitHub Actions Bulk ${JID}"
$gam csv sample.csv gam update user ~~email~~ recoveryphone "" recoveryemail ""
$gam config enable_dasa false save
$gam csv sample.csv gam user ~email add license workspaceenterpriseplus
$gam config enable_dasa true save
$gam csv sample.csv gam user $gam_user sendemail recipient ~~email~~@pdl.jaylee.us subject "test message $newbase" message "GHA test message"
$gam csv sample.csv gam update group $newgroup add member ~email
$gam info group $newgroup
$gam info cigroup $newgroup membertree
# confirm mailbox is provisoned before continuing
$gam user $newuser waitformailbox
$gam user $newuser imap on
$gam user $newuser show imap
$gam user $newuser show delegates
#$gam user $newuser add contactdelegate "${newbase}-bulkuser-1"
#$gam user $newuser print contactdelegates
export biohazard=$(echo -e '\xe2\x98\xa3')
$gam user $newuser label "$biohazard unicode biohazard $biohazard"
$gam user $newuser show labels
$gam user $newuser show labels > labels.txt
$gam user $gam_user importemail subject "GHA import $newbase" message "This is a test import" labels IMPORTANT,UNREAD,INBOX,STARRED
$gam user $gam_user insertemail subject "GHA insert $newbase" file gam.py labels INBOX,UNREAD # yep body is gam code
$gam user $gam_user sendemail subject "GHA send $gam_user $newbase" file gam.py recipient admin@pdl.jaylee.us
$gam user $gam_user draftemail subject "GHA draft $newbase" message "Draft message test"
$gam csvfile sample.csv:email waitformailbox
$gam user $newuser delegate to "${newbase}-bulkuser-1" || if [ $? != 50 ]; then exit $?; fi # expect a 50 return code (delegation failed)
$gam users "$gam_user $newbase-bulkuser-1 $newbase-bulkuser-2 $newbase-bulkuser-3" delete messages query in:anywhere maxtodelete 99999 doit || if [ $? != 60 ]; then exit $?; fi # expect a 60 return code (no messages)
$gam users "$newbase-bulkuser-4 $newbase-bulkuser-5 $newbase-bulkuser-6" trash messages query in:anywhere maxtotrash 99999 doit || if [ $? != 60 ]; then exit $?; fi # expect a 60 return code (no messages)
$gam users "$newbase-bulkuser-7 $newbase-bulkuser-8 $newbase-bulkuser-9" modify messages query in:anywhere maxtomodify 99999 addlabel IMPORTANT addlabel STARRED doit || if [ $? != 60 ]; then exit $?; fi # expect a 60 return code (no messages)
$gam user $newuser delete label --ALL_LABELS--
$gam config csv_output_row_filter "name:regex:gha-test-${JID}" print features | $gam csv - gam delete feature ~name
$gam create feature name VC-$newbase
$gam create feature name Whiteboard-$newbase
$gam create building "My Building - $newbase" id $newbuilding floors 1,2,3,4,5,6,7,8,9,10,11,12,14,15 description "No 13th floor here..."
$gam create resource $newresource "Resource Calendar $tstamp" capacity 25 features Whiteboard-$newbase,VC-$newbase building $newbuilding floor 15 type Room
$gam info resource $newresource
$gam user $newuser add drivefile drivefilename "TPS Reports" mimetype gfolder
$gam user $newuser show filelist
$gam calendar $gam_user printacl | $gam csv - gam calendar $gam_user delete ~id # clear ACLs
$gam calendar $gam_user add read domain
$gam calendar $gam_user add freebusy default
$gam calendar $gam_user add editor $newuser
$gam calendar $gam_user showacl
$gam calendar $gam_user printacl | $gam csv - gam calendar $gam_user delete ~id
$gam calendar $gam_user addevent summary "GHA test event" start +1h end +2h attendee $newgroup hangoutsmeet guestscanmodify true sendupdates all
$gam calendar $gam_user printevents after -0d
$gam config enable_dasa false save
matterid=uid:$($gam create vaultmatter name "GHA matter $newbase" description "test matter" collaborators $newuser returnidonly)
$gam create vaulthold matter $matterid name "GHA hold $newbase" corpus mail accounts $newuser
$gam print vaultmatters matterstate open
$gam print vaultholds matter $matterid
$gam print vaultcount matter $matterid corpus mail everyone todrive tdnobrowser
$gam create vaultexport matter $matterid name "GHA export $newbase" corpus mail accounts $newuser
$gam print exports matter $matterid | $gam csv - gam info export $matterid id:~~id~~
$gam config enable_dasa true save
$gam csv sample.csv gam user ~email add calendar id:$newresource
$gam delete resource $newresource
$gam delete feature Whiteboard-$newbase
$gam delete feature VC-$newbase
$gam delete building $newbuilding
$gam delete group $newgroup
$gam config enable_dasa false save
echo start
$gam user $newuser delete license workspaceenterpriseplus
echo finish
$gam config enable_dasa true save
$gam whatis $newuser || if [ $? != 20 ]; then exit $?; fi # expect a 20 return code (is a user)
$gam user $gam_user show tokens
$gam config enable_dasa false save
download_dir="${RUNNER_TEMP}/TEMP_DELETE_ME"
mkdir -v "$download_dir"
$gam print exports matter $matterid | $gam csv - gam download export $matterid id:~~id~~ targetfolder "$download_dir"
rm -rvf "$download_dir"
$gam delete hold "GHA hold $newbase" matter $matterid
$gam update matter $matterid action close
$gam update matter $matterid action delete
# shakes off vault hold on user so we can delete
$gam print users query "email:${newuser}" orgunitpath | $gam csv - gam update user ~primaryEmail ou ~orgUnitPath
$gam user $newuser show holds || if [ $? != 55 ]; then exit $?; fi # expect a 55 return code
export sn="$JID$JID$JID$JID-$(openssl rand -base64 32 | sed 's/[^a-zA-Z0-9]//g')"
$gam create device serialnumber $sn devicetype android
$gam config enable_dasa true save
$gam print users query "gha.jid=$JID" | $gam csv - gam delete user ~primaryEmail || if [ $? != 50 ]; then exit $?; fi # expect a 50 return code (vault hold on user)
$gam delete contacts emailmatchpattern "^${newbase}@example.com$"
$gam print mobile
$gam print devices
$gam print browsers
$gam print cros allfields orderby serialnumber
$gam show crostelemetry storagepercentonly
$gam report usageparameters customer
$gam report usage customer parameters gmail:num_emails_sent,accounts:num_1day_logins
$gam report customer todrive tdnobrowser
#$gam report users fields accounts:is_less_secure_apps_access_allowed,gmail:last_imap_time,gmail:last_pop_time filters "accounts:last_login_time>2019-01-01T00:00:00.000Z" todrive tdnobrowser
$gam report users todrive tdnobrowser
$gam report admin start -3d todrive tdnobrowser
$gam print devices nopersonaldevices nodeviceusers filter "serial:$JID$JID$JID$JID-" | $gam csv - gam delete device id ~name
$gam config enable_dasa false save
$gam print userinvitations
$gam print userinvitations | $gam csv - gam send userinvitation ~name
$gam config enable_dasa false save
$gam create caalevel "zzz_${newbase}" basic condition ipsubnetworks 1.1.1.1/32,2.2.2.2/32 endcondition
$gam print caalevels
$gam delete caalevel "zzz_${newbase}"
$gam user $gam_user add drivefile localfile gam.py parentid "${driveid}"
$gam user $gam_user update shareddrive "${driveid}" ou "${newou}"
$gam user $gam_user show shareddrives asadmin
$gam user $gam_user update shareddrive "${driveid}" ou "aaaGithub Actions" # so we can delete our OU...
$gam user $gam_user delete shareddrive "${driveid}" nukefromorbit
echo "printer model count:"
ssoprofile=$($gam create inboundssoprofile name "El Goog ${newbase}" loginurl https://www.google.com logouturl https://www.google.com changepasswordurl https://www.google.com entityid ElGoog return_name_only)
$gam create inboundssocredential profile "id:${ssoprofile}" generate_key
#$gam create inboundssoassignment profile "id:${ssoprofile}" orgunit "${newou}" mode SAML_SSO
#$gam delete inboundssoassignment "orgunit:${newou}"
$gam delete inboundssoprofile "id:${ssoprofile}"
$gam print printermodels | wc -l
$gam print printers
printerid=$($gam create printer displayname "${newbase}" uri ipp://localhost:631 driverless description "made by $(gam_user)" ou "${newou}" nodetails | awk '{print substr($2, 1, length($2)-1)}')
$gam info printer "$printerid"
$gam delete printer "$printerid"
$gam delete ou "${newou}"
- name: Archive production artifacts
uses: actions/upload-artifact@v3
if: (github.event_name == 'push' || github.event_name == 'schedule') && matrix.goal != 'test'
with:
name: gam-binaries
path: |
src/*.tar.xz
src/*.zip
src/*.msi
echo "Creating OrgUnit ${newou}"
$gam create ou "${newou}"
export GAM_THREADS=5
echo email > sample.csv;
for i in {1..10}; do
echo "${newbase}-bulkuser-$i" >> sample.csv;
done
driveid=$($gam user $gam_user add shareddrive "${newbase}" returnidonly)
echo "Created shared drive ${driveid}"
$gam create user $newuser firstname GHA lastname $JID displayname "Github Actions ${JID}" password random ou "${newou}" recoveryphone 12125121110 recoveryemail jay0lee@gmail.com gha.jid $JID languages en+,en-GB-
$gam user $newuser update photo https://dummyimage.com/400x600/000/fff
$gam user $newuser get photo
$gam user $newuser delete photo
$gam create alias $newalias user $newuser
$gam create group $newgroup name "GHA $JID group" description "This is a description" isarchived true
$gam user $gam_user sendemail recipient $newuser subject "test message $newbase" message "GHA test message"
$gam user $gam_user sendemail recipient exchange@pdl.jaylee.us subject "test ${tstamp}" message "test message"
$gam config enable_dasa false save
$gam create contact firstname GHA lastname "$JID" email work "${newbase}@example.com" primary
$gam print contacts
$gam user $newuser add license workspaceenterpriseplus
$gam print privileges
$gam config enable_dasa true save
$gam update cigroup $newgroup security memberrestriction 'member.type == 1 || member.customer_id == groupCustomerId()'
$gam info cigroup $newgroup
$gam update group $newgroup add owner $gam_user
$gam update group $newgroup add member $newuser
$gam config enable_dasa false save
$gam create admin $newuser _GROUPS_EDITOR_ROLE CUSTOMER # condition nonsecuritygroup
$gam create admin $newgroup _HELP_DESK_ADMIN_ROLE org_unit "${newou}"
$gam config csv_output_row_filter "assignedToUser:regex:${newuser}" print admins | $gam csv - gam delete admin "~roleAssignmentId"
$gam config csv_output_row_filter "assignedToGroup:regex:${newgroup}" print admins | $gam csv - gam delete admin "~roleAssignmentId"
$gam config enable_dasa false save
$gam csv sample.csv gam create user ~~email~~ firstname "GHA Bulk" lastname ~~email~~ gha.jid $JID ou "${newou}"
$gam csv sample.csv gam update user ~~email~~ recoveryphone 12125121110 recoveryemail jay0lee@gmail.com password random displayname "GitHub Actions Bulk ${JID}"
$gam csv sample.csv gam update user ~~email~~ recoveryphone "" recoveryemail ""
$gam config enable_dasa false save
$gam csv sample.csv gam user ~email add license workspaceenterpriseplus
$gam config enable_dasa true save
$gam csv sample.csv gam user $gam_user sendemail recipient ~~email~~@pdl.jaylee.us subject "test message $newbase" message "GHA test message"
$gam csv sample.csv gam update group $newgroup add member ~email
$gam info group $newgroup
$gam info cigroup $newgroup membertree
# confirm mailbox is provisoned before continuing
$gam user $newuser waitformailbox
$gam user $newuser imap on
$gam user $newuser show imap
$gam user $newuser show delegates
#$gam user $newuser add contactdelegate "${newbase}-bulkuser-1"
#$gam user $newuser print contactdelegates
export biohazard=$(echo -e '\xe2\x98\xa3')
$gam user $newuser label "$biohazard unicode biohazard $biohazard"
$gam user $newuser show labels
$gam user $newuser show labels > labels.txt
$gam user $gam_user importemail subject "GHA import $newbase" message "This is a test import" labels IMPORTANT,UNREAD,INBOX,STARRED
$gam user $gam_user insertemail subject "GHA insert $newbase" file gam.py labels INBOX,UNREAD # yep body is gam code
$gam user $gam_user sendemail subject "GHA send $gam_user $newbase" file gam.py recipient admin@pdl.jaylee.us
$gam user $gam_user draftemail subject "GHA draft $newbase" message "Draft message test"
$gam csvfile sample.csv:email waitformailbox
$gam user $newuser delegate to "${newbase}-bulkuser-1" || if [ $? != 50 ]; then exit $?; fi # expect a 50 return code (delegation failed)
$gam users "$gam_user $newbase-bulkuser-1 $newbase-bulkuser-2 $newbase-bulkuser-3" delete messages query in:anywhere maxtodelete 99999 doit || if [ $? != 60 ]; then exit $?; fi # expect a 60 return code (no messages)
$gam users "$newbase-bulkuser-4 $newbase-bulkuser-5 $newbase-bulkuser-6" trash messages query in:anywhere maxtotrash 99999 doit || if [ $? != 60 ]; then exit $?; fi # expect a 60 return code (no messages)
$gam users "$newbase-bulkuser-7 $newbase-bulkuser-8 $newbase-bulkuser-9" modify messages query in:anywhere maxtomodify 99999 addlabel IMPORTANT addlabel STARRED doit || if [ $? != 60 ]; then exit $?; fi # expect a 60 return code (no messages)
$gam user $newuser delete label --ALL_LABELS--
$gam config csv_output_row_filter "name:regex:gha-test-${JID}" print features | $gam csv - gam delete feature ~name
$gam create feature name VC-$newbase
$gam create feature name Whiteboard-$newbase
$gam create building "My Building - $newbase" id $newbuilding floors 1,2,3,4,5,6,7,8,9,10,11,12,14,15 description "No 13th floor here..."
$gam create resource $newresource "Resource Calendar $tstamp" capacity 25 features Whiteboard-$newbase,VC-$newbase building $newbuilding floor 15 type Room
$gam info resource $newresource
$gam user $newuser add drivefile drivefilename "TPS Reports" mimetype gfolder
$gam user $newuser show filelist
$gam calendar $gam_user printacl | $gam csv - gam calendar $gam_user delete ~id # clear ACLs
$gam calendar $gam_user add read domain
$gam calendar $gam_user add freebusy default
$gam calendar $gam_user add editor $newuser
$gam calendar $gam_user showacl
$gam calendar $gam_user printacl | $gam csv - gam calendar $gam_user delete ~id
$gam calendar $gam_user addevent summary "GHA test event" start +1h end +2h attendee $newgroup hangoutsmeet guestscanmodify true sendupdates all
$gam calendar $gam_user printevents after -0d
$gam config enable_dasa false save
matterid=uid:$($gam create vaultmatter name "GHA matter $newbase" description "test matter" collaborators $newuser returnidonly)
$gam create vaulthold matter $matterid name "GHA hold $newbase" corpus mail accounts $newuser
$gam print vaultmatters matterstate open
$gam print vaultholds matter $matterid
$gam print vaultcount matter $matterid corpus mail everyone todrive tdnobrowser
$gam create vaultexport matter $matterid name "GHA export $newbase" corpus mail accounts $newuser
$gam print exports matter $matterid | $gam csv - gam info export $matterid id:~~id~~
$gam config enable_dasa true save
$gam csv sample.csv gam user ~email add calendar id:$newresource
$gam delete resource $newresource
$gam delete feature Whiteboard-$newbase
$gam delete feature VC-$newbase
$gam delete building $newbuilding
$gam delete group $newgroup
$gam config enable_dasa false save
echo start
$gam user $newuser delete license workspaceenterpriseplus
echo finish
$gam config enable_dasa true save
$gam whatis $newuser || if [ $? != 20 ]; then exit $?; fi # expect a 20 return code (is a user)
$gam user $gam_user show tokens
$gam config enable_dasa false save
download_dir="${RUNNER_TEMP}/TEMP_DELETE_ME"
mkdir -v "$download_dir"
$gam print exports matter $matterid | $gam csv - gam download export $matterid id:~~id~~ targetfolder "$download_dir"
rm -rvf "$download_dir"
$gam delete hold "GHA hold $newbase" matter $matterid
$gam update matter $matterid action close
$gam update matter $matterid action delete
# shakes off vault hold on user so we can delete
$gam print users query "email:${newuser}" orgunitpath | $gam csv - gam update user ~primaryEmail ou ~orgUnitPath
$gam user $newuser show holds || if [ $? != 55 ]; then exit $?; fi # expect a 55 return code
export sn="$JID$JID$JID$JID-$(openssl rand -base64 32 | sed 's/[^a-zA-Z0-9]//g')"
$gam create device serialnumber $sn devicetype android
$gam config enable_dasa true save
$gam print users query "gha.jid=$JID" | $gam csv - gam delete user ~primaryEmail || if [ $? != 50 ]; then exit $?; fi # expect a 50 return code (vault hold on user)
$gam delete contacts emailmatchpattern "^${newbase}@example.com$"
$gam print mobile
$gam print devices
$gam print browsers
$gam print cros allfields orderby serialnumber
$gam show crostelemetry storagepercentonly
$gam report usageparameters customer
$gam report usage customer parameters gmail:num_emails_sent,accounts:num_1day_logins
$gam report customer todrive tdnobrowser
#$gam report users fields accounts:is_less_secure_apps_access_allowed,gmail:last_imap_time,gmail:last_pop_time filters "accounts:last_login_time>2019-01-01T00:00:00.000Z" todrive tdnobrowser
$gam report users todrive tdnobrowser
$gam report admin start -3d todrive tdnobrowser
$gam print devices nopersonaldevices nodeviceusers filter "serial:$JID$JID$JID$JID-" | $gam csv - gam delete device id ~name
$gam config enable_dasa false save
$gam print userinvitations
$gam print userinvitations | $gam csv - gam send userinvitation ~name
$gam config enable_dasa false save
$gam create caalevel "zzz_${newbase}" basic condition ipsubnetworks 1.1.1.1/32,2.2.2.2/32 endcondition
$gam print caalevels
$gam delete caalevel "zzz_${newbase}"
$gam user $gam_user add drivefile localfile gam.py parentid "${driveid}"
$gam user $gam_user update shareddrive "${driveid}" ou "${newou}"
$gam user $gam_user show shareddrives asadmin
$gam user $gam_user update shareddrive "${driveid}" ou "aaaGithub Actions" # so we can delete our OU...
$gam user $gam_user delete shareddrive "${driveid}" nukefromorbit
echo "printer model count:"
ssoprofile=$($gam create inboundssoprofile name "El Goog ${newbase}" loginurl https://www.google.com logouturl https://www.google.com changepasswordurl https://www.google.com entityid ElGoog return_name_only)
$gam create inboundssocredential profile "id:${ssoprofile}" generate_key
#$gam create inboundssoassignment profile "id:${ssoprofile}" orgunit "${newou}" mode SAML_SSO
#$gam delete inboundssoassignment "orgunit:${newou}"
$gam delete inboundssoprofile "id:${ssoprofile}"
$gam print printermodels | wc -l
$gam print printers
printerid=$($gam create printer displayname "${newbase}" uri ipp://localhost:631 driverless description "made by $(gam_user)" ou "${newou}" nodetails | awk '{print substr($2, 1, length($2)-1)}')
$gam info printer "$printerid"
$gam delete printer "$printerid"
$gam delete ou "${newou}"
- name: Tar Cache archive
if: matrix.goal == 'build' && steps.cache-python-ssl.outputs.cache-hit != 'true'
working-directory: ${{ github.workspace }}
run: |
tar cJvvf bin.tar.xz bin/
if [[ "${RUNNER_OS}" == "Windows" ]]; then
tar_folders="src/cpython/ bin/ssl"
else
tar_folders="bin/"
fi
tar cJvvf cache.tar.xz $tar_folders
- name: Archive production artifacts
uses: actions/upload-artifact@v4
if: (github.event_name == 'push' || github.event_name == 'schedule') && matrix.goal != 'test'
with:
name: gam-binaries-${{ env.GAMOS }}-${{ env.arch }}-${{ matrix.jid }}
path: |
src/*.tar.xz
src/*.zip
src/*.msi
merge:
if: (github.event_name == 'push' || github.event_name == 'schedule')
runs-on: ubuntu-latest
needs: build
permissions:
contents: write
packages: write
steps:
- name: Merge Artifacts
uses: actions/upload-artifact/merge@v4
with:
name: gam-binaries
pattern: gam-binaries-*
# - name: Delete Artifacts
# uses: geekyeggo/delete-artifact@v4
# with:
# name: gam-binaries-*
publish:
if: github.event_name == 'push'
runs-on: ubuntu-latest
needs: build
needs: merge
permissions:
contents: write
packages: write
@@ -859,16 +892,16 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
persist-credentials: false
fetch-depth: 0
- name: Download artifacts
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
- name: VirusTotal Scan
uses: crazy-max/ghaction-virustotal@v3
uses: crazy-max/ghaction-virustotal@v4
with:
vt_api_key: ${{ secrets.VT_API_KEY }}
files: |

View File

@@ -851,11 +851,15 @@ gam delete adminrole <RoleItem>
## Display administrative roles
```
gam info adminrole <RoleItem> [privileges]
gam print adminroles|roles [todrive <ToDriveAttribute>*] [privileges]
gam print adminroles|roles [todrive <ToDriveAttribute>*]
[privileges] [oneitemperrow]
gam show adminroles|roles [todrive <ToDriveAttribute>*] [privileges]
```
* `privileges` - Display privileges associated with each role
By default, all privileges for a role are shown on one row as a repeating item.
When `oneitemperrow` is specified, each privilege is output on a separate row/line with the other role fields.
## Create an administrator
Add an administrator role to an administrator.
```
@@ -877,7 +881,8 @@ gam delete admin <RoleAssignmentId>
## Display administrators
```
gam print admins [todrive <ToDriveAttribute>*]
[user|group <EmailAddress>|<UniqueID>] [role <RoleItem>] [condition] [privileges]
[user|group <EmailAddress>|<UniqueID>] [role <RoleItem>] [condition]
[privileges] [oneitemperrow]
gam show admins
[user|group <EmailAddress>|<UniqueID>] [role <RoleItem>] [condition] [privileges]
```
@@ -889,6 +894,9 @@ options to limit the display:
* `condition` - Display any conditions associated with a role assignment
* `privileges` - Display privileges associated with each role assignment
By default, all role privileges for an admin are shown on one row as a repeating item.
When `oneitemperrow` is specified, each role privilege is output on a separate row/line with the other admin fields.
In versions prior to 6.07.01, specification of both `user <UserItem>`
and `role <RoleItem>` generated no output due to an undocumented API rule that disallows both.

View File

@@ -85,7 +85,7 @@ gam <UserTypeEntity> delete aliases
```
## Display aliases
Display a specific alise.
Display a specific alias.
```
gam info alias|aliases <EmailAddressEntity>
```
@@ -129,6 +129,9 @@ By default, the aliases in a list are separated by the `csv_output_field_delimit
Specifying both `onerowpertarget` and `suppressnoaliasrows` causes GAM to not display any targets that have no aliases.
Add additional columns of data from the command line to the output
* `addcsvdata <FieldName> <String>`
When multiple domains are specified and a query/queries are specified, an API call is made for each domain/query combination.
```
$ gam print aliases domains school.org,students.school.org queries "'email:admin*','email:test*'"

View File

@@ -7,6 +7,7 @@
- [Definitions](#definitions)
- [Manage Projects](#manage-projects)
- [Authorize a super admin to create projects](#authorize-a-super-admin-to-create-projects)
- [Authorize Service Account Key Uploads](#authorize-service-account-key-uploads)
- [Authorize GAM to create projects](#authorize-gam-to-create-projects)
- [Create a new GCP project folder](#create-a-new-gcp-project-folder)
- [Create a new project for GAM authorization](#create-a-new-project-for-gam-authorization)
@@ -30,6 +31,7 @@
- [Update an existing Service Account key](#update-an-existing-service-account-key)
- [Replace all existing Service Account keys](#replace-all-existing-service-account-keys)
- [Delete Service Account keys](#delete-service-account-keys)
- [Upload a Service Account key to a service account with no keys](#upload-a-service-account-key-to-a-service-account-with-no-keys)
- [Display Service Account keys](#display-service-account-keys)
- [Manage Service Account access](#manage-service-account-access)
- [Full Service Account access](#full-service-account-access)
@@ -89,15 +91,6 @@ If you run a Google Workspace Education SKU, verify that the super admin you'll
* Choose "All users are 18 or older"
* Click "SAVE"
Verify whether the super admin you'll be using is in an OU where reauthentication is required.
* Access the admin console and go to Security -> Overview
* Scroll down and open Google Cloud session control section
* Select the OU containing the super admin
* If Require reauthentication is selected and Exempt Trusted apps is not checked, you'll have to do `gam oauth create` at whatever frequency is specified
* If that sounds unappealing, check Exempt Trusted apps
* Click "OVERRIDE"
* Follow the steps below to mark GAM as a trusted app
Based on your domain policies, you may have to mark GAM as a trusted app. These steps are performed after a project is created.
* Access the admin console and go to Security -> Access and data control -> API controls
* Check Trust internal, domain-owned apps
@@ -114,6 +107,20 @@ Based on your domain policies, you may have to mark GAM as a trusted app. These
* Click Next/Continue
* Click Finish
Verify whether the super admin you'll be using is in an OU where reauthentication is required.
* Access the admin console and go to Security -> Overview
* Scroll down and open Google Cloud session control section
* Select the OU containing the super admin
* If Require reauthentication is selected and Exempt Trusted apps is not checked, you'll have to do `gam oauth create` at whatever frequency is specified
* If that sounds unappealing, check Exempt Trusted apps
* Click "OVERRIDE"
* Follow the steps below to mark GAM as a trusted app
Additional steps may be required if errors are encountered.
* [Authorize a super admin to create projects](#authorize-a-super-admin-to-create-projects)
* [Authorize Service Account Key Uploads](#authorize-service-account-key-uploads)
* [Authorize GAM to create projects](#authorize-gam-to-create-projects)
## Headless computers and Cloud Shells
With many thanks to Jay, `gam oauth create` now uses a new client access authentication flow
as required by Google for headless computers/cloud shells; this is required as of February 28, 2022.
@@ -199,8 +206,51 @@ perform these steps and then retry the create project command.
* Click in the Select a role box
* Type project creator in the Filter box
* Click Project Creator
* Click + Add Another Role
* Type organization policy administrator in the Filter box
* Click Orgainzation Policy Administrator
* Click Save
## Authorize Service Account Key Uploads
If you try to create a project and get an error saying that Constraint `constraints/iam.disableServiceAccountKeyUpload violated for service account projects/gam-project-xxx`,
perform these steps and then you should be able to authorize and use your project.
* Login as an existing super admin at console.cloud.google.com
* In the upper left click the three lines to the left of Google Cloud and select IAM & Admin
* Under IAM & Admin select IAM
* Click the down arrow in the box to the right of Google Cloud
* Click the three dots at the right and select IAM/Permissions
* Now you should be at "Permissions for organization ..."
* Click on Grant Access
* Enter the new admin address in Principals
* Click in the Select a role box
* Type orgpolicy.policies.update in the Filter box
* Click Organization Policy Administrator
* Click Save
* In the upper left click the three lines to the left of Google Cloud and select IAM & Admin
* Under IAM & Admin select IAM
* Click the down arrow in the box to the right of Google Cloud
* Click the three dots at the right and select Manage Resources
* Click the three dots and the end of the line for the GAM project just created
* Click Settings
* Click Organization Policies in the left column
* Now you should be at "Policies for Gam Project"
* Click in the Filter box
* Enter iam.disableServiceAccountKeyUpload
* Click the three dots at the end of the Disable Service Account Key Upload
* Choose Edit policy
* Click Override parent's policy
* Click Add A Rule
* Select Enforcement/Off
* Click Done
* Click Set Policy
Wait a couple of minutes for the policy updates to complete and then do the following to upload the service account key:
```
gam upload sakey [admin <EmailAddress>]
```
## Authorize GAM to create projects
If you try to create a project and get an error saying "This app has been blocked on your domain for either being
insecure or non-edutational"; you'll have to mark the GAM Project Creation app as trusted.
@@ -404,60 +454,70 @@ writes the credentials into the file oauth2.txt.
```
gam oauth create
Select the authorized scopes by entering a number.
Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[*] 0) Calendar API (supports readonly)
[*] 1) Chrome Browser Cloud Management API (supports readonly)
[*] 2) Chrome Management API - Telemetry read only
[*] 3) Chrome Management API - read only
[*] 4) Chrome Policy API (supports readonly)
[*] 5) Chrome Printer Management API (supports readonly)
[*] 6) Chrome Version History API
[*] 7) Classroom API - Course Announcements (supports readonly)
[*] 8) Classroom API - Course Topics (supports readonly)
[*] 9) Classroom API - Course Work/Materials (supports readonly)
[*] 10) Classroom API - Course Work/Submissions (supports readonly)
[*] 11) Classroom API - Courses (supports readonly)
[*] 12) Classroom API - Profile Emails
[*] 13) Classroom API - Profile Photos
[*] 14) Classroom API - Rosters (supports readonly)
[*] 15) Classroom API - Student Guardians (supports readonly)
[*] 16) Cloud Identity Groups API (supports readonly)
[*] 17) Cloud Storage (Vault Export - read only)
[*] 18) Contact Delegation API (supports readonly)
[*] 19) Contacts API - Domain Shared and Users and GAL
[*] 20) Data Transfer API (supports readonly)
[*] 21) Directory API - Chrome OS Devices (supports readonly)
[*] 22) Directory API - Customers (supports readonly)
[*] 23) Directory API - Domains (supports readonly)
[*] 24) Directory API - Groups (supports readonly)
[*] 25) Directory API - Mobile Devices Directory (supports readonly and action)
[*] 26) Directory API - Organizational Units (supports readonly)
[*] 27) Directory API - Resource Calendars (supports readonly)
[*] 28) Directory API - Roles (supports readonly)
[*] 29) Directory API - User Schemas (supports readonly)
[*] 30) Directory API - User Security
[*] 31) Directory API - Users (supports readonly)
[*] 32) Email Audit API
[*] 33) Groups Migration API
[*] 34) Groups Settings API
[*] 35) License Manager API
[*] 36) People API (supports readonly)
[*] 37) People Directory API - read only
[ ] 38) Pub / Sub API
[*] 39) Reports API - Audit Reports
[*] 40) Reports API - Usage Reports
[ ] 41) Reseller API
[*] 42) Site Verification API
[*] 43) Sites API
[*] 44) Vault API (supports readonly)
[*] 2) Chrome Management API - AppDetails read only
[*] 3) Chrome Management API - Telemetry read only
[*] 4) Chrome Management API - read only
[*] 5) Chrome Policy API (supports readonly)
[*] 6) Chrome Printer Management API (supports readonly)
[*] 7) Chrome Version History API
[*] 8) Classroom API - Course Announcements (supports readonly)
[*] 9) Classroom API - Course Topics (supports readonly)
[*] 10) Classroom API - Course Work/Materials (supports readonly)
[*] 11) Classroom API - Course Work/Submissions (supports readonly)
[*] 12) Classroom API - Courses (supports readonly)
[*] 13) Classroom API - Profile Emails
[*] 14) Classroom API - Profile Photos
[*] 15) Classroom API - Rosters (supports readonly)
[*] 16) Classroom API - Student Guardians (supports readonly)
[ ] 17) Cloud Channel API (supports readonly)
[*] 18) Cloud Identity - Inbound SSO Settings (supports readonly)
[*] 19) Cloud Identity Groups API (supports readonly)
[*] 20) Cloud Identity OrgUnits API (supports readonly)
[*] 21) Cloud Identity User Invitations API (supports readonly)
[ ] 22) Cloud Storage API (Read Only, Vault/Takeout Download, Cloud Storage)
[ ] 23) Cloud Storage API (Read/Write, Vault/Takeout Copy/Download, Cloud Storage)
[*] 24) Contact Delegation API (supports readonly)
[*] 25) Contacts API - Domain Shared Contacts and GAL
[*] 26) Data Transfer API (supports readonly)
[*] 27) Directory API - Chrome OS Devices (supports readonly)
[*] 28) Directory API - Customers (supports readonly)
[*] 29) Directory API - Domains (supports readonly)
[*] 30) Directory API - Groups (supports readonly)
[*] 31) Directory API - Mobile Devices Directory (supports readonly and action)
[*] 32) Directory API - Organizational Units (supports readonly)
[*] 33) Directory API - Resource Calendars (supports readonly)
[*] 34) Directory API - Roles (supports readonly)
[*] 35) Directory API - User Schemas (supports readonly)
[*] 36) Directory API - User Security
[*] 37) Directory API - Users (supports readonly)
[ ] 38) Email Audit API
[*] 39) Groups Migration API
[*] 40) Groups Settings API
[*] 41) License Manager API
[*] 42) People API (supports readonly)
[*] 43) People Directory API - read only
[ ] 44) Pub / Sub API
[*] 45) Reports API - Audit Reports
[*] 46) Reports API - Usage Reports
[ ] 47) Reseller API
[*] 48) Site Verification API
[ ] 49) Sites API
[*] 50) Vault API (supports readonly)
s) Select all scopes
u) Unselect all scopes
e) Exit without changes
c) Continue to authorization
Please enter 0-42[a|r] or s|u|e|c:
Select an unselected scope [ ] by entering a number; yields [*]
For scopes that support readonly, enter a number and an 'r' to grant read-only access; yields [R]
For scopes that support action, enter a number and an 'a' to grant action-only access; yields [A]
Clear read-only access [R] or action-only access [A] from a scope by entering a number; yields [*]
Unselect a selected scope [*] by entering a number; yields [ ]
Select all default scopes by entering an 's'; yields [*] for default scopes, [ ] for others
Unselect all scopes by entering a 'u'; yields [ ] for all scopes
Exit without changes/authorization by entering an 'e'
Continue to authorization by entering a 'c'
Note, if all scopes are selected, Google will probably generate an authorization error
Please enter 0-50[a|r] or s|u|e|c: c
Enter your Google Workspace admin email address? admin@domain.com
@@ -487,60 +547,70 @@ writes the credentials into the file `oauth2.txt`.
```
gam oauth update
Select the authorized scopes by entering a number.
Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[*] 0) Calendar API (supports readonly)
[*] 1) Chrome Browser Cloud Management API (supports readonly)
[*] 2) Chrome Management API - Telemetry read only
[*] 3) Chrome Management API - read only
[*] 4) Chrome Policy API (supports readonly)
[*] 5) Chrome Printer Management API (supports readonly)
[*] 6) Chrome Version History API
[*] 7) Classroom API - Course Announcements (supports readonly)
[*] 8) Classroom API - Course Topics (supports readonly)
[*] 9) Classroom API - Course Work/Materials (supports readonly)
[*] 10) Classroom API - Course Work/Submissions (supports readonly)
[*] 11) Classroom API - Courses (supports readonly)
[*] 12) Classroom API - Profile Emails
[*] 13) Classroom API - Profile Photos
[*] 14) Classroom API - Rosters (supports readonly)
[*] 15) Classroom API - Student Guardians (supports readonly)
[*] 16) Cloud Identity Groups API (supports readonly)
[*] 17) Cloud Storage (Vault Export - read only)
[*] 18) Contact Delegation API (supports readonly)
[*] 19) Contacts API - Domain Shared and Users and GAL
[*] 20) Data Transfer API (supports readonly)
[*] 21) Directory API - Chrome OS Devices (supports readonly)
[*] 22) Directory API - Customers (supports readonly)
[*] 23) Directory API - Domains (supports readonly)
[*] 24) Directory API - Groups (supports readonly)
[*] 25) Directory API - Mobile Devices Directory (supports readonly and action)
[*] 26) Directory API - Organizational Units (supports readonly)
[*] 27) Directory API - Resource Calendars (supports readonly)
[*] 28) Directory API - Roles (supports readonly)
[*] 29) Directory API - User Schemas (supports readonly)
[*] 30) Directory API - User Security
[*] 31) Directory API - Users (supports readonly)
[*] 32) Email Audit API
[*] 33) Groups Migration API
[*] 34) Groups Settings API
[*] 35) License Manager API
[*] 36) People API (supports readonly)
[*] 37) People Directory API - read only
[ ] 38) Pub / Sub API
[*] 39) Reports API - Audit Reports
[*] 40) Reports API - Usage Reports
[ ] 41) Reseller API
[*] 42) Site Verification API
[*] 43) Sites API
[*] 44) Vault API (supports readonly)
[*] 2) Chrome Management API - AppDetails read only
[*] 3) Chrome Management API - Telemetry read only
[*] 4) Chrome Management API - read only
[*] 5) Chrome Policy API (supports readonly)
[*] 6) Chrome Printer Management API (supports readonly)
[*] 7) Chrome Version History API
[*] 8) Classroom API - Course Announcements (supports readonly)
[*] 9) Classroom API - Course Topics (supports readonly)
[*] 10) Classroom API - Course Work/Materials (supports readonly)
[*] 11) Classroom API - Course Work/Submissions (supports readonly)
[*] 12) Classroom API - Courses (supports readonly)
[*] 13) Classroom API - Profile Emails
[*] 14) Classroom API - Profile Photos
[*] 15) Classroom API - Rosters (supports readonly)
[*] 16) Classroom API - Student Guardians (supports readonly)
[ ] 17) Cloud Channel API (supports readonly)
[*] 18) Cloud Identity - Inbound SSO Settings (supports readonly)
[*] 19) Cloud Identity Groups API (supports readonly)
[*] 20) Cloud Identity OrgUnits API (supports readonly)
[*] 21) Cloud Identity User Invitations API (supports readonly)
[ ] 22) Cloud Storage API (Read Only, Vault/Takeout Download, Cloud Storage)
[ ] 23) Cloud Storage API (Read/Write, Vault/Takeout Copy/Download, Cloud Storage)
[*] 24) Contact Delegation API (supports readonly)
[*] 25) Contacts API - Domain Shared Contacts and GAL
[*] 26) Data Transfer API (supports readonly)
[*] 27) Directory API - Chrome OS Devices (supports readonly)
[*] 28) Directory API - Customers (supports readonly)
[*] 29) Directory API - Domains (supports readonly)
[*] 30) Directory API - Groups (supports readonly)
[*] 31) Directory API - Mobile Devices Directory (supports readonly and action)
[*] 32) Directory API - Organizational Units (supports readonly)
[*] 33) Directory API - Resource Calendars (supports readonly)
[*] 34) Directory API - Roles (supports readonly)
[*] 35) Directory API - User Schemas (supports readonly)
[*] 36) Directory API - User Security
[*] 37) Directory API - Users (supports readonly)
[ ] 38) Email Audit API
[*] 39) Groups Migration API
[*] 40) Groups Settings API
[*] 41) License Manager API
[*] 42) People API (supports readonly)
[*] 43) People Directory API - read only
[ ] 44) Pub / Sub API
[*] 45) Reports API - Audit Reports
[*] 46) Reports API - Usage Reports
[ ] 47) Reseller API
[*] 48) Site Verification API
[ ] 49) Sites API
[*] 50) Vault API (supports readonly)
s) Select all scopes
u) Unselect all scopes
e) Exit without changes
c) Continue to authorization
Please enter 0-42[a|r] or s|u|e|c:
Select an unselected scope [ ] by entering a number; yields [*]
For scopes that support readonly, enter a number and an 'r' to grant read-only access; yields [R]
For scopes that support action, enter a number and an 'a' to grant action-only access; yields [A]
Clear read-only access [R] or action-only access [A] from a scope by entering a number; yields [*]
Unselect a selected scope [*] by entering a number; yields [ ]
Select all default scopes by entering an 's'; yields [*] for default scopes, [ ] for others
Unselect all scopes by entering a 'u'; yields [ ] for all scopes
Exit without changes/authorization by entering an 'e'
Continue to authorization by entering a 'c'
Note, if all scopes are selected, Google will probably generate an authorization error
Please enter 0-50[a|r] or s|u|e|c: c
Enter your Google Workspace admin email address? admin@domain.com
@@ -790,10 +860,26 @@ delete a service account key for a distributed copy of an `oauth2service.json` f
that user's service account access.
You can disable your current Service Account key if you specify the `doit` argument. This is your
acknowledgement that you will have to manually create a new Service Account key in the Developer's Console.
acknowledgement that you will have to manually create a new Service Account key in the Developer's Console
or upload a new key with the `gam upload sakey` command.
```
gam delete sakeys <ServiceAccountKeyList>+ [doit]
```
## Upload a Service Account key to a service account with no keys
There are two cases where you will use this command:
* Your workspace is configured to disable service account private key uploads and you are creating a project.
* All of your service account keys have been deleted, either manually or with the `gam delete sakeys` command.
The `oauth2service.json` file is updated with the new private key. If you had previously distributed
any `oauth2service.json` file to other users, you must redistribute the updated file with the new key.
```
gam upload sakey [admin <EmailAddress>]
(algorithm KEY_ALG_RSA_1024|KEY_ALG_RSA_2048)|
((localkeysize 1024|2048|4096 [validityhours <Number>])|
(yubikey yubikey_pin yubikey_slot AUTHENTICATION
yubikey_serialnumber <Number>
[localkeysize 1024|2048|4096])
```
## Display Service Account keys
There are system keys and user keys; user keys are what Gam uses; GCP uses system keys.
@@ -817,24 +903,38 @@ By default, the following scopes are verified:
```
https://mail.google.com/
https://sites.google.com/feeds
https://www.google.com/m8/feeds
https://www.googleapis.com/auth/analytics.readonly
https://www.googleapis.com/auth/apps.alerts
https://www.googleapis.com/auth/calendar
https://www.googleapis.com/auth/chat.delete
https://www.googleapis.com/auth/chat.memberships
https://www.googleapis.com/auth/chat.messages
https://www.googleapis.com/auth/chat.spaces
https://www.googleapis.com/auth/classroom.announcements
https://www.googleapis.com/auth/classroom.coursework.students
https://www.googleapis.com/auth/classroom.courseworkmaterials
https://www.googleapis.com/auth/classroom.profile.emails
https://www.googleapis.com/auth/classroom.profile.photos
https://www.googleapis.com/auth/classroom.rosters
https://www.googleapis.com/auth/classroom.topics
https://www.googleapis.com/auth/cloud-identity
https://www.googleapis.com/auth/cloud-platform
https://www.googleapis.com/auth/cloudprint
https://www.googleapis.com/auth/contacts
https://www.googleapis.com/auth/contacts.other.readonly
https://www.googleapis.com/auth/datastudio
https://www.googleapis.com/auth/directory.readonly
https://www.googleapis.com/auth/documents
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/drive.activity
https://www.googleapis.com/auth/drive.admin.labels
https://www.googleapis.com/auth/drive.labels
https://www.googleapis.com/auth/gmail.modify
https://www.googleapis.com/auth/gmail.settings.basic
https://www.googleapis.com/auth/gmail.settings.sharing
https://www.googleapis.com/auth/keep
https://www.googleapis.com/auth/spreadsheets
https://www.googleapis.com/auth/tasks
https://www.googleapis.com/auth/userinfo.profile
```
This scope is verified when `user_service_account_access_only = true` in `gam.cfg`.
```
@@ -862,6 +962,118 @@ gam <UserTypeEntity> update serviceaccount (scope|scopes <APIScopeURLList>)*
* `<UserTypeEntity>` - Typically `user <EmailAddress>`, a non-Google Workspace administrator.
* `scopes <APIScopeURLList>` - Verify/enable service account access for a set of specific scopes rather than selecting the scopes.
```
gam user user@domain.com update serviceaccount
[*] 0) AlertCenter API
[*] 1) Analytics API - read only
[*] 2) Analytics Admin API - read only
[*] 3) Calendar API (supports readonly)
[*] 4) Chat API - Memberships (supports readonly)
[*] 5) Chat API - Messages (supports readonly)
[*] 6) Chat API - Spaces (supports readonly)
[*] 7) Chat API - Spaces Delete
[*] 8) Classroom API - Course Announcements (supports readonly)
[*] 9) Classroom API - Course Topics (supports readonly)
[*] 10) Classroom API - Course Work/Materials (supports readonly)
[*] 11) Classroom API - Course Work/Submissions (supports readonly)
[*] 12) Classroom API - Profile Emails
[*] 13) Classroom API - Profile Photos
[*] 14) Classroom API - Rosters (supports readonly)
[*] 15) Cloud Identity Devices API (supports readonly)
[*] 16) Cloud Resource Manager API v3
[*] 17) Docs API (supports readonly)
[*] 18) Drive API (supports readonly)
[*] 19) Drive API - todrive
[*] 20) Drive Activity API v2 - must pair with Drive API
[*] 21) Drive Labels API v2beta - Admin (supports readonly)
[*] 22) Drive Labels API v2beta - User (supports readonly)
[*] 23) Forms API
[*] 24) Gmail API - Basic Settings (Filters,IMAP, Language, POP, Vacation) - read/write, Sharing Settings (Delegates, Forwarding, SendAs) - read
[*] 25) Gmail API - Full Access (Labels, Messages)
[*] 26) Gmail API - Full Access (Labels, Messages) except delete message
[ ] 27) Gmail API - Full Access - read only
[ ] 28) Gmail API - Send Messages - including todrive
[*] 29) Gmail API - Sharing Settings (Delegates, Forwarding, SendAs) - write
[*] 30) Identity and Access Management API
[*] 31) Keep API (supports readonly)
[*] 32) Looker Studio API (supports readonly)
[*] 33) OAuth2 API
[*] 34) People API (supports readonly)
[*] 35) People API - Other Contacts - read only
[*] 36) People Directory API - read only
[*] 37) Sheets API (supports readonly)
[*] 38) Sheets API - todrive
[*] 39) Sites API
[*] 40) Tasks API (supports readonly)
[ ] 41) Youtube API - read only
Select an unselected scope [ ] by entering a number; yields [*]
For scopes that support readonly, enter a number and an 'r' to grant read-only access; yields [R]
For scopes that support action, enter a number and an 'a' to grant action-only access; yields [A]
Clear read-only access [R] or action-only access [A] from a scope by entering a number; yields [*]
Unselect a selected scope [*] by entering a number; yields [ ]
Select all default scopes by entering an 's'; yields [*] for default scopes, [ ] for others
Unselect all scopes by entering a 'u'; yields [ ] for all scopes
Exit without changes/authorization by entering an 'e'
Continue to authorization by entering a 'c'
Please enter 0-41[a|r] or s|u|e|c: c
System time status
Your system time differs from admin.googleapis.com by less than 1 second PASS
Service Account Private Key Authentication
Authentication PASS
Service Account Private Key age; Google recommends rotating keys on a routine basis
Service Account Private Key age: 364 days WARN
Domain-wide Delegation authentication:, User: user@domain.com, Scopes: 34
https://mail.google.com/ PASS (1/34)
https://sites.google.com/feeds PASS (2/34)
https://www.googleapis.com/auth/analytics.readonly PASS (3/34)
https://www.googleapis.com/auth/apps.alerts PASS (4/34)
https://www.googleapis.com/auth/calendar PASS (5/34)
https://www.googleapis.com/auth/chat.delete PASS (6/34)
https://www.googleapis.com/auth/chat.memberships PASS (7/34)
https://www.googleapis.com/auth/chat.messages PASS (8/34)
https://www.googleapis.com/auth/chat.spaces PASS (9/34)
https://www.googleapis.com/auth/classroom.announcements PASS (10/34)
https://www.googleapis.com/auth/classroom.coursework.students PASS (11/34)
https://www.googleapis.com/auth/classroom.courseworkmaterials PASS (12/34)
https://www.googleapis.com/auth/classroom.profile.emails PASS (13/34)
https://www.googleapis.com/auth/classroom.profile.photos PASS (14/34)
https://www.googleapis.com/auth/classroom.rosters PASS (15/34)
https://www.googleapis.com/auth/classroom.topics PASS (16/34)
https://www.googleapis.com/auth/cloud-identity PASS (17/34)
https://www.googleapis.com/auth/cloud-platform PASS (18/34)
https://www.googleapis.com/auth/contacts PASS (19/34)
https://www.googleapis.com/auth/contacts.other.readonly PASS (20/34)
https://www.googleapis.com/auth/datastudio PASS (21/34)
https://www.googleapis.com/auth/directory.readonly PASS (22/34)
https://www.googleapis.com/auth/documents PASS (23/34)
https://www.googleapis.com/auth/drive PASS (24/34)
https://www.googleapis.com/auth/drive.activity PASS (25/34)
https://www.googleapis.com/auth/drive.admin.labels FAIL (26/34)
https://www.googleapis.com/auth/drive.labels FAIL (27/34)
https://www.googleapis.com/auth/gmail.modify PASS (28/34)
https://www.googleapis.com/auth/gmail.settings.basic PASS (29/34)
https://www.googleapis.com/auth/gmail.settings.sharing PASS (30/34)
https://www.googleapis.com/auth/keep PASS (31/34)
https://www.googleapis.com/auth/spreadsheets PASS (32/34)
https://www.googleapis.com/auth/tasks PASS (33/34)
https://www.googleapis.com/auth/userinfo.profile PASS (34/34)
Some scopes Failed!
To authorize them, please go to the following link in your browser:
https://admin.google.com/ac/owl/domainwidedelegation?clientScopeToAdd=https://mail.google.com/,...
You will be directed to the Google Workspace admin console Security > API Controls > Domain-wide Delegation page
The "Add a new Client ID" box will open
Make sure that "Overwrite existing client ID" is checked
Click AUTHORIZE
When the box closes you're done
After authorizing it may take some time for this test to pass so wait a few moments and then try this command again.
```
## Configure Limited access
You can configure GAM to allow users limited access to your domain via GAM.
You can limit both client and service account access.

View File

@@ -119,7 +119,7 @@
#7a4706|#8a1c0a|#994a64|#ffffff
<LanguageCode> ::=
ach|af|ag|ak|am|ar|az|be|bem|bg|bn|br|bs|ca|chr|ckb|co|crs|cs|cy|da|de|
ee|el|en|en-gb|en-us|eo|es|es-419|et|eu|fa|fi|fil|fo|fr|fr-ca|fy|
ee|el|en|en-ca|en-gb|en-us|eo|es|es-419|et|eu|fa|fi|fil|fo|fr|fr-ca|fy|
ga|gaa|gd|gl|gn|gu|ha|haw|he|hi|hr|ht|hu|hy|ia|id|ig|in|is|it|iw|ja|jw|
ka|kg|kk|km|kn|ko|kri|ku|ky|la|lg|ln|lo|loz|lt|lua|lv|
mfe|mg|mi|mk|ml|mn|mo|mr|ms|mt|my|ne|nl|nn|no|nso|ny|nyn|oc|om|or|
@@ -222,72 +222,6 @@
shortcut
<MimeTypeName> ::= application|audio|font|image|message|model|multipart|text|video
<MimeType> ::= <MimeTypeShortcut>|(<MimeTypeName>/<String>)
<ProductID> ::=
nv:<String> |
101001 |
101005 |
101031 |
101033 |
101034 |
101035 |
101036 |
101037 |
101039 |
101040 |
Google-Apps |
Google-Chrome-Device-Management |
Google-Drive-storage |
Google-Vault
<SKUID> ::=
nv:<String>:<String> |
20gb | drive20gb | googledrivestorage20gb | Google-Drive-storage-20GB |
50gb | drive50gb | googledrivestorage50gb | Google-Drive-storage-50GB |
200gb | drive200gb | googledrivestorage200gb | Google-Drive-storage-200GB |
400gb | drive400gb | googledrivestorage400gb | Google-Drive-storage-400GB |
1tb | drive1tb | googledrivestorage1tb | Google-Drive-storage-1TB |
2tb | drive2tb | googledrivestorage2tb | Google-Drive-storage-2TB |
4tb | drive4tb | googledrivestorage4tb | Google-Drive-storage-4TB |
8tb | drive8tb | googledrivestorage8tb | Google-Drive-storage-8TB |
16tb | drive16tb | googledrivestorage16tb | Google-Drive-storage-16TB |
assuredcontrols | 1010390001 |
bce | beyondcorp | beyondcorpenterprise | 1010400001 |
cdm | chrome | googlechromedevicemanagement | Google-Chrome-Device-Management |
cloudidentity | identity | 1010010001 |
cloudidentitypremium | identitypremium | 1010050001 |
cloudsearch | 1010350001 |
gsuitebasic | gafb | gafw | basic | Google-Apps-For-Business |
gsuitebusiness | gau | gsb | unlimited | Google-Apps-Unlimited |
gsuitebusinessarchived | gsbau | businessarchived | 1010340002 |
gsuiteenterprisearchived | gseau | enterprisearchived | 1010340001 |
gsuiteenterpriseeducation | gsefe | e4e | 1010310002 |
gsuiteenterpriseeducationstudent | gsefes | e4es | 1010310003 |
gsuitegov | gafg | gsuitegovernment | Google-Apps-For-Government |
gsuitelite | gal | gsl | lite | Google-Apps-Lite |
gwep | workspaceeducationplus | 1010310008 |
gwepstaff | workspaceeducationplusstaff | 1010310009 |
gwepstudent | workspaceeducationplusstudent | 1010310010 |
gwes | workspaceeducationstandard | 1010310005 |
gwesstaff | workspaceeducationstandardstaff | 1010310006 |
gwesstudent | workspaceeducationstandardstudent | 1010310007 |
gwetlu | workspaceeducationupgrade | 1010370001 |
meetdialing | googlemeetglobaldialing | 1010360001 |
postini | gams | gsuitegams | gsuitepostini | gsuitemessagesecurity | Google-Apps-For-Postini |
standard | free | Google-Apps |
vault | googlevault | Google-Vault |
vfe | googlevaultformeremployee | Google-Vault-Former-Employee |
voicepremier | gvpremier | googlevoicepremier | 1010330002 |
voicestandard | gvstandard | googlevoicestandard | 1010330004 |
voicestarter | gvstarter | googlevoicestarter | 1010330003 |
wsbizplus | workspacebusinessplus | 1010020025 |
wsbizplusarchived | workspacebusinessplusarchived | 1010340003 |
wsbizstan | workspacebusinessstandard | 1010020028 |
wsbizstarter | workspacebusinessstarter | wsbizstart | 1010020027 |
wsentess | workspaceenterpriseessentials | 1010060003 |
wsentplus | workspaceenterpriseplus | gae | gse | enterprise | gsuiteenterprise | 1010020020 |
wsentstan | workspaceenterprisestandard | 1010020026 |
wsentstanarchived | workspaceenterprisestandardarchived | 1010340004 |
wsess | workspaceesentials | gsuiteessentials | essentials | d4e | driveenterprise | drive4enterprise | 1010060001 |
wsflw | workspacefrontline | workspacefrontlineworker | 1010020030
```
## Items built from primitives
```
@@ -346,7 +280,7 @@
<ChannelCustomerID> ::= <String>
<ChatMember> ::= spaces/<String>/members/<String>
<ChatMessage> ::= spaces/<String>/messages/<String>
<ChatSpace> ::= spaces/<String> | <String>
<ChatSpace> ::= spaces/<String> | space <String> | space spaces/<String>
<ChatThread> ::= spaces/<String>/threads/<String>
<ClassroomInvitationID> ::= <String>
<ClientID> ::= <String>
@@ -421,6 +355,7 @@
<DriveLabelFieldID> ::= <String>
<DriveLabelSelectionID> ::= <String>
<DriveLabelName> ::= labels/<DriveLabelID>[@latest|@published|@<Number>]
<DriveLabelPermissionName> ::= labels/<DriveLabelID>[@latest|@published|@<Number>]/permissions/(audiences|groups|people)/<String>
<EmailAddress> ::= <String>@<DomainName>
<EmailItem> ::= <EmailAddress>|<UniqueID>|<String>
<EmailReplacement> ::= <String>
@@ -456,6 +391,11 @@
<Marker> ::= <String>
<MatterItem> ::= <UniqueID>|<String>
<MatterState> ::= open|closed|deleted
<MessageContent> ::=
(message|textmessage|htmlmessage <String>)|
(file|textfile|htmlfile <FileName> [charset <Charset>])|
(gdoc|ghtml <UserGoogleDoc>)|
(gcsdoc|gcshtml <StorageBucketObjectName>)
<MessageID> ::= <String>
<Namespace> ::= <String>
<NotesName> ::= notes/<String>
@@ -529,7 +469,7 @@
<ServiceAccountDisplayName> ::= <String>
Maximum of 100 characters
<ServiceAccountDescrition> ::= <String>
Maximumof 256 chcracters
Maximum of 256 chcracters
<ServiceAccountEmail> ::= <ServiceAccountName>@<ProjectID>.iam.gserviceaccount.com
<ServiceAccountUniqueID> ::= <Number>
<ServiceAccountKey> ::= <String>
@@ -584,8 +524,10 @@
(tdlocale <Locale>)|
(tdnobrowser [<Boolean>])|
(tdnoemail [<Boolean>])|
(tdnoescapechar [<Boolean>])|
(tdparent (id:<DriveFolderID>)|<DriveFolderName>)|
(tdshare <EmailAddress> commenter|reader|writer)|
(tdretaintitle [<Boolean>])|
(tdshare <EmailAddress> commenter|reader|writer)*|
(tdsheet (id:<Number>)|<String>)|
(tdsheettimestamp [<Boolean>] [tdsheettimeformat <String>])
(tdsheettitle <String>)|

View File

@@ -41,6 +41,7 @@ Batch files can contain the following types of lines:
* GAM waits for all running GAM commands to complete
* GAM prints \<String\> and waits for the user to press any key
* GAM continues
* sleep \<Integer\> - Batch processing will suspend for \<Integer\> seconds before the next command line is processed
* print \<String\> - Print \<String\> on stderr
* set \<KeywordString\> \<ValueString\>
* Subsequent lines will have %\<KeywordString\>% replaced with \<ValueString\>
@@ -71,13 +72,13 @@ gam redirect stdout ./NewStudents.out redirect stderr ./NewStudents.err tbatch N
gam csv <FileName>|-|(gsheet <UserGoogleSheet>)|(gdoc <UserGoogleDoc>) [charset <Charset>] [warnifnodata]
[columndelimiter <Character>] [noescapechar <Boolean>] [quotechar <Character>] [fields <FieldNameList>]
(matchfield|skipfield <FieldName> <RegularExpression>)* [showcmds [<Boolean>]]
[maxrows <Integer>]
[skiprows <Integer>] [maxrows <Integer>]
gam <GAMArgumentList>
gam loop <FileName>|-|(gsheet <UserGoogleSheet>)|(gdoc <UserGoogleDoc>) [charset <Charset>] [warnifnodata]
[columndelimiter <Character>] [noescapechar <Boolean>] [quotechar <Character>] [fields <FieldNameList>]
(matchfield|skipfield <FieldName> <RegularExpression>)* [showcmds [<Boolean>]]
[maxrows <Integer>]
[skiprows <Integer>] [maxrows <Integer>]
gam <GAMArgumentList>
```
* `gam csv` - Use parallel processing
@@ -92,7 +93,10 @@ gam loop <FileName>|-|(gsheet <UserGoogleSheet>)|(gdoc <UserGoogleDoc>) [charset
* `fields <FieldNameList>` - The column headings of a CSV file that does not contain column headings.
* `(matchfield|skipfield <FieldName> <RegularExpression>)*` - The criteria to select rows from the CSV file; can be used multiple times; if not specified, all rows are selected
* `showcmds` - Write `timestamp,command number/number of commands,command` to stderr when each command starts; write `timestamp, command number/numberof commands,complete` to stderr when command completes
* `maxrows <Integer>` - Limit the number of filtered rows processed from the CSV file/Google Sheet.
* `skiprows <Integer>` - Skip filtered rows from the CSV file/Google Sheet.
* `skiprows 0` - All rows are processed, this is the default
* `skiprows N` - The first N filtered rows are skipped
* `maxrows <Integer>` - Limit the number of filtered rows processed from the CSV file/Google Sheet after any skipped rows.
* `maxrows 0` - All rows are processed, this is the default
* `maxrows N` - N filtered rows are processed

View File

@@ -3,6 +3,10 @@
- [Definitions](#definitions)
- [Quoting rules](#quoting-rules)
- [Column row filtering](#column-row-filtering)
- [Field names](#field-names)
- [Inclusive filters](#inclusive-filters)
- [Exclusive filters](#exclusive-filters)
- [Matches](#matches)
- [Column row limiting](#column-row-limiting)
- [Saving filters in gam.cfg](#saving-filters-in-gamcfg)
- [Validate filters](#validate-filters)
@@ -39,28 +43,30 @@ These filters can be used alone or in conjunction with the `matchfield|skipfield
<FieldNameFilter> :: = <RegularExpression>
<RowValueFilter> ::=
[(any|all):]count<Operator><Number>|
[(any|all):]countrange=<Number>/<Number>|
[(any|all):]countrange!=<Number>/<Number>|
[(any|all):]date<Operator><Date>|
[(any|all):]daterange=<Date>/<Date>|
[(any|all):]daterange!=<Date>/<Date>|
[(any|all):]length<Operator><Number>|
[(any|all):]lengthrange=<Number>/<Number>|
[(any|all):]lengthrange!=<Number>/<Number>|
[(any|all):]text<Operator><String>|
[(any|all):]textrange=<String>/<String>|
[(any|all):]textrange!=<String>/<String>|
[(any|all):]time<Operator><Time>|
[(any|all):]timerange=<Time>/<Time>|
[(any|all):]timerange!=<Time>/<Time>|
[(any|all):]boolean:<Boolean>|
[(any|all):]regex:<RegularExpression>|
[(any|all):]regexcs:<RegularExpression>|
[(any|all):]count<Operator><Number>|
[(any|all):]countrange!=<Number>/<Number>|
[(any|all):]countrange=<Number>/<Number>|
[(any|all):]data:<DataSelector>|
[(any|all):]date<Operator><Date>|
[(any|all):]daterange!=<Date>/<Date>|
[(any|all):]daterange=<Date>/<Date>|
[(any|all):]length<Operator><Number>|
[(any|all):]lengthrange!=<Number>/<Number>|
[(any|all):]lengthrange=<Number>/<Number>|
[(any|all):]notdata:<DataSelector>|
[(any|all):]notregex:<RegularExpression>|
[(any|all):]notregexcs:<RegularExpression>|
[(any|all):]data:<DataSelector>|
[(any|all):]notdata:<DataSelector>|
[(any|all):]regex:<RegularExpression>|
[(any|all):]regexcs:<RegularExpression>|
[(any|all):]text<Operator><String>|
[(any|all):]textrange!=<String>/<String>|
[(any|all):]textrange=<String>/<String>|
[(any|all):]time<Operator><Time>|
[(any|all):]timeofdayrange!=<Hour>:<Minute>/<Hour>:<Minute>|
[(any|all):]timeofdayrange=<Hour>:<Minute>/<Hour>:<Minute>|
[(any|all):]timerange!=<Time>/<Time>|
[(any|all):]timerange=<Time>/<Time>|
<RowValueFilterList> ::=
"'<FieldNameFilter>:<RowValueFilter>'(,'<FieldNameFilter>:<RowValueFilter>')*"
<RowValueFilterJSONList> ::=
@@ -156,11 +162,13 @@ In the case of `notregex|notregexcs|notdata`, the filter matches if some (not al
If neither `any` or `all` is explicitly specified, `any` is the default.
These are the row value filter types:
* `boolean:<Boolean>` - Used on fields with Boolean values; a blank field is considered False
* `count<Operator><Number>` - Used on fields with numbers; a blank field will not match
* `countrange=<Number>/<Number>` - Used on fields with numbers; a blank field will not match
* The field value must be `>=` the left `<Number>` and `<=` the right `<Number>`
* `countrange!=<Number>/<Number>` - Used on fields with numbers; a blank field will not match
* The field value must be `<` the left `<Number>` or `>` the right `<Number>`
* `data:<DataSelector>` - Used on fields with text; field value must match some value in `<DataSelector>`; case sensitive
* `date<Operator><Date>` - Used on fields with dates or times; only the date portion of a time field is compared; a blank field will not match
* `daterange=<Date>/<Date>` - Used on fields with dates or times; only the date portion of a time field is compared; a blank field will not match
* The field value must be `>=` the left `<Date>` and `<=` the right `<Date>`
@@ -171,23 +179,25 @@ These are the row value filter types:
* The field length must be `>=` the left `<Number>` and `<=` the right `<Number>`
* `lengthrange!=<Number>/<Number>` - Used on fields with strings; non string fields will not match
* The field length must be `<` the left `<Number>` or `>` the right `<Number>`
* `notdata:<DataSelector>` - Used on fields with text; field value must not match any value in `<DataSelector>`; case sensitive
* `notregex:<RegularExpression>` - Used on fields with text; field value must not match `<RegularExpression>`; case insensitive
* `notregexcs:<RegularExpression>` - Used on fields with text; field value must not match `<RegularExpression>`; case sensitive
* `regex:<RegularExpression>` - Used on fields with text; field value must match `<RegularExpression>`; case insensitive
* `regexcs:<RegularExpression>` - Used on fields with text; field value must match `<RegularExpression>`; case sensitive
* `text<Operator><String>` - Used on fields with text
* `textrange=<String>/<String>` - Used on fields with strings
* The field value must be `>=` the left `<String>` and `<=` the right `<String>`
* `textrange!=<String>/<String>` - Used on fields with strings
* The field value must be `<` the left `<String>` or `>` the right `<String>`
* `time<Operator><Time>` - Used on fields with times; a blank field will not match
* `timeofdayrange=<Hour>:<Minute>/<Hour>:<Minute>` - Used on fields with times; a blank field will not match
* The field value must be `>=` the left `<Hour>:<Minute>` and `<=` the right `<Hour>:<Minute>`
* `timeofdayrange!=<Hour>:<Minute>/<Hour>:<Minute>` - Used on fields with times; a blank field will not match
* The field value must be `<` the left `<Hour>:<Minute>` or `>` the right `<Hour>:<Minute>`
* `timerange=<Time>/<Time>` - Used on fields with times; a blank field will not match
* The field value must be `>=` the left `<Time>` and `<=` the right `<Time>`
* `timerange!=<Time>/<Time>` - Used on fields with times; a blank field will not match
* The field value must be `<` the left `<Time>` or `>` the right `<Time>`
* `boolean:<Boolean>` - Used on fields with Boolean values; a blank field is considered False
* `regex:<RegularExpression>` - Used on fields with text; field value must match `<RegularExpression>`; case insensitive
* `regexcs:<RegularExpression>` - Used on fields with text; field value must match `<RegularExpression>`; case sensitive
* `notregex:<RegularExpression>` - Used on fields with text; field value must not match `<RegularExpression>`; case insensitive
* `notregexcs:<RegularExpression>` - Used on fields with text; field value must not match `<RegularExpression>`; case sensitive
* `data:<DataSelector>` - Used on fields with text; field value must match some value in `<DataSelector>`; case sensitive
* `notdata:<DataSelector>` - Used on fields with text; field value must not match any value in `<DataSelector>`; case sensitive
### **Change in behavior.**
In versions prior to `5.12.00`, `regex:<RegularExpression>` and `notregex:<RegularExpression>` were processed in a case sensitive manner;

View File

@@ -4,6 +4,10 @@
- [Quoting rules](#quoting-rules)
- [Column header filtering](#column-header-filtering)
- [Column row filtering](#column-row-filtering)
- [Field names](#field-names)
- [Inclusive filters](#inclusive-filters)
- [Exclusive filters](#exclusive-filters)
- [Matches](#matches)
- [Column row limiting](#column-row-limiting)
- [Saving filters in gam.cfg](#saving-filters-in-gamcfg)
@@ -44,28 +48,30 @@ on all platforms.
<FieldNameFilter> :: = <RegularExpression>
<ColumnFieldNameFilterList> ::= "<FieldNameFilter>(,<FieldNameFilter>)*"
<RowValueFilter> ::=
[(any|all):]count<Operator><Number>|
[(any|all):]countrange=<Number>/<Number>|
[(any|all):]countrange!=<Number>/<Number>|
[(any|all):]date<Operator><Date>|
[(any|all):]textrange=<String>/<String>|
[(any|all):]textrange!=<String>/<String>|
[(any|all):]daterange=<Date>/<Date>|
[(any|all):]daterange!=<Date>/<Date>|
[(any|all):]length<Operator><Number>|
[(any|all):]lengthrange=<Number>/<Number>|
[(any|all):]lengthrange!=<Number>/<Number>|
[(any|all):]text<Operator><String>|
[(any|all):]time<Operator><Time>|
[(any|all):]timerange=<Time>/<Time>|
[(any|all):]timerange!=<Time>/<Time>|
[(any|all):]boolean:<Boolean>|
[(any|all):]regex:<RegularExpression>|
[(any|all):]regexcs:<RegularExpression>|
[(any|all):]count<Operator><Number>|
[(any|all):]countrange!=<Number>/<Number>|
[(any|all):]countrange=<Number>/<Number>|
[(any|all):]data:<DataSelector>|
[(any|all):]date<Operator><Date>|
[(any|all):]daterange!=<Date>/<Date>|
[(any|all):]daterange=<Date>/<Date>|
[(any|all):]length<Operator><Number>|
[(any|all):]lengthrange!=<Number>/<Number>|
[(any|all):]lengthrange=<Number>/<Number>|
[(any|all):]notdata:<DataSelector>
[(any|all):]notregex:<RegularExpression>|
[(any|all):]notregexcs:<RegularExpression>|
[(any|all):]data:<DataSelector>|
[(any|all):]notdata:<DataSelector>
[(any|all):]regex:<RegularExpression>|
[(any|all):]regexcs:<RegularExpression>|
[(any|all):]text<Operator><String>|
[(any|all):]textrange!=<String>/<String>|
[(any|all):]textrange=<String>/<String>|
[(any|all):]time<Operator><Time>|
[(any|all):]timeofdayrange!=<Hour>:<Minute>/<Hour>:<Minute>|
[(any|all):]timeofdayrange=<Hour>:<Minute>/<Hour>:<Minute>|
[(any|all):]timerange!=<Time>/<Time>|
[(any|all):]timerange=<Time>/<Time>|
<RowValueFilterList> ::=
"'<FieldNameFilter>:<RowValueFilter>'(,'<FieldNameFilter>:<RowValueFilter>')*"
<RowValueFilterJSONList> ::=
@@ -214,11 +220,13 @@ In the case of `notregex|notregexcs|notdata`, the filter matches if some (not al
If neither `any` or `all` is explicitly specified, `any` is the default.
These are the row value filter types:
* `boolean:<Boolean>` - Used on fields with Boolean values; a blank field is considered False
* `count<Operator><Number>` - Used on fields with numbers; a blank field will not match
* `countrange=<Number>/<Number>` - Used on fields with numbers; a blank field will not match
* The field value must be `>=` the left `<Number>` and `<=` the right `<Number>`
* `countrange!=<Number>/<Number>` - Used on fields with numbers; a blank field will not match
* The field value must be `<` the left `<Number>` or `>` the right `<Number>`
* `data:<DataSelector>` - Used on fields with text; field value must match some value in `<DataSelector>`; case sensitive
* `date<Operator><Date>` - Used on fields with dates or times; only the date portion of a time field is compared; a blank field will not match
* `daterange=<Date>/<Date>` - Used on fields with dates or times; only the date portion of a time field is compared; a blank field will not match
* The field value must be `>=` the left `<Date>` and `<=` the right `<Date>`
@@ -229,23 +237,25 @@ These are the row value filter types:
* The field length must be `>=` the left `<Number>` and `<=` the right `<Number>`
* `lengthrange!=<Number>/<Number>` - Used on fields with strings; non string fields will not match
* The field length must be `<` the left `<Number>` or `>` the right `<Number>`
* `notdata:<DataSelector>` - Used on fields with text; field value must not match any value in `<DataSelector>`; case sensitive
* `notregex:<RegularExpression>` - Used on fields with text; field value must not match `<RegularExpression>`; case insensitive
* `notregexcs:<RegularExpression>` - Used on fields with text; field value must not match `<RegularExpression>`; case sensitive
* `regex:<RegularExpression>` - Used on fields with text; field value must match `<RegularExpression>`; case insensitive
* `regexcs:<RegularExpression>` - Used on fields with text; field value must match `<RegularExpression>`; case sensitive
* `text<Operator><String>` - Used on fields with text
* `textrange=<String>/<String>` - Used on fields with strings
* The field value must be `>=` the left `<String>` and `<=` the right `<String>`
* `textrange!=<String>/<String>` - Used on fields with strings
* The field value must be `<` the left `<String>` or `>` the right `<String>`
* `time<Operator><Time>` - Used on fields with times; a blank field will not match
* `timeofdayrange=<Hour>:<Minute>/<Hour>:<Minute>` - Used on fields with times; a blank field will not match
* The field value must be `>=` the left `<Hour>:<Minute>` and `<=` the right `<Hour>:<Minute>`
* `timeofdayrange!=<Hour>:<Minute>/<Hour>:<Minute>` - Used on fields with times; a blank field will not match
* The field value must be `<` the left `<Hour>:<Minute>` or `>` the right `<Hour>:<Minute>`
* `timerange=<Time>/<Time>` - Used on fields with times; a blank field will not match
* The field value must be `>=` the left `<Time>` and `<=` the right `<Time>`
* `timerange!=<Time>/<Time>` - Used on fields with times; a blank field will not match
* The field value must be `<` the left `<Time>` or `>` the right `<Time>`
* `boolean:<Boolean>` - Used on fields with Boolean values; a blank field is considered False
* `regex:<RegularExpression>` - Used on fields with text; field value must match `<RegularExpression>`; case insensitive
* `regexcs:<RegularExpression>` - Used on fields with text; field value must match `<RegularExpression>`; case sensitive
* `notregex:<RegularExpression>` - Used on fields with text; field value must not match `<RegularExpression>`; case insensitive
* `notregexcs:<RegularExpression>` - Used on fields with text; field value must not match `<RegularExpression>`; case sensitive
* `data:<DataSelector>` - Used on fields with text; field value must match some value in `<DataSelector>`; case sensitive
* `notdata:<DataSelector>` - Used on fields with text; field value must not match any value in `<DataSelector>`; case sensitive
### **Change in behavior.**
In versions prior to `5.12.00`, `regex:<RegularExpression>` and `notregex:<RegularExpression>` were processed in a case sensitive manner;

View File

@@ -196,6 +196,9 @@ Client access works when accessing Resource calendars.
<EventMatchProperty> ::=
(matchfield attendees <EmailAddressEntity>)|
(matchfield attendeesonlydomainlist <DomainNameList>)|
(matchfield attendeesdomainlist <DomainNameList>)|
(matchfield attendeesnotdomainlist <DomainNameList>)|
(matchfield attendeespattern <RegularExpression>)|
(matchfield attendeesstatus [<AttendeeAttendance>] [<AttendeeStatus>] <EmailAddressEntity>)|
(matchfield creatoremail <RegularExpression>)|
@@ -216,7 +219,6 @@ Client access works when accessing Resource calendars.
(event|events <EventIdList> |
<FileSelector> | <CSVFileSelector> | <CSVkmdSelector> | <CSVSubkeySelector> | <CSVDataSelector>)
See: https://github.com/taers232c/GAMADV-XTD3/wiki/Collections-of-Items
<EventSelectEntity> ::=
(<EventSelectProperty>+ <EventMatchProperty>*)
@@ -229,6 +231,7 @@ Client access works when accessing Resource calendars.
lavender|peacock|sage|tangerine|tomato
<PropertyKey> ::= <String>
<PropertyValue> ::= <String>
<TimeZone> ::= <String>
<EventAttribute> ::=
(allday <Date>)|
@@ -356,7 +359,13 @@ The Google Calendar API processes `<EventSelectProperty>*`; you may specify none
GAM processes `<EventMatchProperty>*`; you may specify none or multiple properties.
* `matchfield attendees <EmailAddressEntity>` - All of the attendees in `<EmailAddressEntity>` must be present
* `matchfield attendeespattern <RegularExpression>` - Some attendee must match `<RegularExpression>`
* `matchfield attendeesonlydomainlist <DomainNameList>` - All attendee's email addresses must be in a domain in `<DomainNameList>`
* For example, this lets you look for events with all attendees in your internal domains
* `matchfield attendeesdomainlist <DomainNameList>` - Some attendee's email address must be in a domain in `<DomainNameList>`
* For example, this lets you look for events with attendees in specific external domains
* `matchfield attendeesnotdomainlist <DomainNameList>` - Some attendee's email address must be in a domain not in `<DomainNameList>`
* For example, this lets you look for events with attendees not in your internal domains
* `matchfield attendeespattern <RegularExpression>` - Some attendee's email address must match `<RegularExpression>`
* `matchfield attendeesstatus [<AttendeeAttendance>] [<AttendeeStatus>] <EmailAddressEntity>` - All of the attendees in `<EmailAddressEntity>` must be present
and must have the specified values.
* `<AttendeeAttendance>` - Default is `required`

View File

@@ -23,7 +23,7 @@
- [Print a header row and fields for selected CrOS devices](#print-a-header-row-and-fields-for-selected-cros-devices)
- [Print a header row and fields for specified CrOS devices](#print-a-header-row-and-fields-for-specified-cros-devices)
- [Display Examples](#display-examples)
- [Display CrOS device count](#display-cros-device-count)
- [Display CrOS device counts](#display-cros-device-counts)
- [Print ChromeOS device activity](#print-chromeos-device-activity)
- [Print a header row and activity for selected CrOS devices](#print-a-header-row-and-activity-for-selected-cros-devices)
- [Print a header row and activity for specified CrOS devices](#print-a-header-row-and-activity-for-specified-cros-devices)
@@ -105,7 +105,9 @@ The second form is backwards compatible with Standard GAM and selection with `<C
annotatedlocation|location|
annotateduser|user|
autoupdateexpiration|
backlightinfo|
bootmode|
cpuinfo|
cpustatusreports|
deprovisionreason|
devicefiles|
@@ -551,6 +553,7 @@ gam print cros [todrive <ToDriveAttribute>*]
[start <Date>] [end <Date>] [listlimit <Number>]
[reverselists <CrOSListFieldNameList>]
[timerangeorder ascending|descending] [showdvrsfp]
(addcsvdata <FieldName> <String>)*
[sortheaders]
[formatjson [quotechar <Character>]]
```
@@ -593,6 +596,9 @@ otherwise, the remaining field names will appear in the order specified.
- `timerangeorder descending` - Change the `activetimeranges` order from ascending (oldest to newest) to descending (newest to oldest); this makes it easy to get the `N` most recent values with `timeranges listlimit N timerangeorder descending`.
- `showdvrsfp` - Display a field `diskVolumeReports.volumeInfo.storageFreePercentage` which is calculated as: `(diskVolumeReports.volumeInfo.storageFree/diskVolumeReports.volumeInfo.storageTotal)*100`
Add additional columns of data from the command line to the output
* `addcsvdata <FieldName> <String>`
By default, Gam displays the information as columns of fields; the following option causes the output to be in JSON format:
- `formatjson` - Display the fields in JSON format.
@@ -615,6 +621,7 @@ gam <CrOSTypeEntity> print cros [todrive <ToDriveAttribute>*]
[start <Date>] [end <Date>] [listlimit <Number>]
[reverselists <CrOSListFieldNameList>]
[timerangeorder ascending|descending] [showdvrsfp]
(addcsvdata <FieldName> <String>)*
[sortheaders]
[formatjson [quotechar <Character>]]
@@ -643,6 +650,9 @@ otherwise, the remaining field names will appear in the order specified.
- `timerangeorder descending` - Change the `activetimeranges` order from ascending (oldest to newest) to descending (newest to oldest); this makes it easy to get the `N` most recent values with `timeranges listlimit N timerangeorder descending`.
- `showdvrsfp` - Display a field `diskVolumeReports.volumeInfo.storageFreePercentage` which is calculated as: `(diskVolumeReports.volumeInfo.storageFree/diskVolumeReports.volumeInfo.storageTotal)*100`
Add additional columns of data from the command line to the output
* `addcsvdata <FieldName> <String>`
By default, Gam displays the information as columns of fields; the following option causes the output to be in JSON format:
- `formatjson` - Display the fields in JSON format.
@@ -683,10 +693,34 @@ Print information about CrOS devices synced between 45 days ago and 30 days ago:
gam print cros query "sync:#querytime1#..#querytime2#" querytime1 -45d querytime2 -30d
```
## Display CrOS device count
## Display CrOS device counts
Display the number of CrOS devices in an entity.
```
gam <CrOSTypeEntity> show count
gam <CrOSTypeEntity> print cros showitemcountonly
gam print cros select <CrOSTypeEntity> showitemcountonly
gam print cros
[(query <QueryCrOS>)|(queries <QueryCrOSList>) [querytime<String> <Time>]
[(limittoou|cros_ou <OrgUnitItem>)|(cros_ou_and_children <OrgUnitItem>)|
(cros_ous <OrgUnitList>)|(cros_ous_and_children <OrgUnitList>)]]
showitemcountonly
```
Example
```
$ gam print cros query "sync:..2020-01-01" showitemcountonly
Getting all CrOS Devices that match query (sync:..2020-01-01) for /, may take some time on a large Organizational Unit...
Got 77 CrOS Devices that matched query (sync:..2020-01-01) for /...
Got 77 CrOS Devices that matched query (sync:..2020-01-01)
77
```
The `Getting` and `Got` messages are written to stderr, the count is writtem to stdout.
To retrieve the count with `showitemcountonly`:
```
Linux/MacOS
count=$(gam print cros query "sync:..2020-01-01" showitemcountonly)
Windows PowerShell
count = & gam print cros query "sync:..2020-01-01" showitemcountonly
```
## Print ChromeOS device activity

View File

@@ -10,6 +10,7 @@
- [Manage course aliases](#manage-course-aliases)
- [Manage course topics](#manage-course-topics)
- [Display courses](#display-courses)
- [Display course counts](#display-course-counts)
- [Display course announcements](#display-course-announcements)
- [Display course materials](#display-course-materials)
- [Display course topics](#display-course-topics)
@@ -432,6 +433,33 @@ When using the `formatjson` option, double quotes are used extensively in the da
The `quotechar <Character>` option allows you to choose an alternate quote character, single quote for instance, that makes for readable/processable output.
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.
## Display course counts
Display the number of courses.
```
gam print courses
(course|class <CourseEntity>)*|([teacher <UserItem>] [student <UserItem>] [states <CourseStateList>])
[owneremailmatchpattern <RegularExpression>]
showitemcountonly
```
Example
```
$ gam print courses states active showitemcountonly
Getting all Courses that match query (Course State: ACTIVE), may take some time on a large Google Workspace Account...
Got 268 Courses...
Got 272 Courses...
Got 272 Courses...
272
```
The `Getting` and `Got` messages are written to stderr, the count is writtem to stdout.
To retrieve the count with `showitemcountonly`:
```
Linux/MacOS
count=$(gam print courses states active showitemcountonly)
Windows PowerShell
count = & gam print courses states active showitemcountonly
```
## Display course announcements
```
gam print course-announcements [todrive <ToDriveAttribute>*]

View File

@@ -3,9 +3,10 @@
- [Notes](#notes)
- [Definitions](#definitions)
- [Create classroom invitations](#create-classroom-invitations)
- [Accept classroom invitations](#accept-classroom-invitations)
- [Delete classroom invitations](#delete-classroom-invitations)
- [Accept classroom invitations by user](#accept-classroom-invitations-by-user)
- [Delete classroom invitations by user](#delete-classroom-invitations-by-user)
- [Display classroom invitations by user](#display-classroom-invitations-by-user)
- [Delete classroom invitations by course](#delete-classroom-invitations-by-course)
- [Display classroom invitations by course](#display-classroom-invitations-by-course)
## API documentation
@@ -24,8 +25,6 @@ Scope: https://www.googleapis.com/auth/classroom.rosters , Checked: FA
```
Follow the directions to authorize the Service Account scopes.
The Classroom API does not support inviting users from outside your domain.
## Definitions
```
<DomainName> ::= <String>(.<String>)+
@@ -49,12 +48,18 @@ The Classroom API does not support inviting users from outside your domain.
Invite users to classes.
```
gam <UserTypeEntity> create classroominvitation courses <CourseEntity> [role owner|student|teacher]
[adminaccess|asadmin] [csvformat] [todrive <ToDriveAttributes>*] [formatjson [quotechar <Character>]]
[adminaccess|asadmin]
[csv|csvformat] [todrive <ToDriveAttributes>*] [formatjson [quotechar <Character>]]
```
If `role` is not specified, `student` will be used.
You can only invite a co-teacher to be an owner of a course.
By default, classroom invitations are issued by the owner of the course, the `adminaccess` option causes the invitations to be issued by the admin named in `oauth2.txt`.
By default, when an invitation is created, GAM outputs details of the invitation as indented keywords and values.
* `csv|csvformat [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]]` - Output the details in CSV format.
### Example
Suppose you have a CSV file CourseStudent.csv with two columns: Course,Student.
@@ -66,11 +71,13 @@ This command will invite all students to their courses in parallel
```
gam redirect stdout ./Invites.out multiprocess redirect stderr stdout multiprocess csv CourseStudent.csv gam user ~Student create classroominvitation role student course ~Course
```
## Accept classroom invitations
Accept classroom invitations for users. You can only invite a co-teacher to be an owner of a course.
## Accept classroom invitations by user
Accept classroom invitations for users.
```
gam <UserTypeEntity> accept classroominvitation (ids <ClassroomInvitationIDEntity>)|([courses <CourseEntity>] [role all|owner|student|teacher])
```
`<UserTypeEntity>` must specify users in your domain.
By default, all invitations for the specified users will be accepted.
Select specific invitations to accept:
@@ -81,11 +88,13 @@ Select courses and accept invitations for those courses.
By default, invitations for all roles will be accepted; you can limit the acceptances to invitations of a specific role.
## Delete classroom invitations
## Delete classroom invitations by user
Delete classroom invitations for users.
```
gam <UserTypeEntity> delete classroominvitation (ids <ClassroomInvitationIDEntity>)|([courses <CourseEntity>] [role all|owner|student|teacher])
```
`<UserTypeEntity>` must specify users in your domain.
By default, all invitations for the specified users will be deleted.
Select specific invitations to delete:
@@ -104,8 +113,23 @@ gam <UserTypeEntity> show classroominvitations [role all|owner|student|teacher]
gam <UserTypeEntity> print classroominvitations [todrive <ToDriveAttributes>*] [role all|owner|student|teacher]
[formatjson [quotechar <Character>]]
```
`<UserTypeEntity>` must specify users in your domain.
By default, invitations for all roles will be displayed; you can limit the display to invitations of a specific role.
## Delete classroom invitations by course
Delete classroom invitations for courses. This command must be used to delete non-domain member invitations.
```
gam delete classroominvitation courses <CourseEntity> (ids <ClassroomInvitationIDEntity>)|(role all|owner|student|teacher)
```
Select courses and delete invitations for those courses.
* `courses <CourseEntity>` - Specify courses
Select specific invitations to delete:
* `ids <ClassroomInvitationIDEntity>` - Specify invitation IDs
Select invitations to delete by role. By default, invitations for all roles will be deleted; you can limit the deletions to invitations of a specific role.
## Display classroom invitations by course
```
gam show classroominvitations (course|class <CourseEntity>)*|([teacher <UserItem>] [student <UserItem>] [states <CourseStateList>])

View File

@@ -6,6 +6,7 @@
- [Legacy manage membership](#legacy-manage-membership)
- [Bulk membership changes](#bulk-membership-changes)
- [Display course membership](#display-course-membership)
- [Display course membership counts](#display-course-membership-counts)
## API documentation
* https://developers.google.com/classroom/reference/rest/
@@ -131,3 +132,35 @@ the quote character itself, the column delimiter (comma by default) and new-line
When using the `formatjson` option, double quotes are used extensively in the data resulting in hard to read/process output.
The `quotechar <Character>` option allows you to choose an alternate quote character, single quote for instance, that makes for readable/processable output.
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.
## Display course membership counts
Display the number of course participants.
```
gam print course-participants
(course|class <CourseID>)*|([teacher <UserItem>] [student <UserItem>]) [states <CourseStateList>]
[show all|students|teachers]
showitemcountonly
```
Example
```
$ gam print course-participants teacher asmith states active show students showitemcountonly
Getting all Courses that match query (Teacher: asmith@domain.com, Course State: ACTIVE), may take some time on a large Google Workspace Account...
Got 3 Courses...
Getting Students for Course: 636981507234 (1/3)
Got 30 Students...
Got 43 Students...
Getting Students for Course: 589346784341 (2/3)
Got 22 Students...
Getting Students for Course: 589345535881 (3/3)
Got 23 Students...
88
```
The `Getting` and `Got` messages are written to stderr, the count is writtem to stdout.
To retrieve the count with `showitemcountonly`:
```
Linux/MacOS
count=$(gam print course-participants teacher asmith states active show students showitemcountonly)
Windows PowerShell
count = & gam print course-participants teacher asmith states active show students showitemcountonly
```

View File

@@ -9,11 +9,13 @@
- [Synchronize devices](#synchronize-devices)
- [Display devices](#display-devices)
- [Print devices](#print-devices)
- [Display device counts](#display-device-counts)
- [Approve or block device users](#approve-or-block-device-users)
- [Delete device users](#delete-device-users)
- [Wipe device users](#wipe-device-users)
- [Perform device user actions](#perform-device-user-actions)
- [Display device users](#display-device-users)
- [Display device user counts](#display-device-user-counts)
- [Print device users](#print-device-users)
- [Display device user client state](#display-device-user-client-state)
- [Update device user client state](#update-device-user-client-state)
@@ -225,6 +227,37 @@ When using the `formatjson` option, double quotes are used extensively in the da
The `quotechar <Character>` option allows you to choose an alternate quote character, single quote for instance, that makes for readable/processable output.
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.
## Display device counts
Display the number of devices.
```
gam print devices
[(query <QueryDevice>)|(queries <QueryDeviceList>) (querytime<String> <Time>)*]
[all|company|personal|nocompanydevices|nopersonaldevices]
showitemcountonly
```
Example
```
$ gam print devices queries "'model:Mac'" showitemcountonly
Getting all Devices that match query (model:Mac), may take some time on a large Google Workspace Account...
Got 100 Devices...
Got 200 Devices...
Got 300 Devices...
...
Got 900 Devices...
Got 995 Devices...
Got 995 Devices...
995
```
The `Getting` and `Got` messages are written to stderr, the count is writtem to stdout.
To retrieve the count with `showitemcountonly`:
```
Linux/MacOS
count=$(gam print devices queries "'model:Mac'" showitemcountonly)
Windows PowerShell
count = & gam print devices queries "'model:Mac'" showitemcountonly
```
## Approve or block device users
Approve or block user profiles on a device.
```
@@ -285,6 +318,38 @@ When using the `formatjson` option, double quotes are used extensively in the da
The `quotechar <Character>` option allows you to choose an alternate quote character, single quote for instance, that makes for readable/processable output.
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.
## Display device user counts
Display the number of device users.
```
gam print deviceusers [todrive <ToDriveAttribute>*]
[select <DeviceID>]
[(query <QueryDevice>)|(queries <QueryDeviceList>) (querytime<String> <Time>)*]
showitemcountonly
```
Example
```
$ gam print deviceusers queries "'model:Mac'" showitemcountonly
Getting all Device Users that match query (model:Mac), may take some time on a large Google Workspace Account...
Got 20 Device Users...
Got 40 Device Users...
Got 60 Device Users...
...
Got 980 Device Users...
Got 995 Device Users...
Got 995 Device Users...
995
```
The `Getting` and `Got` messages are written to stderr, the count is writtem to stdout.
To retrieve the count with `showitemcountonly`:
```
Linux/MacOS
count=$(gam print deviceusers queries "'model:Mac'" showitemcountonly)
Windows PowerShell
count = & gam print deviceusers queries "'model:Mac'" showitemcountonly
```
## Display device user client state
```
gam info deviceuserstate <DeviceUserEntity> [clientid <String>]

View File

@@ -9,6 +9,7 @@
- [Manage groups](#manage-groups)
- [Display information about individual groups](#display-information-about-individual-groups)
- [Display information about multiple groups](#display-information-about-multiple-groups)
- [Display group counts](#display-group-counts)
## API documentation
* https://developers.google.com/admin-sdk/directory/reference/rest/v1/groups
@@ -376,3 +377,29 @@ gam print cigroups query "'cloudidentity.googleapis.com/groups.dynamic' in label
```
gam print cigroups query "'cloudidentity.googleapis.com/groups.security' in labels"
```
## Display group counts
Display the number of groups.
```
gam print cigroups
[(cimember|showownedby <UserItem>)|(select <GroupEntity>)|(query <String>)]
[emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
[descriptionmatchpattern [not] <RegularExpression>]
showitemcountonly
```
Example
```
$ gam print cigroups showitemcountonly
Getting all Cloud Identity Groups, may take some time on a large Google Workspace Account...
Got 242 Cloud Identity Groups: td.current@domain.com - postmaster@domain.com
242
```
The `Getting` and `Got` messages are written to stderr, the count is writtem to stdout.
To retrieve the count with `showitemcountonly`:
```
Linux/MacOS
count=$(gam print cigroups showitemcountonly)
Windows PowerShell
count = & gam print cidgroups showitemcountonly
```

View File

@@ -262,6 +262,8 @@ Data fields identified in a `csvkmd` argument.
<FileSelector> | <CSVFileSelector> | <CSVkmdSelector> | <CSVDataSelector>
<DriveLabelNameEntity> ::=
<DriveLabelNameList> | <FileSelector> | <CSVFileSelector> | <CSVDataSelector>
<DriveLabelPermissionNameEntity> ::=
<DriveLabelPermissionNameList> | <FileSelector> | <CSVFileSelector> | <CSVDataSelector>
<EmailAddressEntity> ::=
<EmailAddressList> | <FileSelector> | <CSVFileSelector> | <CSVkmdSelector> | <CSVDataSelector>
<FilterIDEntity> ::=

View File

@@ -133,7 +133,7 @@ The `quotechar <Character>` option allows you to choose an alternate quote chara
## Display Domain Profiles
### Display as an indented list of keys and values.
```
gam info people|domainprofiles <PeopleResourceNameEntity>
gam info domainprofiles|people|peopleprofiles <PeopleResourceNameEntity>
[allfields|(fields <PeopleFieldNameList>)]
[formatjson]
```
@@ -143,7 +143,7 @@ By default, Gam displays the fields `names,emailaddresses`.
By default, Gam displays the information as an indented list of keys and values.
* `formatjson` - Display the fields in JSON format.
```
gam show people|domainprofiles
gam show domainprofiles|people|peopleprofiles
[query <String>]
[mergesources <PeopleMergeSourceName>]
[allfields|(fields <PeopleFieldNameList>)]
@@ -163,7 +163,7 @@ By default, Gam displays the information as an indented list of keys and values.
### Display as a CSV file.
```
gam print people|domainprofiles [todrive <ToDriveAttribute>*]
gam print domainprofiles|people|peopleprofiles [todrive <ToDriveAttribute>*]
[query <String>]
[mergesources <PeopleMergeSourceName>]
[allfields|(fields <PeopleFieldNameList>)]

View File

@@ -48,10 +48,7 @@ The `quotechar <Character>` option allows you to choose an alternate quote chara
## Display File Ownership for Old files
If the above commands fail, you can try to loop through all accounts, however this might take a long time if you are on a large Google Workspace Account.
```
gam config auto_batch_min 1 redirect csv - multiprocess redirect stderr null multiprocess all users print filelist select id <DriveFileID> fields id,name,owners.emailaddress norecursion showownedby any
```
Starting with version 6.07.26, this can be made more efficient by terminating processing after the owner is identified.
```
gam config auto_batch_min 1 multiprocessexit rc=0 redirect csv - multiprocess redirect stderr null multiprocess all users print filelist select id <DriveFileID> fields id,name,owners.emailaddress norecursion showownedby any
gam config auto_batch_min 1 multiprocessexit rc=0 redirect csv - multiprocess redirect stderr null multiprocess all users print filelist select name <DriveFileName> fields id,name,owners.emailaddress norecursion showownedby any
```

View File

@@ -1,4 +1,3 @@
# Update GAMADV-XTD3 to latest version
Automatic update to the latest version on Linux/Mac OS/Google Cloud Shell/Raspberry Pi/ChromeOS:
- Do not create project or authorizations, default path `$HOME/bin`
@@ -11,6 +10,673 @@ Add the `-s` option to the end of the above commands to suppress creating the `g
See [Downloads](https://github.com/taers232c/GAMADV-XTD3/wiki/Downloads) for Windows or other options, including manual installation
### 6.75.02
Updated `gam report <ActivityApplictionName>` to retry/handle the following error:
```
ERROR: 503: serviceNotAvailable - The service is currently unavailable.
```
### 6.75.01
Added option `admin <EmailAddress>` to `gam upload sakey`.
### 6.75.00
Updated `gam create project` to simplify handling the situation where your workspace is configured to disable service account private key uploads.
Added command `gam upload sakey` to aid in this process.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Authorization#upload-a-service-account-key-to-a-service-account-with-no-keys
### 6.74.02
Fixed bug in `gam <UserTypeEntity> print shareddrives ... formatjson` that caused a trap.
### 6.74.01
Updated `gam create|update drivefileacl <DriveFileEntity> ... expiration <Time>` to handle
the following error caused by tryig to add an expiration time to a member of a Shared Drive.
```
ERROR: 403: expirationDateNotAllowedForSharedDriveMembers - Expiration dates are not allowed for shared drive members.
```
### 6.74.00
Added `truncate_client_id` Boolean variable to `gam.cfg`. Prior to version 6.74.00, GAM stripped
'.apps.googleusercontent.com' from `client_id` in `oauth2.txt` and passed the truncated value in API calls.
At Jay's suggestion this is no longer performed by default; setting `truncate_client_id = true` restores the previous behavior.
Do `gam oauth delete` and `gam oauth create` to set the untruncated value of `client_id` in `oauth2.txt`.
### 6.73.00
The Google Chat API has been updated so that chat members can now have their role set to manager.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Users-Chat#manage-chat-members
### 6.72.16
Updated `emailaddressList <EmailAddressList>` and `domainlist|notdomainlist <DomainNameList>`
in `<PermissionMatch>` to perform case-insensitive matches as the API is returning mixed case
ACL email addresses in some cases.
### 6.75.15
Updated all commands that display tasks to display the due date in GMT as the time portion
is not supported by the API and converting the due date to local time may display the wrong date.
Renamed license SKU `1010400001` from `Beyond Corp Enterprise` to `Chrome Enterprise Premium`.
### 6.72.14
Upgraded to Python 3.12.3 where possible.
### 6.72.13
Added the following option to `<EventMatchProperty>` that can be used to select
events based on the domains of the attendees.
```
matchfield attendeesonlydomainlist <DomainNameList>
```
This returns true if all attendee's email addresses are in a domain in `<DomainNameList>`;
for example this lets you look for events with attendees only in your internal domains.
### 6.72.12
Added the following options to `<EventMatchProperty>` that can be used to select
events based on the domains of the attendees.
```
matchfield attendeesdomainlist <DomainNameList>
matchfield attendeesnotdomainlist <DomainNameList>
```
The first returns true if any attendee's email address is in a domain in `<DomainNameList>`;
for example this lets you look for events with attendees in specific external domains.
The second returns true if any attendee's email address is in a domain other than those in `<DomainNameList>`;
for example this lets you look for events with attendees not in your internal domains.
### 6.72.11
Added option `oneitemperrow` to 'gam print vaultholds` to have each of a
hold's accounts displayed on a separate row with all of the other hold fields.
### 6.72.10
Added `timeofdayrange=<HH:MM>/<HH:MM>` and `timeofdayrange!=<HH:MM>/<HH:MM>` to `<RowValueFilter>` that allows
CSV row filtering based on time-of-day.
### 6.72.09
Updated `countsonly` option of `gam <UserTypeEntity> print|show notes` to additionally display the total number of notes.
### 6.72.08
Added option `countsonly` to `gam <UserTypeEntity> print|show notes` that displays
the number of notes a user owns and the number of notes a user can edit.
### 6.72.07
Updated commands that send emails to not downshift `'First Last<firstlast@domain.com>'` to `'first last<firstlast@domain.com>'`.
### 6.72.06
Updated the following commands to properly handle emailaddress lists containing addresses of the form: `'First Last<firstlast@domain.com>'`.
```
gam <UserTypeEntity> sendemail recipient|to <RecipientEntity> [cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage]
gam create|update user ... notify <EmailAddressList>
```
### 6.72.05
Cleaned up code for all commands that display Chat objects.
### 6.72.04
Added commands to display Chat events.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Users-Chat#display-chat-events
### 6.72.03
Fixed bug in `gam <UserTypeEntity> create chatspace` that caused a trap.
### 6.72.02
Updated `gam delete admin <RoleAssignmentId>` to handle the following error that
occurs when `<RoleAssignmentId>` references a user that has been deleted.
```
ERROR: 404: resourceNotFound - Does not exist
```
### 6.72.01
Improved commands to display drive file comments.
### 6.72.00
Added commands to display drive file comments.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Users-Drive-Comments
### 6.71.18
Updated `<CrOSFieldName>` to include `cpuinfo` and `backlightinfo`.
### 6.71.17
Added `depth` column to output of `gam <UserTypeEntity> print diskusage <DriveFileEntity>` that can
be used to filter the depth of the folders displayed. Depth `-1` is the top level folder, depth `0`
are its immediate children, depth `2` are the children of depth `1` and so forth.
```
gam config csv_output_row_filter "depth:count<1" user organizer@domain.com print diskusage teamdriveid <TeamDriveID>
```
### 6.71.16
Updated `gam <UserTypeEntity> create|update sendas <EmailAddress> ... replyto <EmailAddress>`
to allow uppercase letters in `sendas <EmailAddress>` and `replyto <EmailAddress>`.
### 6.71.15
Updated `gam create project` to handle the following error:
```
ERROR: 403: permissionDenied - Authentication error: 7; Error Details: User not allowed to access GCP services.
```
This error occurs when the Google Workspace admin or GCP project manager email address used in the command
is in an OU where Google Cloud Platform is not enabled in Apps/Additional Google services.
### 6.71.14
Added a command to update a Gmail label's settings by specifying it's ID rather than it's name.
```
gam <UserTypeEntity> update labelid <LabelID> [name <String>]
[messagelistvisibility hide|show] [labellistvisibility hide|show|showifunread]
[backgroundcolor <LabelColorHex>] [textcolor <LabelColorHex>]
```
### 6.71.13
Updated `<UserMultiAttribute>.location.buildingid <String>` to allow non-validated building IDs
by specifying `nv:` at the beginning of `<String>`; e.g., `nv:Building X' sets the building ID to `Building X`.
### 6.71.12
Added option `showmimetype category <MimeTypeNameList>` to `gam <UserTypeEntity> print|show filecounts|filelist|filetree`
```
<MimeTypeName> ::= application|audio|font|image|message|model|multipart|text|video
<MimeTypeNameList> ::= "<MimeTypeName>(,<MimeTypeName>)*"
gam user user@domain.com print filelist fields id,name,mimetype showmimetype prefixes audio,video
```
### 6.71.11
Added option `addcsvdata <FieldName> <String>` to `gam print cros` that adds
additional columns of data to the CSV file output. Typically, you would read CSV file of device IDs/serial numbers
to generate a CSV file of results and copy data from the input CSV to the outout CSV.
### 6.71.10
Reverted change made in 6.71.09 to `gam <UserTypeEntity> print filelist` when `showmimetype` and `filepath|fullpath`
were both specified. The change improved the performance when `showmimetype` selected a small number of files;
the information for just those files was downloaded and then additional API calls were made to construct the file paths.
However, if a large number of files were selected, the additional APIs calls decreased performance.
Added option `mimetypeinquery` can be used when you expect the query to return a small number of files
relative to the total number of files.
### 6.71.09
Improved the performance of `gam <UserTypeEntity> print filelist` when `showmimetype` and `filepath|fullpath`
are both specified.
### 6.71.08
Added option `oneitemperrow` to 'gam print admins|adminroles` to have each of a
roles privileges displayed on a separate row with all of the other admin/role fields.
This produces a CSV file that can be used in subsequent commands without further script processing.
### 6.71.07
Added command to upload changes to Google Docs.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Users-Drive-Files-Manage#upload-changes-to-google-documents
### 6.71.06
Added additional error handling to Gmail Client Side Encryption commands.
Added license product Education Endpoint Management
* ProductID - 101049
Added license SKU Endpoint Education Upgrade
* ProductID - 101049
* SKUID - 1010490001 | eeu
### 6.71.05
Fixed a bug introduced in 6.71.00 that caused a trap in `gam <UserTypeEntity> print filelist`.
Added option `tdfrom <EmailAddress>` to `<ToDriveAttribute>` that causes GAM to use `<EmailAddress>` as the from address
in all emails sent. By default, the from address is the Google Workspace Admin in `gam oauth info`.
### 6.71.04
Updated `gam <UserTypeEntity> create|update cseidentity` to accept either of the following key pair options:
* `primarykeypairid <KeyPairID>` - The configuration of a CSE identity that uses the same key pair for signing and encryption.
* `signingkeypairid <KeyPairID> encryptionkeypairid <KeyPairID>` - The configuration of a CSE identity that uses different key pairs for signing and encryption.
Updated CSV output row sorting to avoid a trap that occurred when a row was missing one of the sort fields.
### 6.71.03
Added option `tdalert <EmailAddress>` to `<ToDriveAttribute>`. When a todrive file is created or updated,
GAM will send notification emails to all `tdalert <EmailAddress>` users if `tdnotify` is true.
`<EmailAddress>` must be valid within your Google Workspace.
### 6.71.02
Added additional error handling to Gmail Client Side Encryption commands.
### 6.71.01
Fixed bug in `gam audit monitor create` that caused a trap.
### 6.71.00
Added `csv_output_sort_headers` string list variable to `gam.cfg` that causes GAM to sort CSV output
rows by the column headers specified in the variable. The column headers are case insensitive and
if column header does not appear in the CSV output, it is ignored.
Added `sortheaders <StringList>` to `redirect csv <FileName>` that has the same effect as above.
The sort keys specified in `redirect csv ... sortheaders <StringList>` take precedence over the values from `gam.cfg`.
Added option `tdsubject <String>` to `<ToDriveAttribute>` that causes GAM to use `<String>` as the subject
in all emails sent. In `<String>`, `#file#` will, be replaced by the file title and `#sheet#` will be replaced
by the sheet/tab title. By default, the subject is the file title.
### 6.70.09
Added additional error handling to Gmail Client Side Encryption commands.
Added options `showpem` and `showkaclsdata` to all Gmail CSE commands that process/display
CSE key pairs. By default, the `pem` and `kaclsdata` fields will not be displayed unless
the corresponding `show` option is specified.
### 6.70.08
Fixed bug in `gam <UserTypeEntity> create cseidentity <KeyPairID>` that caused an error.
### 6.70.07
Updated user instructions in `gam oauth create` and `gam <UserTypeEntity> update serviceaccount`
and changed `s` from selecting all scopes to selecting default scopes.
### 6.70.06
Updated `gam info users <UserTypeEntity>` to not include group tree infornation unless option `grouptree` is specified.
### 6.70.05
Added commands to create|delete|display Drive Label permissions.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Users-Drive-Labels
### 6.70.04
Added option `showvalidcolumn` to `gam print users` that can be used to identify whether
users are defined in the domain. Typically, you would read CSV file of email addresses
to verify as domain members.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Users#verify-domain-membership
Added option `addcsvdata <FieldName> <String>` to `gam print users` that adds
additional columns of data to the CSV file output. Typically, you would read CSV file of email addresses
to generate a CSV file of results and copy data from the input CSV to the outout CSV.
### 6.70.03
Renamed license product DuetAI to Gemini
* ProductID - 101047
Renamed license SKU DuetAI for Google Workspace to Gemini Enterprise
* ProductID - 101047
* SKUID - 1010470001 | geminient | duetai
Added support for license SKU Gemini Business
* ProductID - 101047
* SKUID - 1010470003 | geminibiz
### 6.70.02
In 6.69.00, GAM starting using course owner access when using `copyfrom` in `gam create|update course`
regardless of the value of `gam.cfg/use_course_owner_access`. This prevents copying from courses
with a deleted user. GAM now uses the value of `gam.cfg/use_course_owner_access` when `copyfrom` is used.o
### 6.70.01
Added `gmail_cse_incert_dir` and `gmail_cse_inkey_dir` path variables to `gam.cfg` that provide
default values for the `incertdir <FilePath>` and `inkeydir <FilePath>` options in `gam <UserTypeEntity> create csekeypair`.
### 6.70.00
Added support for Gmail Client Side Encryption.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Users-Gmail-CSE
This is an initial, minimally tested release; proceed with care and report all issues.
### 6.69.00
Added `use_course_owner_access` Boolean variable to `gam.cfg` that controls how GAM gets
classroom member information and removes students/teachers. Client/admin access does not provide
complete information about non-domain students/teachers.
* `False` - Use client/admin access; this is the default. Use if you don't have non-domain members in your courses.
* `True` - Use service account access as the classroom owner. An extra API call is required per course to authenticate the owner; this will affect performance
Added the following command which must be used to delete classroom invitations for non-domain students/teachers.
```
gam delete classroominvitation courses <CourseEntity> (ids <ClassroomInvitationIDEntity>)|(role all|owner|student|teacher)
```
You can obtain the classroom invitation IDs with these commands:
```
gam show classroominvitations (course|class <CourseEntity>)*|([teacher <UserItem>] [student <UserItem>] [states <CourseStateList>])
[role all|owner|student|teacher] [formatjson]
gam print classroominvitations [todrive <ToDriveAttribute>*] (course|class <CourseEntity>)*|([teacher <UserItem>] [student <UserItem>] [states <CourseStateList>])
[role all|owner|student|teacher] [formatjson [quotechar <Character>]]
```
### 6.68.08
Updated `gam <UserTypeEntity> print filelist|drivefileacls|shareddriveacls ... oneitemperrow` to print
ACLs with multiple permission details on separate rows for each basic permission/permission detail combination.
This case occurs when a member of a Shared Drive has access to a file and also has explicitly granted access to the same file.
Added `pmtype member|file` to `<PermissionMatch>` that allows determining whether an ACL on a Shared Drive file was
derived from membership or explicitly granted.
### 6.68.07
Updated `gam info user ... locations formatjson` to include the `buildingName` field in the
`locations` entries. If `gam.cfg` contains `quick_info_user = true` or the `quick` option
is included on the command line, add the option `buildingnames` to the command line.
### 6.68.06
Fixed bug in `gam <UserTypeEntity> copy drivefile <DriveFileID> ... mergewithparent` that incorrectly named
the copied file with the name of the parent folder.
Updated `gam <UserTypeEntity> copy|move drivefile` to avoid copying/moving the same file twice.
### 6.68.05
Updated `gam print groups ... ciallfields|(cifields <CIGroupFieldNameList>)` to account for an
API shortcoming that failed to get all of the Cloud Identity fields.
### 6.68.04
Added option `skiprows <Integer>` to `gam csv|loop` that causes GAM to skip processing the first `<Integer>` filtered rows.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Bulk-Processing#csv-files
### 6.68.03
Fixed bug in `gam <UserTypeEntity> create drivefileacl` that caused a trap.
### 6.68.02
Upgraded to Python 3.12.2 where possible.
Added options `restricted|(audience <String>)` to `gam <UserTypeEntity> create|update chatspace` that
sets the access options for the chat space. These options are in Developer Preview and will not be generally available.
### 6.68.01
Fixed `<PermissionMatch>` bug for real.
### 6.68.00
Fixed `<PermissionMatch>` bug introduced in 6.67.35 that caused a command error like the following or would
not properly match `type|nottype <DriveFileACLType>` and `role|notrole <DriveFileACLRole>`.
```
ERROR: permission attribute allowfilediscovery/withlink not allowed with type {'a', 'y', 'e', 'o', 'n'}
```
My sincere apologies.
### 6.67.39
Added option `wait <Integer> <Integer>` to `gam create datatransfer` that causes GAM to wait
for the transfer to complete. The first `<Integer>` must be in the range 5-60 and is the number
of seconds between checks to see if the transfer has completed. The second `<Integer>` is the maximum
number of checks to perform. By default, GAM does not wait for the transfer to complete.
### 6.67.38
Added option `tdnotify [<Boolean>]` to `<ToDriveAttribute>` that causes GAM to send notification
emails to all `tdshare <EmailAddress>` users when the file is uploaded/updated.
### 6.67.37
Fixed bug in `gam <UserTypeEntity> show messages ... showattachments` to avoid a trap when `text/plain` attachments
in character sets other than `UTF-8` are displayed.
### 6.67.36
Updated `gam batch <BatchContent>` and `gam tbatch <BatchContent>` commands to accept lines with the following form:
```
sleep <Integer>
```
Batch processing will suspend for `<Integer>` seconds before the next command line is processed.
### 6.67.35
Added the following options to `<PermissionMatch>` that allow more powerful matching.
```
nottype <DriveFileACLType>
typelist <DriveFileACLTypeList>
nottypelist <DriveFileACLTypeList>
rolelist <DriveFileACLRoleList>
notrolelist <DriveFileACLRoleList>
```
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Permission-Matches#define-a-match
### 6.67.34
Added option `movetoorgunitdelay <Integer>` to `gam <UserTypeEntity> create shareddrive <Name> ... ou|org|orgunit <OrgUnitItem>`.
GAM creates the Shared Drive, verifies that it has been created and then tries to move it to `<OrgUnitItem>`. Google seems to
require a delay or the following error is generated.
```
ERROR: 409: 409 - The operation was aborted.
```
`movetoorgunitdelay` defaults to 20 seconds which seems to work; `<Integer>` can range from 0 to 60.
### 6.67.33
Upgraded to OpenSSL 3.2.1 where possible.
Fixed bug in `gam <UserTypeEntity> print shareddrives` where `role` was improperly displayed as `fileOrganizer`
rather than `writer`.
Added option `guiroles [<Boolean>]` to `gam <UserTypeEntity> info|print|show shareddrive` that maps
the Drive API role names to the Google Drive GUI role names.
```
API: GUI
commenter: Commenter
fileOrganizer: Content manager
organizer: Manager
reader: Viewer
writer: Contributor
```
### 6.67.32
Updated `<ToDriveAttribute>` to allow multiple `tdshare <EmailAddress> commenter|reader|writer` options.
Fixed bug in `gam <UserTypeEntity> print shareddrives` where `role` was improperly displayed as `unknown`
rather than `reader` when `Allow viewers and commenters to download, print, and copy files` was unchecked for the Shared Drive.
### 6.67.31
Updated `gam <UserTypeEntity> claim|transfer ownership <DriveFileEntity>` to properly
handle the case where `<DriveFileEntity>` referencess a Drive shortcut.
### 6.67.30
Fixed bug where the `fullpath` option in various commands was not converting the generic shared drive name `Drive` to the drive's actual name.
### 6.67.29
Added optional argument `owneraccess` to `gam courses <CourseEntity> remove teachers|students [owneracccess] <UserTypeEntity` and
`gam course <CourseID> remove teacher|student [owneraccess] <EmailAddress>` in order to test a possible API change.
Updated code to avoid a trap when `gam config auto_batch_min 1 csv file.csv gam ...` was entered.
The `config auto_batch_min 1` is not appropriate in this context and will be ignored.
### 6.67.28
Improved handling of `Bad Request` error in `gam <UserTypeEntity> collect orphans`.
### 6.67.27
Updated `gam <UserTypeEntity> collect orphans` to handle the following error:
```
ERROR: 400: badRequest - Bad Request
```
### 6.67.26
Fixed bug in `gam print vaultexports ... formatjson` that caused a trap.
### 6.67.25
Added option `owneraccess` to `gam info courses <CourseEntity>` and `gam info course <CourseID>` in order
to test a possible API change.
### 6.67.24
Fixed bug that caused HTML password notification email messages to be displayed in raw form.
### 6.67.23
Use local copy of `googleapiclient` to remove static discovery documents to improve performance.
### 6.67.22
Added `permissionidlist <PermissionIDList>` to `<PermissionMatch>` that allows matching any permission ID in a list.
Added option `exportlinkeddrivefiles <Boolean>` to `gam create vaultexport` that is used with `corpus mail`.
### 6.67.21
Updated `gam remove aliases <EmailAddress> user|group <EmailAddressEntity>` to give a more informative
error message when the target/alias combination does not exist.
```
Old: User: testsimple@rdschool.org, User Alias: tsalias@rdschool.org, Remove Failed: Invalid Input: resource_id
New: User: testsimple@rdschool.org, User Alias: tsalias@rdschool.org, Remove Failed: Does not exist
```
### 6.67.20
Added option `onelicenseperrow|onelicenceperrow` to `gam print users ... licenses` that causes GAM to print
a seperate user information row for each license a user is assigned. This makes processing
the licenses in a script possible and allows better sorting in a CSV File.
By default, all licenses for a user are displayed in a list on one row:
```
primaryEmail,LicensesCount,Licenses,LicensesDisplay
user@domain.com,2,1010020020 1010330004,Google Workspace Enterprise Plus Google Voice Standard
```
With `onelicenseperrow|onelicenceperrow`, each license is on a separate row:
```
primaryEmail,License,LicenseDisplay
user@domain.com,1010020020,Google Workspace Enterprise Plus
user@domain.com 1010330004,Google Voice Standard
```
### 6.67.19
Updated `gam create|update user ... notify` to encode the characters `<>&` in the password
so that they display correctly when the notify message content is HTML.
### 6.67.18
Cleaned up `Getting/Got` messages for `gam print courses|course-participants`.
### 6.67.17
Added option `showitemcountonly` to various commands that causes GAM to display the
item count on stdout; no CSV file is written.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Cloud-Identity-Groups#display-group-counts
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Classroom-Courses#display-course-counts
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Classroom-Membership#display-course-membership-counts
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/ChromeOS-Devices#display-cros-device-counts
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Cloud-Identity-Devices#display-device-counts
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Cloud-Identity-Devices#display-device-user-counts
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Groups#display-group-counts
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Mobile-Devices#display-mobile-device-counts
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Organizational-Units#display-organizational-unit-counts
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Resources#display-resource-counts
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Users#display-user-counts
### 6.67.16
By default, `gam print group-members membernames` displays `Unknown` for members whose names can not be determined.
Added option `unknownname <String>` that let's you specify an alternative value.
Further improved performance of `gam print group-members membernames cachememberinfo`.
### 6.67.15
Update `gam print group-members membernames` to handle the following error:
```
ERROR: 400: failedPrecondition - Precondition check failed.
```
Added option `cachememberinfo [Boolean]` to `gam print group-members` that causes GAM to cache member info
so that only one API call is made to get information for each user/group. This consumes
more memory but dramatically reduces the number of API calls.
### 6.67.14
Updated reseller commands to handle the following error:
```
ERROR: 400: invalid - Customer domain [domain.com] is linked to one or more email verified customers, please provide a customer id.
```
### 6.67.13
Updated `gam create domain <DomainName>` to handle the following error:
```
ERROR: 409: conflict - Domain in request is in use by an email verified customer.
```
### 6.67.12
Added option `addcsvdata <FieldName> <String>` to `gam print datatransfers` that adds
additional columns of data to the CSV file output.
### 6.67.11
Updated various Gmail related commands to handle this error:
```
ERROR: 403: permissionDenied - Insufficient Permission
```
when the following service account scopes are selected:
```
[ ] 23) Gmail API - Basic Settings (Filters,IMAP, Language, POP, Vacation) - read/write, Sharing Settings (Delegates, Forwarding, SendAs) - read
[ ] 24) Gmail API - Full Access (Labels, Messages)
[ ] 25) Gmail API - Full Access (Labels, Messages) except delete message
[*] 26) Gmail API - Full Access - read only
[ ] 27) Gmail API - Send Messages - including todrive
[ ] 28) Gmail API - Sharing Settings (Delegates, Forwarding, SendAs) - write
```
### 6.67.10
Fixed bug that caused a trap when optional argument `charset <Charset>` was used with `emlfile <FileName>` in `gam <UserTypeEntity> draft|import|insert message`.

View File

@@ -15,7 +15,7 @@
<DataTransferService> ::=
calendar|
currents|
datastudio|"google data studio"|
datastudio|lookerstudio|"google data studio"|
drive|gdrive|googledrive|"drive and docs"
<DataTransferServiceList> ::= "<DataTransferService>(,<DataTransferService>)*"
@@ -37,6 +37,7 @@ gam create|add datatransfer|transfer <OldOwnerID> <DataTransferServiceList> <New
[private|shared|all] [privacy_level private|shared|private,shared]
[releaseresources [<Boolean>]]
(<ParameterKey> <ParameterValue>)*
[wait <Integer> <Integer>]
```
For`datastudio` and `drive`, there are options to control the privacy level of the files to be transferred.
* `private` or `privacy_level private` - Transfer files that are not shared with anyone
@@ -54,6 +55,10 @@ As of 2020-06-10, background transfers only transfer future non-private events w
The option `<ParameterKey> <ParameterValue>` is for future expansion.
By default, GAM does not wait for the transfer to complete. The option `wait <Integer> <Integer>` causes GAM to wait
for the transfer to complete. The first `<Integer>` must be in the range 5-60 and is the number
of seconds between checks to see if the transfer has completed. The second `<Integer>` is the maximum number of checks to perform.
## Display transfers
```
gam info datatransfer|transfer <TransferID>
@@ -63,6 +68,7 @@ gam show datatransfers|transfers
gam print datatransfers|transfers [todrive <ToDriveAttribute>*]
[olduser|oldowner <UserItem>] [newuser|newowner <UserItem>]
[status completed|failed|inprogress|<String>] [delimiter <Character>]
(addcsvdata <FieldName> <String>)*
```
By default, all data transfer operations are printed, use these options to select specific transfers.
* `olduser|oldowner <UserItem>`
@@ -72,3 +78,5 @@ By default, all data transfer operations are printed, use these options to selec
By default, the entries in lists of items are separated by the `csv_output_field_delimiter` from `gam.cfg`.
* `delimiter <Character>` - Separate list items with `<Character>`
Add additional columns of data from the command line to the output
* `addcsvdata <FieldName> <String>`

View File

@@ -583,6 +583,7 @@ gam print group-members [todrive <ToDriveAttribute>*]
[userfields <UserFieldNameList>]
[(recursive [noduplicates])|includederivedmembership] [nogroupemail]
[peoplelookup|(peoplelookupuser <EmailAddress>)]
[unknownname <String>] [cachememberinfo [Boolean]]
[formatjson [quotechar <Character>]]
```
By default, the group membership of all groups in the account are displayed, these options allow selection of subsets of groups:
@@ -646,11 +647,17 @@ these options specify which fields to display:
* `delivery|deliverysettings` - Specify this field to get delivery information; an additional API call per member is required
* `userfields <UserFieldNameList>` - For members that are users, display these user fields; an additional API call per member is required
The additional API calls can be reduced with the `cachememberinfo` option; a single API call is made for each user/group
and the data is cached to eliminate to need to repeat the API call; this consumes more memory but dramatically reduces the number of API calls.
If member names are requested, names are not available for users not in the domain; you can request that GAM use the People API to retrieve
names for these users. Names are not retrieved in all cases and success is dependent on what user is used to perform the retrievals.
* `peoplelookup` - Use the administrator named in oauth2.txt to perform the retrievals
* `peoplelookupuser <EmailAddress>` - Use `<EmailAddress>` to perform the retrievals
By default, when `membernames` is specified, GAM displays `Unknown` for members whose names can not be determined.
Use `unknownname <String>` to specify an alternative value.
By default, the group email address is always shown, you can suppress it with the `nogroupemail` option.
By default, members that are groups are displayed as a single entry of type GROUP; this option recursively expands group members to display their user members.

View File

@@ -16,6 +16,7 @@
- [Display information about selected groups](#display-information-about-selected-groups)
- [Display a group and its parents](#Display-a-group-and-its-parents)
- [Examples](#Examples)
- [Display group counts](#display-group-counts)
## API documentation
* https://developers.google.com/admin-sdk/directory/reference/rest/v1/groups
@@ -24,7 +25,7 @@
* https://cloud.google.com/identity/docs/reference/rest/v1/groups
## Name guidelines
* https://support.google.com/a/answer/9193374?hl=en
* https://support.google.com/a/answer/9193374
## Query documentation
* https://developers.google.com/admin-sdk/directory/v1/guides/search-groups
@@ -563,3 +564,31 @@ Group,Name,ParentsCount,Parents,ParentsName
testgroup2@domain.com,Test - Group 2,2,testgroup1@domain.com|testgroup@domain.com,Test Group1|Test Group Org
testgroup2@domain.com,Test - Group 2,1,testgroup@domain.net,Test Group Net
```
## Display group counts
Display the number of groups.
```
gam print groups
[([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryGroupList>)]))|
(select <GroupEntity>)]
[emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
[descriptionmatchpattern [not] <RegularExpression>] (matchsetting [not] <GroupAttribute>)*
[admincreatedmatch <Boolean>]
showitemcountonly
```
Example
```
$ gam print groups showitemcountonly
Getting all Groups, may take some time on a large Google Workspace Account...
Got 200 Groups: 1aparents@domain.com - students-genderfood@domain.com
Got 238 Groups: students-worldculture@domain.com - xcarestaff@domain.com
238
```
The `Getting` and `Got` messages are written to stderr, the count is writtem to stdout.
To retrieve the count with `showitemcountonly`:
```
Linux/MacOS
count=$(gam print groups showitemcountonly)
Windows PowerShell
count = & gam print groups showitemcountonly
```

View File

@@ -264,9 +264,6 @@ writes the credentials into the file oauth2.txt.
```
admin@server:/Users/admin/bin/gamadv-xtd3$ ./gam oauth create
Select the authorized scopes by entering a number.
Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[*] 0) Calendar API (supports readonly)
[*] 1) Chrome Browser Cloud Management API (supports readonly)
[*] 2) Chrome Management API - AppDetails read only
@@ -274,7 +271,7 @@ Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[*] 4) Chrome Management API - read only
[*] 5) Chrome Policy API (supports readonly)
[*] 6) Chrome Printer Management API (supports readonly)
[ ] 7) Chrome Version History API
[*] 7) Chrome Version History API
[*] 8) Classroom API - Course Announcements (supports readonly)
[*] 9) Classroom API - Course Topics (supports readonly)
[*] 10) Classroom API - Course Work/Materials (supports readonly)
@@ -284,7 +281,7 @@ Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[*] 14) Classroom API - Profile Photos
[*] 15) Classroom API - Rosters (supports readonly)
[*] 16) Classroom API - Student Guardians (supports readonly)
[*] 17) Cloud Channel API (supports readonly)
[ ] 17) Cloud Channel API (supports readonly)
[*] 18) Cloud Identity - Inbound SSO Settings (supports readonly)
[*] 19) Cloud Identity Groups API (supports readonly)
[*] 20) Cloud Identity OrgUnits API (supports readonly)
@@ -314,15 +311,22 @@ Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[ ] 44) Pub / Sub API
[*] 45) Reports API - Audit Reports
[*] 46) Reports API - Usage Reports
[*] 47) Reseller API
[ ] 47) Reseller API
[*] 48) Site Verification API
[ ] 49) Sites API
[*] 50) Vault API (supports readonly)
s) Select all scopes
u) Unselect all scopes
e) Exit without changes
c) Continue to authorization
Select an unselected scope [ ] by entering a number; yields [*]
For scopes that support readonly, enter a number and an 'r' to grant read-only access; yields [R]
For scopes that support action, enter a number and an 'a' to grant action-only access; yields [A]
Clear read-only access [R] or action-only access [A] from a scope by entering a number; yields [*]
Unselect a selected scope [*] by entering a number; yields [ ]
Select all default scopes by entering an 's'; yields [*] for default scopes, [ ] for others
Unselect all scopes by entering a 'u'; yields [ ] for all scopes
Exit without changes/authorization by entering an 'e'
Continue to authorization by entering a 'c'
Note, if all scopes are selected, Google will probably generate an authorization error
Please enter 0-50[a|r] or s|u|e|c: c
Enter your Google Workspace admin email address? admin@domain.com
@@ -709,9 +713,6 @@ writes the credentials into the file oauth2.txt.
```
C:\GAMADV-XTD3>gam oauth create
Select the authorized scopes by entering a number.
Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[*] 0) Calendar API (supports readonly)
[*] 1) Chrome Browser Cloud Management API (supports readonly)
[*] 2) Chrome Management API - AppDetails read only
@@ -719,7 +720,7 @@ Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[*] 4) Chrome Management API - read only
[*] 5) Chrome Policy API (supports readonly)
[*] 6) Chrome Printer Management API (supports readonly)
[ ] 7) Chrome Version History API
[*] 7) Chrome Version History API
[*] 8) Classroom API - Course Announcements (supports readonly)
[*] 9) Classroom API - Course Topics (supports readonly)
[*] 10) Classroom API - Course Work/Materials (supports readonly)
@@ -729,7 +730,7 @@ Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[*] 14) Classroom API - Profile Photos
[*] 15) Classroom API - Rosters (supports readonly)
[*] 16) Classroom API - Student Guardians (supports readonly)
[*] 17) Cloud Channel API (supports readonly)
[ ] 17) Cloud Channel API (supports readonly)
[*] 18) Cloud Identity - Inbound SSO Settings (supports readonly)
[*] 19) Cloud Identity Groups API (supports readonly)
[*] 20) Cloud Identity OrgUnits API (supports readonly)
@@ -759,15 +760,22 @@ Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[ ] 44) Pub / Sub API
[*] 45) Reports API - Audit Reports
[*] 46) Reports API - Usage Reports
[*] 47) Reseller API
[ ] 47) Reseller API
[*] 48) Site Verification API
[ ] 49) Sites API
[*] 50) Vault API (supports readonly)
s) Select all scopes
u) Unselect all scopes
e) Exit without changes
c) Continue to authorization
Select an unselected scope [ ] by entering a number; yields [*]
For scopes that support readonly, enter a number and an 'r' to grant read-only access; yields [R]
For scopes that support action, enter a number and an 'a' to grant action-only access; yields [A]
Clear read-only access [R] or action-only access [A] from a scope by entering a number; yields [*]
Unselect a selected scope [*] by entering a number; yields [ ]
Select all default scopes by entering an 's'; yields [*] for default scopes, [ ] for others
Unselect all scopes by entering a 'u'; yields [ ] for all scopes
Exit without changes/authorization by entering an 'e'
Continue to authorization by entering a 'c'
Note, if all scopes are selected, Google will probably generate an authorization error
Please enter 0-50[a|r] or s|u|e|c: c
Enter your Google Workspace admin email address? admin@domain.com

View File

@@ -99,9 +99,6 @@ writes the credentials into the file oauth2.txt.
```
admin@server:/Users/admin/bin/gamadv-xtd3$ ./gam oauth create
Select the authorized scopes by entering a number.
Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[*] 0) Calendar API (supports readonly)
[*] 1) Chrome Browser Cloud Management API (supports readonly)
[*] 2) Chrome Management API - AppDetails read only
@@ -109,7 +106,7 @@ Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[*] 4) Chrome Management API - read only
[*] 5) Chrome Policy API (supports readonly)
[*] 6) Chrome Printer Management API (supports readonly)
[ ] 7) Chrome Version History API
[*] 7) Chrome Version History API
[*] 8) Classroom API - Course Announcements (supports readonly)
[*] 9) Classroom API - Course Topics (supports readonly)
[*] 10) Classroom API - Course Work/Materials (supports readonly)
@@ -119,7 +116,7 @@ Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[*] 14) Classroom API - Profile Photos
[*] 15) Classroom API - Rosters (supports readonly)
[*] 16) Classroom API - Student Guardians (supports readonly)
[*] 17) Cloud Channel API (supports readonly)
[ ] 17) Cloud Channel API (supports readonly)
[*] 18) Cloud Identity - Inbound SSO Settings (supports readonly)
[*] 19) Cloud Identity Groups API (supports readonly)
[*] 20) Cloud Identity OrgUnits API (supports readonly)
@@ -149,15 +146,22 @@ Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[ ] 44) Pub / Sub API
[*] 45) Reports API - Audit Reports
[*] 46) Reports API - Usage Reports
[*] 47) Reseller API
[ ] 47) Reseller API
[*] 48) Site Verification API
[ ] 49) Sites API
[*] 50) Vault API (supports readonly)
s) Select all scopes
u) Unselect all scopes
e) Exit without changes
c) Continue to authorization
Select an unselected scope [ ] by entering a number; yields [*]
For scopes that support readonly, enter a number and an 'r' to grant read-only access; yields [R]
For scopes that support action, enter a number and an 'a' to grant action-only access; yields [A]
Clear read-only access [R] or action-only access [A] from a scope by entering a number; yields [*]
Unselect a selected scope [*] by entering a number; yields [ ]
Select all default scopes by entering an 's'; yields [*] for default scopes, [ ] for others
Unselect all scopes by entering a 'u'; yields [ ] for all scopes
Exit without changes/authorization by entering an 'e'
Continue to authorization by entering a 'c'
Note, if all scopes are selected, Google will probably generate an authorization error
Please enter 0-50[a|r] or s|u|e|c: c
Enter your Google Workspace admin email address? admin@domain.com
@@ -382,9 +386,6 @@ writes the credentials into the file oauth2.txt.
```
C:\GAMADV-XTD3>gam oauth create
Select the authorized scopes by entering a number.
Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[*] 0) Calendar API (supports readonly)
[*] 1) Chrome Browser Cloud Management API (supports readonly)
[*] 2) Chrome Management API - AppDetails read only
@@ -392,7 +393,7 @@ Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[*] 4) Chrome Management API - read only
[*] 5) Chrome Policy API (supports readonly)
[*] 6) Chrome Printer Management API (supports readonly)
[ ] 7) Chrome Version History API
[*] 7) Chrome Version History API
[*] 8) Classroom API - Course Announcements (supports readonly)
[*] 9) Classroom API - Course Topics (supports readonly)
[*] 10) Classroom API - Course Work/Materials (supports readonly)
@@ -402,7 +403,7 @@ Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[*] 14) Classroom API - Profile Photos
[*] 15) Classroom API - Rosters (supports readonly)
[*] 16) Classroom API - Student Guardians (supports readonly)
[*] 17) Cloud Channel API (supports readonly)
[ ] 17) Cloud Channel API (supports readonly)
[*] 18) Cloud Identity - Inbound SSO Settings (supports readonly)
[*] 19) Cloud Identity Groups API (supports readonly)
[*] 20) Cloud Identity OrgUnits API (supports readonly)
@@ -432,15 +433,22 @@ Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[ ] 44) Pub / Sub API
[*] 45) Reports API - Audit Reports
[*] 46) Reports API - Usage Reports
[*] 47) Reseller API
[ ] 47) Reseller API
[*] 48) Site Verification API
[ ] 49) Sites API
[*] 50) Vault API (supports readonly)
s) Select all scopes
u) Unselect all scopes
e) Exit without changes
c) Continue to authorization
Select an unselected scope [ ] by entering a number; yields [*]
For scopes that support readonly, enter a number and an 'r' to grant read-only access; yields [R]
For scopes that support action, enter a number and an 'a' to grant action-only access; yields [A]
Clear read-only access [R] or action-only access [A] from a scope by entering a number; yields [*]
Unselect a selected scope [*] by entering a number; yields [ ]
Select all default scopes by entering an 's'; yields [*] for default scopes, [ ] for others
Unselect all scopes by entering a 'u'; yields [ ] for all scopes
Exit without changes/authorization by entering an 'e'
Continue to authorization by entering a 'c'
Note, if all scopes are selected, Google will probably generate an authorization error
Please enter 0-50[a|r] or s|u|e|c: c
Enter your Google Workspace admin email address? admin@domain.com

View File

@@ -213,6 +213,7 @@ Section: DEFAULT
todrive_timezone = ''
todrive_upload_nodata = true
todrive_user = ''
truncate_client_id = false
update_cros_ou_with_id = false
use_projectid_as_name = false
user_max_results = 500
@@ -334,18 +335,15 @@ writes the credentials into the file oauth2.txt.
admin@server:/Users/admin/bin/gamadv-xtd3$ rm -f /Users/admin/GAMConfig/oauth2.txt
admin@server:/Users/admin/bin/gamadv-xtd3$ ./gam version
WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/admin/GAMConfig/oauth2.txt, Not Found
GAMADV-XTD3 6.67.10 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.75.02 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.10.8 64-bit final
MacOS High Sierra 10.13.6 x86_64
Python 3.12.3 64-bit final
MacOS Sonoma 14.4.1 x86_64
Path: /Users/admin/bin/gamadv-xtd3
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain.com
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
admin@server:/Users/admin/bin/gamadv-xtd3$ ./gam oauth create
Select the authorized scopes by entering a number.
Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[*] 0) Calendar API (supports readonly)
[*] 1) Chrome Browser Cloud Management API (supports readonly)
[*] 2) Chrome Management API - AppDetails read only
@@ -353,7 +351,7 @@ Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[*] 4) Chrome Management API - read only
[*] 5) Chrome Policy API (supports readonly)
[*] 6) Chrome Printer Management API (supports readonly)
[ ] 7) Chrome Version History API
[*] 7) Chrome Version History API
[*] 8) Classroom API - Course Announcements (supports readonly)
[*] 9) Classroom API - Course Topics (supports readonly)
[*] 10) Classroom API - Course Work/Materials (supports readonly)
@@ -363,7 +361,7 @@ Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[*] 14) Classroom API - Profile Photos
[*] 15) Classroom API - Rosters (supports readonly)
[*] 16) Classroom API - Student Guardians (supports readonly)
[*] 17) Cloud Channel API (supports readonly)
[ ] 17) Cloud Channel API (supports readonly)
[*] 18) Cloud Identity - Inbound SSO Settings (supports readonly)
[*] 19) Cloud Identity Groups API (supports readonly)
[*] 20) Cloud Identity OrgUnits API (supports readonly)
@@ -393,15 +391,22 @@ Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[ ] 44) Pub / Sub API
[*] 45) Reports API - Audit Reports
[*] 46) Reports API - Usage Reports
[*] 47) Reseller API
[ ] 47) Reseller API
[*] 48) Site Verification API
[ ] 49) Sites API
[*] 50) Vault API (supports readonly)
s) Select all scopes
u) Unselect all scopes
e) Exit without changes
c) Continue to authorization
Select an unselected scope [ ] by entering a number; yields [*]
For scopes that support readonly, enter a number and an 'r' to grant read-only access; yields [R]
For scopes that support action, enter a number and an 'a' to grant action-only access; yields [A]
Clear read-only access [R] or action-only access [A] from a scope by entering a number; yields [*]
Unselect a selected scope [*] by entering a number; yields [ ]
Select all default scopes by entering an 's'; yields [*] for default scopes, [ ] for others
Unselect all scopes by entering a 'u'; yields [ ] for all scopes
Exit without changes/authorization by entering an 'e'
Continue to authorization by entering a 'c'
Note, if all scopes are selected, Google will probably generate an authorization error
Please enter 0-50[a|r] or s|u|e|c: c
Enter your Google Workspace admin email address? admin@domain.com
@@ -657,6 +662,7 @@ Section: DEFAULT
todrive_timezone = ''
todrive_upload_nodata = true
todrive_user = ''
truncate_client_id = false
update_cros_ou_with_id = false
use_projectid_as_name = false
user_max_results = 500
@@ -859,6 +865,7 @@ Section: DEFAULT
todrive_timezone = ''
todrive_upload_nodata = true
todrive_user = ''
truncate_client_id = false
update_cros_ou_with_id = false
use_projectid_as_name = false
user_max_results = 500
@@ -1002,18 +1009,15 @@ writes the credentials into the file oauth2.txt.
C:\GAMADV-XTD3>del C:\GAMConfig\oauth2.txt
C:\GAMADV-XTD3>gam version
WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found
GAMADV-XTD3 6.67.10 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.75.02 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.0 64-bit final
Python 3.12.3 64-bit final
Windows-10-10.0.17134 AMD64
Path: C:\GAMADV-XTD3
Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, customer_id: my_customer, domain.com
Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
C:\GAMADV-XTD3>gam oauth create
Select the authorized scopes by entering a number.
Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[*] 0) Calendar API (supports readonly)
[*] 1) Chrome Browser Cloud Management API (supports readonly)
[*] 2) Chrome Management API - AppDetails read only
@@ -1021,7 +1025,7 @@ Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[*] 4) Chrome Management API - read only
[*] 5) Chrome Policy API (supports readonly)
[*] 6) Chrome Printer Management API (supports readonly)
[ ] 7) Chrome Version History API
[*] 7) Chrome Version History API
[*] 8) Classroom API - Course Announcements (supports readonly)
[*] 9) Classroom API - Course Topics (supports readonly)
[*] 10) Classroom API - Course Work/Materials (supports readonly)
@@ -1031,7 +1035,7 @@ Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[*] 14) Classroom API - Profile Photos
[*] 15) Classroom API - Rosters (supports readonly)
[*] 16) Classroom API - Student Guardians (supports readonly)
[*] 17) Cloud Channel API (supports readonly)
[ ] 17) Cloud Channel API (supports readonly)
[*] 18) Cloud Identity - Inbound SSO Settings (supports readonly)
[*] 19) Cloud Identity Groups API (supports readonly)
[*] 20) Cloud Identity OrgUnits API (supports readonly)
@@ -1061,15 +1065,22 @@ Append an 'r' to grant read-only access or an 'a' to grant action-only access.
[ ] 44) Pub / Sub API
[*] 45) Reports API - Audit Reports
[*] 46) Reports API - Usage Reports
[*] 47) Reseller API
[ ] 47) Reseller API
[*] 48) Site Verification API
[ ] 49) Sites API
[*] 50) Vault API (supports readonly)
s) Select all scopes
u) Unselect all scopes
e) Exit without changes
c) Continue to authorization
Select an unselected scope [ ] by entering a number; yields [*]
For scopes that support readonly, enter a number and an 'r' to grant read-only access; yields [R]
For scopes that support action, enter a number and an 'a' to grant action-only access; yields [A]
Clear read-only access [R] or action-only access [A] from a scope by entering a number; yields [*]
Unselect a selected scope [*] by entering a number; yields [ ]
Select all default scopes by entering an 's'; yields [*] for default scopes, [ ] for others
Unselect all scopes by entering a 'u'; yields [ ] for all scopes
Exit without changes/authorization by entering an 'e'
Continue to authorization by entering a 'c'
Note, if all scopes are selected, Google will probably generate an authorization error
Please enter 0-50[a|r] or s|u|e|c: c
Enter your Google Workspace admin email address? admin@domain.com
@@ -1327,6 +1338,7 @@ Section: DEFAULT
todrive_timezone = ''
todrive_upload_nodata = true
todrive_user = ''
truncate_client_id = false
update_cros_ou_with_id = false
use_projectid_as_name = false
user_max_results = 500

View File

@@ -20,11 +20,12 @@
|--------------|------------|
| AppSheet | 101038 |
| Assured Controls | 101039 |
| Beyond Corp Enterprise | 101040 |
| Chrome Enterprise | 101040 |
| Cloud Identity Free | 101001 |
| Cloud Identity Premium | 101005 |
| Cloud Search | 101035 |
| Duet AI | 101047 |
| Education Endpoint Management | 101049 |
| Gemini | 101047 |
| Google Chrome Device Management | Google-Chrome-Device-Management |
| Google Drive Storage | Google-Drive-storage |
| Google Meet Global Dialing | 101036 |
@@ -42,15 +43,17 @@
| AppSheet Enterprise Standard | 1010380002 | appsheetstandard |
| AppSheet Enterprise Plus | 1010380003 | appsheetplus |
| Assured Controls | 1010390001 | assuredcontrols |
| Beyond Corp Enterprise | 1010400001 | bce |
| Chrome Enterprise Premium | 1010400001 | cep | chromeenterprisepremium |
| Cloud Identity Free | 1010010001 | cloudidentity |
| Cloud Identity Premium | 1010050001 | cloudidentitypremium |
| Cloud Search | 1010350001 | cloudsearch |
| Duet AI | 1010470001 | duetai |
| Endpoint Education Upgrade | 1010490001 | eeu |
| G Suite Basic | Google-Apps-For-Business | gsuitebasic |
| G Suite Business | Google-Apps-Unlimited | gsuitebusiness |
| G Suite Legacy | Google-Apps | standard |
| G Suite Lite | Google-Apps-Lite | gsuitelite |
| Gemini Business | 1010470003 | geminibiz
| Gemini Enterprise | 1010470001 | geminient | duetai |
| Google Apps Message Security | Google-Apps-For-Postini | postini |
| Google Chrome Device Management | Google-Chrome-Device-Management | cdm |
| Google Drive Storage 16TB | Google-Drive-storage-16TB | 16tb |
@@ -115,6 +118,7 @@
101040 |
101043 |
101047 |
101049 |
Google-Apps |
Google-Chrome-Device-Management |
Google-Drive-storage |
@@ -132,52 +136,58 @@
4tb | drive4tb | googledrivestorage4tb | Google-Drive-storage-4TB |
8tb | drive8tb | googledrivestorage8tb | Google-Drive-storage-8TB |
16tb | drive16tb | googledrivestorage16tb | Google-Drive-storage-16TB |
appsheetcore | 1010380001 |
appsheetstandard | appsheetenterprisestandard | 1010380002 |
appsheetplus | appsheetenterpriseplus | 1010380003 |
assuredcontrols | 1010390001 |
bce | beyondcorp | beyondcorpenterprise | 1010400001 |
appsheetcore | 1010380001 | AppSheet Core |
appsheetstandard | appsheetenterprisestandard | 1010380002 | AppSheet Enterprise Standard |
appsheetplus | appsheetenterpriseplus | 1010380003 | AppSheet Enterprise Plus |
assuredcontrols | 1010390001 | Assured Controls |
bce | beyondcorp | beyondcorpenterprise | cep | chromeenterprisepremium | 1010400001 | Chrome Enterprise Premium |
cdm | chrome | googlechromedevicemanagement | Google-Chrome-Device-Management |
cloudidentity | identity | 1010010001 |
cloudidentitypremium | identitypremium | 1010050001 |
cloudsearch | 1010350001 |
duetai | 1010470001 |
cloudidentity | identity | 1010010001 | Cloud Identity |
cloudidentitypremium | identitypremium | 1010050001 | Cloud Identity Premium |
cloudsearch | 1010350001 | Cloud Search |
eeu | 1010490001 | SKU Endpoint Education Upgrade |
geminibiz | 1010470003 | Gemini Business |
geminient| duetai | 1010470001 | Gemini Enterprise |
gsuitebasic | gafb | gafw | basic | Google-Apps-For-Business |
gsuitebusiness | gau | gsb | unlimited | Google-Apps-Unlimited |
gsuitebusinessarchived | gsbau | businessarchived | 1010340002 |
gsuiteenterprisearchived | gseau | enterprisearchived | 1010340001 |
gsuiteenterpriseeducation | gsefe | e4e | 1010310002 |
gsuiteenterpriseeducationstudent | gsefes | e4es | 1010310003 |
gsuitebusinessarchived | gsbau | businessarchived | 1010340002 | Google Workspace Business - Archived User |
gsuiteenterprisearchived | gseau | enterprisearchived | 1010340001 | Google Workspace Enterprise Plus - Archived User |
gsuiteenterpriseeducation | gsefe | e4e | 1010310002 | Google Workspace for Education Plus - Legacy |
gsuiteenterpriseeducationstudent | gsefes | e4es | 1010310003 | Google Workspace for Education Plus - Legacy (Student) |
gsuitegov | gafg | gsuitegovernment | Google-Apps-For-Government |
gsuitelite | gal | gsl | lite | Google-Apps-Lite |
gwep | workspaceeducationplus | 1010310008 |
gwepstaff | workspaceeducationplusstaff | 1010310009 |
gwepstudent | workspaceeducationplusstudent | 1010310010 |
gwes | workspaceeducationstandard | 1010310005 |
gwesstaff | workspaceeducationstandardstaff | 1010310006 |
gwesstudent | workspaceeducationstandardstudent | 1010310007 |
gwetlu | workspaceeducationupgrade | 1010370001 |
gwlabs | workspacelabs | 1010470002
meetdialing | googlemeetglobaldialing | 1010360001 |
gwep | workspaceeducationplus | 1010310008 | Google Workspace for Education Plus |
gwepstaff | workspaceeducationplusstaff | 1010310009 | Google Workspace for Education Plus (Staff) |
gwepstudent | workspaceeducationplusstudent | 1010310010 | Google Workspace for Education Plus (Extra Student)|
gwes | workspaceeducationstandard | 1010310005 | Google Workspace for Education Standard |
gwesstaff | workspaceeducationstandardstaff | 1010310006 | Google Workspace for Education Standard (Staff) |
gwesstudent | workspaceeducationstandardstudent | 1010310007 | Google Workspace for Education Standard (Extra Student)
gwetlu | workspaceeducationupgrade | 1010370001 | Google Workspace for Education: Teaching and Learning Upgrade |
gwlabs | workspacelabs | 1010470002 | Google Workspace Labs |
meetdialing | googlemeetglobaldialing | 1010360001 | Google Meet Global Dialing |
postini | gams | gsuitegams | gsuitepostini | gsuitemessagesecurity | Google-Apps-For-Postini |
standard | free | Google-Apps |
vault | googlevault | Google-Vault |
vfe | googlevaultformeremployee | Google-Vault-Former-Employee |
voicepremier | gvpremier | googlevoicepremier | 1010330002 |
voicestandard | gvstandard | googlevoicestandard | 1010330004 |
voicestarter | gvstarter | googlevoicestarter | 1010330003 |
wsas | plusstorage | 1010430001 |
wsbizplus | workspacebusinessplus | 1010020025 |
wsbizplusarchived | workspacebusinessplusarchived | 1010340003 |
wsbizstan | workspacebusinessstandard | 1010020028 |
wsbizstarter | workspacebusinessstarter | wsbizstart | 1010020027 |
wsentess | workspaceenterpriseessentials | 1010060003 |
wsentplus | workspaceenterpriseplus | gae | gse | enterprise | gsuiteenterprise | 1010020020 |
wsentstan | workspaceenterprisestandard | 1010020026 |
wsentstanarchived | workspaceenterprisestandardarchived | 1010340004 |
wsentstarter | workspaceenterprisestarter | 1010020029 | wes |
wsess | workspaceesentials | gsuiteessentials | essentials | d4e | driveenterprise | drive4enterprise | 1010060001 |
wsflw | workspacefrontline | workspacefrontlineworker | 1010020030
voicepremier | gvpremier | googlevoicepremier | 1010330002 | Google Voice Premier
voicestandard | gvstandard | googlevoicestandard | 1010330004 | Google Voice Standard |
voicestarter | gvstarter | googlevoicestarter | 1010330003 | Google Voice Starter |
wsas | plusstorage | 1010430001 | Google Workspace Additional Storage |
wsbizplus | workspacebusinessplus | 1010020025 | Google Workspace Business Plus |
wsbizplusarchived | workspacebusinessplusarchived | 1010340003 | Google Workspace Business Plus - Archived User |
wsbizstan | workspacebusinessstandard | 1010020028 | Google Workspace Business Standard }
wsbizstanarchived | workspacebusinessstandardarchived | 1010340006 | Google Workspace Business Standard - Archived User |
wsbizstarter | workspacebusinessstarter | wsbizstart | 1010020027 | Google Workspace Business Starter |
wsbizstarterarchived | workspacebusinessstarterarchived | 1010340005 | Google Workspace Business Starter - Archived User |
wsentess | workspaceenterpriseessentials | 1010060003 | Google Workspace Enterprise Essentials |
wsentplus | workspaceenterpriseplus | gae | gse | enterprise | gsuiteenterprise | 1010020020 | Google Workspace Enterprise Plus |
wsentstan | workspaceenterprisestandard | 1010020026 | Google Workspace Enterprise Standard |
wsentstanarchived | workspaceenterprisestandardarchived | 1010340004 | Google Workspace Enterprise Standard - Archived User |
wsentstarter | workspaceenterprisestarter | wes | 1010020029 | Workspace Enterprise Starter |
wsess | workspaceesentials | gsuiteessentials | essentials | d4e | driveenterprise | drive4enterprise | 1010060001 | Google Workspace Essentials |
wsessplus | workspaceessentialsplus | 1010060005 | Google Workspace Essentials Plus |
wsflw | workspacefrontline | workspacefrontlineworker | 1010020030 | Google Workspace Frontline Starter |
wsflwstan | workspacefrontlinestan | workspacefrontlineworkerstan | 1010020031 | Google Workspace Frontline Standard
<SKUIDList> ::= "<SKUID>(,<SKUID>)*"
```
## Notes

View File

@@ -34,6 +34,7 @@
<DeviceUserList> ::= "<DeviceUserID>(,<DeviceUserID>)*"
<DomainNameList> ::= "<DomainName>(,<DomainName>)*"
<DriveFileACLRoleList> ::= "<DriveFileACLRole>(,<DriveFileACLRole>)*"
<DriveFileACLTypeList> ::= "<DriveFileACLType>(,<DriveFileACLType>)*"
<DriveFileList> ::= "<DriveFileItem>(,<DriveFileItem>)*"
<DriveFilePermissionList> ::= "<DriveFilePermission>(,<DriveFilePermission>)*"
<DriveFilePermissionIDList> ::= "<DriveFilePermissionID>(,<DriveFilePermissionID>)*"
@@ -42,6 +43,7 @@
<DriveFolderNameList> ::= "<DriveFolderName>(,<DriveFolderName>)*"
<DriveLabelIDList> ::= "<DriveLabelID>(,<DriveLabelID>)*"
<DriveLabelNameList> ::= "<DriveLabelName>(,<DriveLabelName>)*"
<DriveLabelPermissionNameList> ::= "<DriveLabelPermissionName>(,<DriveLabelPermissionName>)*"
<DriveLabelFieldIDList> ::= "<DriveLabelFieldID>(,<DriveLabelFieldID>)*"
<DriveLabelSelectionIDList> ::= "<DriveLabelSelectionID>(,<DriveLabelSelectionID>)*"
<EmailAddressList> ::= "<EmailAddress>(,<EmailAddress>)*"
@@ -67,6 +69,7 @@
<MatterStateList> ::= "<MatterState>(,<MatterState>)*"
<MessageIDList> ::= "<MessageID>(,<MessageID>)*"
<MimeTypeList> ::= "<MimeType>(,<MimeType>)*"
<MimeTypeNameList> ::= "<MimeTypeName>(,<MimeTypeName>)*"
<NamespaceList> ::= "<Namespace>(,<Namespace>)*"
<NotesNameList> ::= "<NotesName>(,<NotesName>)*"
<OrgUnitList> ::= "<OrgUnitItem>(,<OrgUnitItem>)*"

View File

@@ -56,6 +56,7 @@ The only `<VariableNames>` recognized in this `<Section>` are:
* `csv_output_row_drop_filter`
* `csv_output_row_drop_filter_mode`
* `csv_output_row_limit`
* `csv_output_sort_headers`
### Select input filter section
Select an input filter section from gam.cfg and process a GAM command using values from that section.
@@ -113,7 +114,7 @@ You can redirect stdout and stderr to null and stderr can be redirected to stdou
<Redirect> ::=
redirect csv <FileName> [multiprocess] [append] [noheader] [charset <Charset>]
[columndelimiter <Character>] [noescapechar <Boolean>] [quotechar <Character>]
[timestampcolumn <String>]
[sortheaders <StringList>] [timestampcolumn <String>]
[todrive <ToDriveAttribute>*] |
redirect stdout <FileName> [multiprocess] [append] |
redirect stdout null [multiprocess] |
@@ -151,6 +152,9 @@ The `quotechar <Character>` subargument sets the character used to quote fields
that contaim special charactere; the default value is the value of `csv_output_quote_char` in `gam.cfg`
which defaults to double quote.
The `sortheaders <StringList>` argument causes GAM to sort CSV output rows by the column headers specified in `<StringList>`.
The column headers are case insensitive and if column header does not appear in the CSV output, it is ignored.
The `timestampcolumn <String>` adds a column named `<String>` to the CSV file; the value is the
timestamp of when the GAM command started.

View File

@@ -5,6 +5,7 @@
- [Manage mobile devices](#manage-mobile-devices)
- [Display mobile devices](#display-mobile-devices)
- [Print mobile devices](#print-mobile-devices)
- [Display mobile device counts](#display-mobile-device-counts)
## API documentation
* https://developers.google.com/admin-sdk/directory/reference/rest/v1/mobiledevices
@@ -150,3 +151,27 @@ When using the `formatjson` option, double quotes are used extensively in the da
The `quotechar <Character>` option allows you to choose an alternate quote character, single quote for instance, that makes for readable/processable output.
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.
## Display mobile device counts
Display the number of mobile devices.
```
gam print mobile
[(query <QueryMobile>)|(queries <QueryMobileList>) (querytime<String> <Time>)*]
showitemcountonly
```
Example
```
$ gam print mobile showitemcountonly
Getting all Mobile Devices, may take some time on a large Google Workspace Account...
Got 100 Mobile Devices...
Got 115 Mobile Devices
115
```
The `Getting` and `Got` messages are written to stderr, the count is writtem to stdout.
To retrieve the count with `showitemcountonly`:
```
Linux/MacOS
count=$(gam print mobile showitemcountonly)
Windows PowerShell
count = & gam print mobile showitemcountonly
```

View File

@@ -13,6 +13,7 @@
- [Synchronize ChromeOS devices with an organizational unit](#synchronize-chromeos-devices-with-an-organizational-unit)
- [Display organizational units](#display-organizational-units)
- [Print organizational units](#print-organizational-units)
- [Display organizational unit counts](#display-organizational-unit-counts)
- [Display indented organizational unit tree](#display-indented-organizational-unit-tree)
- [Special case handling for large number of organizational units](#special-case-handling-for-large-number-of-organizational-units)
@@ -191,6 +192,7 @@ By default, all users of the org units are displayed:
* `nousers` - Don't display users of the org units
* `notsuspended` - Display non-suspended users of the org units
* `suspended` - Display suspended users of the org units
* `children|child` - Display users in any child org unit
## Print organizational units
This command displays information in CSV format.
@@ -237,6 +239,30 @@ Get file count summaries by OU; top level selector is ou, sub level selectors ar
gam redirect csv ./TopLevelOUs.csv print ous showparent toplevelonly parentselector ou childselector ou_and_children fields orgunitpath
gam redirect csv ./FileCounts.csv multiprocess csv ./TopLevelOUs.csv gam "~orgUnitSelector" "~orgUnitPath" print filecounts excludetrashed summary only summaryuser "~orgUnitPath"
```
## Display organizational unit counts
Display the number of organizational units.
```
gam print orgs|ous
[fromparent <OrgUnitItem>] [showparent [Boolean>]] [toplevelonly]
showitemcountonly
```
Example
```
$ gam print orgs showitemcountonly
Getting all Organizational Units, may take some time on a large Google Workspace Account...
Got 98 Organizational Units
98
```
The `Getting` and `Got` messages are written to stderr, the count is writtem to stdout.
To retrieve the count with `showitemcountonly`:
```
Linux/MacOS
count=$(gam print orgs showitemcountonly)
Windows PowerShell
count = & gam print orgs showitemcountonly
```
## Display indented organizational unit tree
```
gam show orgtree [fromparent <OrgUnitItem>] [batchsuborgs [<Boolean>]]

View File

@@ -6,6 +6,7 @@ Thank you.
* Gabriel Clifton - https://docs.google.com/document/d/1p32QOBTr89GaG7RfCafSbFuhlUQ9r3qBM_666E0xvQM/edit
* Steve Larsen - https://docs.google.com/spreadsheets/d/1MzzA-u-cmoQcJnQOovCnZcEKMjvOyFhfkdFdf10X_GI/edit
* Iain Macleod - https://docs.google.com/document/d/1QxWAPdhROcx70OXLpSD9Trh3vs-nJKSMiaMZCTwOOTg/edit?pli=1#heading=h.2a2azzpy36k0
* Kevin Melillo - https://github.com/KevinMelilloIEEE/gam-script
* James Seymour - https://sites.google.com/jis.edu.bn/gam-commands/home
* Amado Tejada - https://github.com/amadotejada/GAMpass
@@ -13,3 +14,4 @@ Thank you.
* Goldy Arora - https://www.goldyarora.com/license-notifier/
* Paul Ogier (Taming.Tech) - GAMADV-XTD3 Tutorials https://www.youtube.com/watch?v=g9LDeyXQNLI&list=PL_dLiK09pJVhKJxZHNk9CHK0q5hkZ856w
* Paul Ogier (Taming.Tech) - GAMADV-XTD3 Course on Udemy https://taming.tech/GAMCourse
* Paul Ogier (Taming.Tech) - https://taming.tech/taming-gam-a-practical-guide-to-gam-and-gamadv-xtd3/

View File

@@ -18,7 +18,10 @@
contributor|editor|writer|
manager|organizer|owner|
reader|viewer
<DriveFileACLRoleList> ::= "<DriveFileACLRole>(,<DriveFileACLRole>)*"
<DriveFileACLType> ::= anyone|domain|group|user
<DriveFileACLTypeList> ::= "<DriveFileACLType>(,<DriveFileACLType>)*"
<EmailAddress> ::= <String>@<DomainName>
<EmailAddressList> ::= "<EmailAddress>(,<EmailAddress>)*"
@@ -31,13 +34,15 @@
<PermissionMatch> ::=
pm|permissionmatch [not]
[type <DriveFileACLType>] [role|notrole <DriveFileACLRole>]
[type|nottype <DriveFileACLType>] [role|notrole <DriveFileACLRole>]
[typelist|nottypelist <DriveFileACLTypeList>] [rolelist|notrolelist <DriveFileACLRoleList>]
[allowfilediscovery|withlink <Boolean>]
[emailaddress <RegularExpression>] [emailaddressList <EmailAddressList>]
[permissionidlist <PermissionIDList>
[name|displayname <String>]
[domain|notdomain <RegularExpression>] [domainlist|notdomainlist <DomainNameList>]
[expirationstart <Time>] [expirationend <Time>]
[deleted <Boolean>] [inherited <Boolean>]
[deleted <Boolean>] [inherited <Boolean>] [pmtype member|file]
em|endmatch
<PermissionMatchMode> ::=
pmm|permissionmatchmode or|and
@@ -71,12 +76,18 @@ In the `print/show drivefileacls` and `create/delete permissions` commands you c
## Define a Match
* `pm|permissionmatch` - Start of permission match definition.
* `not` - Negate the match.
* `type <DriveFileACLType>` - The type of the grantee.
* `role <DriveFileACLRole>` - The role granted by this permission.
* `notrole <DriveFileACLRole>` - The role granted by this permission.
* `type <DriveFileACLType>` - The type of the grantee must match.
* `nottype <DriveFileACLType>` - The type of the grantee must not match.
* `typelist <DriveFileACLTypeList>` - The type of the grantee must match any value in the list.
* `nottypelist <DriveFileACLTypeList>` - The type of the grantee must not match any value in the list.
* `role <DriveFileACLRole>` - The role granted by this permission must match.
* `notrole <DriveFileACLRole>` - The role granted by this permission must not match.
* `rolelist <DriveFileACLRoleList>` - The role granted by this permission must match any value in the list..
* `notrolelist <DriveFileACLRoleList>` - The role granted by this permission must not match any value in the list..
* `allowfilediscovery|withlink <Boolean>` - Whether a link is required or whether the file can be discovered through search.
* `emailaddress <RegularExpression>` - For types user and group, the required email address.
* `emailaddresslist <EmailAddressList>` - For types user and group, a list of required email addresses; any one of which must match.
* `permissionidlist <PermissionIDListList>` - A list of required permission IDs; any one of which must match.
* `name|displayname <RegularExpression>` - For types domain, user and group, the displayable name.
* `domain <RegularExpression>` - For type domain, the required domain name. For types user and group, the required domain name in the email address.
* `notdomain <RegularExpression>` - For type domain, any domain name that doesn't match. For types user and group, any domain name that doesn't match in the email address.
@@ -86,6 +97,7 @@ In the `print/show drivefileacls` and `create/delete permissions` commands you c
* `expirationend <Time>` - For types user and group, will the permission expire before or on <Time>.
* `deleted <Boolean>` - For types user and groups, has the user or group been deleted.
* `inherited <Boolean>` - For Shared Drive files/folders, is the permission inherited
* `pmtype member|file` - For Shared Drive files/folders, is the permission derived from membership or explicitly granted.
* `em|endmatch` - End of permission match definition
## File Selection Examples

View File

@@ -143,7 +143,8 @@ Get Shared Drives ID and Name
```
gam redirect csv ./SharedDrives.csv print shareddrives fields id,name
```
Options:
Options for the `gam report drive` commands below:
* `maxactivities 1` - Limits the number of activities displayed for Shared Drives with activity.
* `shownoactivities` - Displays a row for Shared Drives with no activity.
* `addcsvdata shared_drive_id "~id"` adds the Shared Drive ID to the output.

View File

@@ -1,6 +1,7 @@
# Reseller
- [API documentation](#api-documentation)
- [Notes](#notes)
- [Manage Multiple Domains](#manage-multiple-domains)
- [Definitions](#definitions)
- [Manage Resold Customers](#manage-resold-customers)
- [Display Resold Customers](#display-resold-customers)
@@ -25,6 +26,11 @@ Prior to version 6.50.00, this is how the `seats <NumberOfSeats> <MaximumNumberO
Now, you can still use the above option which has been corrected or you can specify `seats <Number>` which will be properly passed in the correct form to the API based on plan name.
## Manage Multiple Domains
Thanks to Duncan Isaksen-Loxton for a script to help manage multiple domains.
* See: https://gist.github.com/65/b5e9cee9b5812b487b8ae3e8256e262b
## Definitions
```
<CustomerID> ::= <String>
@@ -53,46 +59,58 @@ Now, you can still use the above option which has been corrected or you can spec
4tb | drive4tb | googledrivestorage4tb | Google-Drive-storage-4TB |
8tb | drive8tb | googledrivestorage8tb | Google-Drive-storage-8TB |
16tb | drive16tb | googledrivestorage16tb | Google-Drive-storage-16TB |
assuredcontrols | 1010390001 |
bce | beyondcorp | beyondcorpenterprise | 1010400001 |
appsheetcore | 1010380001 | AppSheet Core |
appsheetstandard | appsheetenterprisestandard | 1010380002 | AppSheet Enterprise Standard |
appsheetplus | appsheetenterpriseplus | 1010380003 | AppSheet Enterprise Plus |
assuredcontrols | 1010390001 | Assured Controls |
bce | beyondcorp | beyondcorpenterprise | 1010400001 | Beyond Corp Enterprise |
cdm | chrome | googlechromedevicemanagement | Google-Chrome-Device-Management |
cloudidentity | identity | 1010010001 |
cloudidentitypremium | identitypremium | 1010050001 |
cloudsearch | 1010350001 |
cloudidentity | identity | 1010010001 | Cloud Identity |
cloudidentitypremium | identitypremium | 1010050001 | Cloud Identity Premium |
cloudsearch | 1010350001 | Cloud Search |
eeu | 1010490001 | SKU Endpoint Education Upgrade |
geminibiz | 1010470003 | Gemini Business |
geminient| duetai | 1010470001 | Gemini Enterprise |
gsuitebasic | gafb | gafw | basic | Google-Apps-For-Business |
gsuitebusiness | gau | gsb | unlimited | Google-Apps-Unlimited |
gsuitebusinessarchived | gsbau | businessarchived | 1010340002 |
gsuiteenterprisearchived | gseau | enterprisearchived | 1010340001 |
gsuiteenterpriseeducation | gsefe | e4e | 1010310002 |
gsuiteenterpriseeducationstudent | gsefes | e4es | 1010310003 |
gsuitebusinessarchived | gsbau | businessarchived | 1010340002 | Google Workspace Business - Archived User |
gsuiteenterprisearchived | gseau | enterprisearchived | 1010340001 | Google Workspace Enterprise Plus - Archived User |
gsuiteenterpriseeducation | gsefe | e4e | 1010310002 | Google Workspace for Education Plus - Legacy |
gsuiteenterpriseeducationstudent | gsefes | e4es | 1010310003 | Google Workspace for Education Plus - Legacy (Student) |
gsuitegov | gafg | gsuitegovernment | Google-Apps-For-Government |
gsuitelite | gal | gsl | lite | Google-Apps-Lite |
gwep | workspaceeducationplus | 1010310008 |
gwepstaff | workspaceeducationplusstaff | 1010310009 |
gwepstudent | workspaceeducationplusstudent | 1010310010 |
gwes | workspaceeducationstandard | 1010310005 |
gwesstaff | workspaceeducationstandardstaff | 1010310006 |
gwesstudent | workspaceeducationstandardstudent | 1010310007 |
gwetlu | workspaceeducationupgrade | 1010370001 |
meetdialing | googlemeetglobaldialing | 1010360001 |
gwep | workspaceeducationplus | 1010310008 | Google Workspace for Education Plus |
gwepstaff | workspaceeducationplusstaff | 1010310009 | Google Workspace for Education Plus (Staff) |
gwepstudent | workspaceeducationplusstudent | 1010310010 | Google Workspace for Education Plus (Extra Student)|
gwes | workspaceeducationstandard | 1010310005 | Google Workspace for Education Standard |
gwesstaff | workspaceeducationstandardstaff | 1010310006 | Google Workspace for Education Standard (Staff) |
gwesstudent | workspaceeducationstandardstudent | 1010310007 | Google Workspace for Education Standard (Extra Student)
gwetlu | workspaceeducationupgrade | 1010370001 | Google Workspace for Education: Teaching and Learning Upgrade |
gwlabs | workspacelabs | 1010470002 | Google Workspace Labs |
meetdialing | googlemeetglobaldialing | 1010360001 | Google Meet Global Dialing |
postini | gams | gsuitegams | gsuitepostini | gsuitemessagesecurity | Google-Apps-For-Postini |
standard | free | Google-Apps |
vault | googlevault | Google-Vault |
vfe | googlevaultformeremployee | Google-Vault-Former-Employee |
voicepremier | gvpremier | googlevoicepremier | 1010330002 |
voicestandard | gvstandard | googlevoicestandard | 1010330004 |
voicestarter | gvstarter | googlevoicestarter | 1010330003 |
wsbizplus | workspacebusinessplus | 1010020025 |
wsbizplusarchived | workspacebusinessplusarchived | 1010340003 |
wsbizstan | workspacebusinessstandard | 1010020028 |
wsbizstarter | workspacebusinessstarter | wsbizstart | 1010020027 |
wsentess | workspaceenterpriseessentials | 1010060003 |
wsentplus | workspaceenterpriseplus | gae | gse | enterprise | gsuiteenterprise | 1010020020 |
wsentstan | workspaceenterprisestandard | 1010020026 |
wsentstanarchived | workspaceenterprisestandardarchived | 1010340004 |
wsentstarter | workspaceenterprisestarter | 1010020029 | wes |
wsess | workspaceesentials | gsuiteessentials | essentials | d4e | driveenterprise | drive4enterprise | 1010060001 |
wsflw | workspacefrontline | workspacefrontlineworker | 1010020030
voicepremier | gvpremier | googlevoicepremier | 1010330002 | Google Voice Premier
voicestandard | gvstandard | googlevoicestandard | 1010330004 | Google Voice Standard |
voicestarter | gvstarter | googlevoicestarter | 1010330003 | Google Voice Starter |
wsas | plusstorage | 1010430001 | Google Workspace Additional Storage |
wsbizplus | workspacebusinessplus | 1010020025 | Google Workspace Business Plus |
wsbizplusarchived | workspacebusinessplusarchived | 1010340003 | Google Workspace Business Plus - Archived User |
wsbizstan | workspacebusinessstandard | 1010020028 | Google Workspace Business Standard }
wsbizstanarchived | workspacebusinessstandardarchived | 1010340006 | Google Workspace Business Standard - Archived User |
wsbizstarter | workspacebusinessstarter | wsbizstart | 1010020027 | Google Workspace Business Starter |
wsbizstarterarchived | workspacebusinessstarterarchived | 1010340005 | Google Workspace Business Starter - Archived User |
wsentess | workspaceenterpriseessentials | 1010060003 | Google Workspace Enterprise Essentials |
wsentplus | workspaceenterpriseplus | gae | gse | enterprise | gsuiteenterprise | 1010020020 | Google Workspace Enterprise Plus |
wsentstan | workspaceenterprisestandard | 1010020026 | Google Workspace Enterprise Standard |
wsentstanarchived | workspaceenterprisestandardarchived | 1010340004 | Google Workspace Enterprise Standard - Archived User |
wsentstarter | workspaceenterprisestarter | wes | 1010020029 | Workspace Enterprise Starter |
wsess | workspaceesentials | gsuiteessentials | essentials | d4e | driveenterprise | drive4enterprise | 1010060001 | Google Workspace Essentials |
wsessplus | workspaceessentialsplus | 1010060005 | Google Workspace Essentials Plus |
wsflw | workspacefrontline | workspacefrontlineworker | 1010020030 | Google Workspace Frontline Starter |
wsflwstan | workspacefrontlinestan | workspacefrontlineworkerstan | 1010020031 | Google Workspace Frontline Standard
```
## Manage Resold Customers
```

View File

@@ -1,6 +1,7 @@
# Resources
- [API documentation](#api-documentation)
- [Definitions](#definitions)
- [Region Codes](#region-codes)
- [Special quoting](#special-quoting)
- [Manage buildings](#manage-buildings)
- [Display buildings](#display-buildings)
@@ -8,6 +9,7 @@
- [Display features](#display-features)
- [Manage resources](#manage-resources)
- [Display resources](#display-resources)
- [Display resource counts](#display-resource-counts)
- [Manage resource calendar ACLs](#manage-resource-calendar-acls)
- [Display resource calendar ACLs](#display-resource-calendar-acls)
@@ -120,6 +122,252 @@ See [Collections of Items](Collections-of-Items)
uservisibledescription
<ResourceFieldNameList> ::= "<ResourceFieldName>(,<ResourceFieldName>)*"
```
## Region Codes
| Region | Code |
|--------|------|
| Afghanistan | AF |
| Aland Islands | AX |
| Albania | AL |
| Algeria | DZ |
| American Samoa | AS |
| Andorra | AD |
| Angola | AO |
| Anguilla | AI |
| Antarctica | AQ |
| Antigua & Barbuda | AG |
| Argentina | AR |
| Armenia | AM |
| Aruba | AW |
| Ascension Island | AC |
| Australia | AU |
| Austria | AT |
| Azerbaijan | AZ |
| Bahamas | BS |
| Bahrain | BH |
| Bangladesh | BD |
| Barbados | BB |
| Belarus | BY |
| Belgium | BE |
| Belize | BZ |
| Benin | BJ |
| Bermuda | BM |
| Bhutan | BT |
| Bolivia | BO |
| Bosnia & Herzegovina | BA |
| Botswana | BW |
| Bouvet Island | BV |
| Brazil | BR |
| British Indian Ocean Territory | IO |
| British Virgin Islands | VG |
| Brunei | BN |
| Bulgaria | BG |
| Burkina Faso | BF |
| Burundi | BI |
| Cambodia | KH |
| Cameroon | CM |
| Canada | CA |
| Canary Islands | IC |
| Cape Verde | CV |
| Caribbean Netherlands | BQ |
| Cayman Islands | KY |
| Central African Republic | CF |
| Ceuta & Melilla | EA |
| Chad | TD |
| Chile | CL |
| China | CN |
| Christmas Island | CX |
| Clipperton Island | CP |
| Cocos (Keeling) Islands | CC |
| Columbia | CO |
| Comoros | KM |
| Congo - Brazzaville | CG |
| Congo - Kinshasa | CD |
| Cook Islands | CK |
| Costa Rica | CR |
| Cote dIvoire | CI |
| Croatia | HR |
| Cuba | CU |
| Curacao | CW |
| Cyprus | CY |
| Czech Republic | CZ |
| Falkland Islands | FK |
| Faroe Islands | FO |
| Fiji | FJ |
| Finland | FI |
| France | FR |
| Gabon | GA |
| Gambia | GM |
| Georgia | GE |
| Germany | DE |
| Ghana | GH |
| Gibraltar | GI |
| Greece | GR |
| Greenland | GL |
| Grenada | GD |
| Guadeloupe | GP |
| Guam | GU |
| Guatemala | GT |
| Guernsey | GG |
| Guinea | GN |
| Guinea-Bissau | GW |
| Guyana | GY |
| Haiti | HT |
| Heard & McDonald Islands | HM |
| Honduras | HN |
| Hong Kong SAR China | HK |
| Hungary | HU |
| Iceland | IS |
| India | IN |
| Indonesia | ID |
| Iran | IR |
| Iraq | IQ |
| Ireland | IE |
| Isle of Man | IM |
| Israel | IL |
| Italy | IT |
| Jamaica | JM |
| Japan | JP |
| Jersey | JE |
| Jordan | JO |
| Kazakhstan | KZ |
| Kenya | KE |
| Kiribati | KI |
| Kosovo | XK |
| Kuwait | KW |
| Kyrgyzstan | KG |
| Laos | LA |
| Latvia | LV |
| Lebanon | LB |
| Lesotho | LS |
| Liberia | LR |
| Libya | LY |
| Liechtenstein | LI |
| Lithuania | LT |
| Luxembourg | LU |
| Macau SAR China | MO |
| Macedonia | MK |
| Madagascar | MG |
| Malawi | MW |
| Malaysia | MY |
| Maldives | MV |
| Mali | ML |
| Malta | MT |
| Marshall Islands | MH |
| Martinique | MQ |
| Mauritania | MR |
| Mauritius | MU |
| Mayotte | YT |
| Mexico | MX |
| Micronesia | FM |
| Moldova | MD |
| Monaco | MC |
| Mongolia | MN |
| Montenegro | ME |
| Montserrat | MS |
| Morocco | MA |
| Mozambique | MZ |
| Myanmar | MM |
| Namibia | NA |
| Nauru | NR |
| Nepal | NP |
| Netherlands | NL |
| New Caledonia | NC |
| New Zealand | NZ |
| Nicaragua | NI |
| Niger | NE |
| Nigeria | NG |
| Niue | NU |
| Norfolk Island | NF |
| North Korea | KP |
| Northern Mariana Islands | MP |
| Norway | NO |
| Oman | OM |
| Pakistan | PK |
| Palau | PW |
| Palestinia Territories | PS |
| Panama | PA |
| Papua New Guinea | PG |
| Paraguay | PY |
| Peru | PE |
| Philippines | PH |
| Pitcairn Islands | PN |
| Poland | PL |
| Portugal | PT |
| Puerto Rico | PR |
| Qatar | QA |
| Reunion | RE |
| Romania | RO |
| Russia | RU |
| Rwanda | RW |
| Samoa | WS |
| San Marino | SM |
| Sao Tomm & Principe | ST |
| Saudi Arabia | SA |
| Senegal | SN |
| Serbia | RS |
| Seychelles | SC |
| Sierra Leone | SL |
| Singapore | SG |
| Sint Maarten | SX |
| Slovakia | SK |
| Slovenia | SI |
| Solomon Islands | SB |
| Somalia | SO |
| South Africa | ZA |
| South Georgia & South Sandwich Islands | GS |
| South Korea | KR |
| South Sudan | SS |
| Spain | ES |
| Sri Lanka | LK |
| St. Barthelemy | BL |
| St. Helena | SH |
| St. Kitts & Nevis | KN |
| St. Lucia | LC |
| St. Martin | MF |
| St. Pierre & Miquelon | PM |
| St. Vincent & Grenadines | VC |
| Sudan | SD |
| Suriname | SR |
| Svalbard & Jan Mayen | SJ |
| Swaziland | SZ |
| Sweden | SE |
| Switzerland | CH |
| Syria | SY |
| Taiwan | TW |
| Tajikistan | TJ |
| Tanzania | TZ |
| Thailand | TH |
| Timor-Leste | TL |
| Togo | TG |
| Tokelau | TK |
| Tonga | TO |
| Trinidad & Tobago | TT |
| Tristan da Cunha | TA |
| Tunisia | TN |
| Turkey | TR |
| Turkmenistan | TM |
| Turks & Caicos Islands | TC |
| Tuvalu | TV |
| U.S. Outlying Islands | UM |
| U.S. Virgin Islands | VI |
| Uganda | UG |
| Ukraine | UA |
| United Arab Emirates | AE |
| United Kingdom | GB |
| United States | US |
| Unknown Region | ZZ |
| Uraguay | UY |
| Uzbekistan | UZ |
| Vanuatu | VU |
| Vatican City | VA |
| Venezuela | VE |
| Vietnam | VN |
| Yemen | YE |
| Zambia | ZM |
| Zimbabwe | ZW |
## Special quoting
When entering `<FeatureNameList>` with `<FeatureName>s`containing spaces, enclose the list in `"` and the names containing spaces in `'`.
```
@@ -132,10 +380,8 @@ When creating a building, at a minimum you must enter `address|addresslines` and
* Enter a single-line address as `address "123 Main Street"`
* Enter a multi-line address as `addresslines "123 Main Street\nAnytown, US"`
For `country|regioncode` see: http://www.unicode.org/cldr/charts/30/supplemental/territory_information.html
```
gam create|add building <BuildIngID> <Name> <BuildingAttribute>*
gam create|add building <Name> <BuildingAttribute>*
gam update building <BuildIngID> <BuildingAttribute>*
gam delete building <BuildingID>
```
@@ -245,6 +491,30 @@ Print all resources and their owners.
gam config csv_output_row_filter "role:regex:owner" redirect csv Resource.csv print resources acls
```
## Display resource counts
Display the number of mobile devices.
```
gam print resources
[query <String>]
showitemcountonly
```
Example
```
$ gam print resources showitemcountonly
Getting all Resource Calendars, may take some time on a large Google Workspace Account...
Got 32 Resource Calendars: Back 50 - Video Cameras Class Set
32
```
The `Getting` and `Got` messages are written to stderr, the count is writtem to stdout.
To retrieve the count with `showitemcountonly`:
```
Linux/MacOS
count=$(gam print resources showitemcountonly)
Windows PowerShell
count = & gam print resources showitemcountonly
```
## Manage resource calendar ACLs
These commands operate on a single resource calendar.
```

View File

@@ -241,7 +241,7 @@ Your HTML message will contain lines like this:
<img src="cid:image2"/>
```
Your command line will have: `embedimage file1.jpg image1` embedimage file2.jpg image2`
Your command line will have: `embedimage file1.jpg image1 embedimage file2.jpg image2`
## Send an email from a user sendas
You want to send an email from a user's sendas address.
@@ -312,7 +312,7 @@ Your HTML message will contain lines like this:
<img src="cid:image2"/>
```
Your command line will have: `embedimage file1.jpg image1` embedimage file2.jpg image2`
Your command line will have: `embedimage file1.jpg image1 embedimage file2.jpg image2`
### Examples
Send an email to a user's personal address notifying them of their new Google Workspace account;
@@ -376,7 +376,7 @@ Your HTML message will contain lines like this:
<img src="cid:image2"/>
```
Your command line will have: `embedimage file1.jpg image1` embedimage file2.jpg image2`
Your command line will have: `embedimage file1.jpg image1 embedimage file2.jpg image2`
## Send an email to users
```
@@ -418,7 +418,7 @@ Your HTML message will contain lines like this:
<img src="cid:image2"/>
```
Your command line will have: `embedimage file1.jpg image1` embedimage file2.jpg image2`
Your command line will have: `embedimage file1.jpg image1 embedimage file2.jpg image2`
## Example
Send a message to a user, save the Message-ID so that a later reminder message can be sent

View File

@@ -68,6 +68,10 @@
```
<JSONData> ::= (json [charset <Charset>] <String>) | (json file <FileName> [charset <Charset>]) |
<OrgUnitID> ::= id:<String>
<OrgUnitPath> ::= /|(/<String>)+
<OrgUnitItem> ::= <OrgUnitID>|<OrgUnitPath>
<DriveFileACLRole> ::=
manager|organizer|owner|
contentmanager|fileorganizer|

View File

@@ -174,29 +174,33 @@ direct the uploaded file to a particular user and location and add a timestamp t
```
<ToDriveAttribute> ::=
(tdaddsheet [<Boolean>])|
(tdalert <EmailAddress>)*|
(tdbackupsheet (id:<Number>)|<String>)|
(tdcellnumberformat text|number)|
(tdcellwrap clip|overflow|wrap)|
(tdclearfilter [<Boolean>])|
(tdcopysheet (id:<Number>)|<String>)|
(tddescription <String>)|
(tdfileid <DriveFileID>)|
(tdfrom <EmailAddress>)|
(tdlocalcopy [<Boolean>])|
(tdlocale <Locale>)|
(tdnobrowser [<Boolean>])|
(tdnoemail [<Boolean>])|
(tdnoescapechar [<Boolean>])|
(tdnotify [<Boolean>])|
(tdparent (id:<DriveFolderID>)|<DriveFolderName>)|
(tdretaintitle [<Boolean>])|
(tdshare <EmailAddress> commenter|reader|writer)|
(tdshare <EmailAddress> commenter|reader|writer)*|
(tdsheet (id:<Number>)|<String>)|
(tdsheettimestamp [<Boolean>] [tdsheettimeformat <String>])
(tdsheettitle <String>)|
([tdsheetdaysoffset <Number>] [tdsheethoursoffset <Number])|
(tdtimestamp [<Boolean>] [tdtimeformat <String>])|
([tddaysoffset <Number>] [tdhoursoffset <Number])|
(tdsubject <String>)|
([tdsheetdaysoffset <Number>] [tdsheethoursoffset <Number>])|
(tdtimestamp [<Boolean>] [tdtimeformat <String>]
([tddaysoffset <Number>] [tdhoursoffset <Number>])|
(tdtimezone <TimeZone>)|
(tdtitle <String>)|
(tdcellwrap clip|overflow|wrap)|
(tdcellnumberformat text|plain)|
(tdupdatesheet [<Boolean>])|
(tduploadnodata [<Boolean>])|
(tduser <EmailAddress>)
@@ -213,7 +217,7 @@ It is uploaded to the root folder of the admin user named in `oauth2.txt`.
## Create new file
If `tdfileid <DriveFileID>` is not specified, a new file is created.
* `tdparent` - An existing/writable parent folder for the uploaded file; if not specified, the `todrive_parent` value from gam.cfg is used; that value defaults to the root folder.
* `tdshare <EmailAddress> commenter|reader|writer` - Share the new file with `<EmailAddress>` with the specified role. `<EmailAddress>` must be valid within your Google Workspace.
* `tdshare <EmailAddress> commenter|reader|writer` - Share the new file with `<EmailAddress>` with the specified role. `<EmailAddress>` must be valid within your Google Workspace. You can specify multiple shares.
## File name, file description and sheet name
* `tdtitle` - The title for the uploaded file, if not specified, the Gam default title is used.
@@ -226,6 +230,7 @@ If `tdfileid <DriveFileID>` is not specified, a new file is created.
* `tdtimeformat` - Format of the timestamp added to the title of the uploaded file; if not specified, the `todrive_timeformat` value from gam.cfg is used, that value defaults to '' which selects an ISO format timestamp.
* See: https://docs.python.org/3/library/datetime.html#strftime-strptime-behavior
* `tddaysoffset` and `tdhoursoffset` - Values that subtract time from the timestamp, they default to 0. A possible use for these values is as documentation to reflect the end of the time period that the uploaded report covers.
* `tdsubject <String>` - Use `<String>` as the subject in all emails sent. In `<String>`, `#file#` will, be replaced by the file title and `#sheet#` will be replaced by the sheet/tab title. By default, the subject is the file title.
## Spreadsheet settings
* `tdlocale <Locale>` - The Spreadsheet settings Locale value.
@@ -234,8 +239,10 @@ If `tdfileid <DriveFileID>` is not specified, a new file is created.
* `tdcellnumberformat text|number` - The Spreadsheet number format.
## Open browser and send email
* `tdnobrowser` - If False, a browser is opened to view the file uploaded to Google Drive; if not specified, the `todrive_nobrowser` value from gam.cfg is used.
* `tdnoemail` - If False, an email is sent to `tduser` informing them of name and URL of the uploaded file; if not specified, the `todrive_noemail` value from gam.cfg is used.
* `tdnobrowser` - If False, a browser is opened to view the file uploaded to Google Drive; if not specified, the `todrive_nobrowser` value from gam.cfg is used. If True, no browser is opened.
* `tdnoemail` - If False, an email is sent to `tduser` informing them of name and URL of the uploaded file; if not specified, the `todrive_noemail` value from gam.cfg is used. If True, no email is sent to `tduser`.
* `tdnotify` - If True, an email is sent to all `tdshare <EmailAddress>` and `tdalert <EmailAddress>` users informing them of name and URL of the uploaded/updated file. If False, no emails are sent.
* `tdfrom <EmailAddress>` - Emails will be sent with `<EmailAddress>` as the from address. By default, the from address is the Google Workspace Admin in `gam oauth info`.
## Escape character
* `tdnoescapechar <Boolean>` - Should `\` be ignored as an escape character; if not specified, the value of `todrive_no_escape_char` from `gam.cfg` will be used

View File

@@ -261,6 +261,9 @@
<EventMatchProperty> ::=
(matchfield attendees <EmailAddressEntity>)|
(matchfield attendeesonlydomainlist <DomainNameList>)|
(matchfield attendeesdomainlist <DomainNameList>)|
(matchfield attendeesnotdomainlist <DomainNameList>)|
(matchfield attendeespattern <RegularExpression>)|
(matchfield attendeesstatus [<AttendeeAttendance>] [<AttendeeStatus>] <EmailAddressEntity>)|
(matchfield creatoremail <RegularExpression>)|
@@ -438,7 +441,13 @@ The Google Calendar API processes `<EventSelectProperty>*`; you may specify none
GAM processes `<EventMatchProperty>*`; you may specify none or multiple properties.
* `matchfield attendees <EmailAddressEntity>` - All of the attendees in `<EmailAddressEntity>` must be present
* `matchfield attendeespattern <RegularExpression>` - Some attendee must match `<RegularExpression>`
* `matchfield attendeesonlydomainlist <DomainNameList>` - All attendee's email addresses must be in a domain in `<DomainNameList>`
* For example, this lets you look for events with all attendees in your internal domains
* `matchfield attendeesdomainlist <DomainNameList>` - Some attendee's email address must be in a domain in `<DomainNameList>`
* For example, this lets you look for events with attendees in specific external domains
* `matchfield attendeesnotdomainlist <DomainNameList>` - Some attendee's email address must be in a domain not in `<DomainNameList>`
* For example, this lets you look for events with attendees not in your internal domains
* `matchfield attendeespattern <RegularExpression>` - Some attendee's email address must match `<RegularExpression>`
* `matchfield attendeesstatus [<AttendeeAttendance>] [<AttendeeStatus>] <EmailAddressEntity>` - All of the attendees in `<EmailAddressEntity>` must be present
and must have the specified values.
* `<AttendeeAttendance>` - Default is `required`
@@ -592,6 +601,13 @@ To empty the calendar trash a temporary calendar is created, the deleted events
gam <UserTypeEntity> empty calendartrash <UserCalendarEntity>
```
## Move calendar events to another calendar
Generally you won't move all events from one calendar to another; typically, you'll move events created by the event creator
using `matchfield creatoremail <RegularExpression>` in conjunction with other `<EventSelectProperty>` and `<EventMatchProperty>` options.
```
gam <UserTypeEntity> move events <UserCalendarEntity> [<EventEntity>] destination|to <CalendarItem> [<EventNotificationAttribute>]
```
## Display calendar events
```
gam <UserTypeEntity> info events <UserCalendarEntity> [<EventEntity>] [maxinstances <Number>]
@@ -713,12 +729,14 @@ The attendee changes are displayed but not processed unless `doit` is specified.
## Manage focus time events
You can create and delete focus time events; they can not be updated.
To update a working location event, delete the working location event and recreate it.
To update a focus time event, delete the focus time event and recreate it.
```
gam <UserTypeEntity> create focustime
[chatstatus available|donotdisturb]|
[declinemode none|all|new] [declinemessage <String>]|
(timerange <Time> <Time>)+
[summary <String>]
(timerange <Time> <Time>
(recurrence <RRULE, EXRULE, RDATE and EXDATE line>)*
gam <UserTypeEntity> delete focustime
(timerange <Time> <Time>)+
@@ -762,17 +780,20 @@ The `quotechar <Character>` option allows you to choose an alternate quote chara
## Manage out of office events
You can create and delete out of office events; they can not be updated.
To update a working location event, delete the working location event and recreate it.
To update an out of office event, delete the out of office event and recreate it.
```
gam <UserTypeEntity> create outofoffice
[declinemode none|all|new] [declinemessage <String>]|
(timerange <Time> <Time>)+
[declinemode none|all|new]
[declinemessage <String>]
[summary <String>]
(timerange <Time> <Time>
(recurrence <RRULE, EXRULE, RDATE and EXDATE line>)*
gam <UserTypeEntity> delete outofoffice
(timerange <Time> <Time>)+
```
out of office events span a time range:
Out of office events span a time range:
* `timerange <Time> <Time>` - A time range, may span multiple days
## Display out of office events

View File

@@ -9,11 +9,15 @@
- [Display Chat Members](#display-chat-members)
- [Manage Chat Messages](#manage-chat-messages)
- [Display Chat Messages](#display-chat-messages)
- [Display Chat Events](#display-chat-events)
- [Bulk Operations](#bulk-operations)
## API documentation
* https://developers.google.com/chat/concepts
* https://developers.google.com/chat/reference/rest
* https://developers.google.com/workspace/chat/overview
* https://developers.google.com/workspace/chat/api/reference/rest
* https://developers.google.com/workspace/chat/api/reference/rest/v1/spaces.members/list
* https://developers.google.com/workspace/chat/api/reference/rest/v1/spaces.messages/list
* https://developers.google.com/workspace/chat/api/reference/rest/v1/spaces.spaceEvents/list
* https://support.google.com/chat/answer/7655820
## Introduction
@@ -63,6 +67,7 @@ Google requires that you have a Chat Bot configured in order to use the Chat API
(gdoc <UserGoogleDoc>)|
(gcsdoc <StorageBucketObjectName>))
<ChatEvent> ::= spaces/<String>/spaceEvents/<String>
<ChatMember> ::= spaces/<String>/members/<String>
<ChatMemberList> ::= "<ChatMember>(,<ChatMember>)*"
<ChatMessage> ::= spaces/<String>/messages/<String>
@@ -82,6 +87,7 @@ Google requires that you have a Chat Bot configured in order to use the Chat API
```
gam <UserTypeEntity> create chatspace
[type <ChatSpaceType>]
[restricted|(audience <String>)]
[externalusersallowed <Boolean>]
[members <UserTypeEntity>]
[displayname <String>]
@@ -91,21 +97,21 @@ gam <UserTypeEntity> create chatspace
[formatjson|returnidonly]
```
For `type space`, the following apply:
* `member <UserTypeEntity>` - Optional, can not specify more that 20 users
* `members <UserTypeEntity>` - Optional, can not specify more that 20 users
* `displayname <String>` - Required
* `description <String>` - Optional
* `guidelines <String>` - Optional
* `history <Boolean>` - Optional
For `type groupchat`, the following apply:
* `member <UserTypeEntity>` - Required, must specify between 2 and 20 users
* `members <UserTypeEntity>` - Required, must specify between 2 and 20 users
* `displayname <String>` - Ignored
* `description <String>` - Optional
* `guidelines <String>` - Optional
* `history <Boolean>` - Optional
For `type directmessage`, the following apply:
* `member <UserTypeEntity>` - Required, must specify 1 user
* `members <UserTypeEntity>` - Required, must specify 1 user
* `displayname <String>` - Ignored
* `description <String>` - Ignored
* `guidelines <String>` - Ignored
@@ -117,20 +123,26 @@ By default, Gam displays the information about the created chatspace as an inden
Use the `<ChatContent>` option to send an initial message to the created chatspace.
The `restricted|audience` options are in Developer Preview and will not be generally available.
By default, details about the chatmessage are displayed.
* `returnidonly` - Display the chatmessage name only
### Update a chat space
```
gam <UserTypeEntity> update chatspace <ChatSpace>
[type space]
[displayname <String>]
[description <String>] [guidelines <String>]
[history <Boolean>]
[restricted|(audience <String>)]|
([displayname <String>]
[type space]
[description <String>] [guidelines|rules <String>]
[history <Boolean>])
[formatjson]
```
A groupchat space can be upgraded to a space by specifying `type space` and `displayname <String>`.
The `restricted|audience` options can not be combined with options `displayname,type,description,guidelines,history`.
They are in Developer Preview and will not be generally available.
By default, Gam displays the information about the created chatspace as an indented list of keys and values.
* `formatjson` - Display the fields in JSON format.
@@ -226,6 +238,20 @@ Delete members by specifying chatmember names.
gam <UserTypeEntity> remove chatmember members <ChatMemberList>
```
### Update members role
Update members by specifying a chat space, user/group email addresses and role.
```
gam <UserTypeEntity> update chatmember <ChatSpace>
role member|manager
((user <UserItem>)|(members <UserTypeEntity>))+
```
Update members by specifying chatmember names and role.
```
gam <UserTypeEntity> modify chatmember
role member|manager
members <ChatMemberList>
```
## Display Chat Members
### Display information about a specific chat members
```
@@ -371,7 +397,6 @@ Display a specific Chat message.
```
gam <UserTypeEntity> info chatmessage name <ChatMessage>
[filter <String>]
[formatjson]
```
By default, Gam displays the information as an indented list of keys and values.
@@ -440,6 +465,69 @@ filter 'createTime > \"2012-04-21T11:30:00+00:00\" AND createTime < \"2013-01-01
filter 'thread.name = spaces/AAAAAAAAAAA/threads/123'
```
## Display Chat Events
Display a specific Chat event.
```
gam <UserTypeEntity> info chatevent name <ChatEvent>
[formatjson]
```
By default, Gam displays the information as an indented list of keys and values.
* `formatjson` - Display the fields in JSON format.
### Example
```
gam user user@domain.com info chatevent name spaces/AAAAsUhqjkg/spaceEvents/MTcxMTY4ODM2NDE3OTQzOV81X3VwZGF0ZWQ
```
### Display information about all chat events in a chat space
```
gam <UserTypeEntity> show chatevents <ChatSpace>
filter <String>
[formatjson]
```
By default, Gam displays the information as an indented list of keys and values.
* `formatjson` - Display the fields in JSON format.
```
gam <UserTypeEntity> print chatevents [todrive <ToDriveAttribute>*] <ChatSpace>
filter <String>
[formatjson [quotechar <Character>]]
```
By default, Gam displays the information as columns of fields; the following option causes the output to be in JSON format,
* `formatjson` - Display the fields in JSON format.
By default, when writing CSV files, Gam uses a quote character of double quote `"`. The quote character is used to enclose columns that contain
the quote character itself, the column delimiter (comma by default) and new-line characters. Any quote characters within the column are doubled.
When using the `formatjson` option, double quotes are used extensively in the data resulting in hard to read/process output.
The `quotechar <Character>` option allows you to choose an alternate quote character, single quote for instance, that makes for readable/processable output.
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.
Use `filter <String>` to filter events by when they occurred and by the type of event.
To filter events by the date they happened, specify the start_time and end_time with a timestamp in RFC-3339 format and double quotation marks.
You must specify at least one event type (event_types) using the has : operator. To filter by multiple event types, use the OR operator.
For a list of supported event types, see: https://developers.google.com/workspace/chat/api/reference/rest/v1/spaces.spaceEvents#SpaceEvent.FIELDS.event_type
For example, the following queries are valid on Linux/MacOS:
```
filter 'start_time="2024-03-15T11:30:00-04:00" AND event_types:"google.workspace.chat.message.v1.created"'
filter 'start_time="2024-03-15T11:30:00+00:00" AND end_time="2024-03-3100:00:00+00:00"event_types:"google.workspace.chat.message.v1.created"'
```
For example, the following queries are valid on Windows Command Prompt:
```
filter "start_time=\"2024-03-15T11:30:00-04:00\" AND event_types:\"google.workspace.chat.message.v1.created\""
filter "start_time=\"2024-03-15T11:30:00+00:00\" AND end_time=\"2024-03-3100:00:00+00:00\" AND event_types:\"google.workspace.chat.message.v1.created\""
```
For example, the following queries are valid on Windows PowerShell:
```
filter 'start_time=\"2024-03-15T11:30:00-04:00\" AND event_types:\"google.workspace.chat.message.v1.created\"'
filter 'start_time=\"2024-03-15T11:30:00+00:00\" AND end_time=\"2024-03-3100:00:00+00:00\" AND event_types:\"google.workspace.chat.message.v1.created\"'
```
## Bulk Operations
### Display information about all chat spaces for a collection of users
```

View File

@@ -0,0 +1,157 @@
# Users - Drive - Comments
- [API documentation](#api-documentation)
- [Query documentation](Users-Drive-Query)
- [Definitions](#definitions)
- [Display file comments](#display-file-comments)
## API documentation
* https://developers.google.com/drive/api/v3/reference/comments
## Definitions
* [`<DriveFileEntity>`](Drive-File-Selection)
* [`<UserTypeEntity>`](Collections-of-Users)
```
<DomainName> ::= <String>(.<String>)+
<EmailAddress> ::= <String>@<DomainName>
<UniqueID> ::= id:<String>
<UserItem> ::= <EmailAddress>|<UniqueID>|<String>
<CommentsAuthorSubfieldName> ::=
author.displayname|
author.emailaddress|
author.me|
author.permissionid|
author.photolink
<CommentsRepliesSubfieldName> ::=
reply.action|
reply.author|
reply.author.<CommentsAuthorSubfieldName>|
reply.content|
reply.createddate|createdtime|
reply.deleted|
reply.htmlcontent|
reply.id|
reply.modifieddate|modifiedtime
<CommentsFieldName> ::=
action|
author|
content|
<CommentsAuthorSubfieldName>|
<CommentsRepliesSubfieldName>|
createddate|createdtime|
deleted|
htmlcontent|
id|
modifieddate|modifiedtime|
quotedfilecontent|
reply|replies|
resolved
<CommentsFieldNameList> ::= "<CommentsFieldName>(,<CommentsFieldName>)*"
```
## Display file comments
### Display as an indented list of keys and values.
```
gam <UserTypeEntity> show filecomments <DriveFileEntity>
[showdeleted] [start <Date>|<Time>]
[fields <CommentsFieldNameList>] [showphotolinks]
[countsonly]
[formatjson]
```
By default, all non-deleted comments for a file are displayed; use these options to modify that behavior.
* `showdeleted` - Display deleted comments
* `start <Date>|<Time>` - Display comments modified on or after `<Date>|<Time>`
By default, all comment and reply fields except author photolinks are displayed; use these options to modify that behavior.
* `fields <CommentsFieldNameList>` - Select fields to display
* `showphotolinks` - Display author photolinks
* `countsonly` - Display just the number of comments and replies; no fields
By default, Gam displays the information as an indented list of keys and values.
* `formatjson` - Display the fields in JSON format.
### Display as a CSV file.
Each comment/reply pair is output on a separate CSV file row.
```
gam <UserTypeEntity> print filecomments <DriveFileEntity> [todrive <ToDriveAttribute>*]
[showdeleted] [start <Date>|<Time>] [countsonly]
[fields <CommentsFieldNameList>] [showphotolinks]
(addcsvdata <FieldName> <String>)*
[formatjson [quotechar <Character>]]
```
By default, all non-deleted comments for a file are displayed; use these options to modify that behavior.
Files with no comments will not be displayed.
* `showdeleted` - Display deleted comments
* `start <Date>|<Time>` - Display comments modified on or after `<Date>|<Time>`
By default, all comment and reply fields except author photolinks are displayed; use these options to modify that behavior.
* `fields <CommentsFieldNameList>` - Select fields to display
* `showphotolinks` - Display author photolinks
* `countsonly` - Display just the number of comments and replies; no fields. Files with no comments will display zero counts.
Add additional columns of data from the command line to the output:
* `addcsvdata <FieldName> <String>`
By default, when writing CSV files, Gam uses a quote character of double quote `"`. The quote character is used to enclose columns that contain
the quote character itself, the column delimiter (comma by default) and new-line characters. Any quote characters within the column are doubled.
When using the `formatjson` option, double quotes are used extensively in the data resulting in hard to read/process output.
The `quotechar <Character>` option allows you to choose an alternate quote character, single quote for instance, that makes for readable/processable output.
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.
### Example
```
# Get files that may have comments
$ gam redirect csv ./CheckForComments.csv user testsimple@domain.com print filelist showmimetype gdoc,gpresentation,gsheet fields id,name,mimetype
Getting all Drive Files/Folders that match query ('me' in owners and (mimeType = 'application/vnd.google-apps.presentation' or mimeType = 'application/vnd.google-apps.spreadsheet' or mimeType = 'application/vnd.google-apps.document')) for testsimple@domain.com
Got 131 Drive Files/Folders that matched query ('me' in owners and (mimeType = 'application/vnd.google-apps.presentation' or mimeType = 'application/vnd.google-apps.spreadsheet' or mimeType = 'application/vnd.google-apps.document')) for testsimple@domain.com...
# Display file comments
$ gam redirect csv ./FilesWithComments.csv multiprocess csv CheckForComments.csv gam user "~Owner" print filecomments "~id" addcsvdata fileName "~name" addcsvdata mimeType "~mimeType" fields author.displayName,author.me,content,createdTime,deleted,modifiedTime,resolved,reply.author.displayName,reply.author.me,reply.content,reply.createdTime,reply.deleted,reply.modifiedTime
2024-03-24T08:04:46.235-07:00,0/131,Using 10 processes...
2024-03-24T08:04:58.122-07:00,0,Processing item 100/131
2024-03-24T08:05:01.345-07:00,0,Processing item 131/131
2024-03-24T08:07:11.731-07:00,0/131,Processing complete
$ more FilesWithCommnts.csv
User,fileId,fileName,mimeType,commentId,replyId,author.displayName,author.me,content,createdTime,deleted,modifiedTime,resolved,reply.author.displayName,reply.author.me,reply.content,reply.createdTime,reply.deleted,reply.modifiedTime
testsimple@domain.com,xxx,TS Doc,application/vnd.google-apps.document,AAABJFedwm0,,Test-Simple,True,XXX Comment,2024-03-14T11:34:39-07:00,False,2024-03-14T11:34:39-07:00,False,,,,,,
testsimple@domain.com,xxx,TS Doc,application/vnd.google-apps.document,AAABJFedwkw,,Test-Simple,True,Grack Comment,2024-03-14T11:26:30-07:00,False,2024-03-14T11:26:30-07:00,False,,,,,,
testsimple@domain.com,xxx,TS Doc,application/vnd.google-apps.document,AAABJFedwkY,,Test-Simple,True,Again commnt,2024-03-14T11:24:13-07:00,False,2024-03-14T11:24:13-07:00,False,,,,,,
testsimple@domain.com,xxx,TS Doc,application/vnd.google-apps.document,AAABJFedwkQ,,Test-Simple,True,More Comment,2024-03-14T11:23:48-07:00,False,2024-03-14T11:23:48-07:00,False,,,,,,
testsimple@domain.com,xxx,TS Doc,application/vnd.google-apps.document,AAABJFedwkA,,Test-Simple,True,Comment 8,2024-03-14T11:23:14-07:00,False,2024-03-14T11:34:01-07:00,False,,,,,,
testsimple@domain.com,xxx,TS Doc,application/vnd.google-apps.document,AAABJFedwj4,,Test-Simple,True,Comment 7,2024-03-14T11:23:05-07:00,False,2024-03-14T11:23:05-07:00,False,,,,,,
testsimple@domain.com,xxx,TS Doc,application/vnd.google-apps.document,AAABJFedwj0,,Test-Simple,True,Comment 6,2024-03-14T11:22:55-07:00,False,2024-03-14T11:22:55-07:00,False,,,,,,
testsimple@domain.com,xxx,TS Doc,application/vnd.google-apps.document,AAABJFedwjs,,Test-Simple,True,Comment 5,2024-03-14T11:22:38-07:00,False,2024-03-14T11:22:38-07:00,False,,,,,,
testsimple@domain.com,xxx,TS Doc,application/vnd.google-apps.document,AAABJFedwjo,,Test-Simple,True,Comment 4,2024-03-14T11:22:19-07:00,False,2024-03-14T11:22:19-07:00,False,,,,,,
testsimple@domain.com,xxx,TS Doc,application/vnd.google-apps.document,AAABJFedtKQ,,Test-Simple,True,End Comment,2024-03-14T10:32:16-07:00,False,2024-03-14T10:32:16-07:00,False,,,,,,
testsimple@domain.com,xxx,TS Doc,application/vnd.google-apps.document,AAABJFedtKI,AAABJFedwik,Test-Simple,True,My first comment,2024-03-14T10:32:03-07:00,False,2024-03-14T11:15:05-07:00,False,Test-Simple,True,My first reply,2024-03-14T11:14:13-07:00,False,2024-03-14T11:14:13-07:00
testsimple@domain.com,xxx,TS Doc,application/vnd.google-apps.document,AAABJFedtKI,AAABJFedwiw,Test-Simple,True,My first comment,2024-03-14T10:32:03-07:00,False,2024-03-14T11:15:05-07:00,False,Test-Simple,True,Yet another reply,2024-03-14T11:15:05-07:00,False,2024-03-14T11:15:05-07:00
testsimple@domain.com,yyy,TS Sheet,application/vnd.google-apps.spreadsheet,AAABJM6zbc0,,Test-Simple,True,Sheet Comment,2024-03-14T20:43:18-07:00,False,2024-03-14T20:43:18-07:00,False,,,,,,
testsimple@domain.com,zzz,TS Pres,application/vnd.google-apps.presentation,AAABJLy5DpA,,Test-Simple,True,Presentation Comment,2024-03-14T20:42:48-07:00,False,2024-03-14T20:42:48-07:00,False,,,,,,
$ gam redirect csv ./FilesWithComments.csv multiprocess csv CheckForComments.csv gam user "~Owner" print filecomments "~id" addcsvdata fileName "~name" addcsvdata mimeType "~mimeType" fields author.displayName,author.me,content,createdTime,deleted,modifiedTime,resolved,reply.author.displayName,reply.author.me,reply.content,reply.createdTime,reply.deleted,,reply.modifiedTime
2024-03-24T08:04:46.235-07:00,0/131,Using 10 processes...
2024-03-24T08:04:58.122-07:00,0,Processing item 100/131
2024-03-24T08:05:01.345-07:00,0,Processing item 131/131
2024-03-24T08:07:11.731-07:00,0/131,Processing complete
# Display file comment counts
$ gam redirect csv ./FileCommentCounts.csv multiprocess csv CheckForComments.csv gam user "~Owner" print filecomments "~id" addcsvdata fileName "~name" addcsvdata mimeType "~mimeType" countsonly
2024-03-24T07:51:16.881-07:00,0/131,Using 10 processes...
2024-03-24T07:51:28.909-07:00,0,Processing item 100/131
2024-03-24T07:51:32.241-07:00,0,Processing item 131/131
2024-03-24T07:51:37.404-07:00,0/131,Processing complete
$ more FileCommentCounts.csv
User,fileId,fileName,mimeType,comments,replies
...
testsimple@domain.com,yyy,TS Sheet,application/vnd.google-apps.spreadsheet,1,0
testsimple@domain.com,aaa,ViewTest,application/vnd.google-apps.document,0,0
testsimple@domain.com,xxx,TS Doc,application/vnd.google-apps.document,11,2
testsimple@domain.com,zzz,TS Pres,application/vnd.google-apps.presentation,1,0
...
```

View File

@@ -71,6 +71,7 @@
g3pshortcut|
gsite
<MimeTypeName> ::= application|audio|font|image|message|model|multipart|text|video
<MimeTypeNameList> ::= "<MimeTypeName>(,<MimeTypeName>)*"
<MimeType> ::= <MimeTypeShortcut>|(<MimeTypeName>/<String>)
<MimeTypeList> ::= "<MimeType>(,<MimeType>)*"
```
@@ -267,9 +268,10 @@
<DriveOwnersSubfieldName>|
parents|
<DriveParentsSubfieldName>|
permissionids|
permissiondetails|
permissions|
<DrivePermissionsSubfieldName>|
permissionids|
properties|
quotabytesused|quotaused|
resourcekey|
@@ -572,7 +574,7 @@ The `querytime<String> <Time>` value replaces the string `#querytime<String>#` i
The characters following `querytime` can be any combination of lowercase letters and numbers. This is most useful in scripts
where you can specify a relative date without having to change the script.
For example, query for files last modified me than 5 years ago:
For example, query for files last modified more than 5 years ago:
```
querytime5years -5y query "modifiedTime<'#querytime5years#'"
```
@@ -610,9 +612,11 @@ By default, all types of files and folders are selected. You can specify a list
This option updates the current query.
```
showmimetype [not] <MimeTypeList>
showmimetype category <MimeTypeNameList>
```
* `showmimetype <MimeTypeList>` - Select files and folders with the specified MIME types
* `showmimetype not <MimeTypeList>` - Select files and folders with MIME types other than those specified
* `showmimetype category <MimeTypeNameList>` - Select files and folders with the specified MIME type categories
## File selection by file size
These options would typically be used with `showmimetype` to select files of a particular type. This
@@ -648,7 +652,7 @@ gam <UserTypeEntity> print filecounts [todrive <ToDriveAttribute>*]
[corpora <CorporaAttribute>]
[select <SharedDriveEntity>]
[anyowner|(showownedby any|me|others)]
[showmimetype [not] <MimeTypeList>]
[showmimetype [not] <MimeTypeList>] [showmimetype category <MimeTypeNameList>]
[sizefield quotabytesused|size] [minimumfilesize <Integer>] [maximumfilesize <Integer>]
[filenamematchpattern <RegularExpression>]
<PermissionMatch>* [<PermissionMatchMode>] [<PermissionMatchAction>]
@@ -661,7 +665,7 @@ gam <UserTypeEntity> show filecounts
[corpora <CorporaAttribute>]
[select <SharedDriveEntity>]
[anyowner|(showownedby any|me|others)]
[showmimetype [not] <MimeTypeList>]
[showmimetype [not] <MimeTypeList>] [showmimetype category <MimeTypeNameList>]
[sizefield quotabytesused|size] [minimumfilesize <Integer>] [maximumfilesize <Integer>]
[filenamematchpattern <RegularExpression>]
<PermissionMatch>* [<PermissionMatchMode>] [<PermissionMatchAction>]
@@ -893,7 +897,7 @@ gam <UserTypeEntity> print filetree [todrive <ToDriveAttribute>*]
[select <DriveFileEntity> [selectsubquery <QueryDriveFile>]
[depth <Number>]]
[anyowner|(showownedby any|me|others)]
[showmimetype [not] <MimeTypeList>]
[showmimetype [not] <MimeTypeList>] [showmimetype category <MimeTypeNameList>]
[sizefield quotabytesused|size] [minimumfilesize <Integer>] [maximumfilesize <Integer>]
[filenamematchpattern <RegularExpression>]
<PermissionMatch>* [<PermissionMatchMode>] [<PermissionMatchAction>]
@@ -905,7 +909,7 @@ gam <UserTypeEntity> show filetree
[select <DriveFileEntity> [selectsubquery <QueryDriveFile>]
[depth <Number>]]
[anyowner|(showownedby any|me|others)]
[showmimetype [not] <MimeTypeList>]
[showmimetype [not] <MimeTypeList>] [showmimetype category <MimeTypeNameList>]
[sizefield quotabytesused|size] [minimumfilesize <Integer>] [maximumfilesize <Integer>]
[filenamematchpattern <RegularExpression>]
<PermissionMatch>* [<PermissionMatchMode>] [<PermissionMatchAction>]
@@ -998,14 +1002,14 @@ gam <UserTypeEntity> print|show filelist [todrive <ToDriveAttribute>*]
[select <DriveFileEntity> [selectsubquery <QueryDriveFile>]
[(norecursion [<Boolean>])|(depth <Number>)] [showparent]]
[anyowner|(showownedby any|me|others)]
[showmimetype [not] <MimeTypeList>]
[showmimetype [not] <MimeTypeList>] [showmimetype category <MimeTypeNameList>] [mimetypeinquery [<Boolean>]]
[sizefield quotabytesused|size] [minimumfilesize <Integer>] [maximumfilesize <Integer>]
[filenamematchpattern <RegularExpression>]
<PermissionMatch>* [<PermissionMatchMode>] [<PermissionMatchAction>] [pmfilter] [oneitemperrow]
[excludetrashed]
[maxfiles <Integer>] [nodataheaders <String>]
[countsonly [summary none|only|plus] [summaryuser <String>]
[showsource] [showsize] [showmimetypesize]] [countsrowfilter]
[showsource] [showsize] [showmimetypesize]] [countsrowfilter]
[filepath|fullpath [pathdelimiter <Character>] [addpathstojson] [showdepth]] [buildtree]
[allfields|<DriveFieldName>*|(fields <DriveFieldNameList>)]
[showdrivename] [showshareddrivepermissions]
@@ -1538,6 +1542,10 @@ For each folder in `<DriveFileEntity>`, the following items are displayed:
* `totalFileCount` - The number of files directly in the folder and all of its subfolders
* `totalFileSize` - The sum of the sizes of the files directly in the folder and all of its subfolders
* `totalFolderCount` - The number of folders directly in the folder and all of its subfolders
* `depth` - The depth of the folder
* `-1` - The top level folder
* `0` - Immediate children of the top level folder
* `1` - Immediate children of level 0 folders
* `path` - The path of the folder
There is a final row detailing files and folders in the trash; it is omitted if `excludetrashed` or `show summary` are specified.
@@ -1554,8 +1562,13 @@ There is a final row detailing files and folders in the trash; it is omitted if
* `totalFileCount` - The number of files in the trash
* `totalFileSize` - The sum of the sizes of the files in the trash
* `totalFolderCount` - The number of folders in the trash
* `depth` - Always -1
* `path` - Trash
GAM version `6.71.17` added the `depth` column that can be used to filter the depth of the folders displayed.
Depth `-1` is the top level folder, depth `0` are its immediate children, depth `2` are the children of depth `1` and so forth.
For example to limit the display to the top folder and its immediate children, use `config csv_output_row_filter depth:count<1`.
By default, files owned by the user are counted. These options update the current query with the desired ownership.
* `showownedby me` - Count files owned by the user; this is the default
* `showownedby any` or `anyowner` - Count files accessible by the user
@@ -1585,24 +1598,37 @@ Use the `show` option to control the display of data:
$ gam redirect csv ./MyDriveUsage.csv user testsimple@domain.com print diskusage mydrive
User: testsimple@domain.com, Print 1 Drive Disk Usage
$ more MyDriveUsage.csv
User,Owner,id,name,ownedByMe,trashed,explicitlyTrashed,directFileCount,directFileSize,directFolderCount,totalFileCount,totalFileSize,totalFolderCount,path
testsimple@domain.com,testsimple@domain.com,012YenC8f12ALUk9PVA,My Drive,,False,False,100,138212,24,167,189598,79,My Drive
testsimple@domain.com,testsimple@domain.com,456YenC8f12ALfndaQ1NHc0RtUG92Y1BIUUl4bjVBRmNkWG5oakNqVVFDcXJWOHNmdFlwZmc,Classroom,True,False,False,0,0,15,9,6840,17,My Drive/Classroom
User,Owner,id,name,ownedByMe,trashed,explicitlyTrashed,directFileCount,directFileSize,directFolderCount,totalFileCount,totalFileSize,totalFolderCount,depth,path
testsimple@domain.com,testsimple@domain.com,012YenC8f12ALUk9PVA,My Drive,,False,False,100,138212,24,167,189598,79,-1,My Drive
testsimple@domain.com,testsimple@domain.com,456YenC8f12ALfndaQ1NHc0RtUG92Y1BIUUl4bjVBRmNkWG5oakNqVVFDcXJWOHNmdFlwZmc,Classroom,True,False,False,0,0,15,9,6840,17,0,My Drive/Classroom
testsimple@domain.com,testsimple@domain.com,0B3YenC8f12ALfmRuX3I4WFlqaTRnMGhXNkVvWV9UUG1zRDQwY1BwVkJhUGx5WHVIcjJKZUU,TestUpdate,True,False,False,2,3420,0,2,3420,0,1,My Drive/Classroom/TestUpdate
testsimple@domain.com,testsimple@domain.com,1MT5xJ897oYa0Q2OuzBDfLHvig6k_b0EKaovVA2imGYcnrmqZu5hjlJkEPMH-rHKj4qDyy9_j,TS Course,True,False,False,0,0,0,0,0,0,1,My Drive/Classroom/TS Course
testsimple@domain.com,testsimple@domain.com,1gsbqsbhhwBx9hCF0sqtE213tpUn6Ebj2klLFhHb4xkzBKIdEFkvvwCVtZpYWPgOA796fIPEN,TS Course 2,True,False,False,0,0,0,0,0,0,1,My Drive/Classroom/TS Course 2
...
testsimple@domain.com,testsimple@domain.com,1bHS_Tp77W3KSGRNSs_jP1RhAJhIGRCaI,XferFolder,True,False,False,1,1024,0,1,1024,0,My Drive/XferFolder
testsimple@domain.com,testsimple@domain.com,Trash,Trash,,True,True,0,0,1,3,3072,9,Trash
testsimple@domain.com,testsimple@domain.com,1bHS_Tp77W3KSGRNSs_jP1RhAJhIGRCaI,XferFolder,True,False,False,1,1024,0,1,1024,0,0,My Drive/XferFolder
testsimple@domain.com,testsimple@domain.com,Trash,Trash,,True,True,0,0,1,3,3072,9,-1,Trash
$ gam config csv_output_row_filter "depth:count<1" redirect csv ./MyDriveUsage.csv user testsimple@domain.com print diskusage mydrive
User: testsimple@domain.com, Print 1 Drive Disk Usage
$ more MyDriveUsage.csv
User,Owner,id,name,ownedByMe,trashed,explicitlyTrashed,directFileCount,directFileSize,directFolderCount,totalFileCount,totalFileSize,totalFolderCount,depth,path
testsimple@domain.com,testsimple@domain.com,012YenC8f12ALUk9PVA,My Drive,,False,False,100,138212,24,167,189598,79,-1,My Drive
testsimple@domain.com,testsimple@domain.com,456YenC8f12ALfndaQ1NHc0RtUG92Y1BIUUl4bjVBRmNkWG5oakNqVVFDcXJWOHNmdFlwZmc,Classroom,True,False,False,0,0,15,9,6840,17,0,My Drive/Classroom
...
testsimple@domain.com,testsimple@domain.com,1bHS_Tp77W3KSGRNSs_jP1RhAJhIGRCaI,XferFolder,True,False,False,1,1024,0,1,1024,0,0,My Drive/XferFolder
testsimple@domain.com,testsimple@domain.com,Trash,Trash,,True,True,0,0,1,3,3072,9,-1,Trash
$ gam redirect csv ./MyDriveUsage.csv user testsimple@domain.com print diskusage mydrive show summaryandtrash
User: testsimple@domain.com, Print 1 Drive Disk Usage
$ more MyDriveUsage.csv
User,Owner,id,name,ownedByMe,trashed,explicitlyTrashed,directFileCount,directFileSize,directFolderCount,totalFileCount,totalFileSize,totalFolderCount,path
testsimple@domain.com,testsimple@domain.com,012YenC8f12ALUk9PVA,My Drive,,False,False,100,138212,24,167,189598,79,My Drive
testsimple@domain.com,testsimple@domain.com,Trash,Trash,,True,True,0,0,1,3,3072,9,Trash
User,Owner,id,name,ownedByMe,trashed,explicitlyTrashed,directFileCount,directFileSize,directFolderCount,totalFileCount,totalFileSize,totalFolderCount,depth,path
testsimple@domain.com,testsimple@domain.com,012YenC8f12ALUk9PVA,My Drive,,False,False,100,138212,24,167,189598,79,-1,My Drive
testsimple@domain.com,testsimple@domain.com,Trash,Trash,,True,True,0,0,1,3,3072,9,-1,Trash
$ gam redirect csv ./MyDriveUsage.csv user testsimple@domain.com print diskusage shareddriveid 0AL5LiIe4dqxZUk9PVA show summaryandtrash
User: testsimple@domain.com, Print 1 Drive Disk Usage
$ more MyDriveUsage.csv
User,id,name,trashed,explicitlyTrashed,directFileCount,directFileSize,directFolderCount,totalFileCount,totalFileSize,totalFolderCount,path
testsimple@domain.com,0125LiIe4dqxZUk9PVA,TS Shared Drive 1,False,False,16,6144,7,42,73799,25,SharedDrives/TS Shared Drive 1
testsimple@domain.com,Trash,Trash,True,True,1,1024,0,1,1024,0,Trash
User,id,name,trashed,explicitlyTrashed,directFileCount,directFileSize,directFolderCount,totalFileCount,totalFileSize,totalFolderCount,depth,path
testsimple@domain.com,0125LiIe4dqxZUk9PVA,TS Shared Drive 1,False,False,16,6144,7,42,73799,25,-1,SharedDrives/TS Shared Drive 1
testsimple@domain.com,Trash,Trash,True,True,1,1024,0,1,1024,0,-1,Trash
```

View File

@@ -19,6 +19,7 @@
- [Shortcuts](Users-Drive-Shortcuts)
- [Drive Labels](Users-Drive-Labels)
- [Download Google Documents as JSON](#download-google-documents-as-json)
- [Upload changes to Google Documents](#upload-changes-to-google-documents)
## API documentation
* https://developers.google.com/drive/api/v3/reference/files
@@ -642,8 +643,6 @@ gam <UserTypeEntity> delete|del drivefile <DriveFileEntity> purge
```
## Download Google Documents as JSON
This command was added in version 5.31.04, you'll have to do `gam update project` and
`gam <UserTypeEntity> check|update serviceaccount` to enable it.
```
gam <UserTypeEntity> get document <DriveFileEntity>
[viewmode default|suggestions_inline|preview_suggestions_accepted|preview_without_suggestions]
@@ -666,3 +665,31 @@ By default, when getting a document, an existing local file will not be overwrit
* `overwrite` - Overwite an existing file
* `overwrite true` - Overwite an existing file
* `overwrite false` - Do not overwite an existing file; add a numeric prefix and create a new file
## Upload changes to Google Documents
```
<DocumentJSONUpdateRequest> ::=
'{"requests": [{object (Request)}], "writeControl": {object (WriteControl) }`
See: https://developers.google.com/docs/api/reference/rest/v1/documents/request
gam <UserTypeEntity> update document <DriveFileEntity>
((json [charset <Charset>] <DocumentJSONUpdateRequest>) |
(json file <FileName> [charset <Charset>]))
[formatjson]
```
The JSON data can be read from a command line argument or a file. On the command line, the
JSON data is enclosed in single quotes; these should not be present when the JSON data is read from a file.
The output is formatted for human readability. Use the following option to produce JSON output for program parsing.
* `formatjson` - Display output in JSON format.
### Examples
Replace Foo with Goo in a document.
```
File Update.json contains:
{ "requests": [{"replaceAllText": {"replaceText": "Goo", "containsText": {"text": "Foo", "matchCase": "True"}}}]}
gam user testuser@domain.com update document <DriveFileItem> json file Update.json
```

View File

@@ -6,12 +6,15 @@
- [Introduction](#introduction)
- [Display Drive Labels](#display-drive-labels)
- [Process File Drive Labels](#process-file-drive-labels)
- [Manage Drive Label Permissions](#manage-drive-label-permissions)
- [Display Drive Label Permissions](#display-drive-label-permissions)
## API documentation
* https://support.google.com/a/answer/9292382
* https://developers.google.com/drive/labels/guides/overview
* https://developers.google.com/drive/labels/guides/authorize
* https://developers.google.com/drive/labels/reference/rest/v2beta/labels
* https://developers.google.com/drive/labels/reference/rest/v2beta/labels.permissions
* https://developers.google.com/drive/api/guides/about-labels
* https://developers.google.com/drive/api/v3/reference/files
@@ -19,13 +22,15 @@
To use these commands you must add the 'Drive Labels API' to your project and update your service account authorization.
```
gam update project
gam user user@domain.com check serviceaccount
gam user user@domain.com update serviceaccount
```
Supported editions for this feature: Business Standard and Business Plus; Enterprise; Education Standard and Education Plus; G Suite Business; Essentials.
## Definitions
* [`<DriveFileEntity>`](Drive-File-Selection)
* [`<UserTypeEntity>`](Collections-of-Users)
* [`<DriveLabelNameEntity>`, `<DriveLabelPermissionNameEntity'](Collections-of-Items)
* [`<UserTypeEntity>`](Collections-of-Items)
```
<DriveLabelID> ::= <String>
@@ -35,7 +40,11 @@ Supported editions for this feature: Business Standard and Business Plus; Enterp
<DriveLabelNameList> ::= "<DriveLabelName>(,<DriveLabelName)*"
<DriveLabelNameEntity> ::=
<DriveLabelNameList> | <FileSelector> | <CSVFileSelector> | <CSVDataSelector>
See: https://github.com/taers232c/GAMADV-XTD3/wiki/Collections-of-Items
<DriveLabelPermissionName> ::= labels/<DriveLabelID>[@latest|@published|@<Number>]/permissions/(audiences|groups|people)/<String>
<DriveLabelPermissionNameList> ::= "<DriveLabelPermissionName>(,<DriveLabelPermissionName>)*"
<DriveLabelPermissionNameEntity> ::=
<DriveLabelPermissionNameList> | <FileSelector> | <CSVFileSelector> | <CSVDataSelector>
<DriveLabelFieldID> ::= <String>
<DriveLabelSelectionID> ::= <String>
@@ -86,9 +95,9 @@ A domain administrator with the Drive and Docs administrator privilege can searc
owned by their organization, regardless of the admin's membership in any given Shared Drive.
Three forms of the commands are available:
* `gam action ...` - The administrator named in oauth2.txt is used, domain administrator access implied and labels of type `SHARED` and `ADMIN`can be written
* `gam <UserTypeEntity> action ... adminaccess` - The user named in `<UserTypeEntty>` is used, adminaccess indicates that labels of type `SHARED` and `ADMIN`can be written
* `gam <UserTypeEntity> action ...` - The user named in `<UserTypeEntty>` is used, access is limited, onlylabels of type `SHARED` can be written
* `gam action ...` - The administrator named in oauth2.txt is used, domain administrator access implied and labels of type `SHARED` and `ADMIN`can be processed
* `gam <UserTypeEntity> action ... adminaccess` - The user named in `<UserTypeEntty>` is used, adminaccess indicates that labels of type `SHARED` and `ADMIN`can be processed
* `gam <UserTypeEntity> action ...` - The user named in `<UserTypeEntty>` is used, access is limited, onlylabels of type `SHARED` can be processed
## Display Drive Labels
@@ -156,3 +165,51 @@ gam <UserTypeEntity> process filedrivelabels <DriveFileEntity>
By default, details of the process labels are displayed, use `nodetails` to suppress this display.
## Manage Drive Label Permissions
Create a permission for a Drive Label by specifying the label name and the principal.
```
gam [<UserTypeEntity>] create drivelabelpermission <DriveLabelNameEntity>
(user <UserItem>) | (group <GroupItem) | (audience <String>)
role applier|editor|organizer|reader
[nodetails|formatjson] [adminaccess|asadmin]
```
By default, when a permission is created, GAM outputs details of the permission as indented keywords and values.
* `nodetails` - Suppress the details output.
* `formatjson` - Output the details in JSON format.
Delete a Drive Label permission by specifying the label name and the principal.
```
gam [<UserTypeEntity>] delete drivelabelpermission <DriveLabelNameEntity>
(user <UserItem>) | (group <GroupItem) | (audience <String>)
[adminaccess|asadmin]
```
Delete a Drive Label permission by specifying the label permission name.
```
gam [<UserTypeEntity>] remove drivelabelpermission <DriveLabelPermissionNameEntity>
[adminaccess|asadmin]
```
## Display Drive Label Permissions
Display permissions for a collection of Drive Label permission names.
```
gam [<UserTypeEntity>] show drivelabelpermissions <DriveLabelNameEntity>
[formatjson] [adminaccess|asadmin]
```
By default, Gam displays the information as an indented list of keys and values.
* `formatjson` - Display the fields in JSON format.
```
gam [<UserTypeEntity>] print drivelabelpermissions <DriveLabelNameEntity> [todrive <ToDriveAttribute>*]
[formatjson [quotechar <Character>]] [adminaccess|asadmin]
```
By default, Gam displays the information as columns of fields; the following option causes the output to be in JSON format,
* `formatjson` - Display the fields in JSON format.
By default, when writing CSV files, Gam uses a quote character of double quote `"`. The quote character is used to enclose columns that contain
the quote character itself, the column delimiter (comma by default) and new-line characters. Any quote characters within the column are doubled.
When using the `formatjson` option, double quotes are used extensively in the data resulting in hard to read/process output.
The `quotechar <Character>` option allows you to choose an alternate quote character, single quote for instance, that makes for readable/processable output.
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.

View File

@@ -8,6 +8,7 @@
- [Display file permissions/sharing](#display-file-permissionssharing)
- [Delete all ACLs except owner from a file](#delete-all-acls-except-owner-from-a-file)
- [Change shares to User1 to shares to User2](#change-shares-to-user1-to-shares-to-user2)
- [Map All ACLs from an old domain to a new domain](#map-all-acls-from-an-old-domain-to-a-new-domain)
## API documentation
* https://developers.google.com/drive/api/v3/reference/permissions
@@ -316,4 +317,26 @@ gam redirect csv ./FilesSharedWithU1Settings.csv multiprocess csv FilesSharedWit
gam redirect stdout ./DeleteU1Sharing.txt multiprocess redirect stderr stdout csv FilesSharedWithU1Settings.csv gam user "~Owner" delete drivefileacl "~id" "~permissions.0.emailAddress"
# For each of these files, add the share to User2 with the same role that User1 had
gam redirect stdout ./AddUser2Sharing.txt multiprocess redirect stderr stdout csv FilesSharedWithU1Settings.csv gam user "~Owner" create drivefileacl "~id" user user2@domain.com role "~permissions.0.role"
```
```
## Map All ACLs from an old domain to a new domain
* Get ACLs
```
gam redirect csv ./allUsersFiles.csv multiprocess all users print filelist fields name,id,basicpermissions oneitemperrow pmfilter pm domain olddomain.com em
```
* Delete ACLs with olddomain.com
```
gam redirect stdout ./DeleteOldDomainACLs.txt multiprocess redirect stderr stdout csv ./allUsersFiles.csv gam user "~Owner" delete drivefileacl "~id" "id:~~permission.id~~"
```
* Add user/group ACLs replacing olddomain.com with newdomain.com
```
gam config csv_input_row_filter "permission.type:regex:user|group" redirect stdout ./AddNewDomainACLsUserGroupShares.txt multiprocess redirect stderr stdout csv ./allUsersFiles.csv gam user "~Owner" create drivefileacl "~id" "~permission.type" "~permission.emailAddress" role "~permission.role" mappermissionsdomain olddomain.com newdomain.com
```
* Add domain ACLs replacing olddomain.com with newdomain.com
```
gam config csv_input_row_filter "permission.type:regex:domain" redirect stdout ./AddNewDomainACLsDomainShares.txt multiprocess redirect stderr stdout csv ./allUsersFiles.csv gam user "~Owner" create drivefileacl "~id" "~permission.type" "~permission.domain" role "~permission.role" allowfilediscovery "~permission.allowFileDiscovery" mappermissionsdomain olddomain.com newdomain.com
```

217
docs/Users-Gmail-CSE.md Normal file
View File

@@ -0,0 +1,217 @@
# Users - Gmail - Client Side Encryption
- [API documentation](#api-documentation)
- [Notes](#notes)
- [Definitions](#definitions)
- [Create Gmail CSE Identity](#create-gmail-cse-identity)
- [Update Gmail CSE Identity](#update-gmail-cse-identity)
- [Delete Gmail CSE Identity](#delete-gmail-cse-identity)
- [Display Gmail CSE Identities](#display-gmail-cse-identities)
- [Create Gmail CSE Key Pair](#create-gmail-cse-key-pair)
- [Action Gmail CSE Key Pairs](#action-gmail-cse-key-pairs)
- [Display Gmail CSE Key Pairs](#display-gmail-cse-key-pairs)
## API documentation
* https://developers.google.com/gmail/api/reference/rest/v1/users.settings.cse.identities
* https://developers.google.com/gmail/api/reference/rest/v1/users.settings.cse.keypairs
## Notes
This is an initial, minimally tested release; proceed with care and report all issues.
Setting up Client Side Encryption is not for the faint of heart; here is a start.
* https://support.google.com/a/answer/10741897?hl=en&ref_topic=10742486&sjid=10342493441460488213-NA
Do I personally understand what's going on here? No, I just added the API calls to GAM.
Two sets of files are required for Gmail CSE, these two variables in `gam.cfg` control where GAM looks for these files.
You must edit `gam.cfg` to set the paths you currently use.
```
gmail_cse_incert_dir
Directory for the S/MIME certificate files used by Gmail Client Side Encryption.
Default: Blank
gmail_cse_inkey_dir
Directory for the Key Access Control List (KACL) wrapped private key data files used by Gmail Client Side Encryption.
Default: Blank
```
## Definitions
* [`<UserTypeEntity>`](Collections-of-Users)
```
<DomainName> ::= <String>(.<String>)+
<EmailAddress> ::= <String>@<DomainName>
<FilePath> ::= <String>
<Password> ::= <String>
<KeyPairID> ::= <String>
```
## Create Gmail CSE Identity
Creates and configures a client-side encryption identity that's authorized to send mail from the user account.
Google publishes the S/MIME certificate to a shared domain-wide directory so that people within a Google Workspace organization can encrypt and send mail to the identity.
```
gam <UserTypeEntity> create cseidentity
(primarykeypairid <KeyPairID>) | (signingkeypairid <KeyPairID> encryptionkeypairid <KeyPairID>)
[kpemail <EmailAddress>]
[formatjson]
```
One of the following is required:
* `primarykeypairid <KeyPairID>` - The configuration of a CSE identity that uses the same key pair for signing and encryption.
* `signingkeypairid <KeyPairID> encryptionkeypairid <KeyPairID>` - The configuration of a CSE identity that uses different key pairs for signing and encryption.
If `kpemail <EmailAddress>` is not specified, the user's primary email address is used for the identity.
By default, Gam displays the identity as an indented list of keys and values; the following option causes the output to be in JSON format:
* `formatjson` - Display the fields in JSON format.
## Update Gmail CSE Identity
Associates a different key pair with an existing client-side encryption identity. The updated key pair must validate against Google's S/MIME certificate profiles.
```
gam <UserTypeEntity> update cseidentity
(primarykeypairid <KeyPairID>) | (signingkeypairid <KeyPairID> encryptionkeypairid <KeyPairID>)
[kpemail <EmailAddress>]
[formatjson]
```
One of the following is required:
* `primarykeypairid <KeyPairID>` - The configuration of a CSE identity that uses the same key pair for signing and encryption.
* `signingkeypairid <KeyPairID> encryptionkeypairid <KeyPairID>` - The configuration of a CSE identity that uses different key pairs for signing and encryption.
bIf `kpemail <EmailAddress>` is not specified, the key pair for the user's primary email address is identity updated.
By default, Gam displays the identity as an indented list of keys and values; the following option causes the output to be in JSON format:
* `formatjson` - Display the fields in JSON format.
## Delete Gmail CSE Identity
Deletes a client-side encryption identity. The authenticated user can no longer use the identity to send encrypted messages.
You cannot restore the identity after you delete it. Instead, use the `create cseidentity` to create another identity with the same configuration.
```
gam <UserTypeEntity> delete cseidentity [kpemail <EmailAddress>]
```
If `kpemail <EmailAddress>` is not specified, the identity for the user's primary email address is deleted.
## Display Gmail CSE Identities
### Display a client-side encryption identity configuration.
```
gam <UserTypeEntity> info cseidentity [kpemail <EmailAddress>]
[formatjson]
```
If `kpemail <EmailAddress>` is not specified, the user's primary email address is used for the identity.
### Display all of the client-side encrypted identities for an authenticated user.
```
gam <UserTypeEntity> show cseidentities
[formatjson]
```
By default, Gam displays the identity as an indented list of keys and values; the following option causes the output to be in JSON format:
* `formatjson` - Display the fields in JSON format.
```
gam <UserTypeEntity> print cseidentities [todrive <ToDriveAttribute>*]
[formatjson [quotechar <Character>]]
```
By default, Gam displays the information as columns of fields; the following option causes the output to be in JSON format:
* `formatjson` - Display the fields in JSON format.
By default, when writing CSV files, Gam uses a quote character of double quote `"`. The quote character is used to enclose columns that contain
the quote character itself, the column delimiter (comma by default) and new-line characters. Any quote characters within the column are doubled.
The `quotechar <Character>` option allows you to choose an alternate quote character, single quote for instance, that makes for readable/processable output.
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.
## Create Gmail CSE Key Pair
Create a CSE Key Pair for the primary address of a user.
```
gam <UserTypeEntity> create csekeypair
[incertdir <FilePath>] [inkeydir <FilePath>]
[addidentity [<Boolean>]] [kpemail <EmailAddress>]
[showpem] [showkaclsdata] [formatjson|returnidonly]
```
* The S/MIME certificate files for the users are in the `incertdir <FilePath>` folder/directory.
* If this option is not specified, the directory is taken from `gam.cfg/gmail_cse_incert_dir`.
* The files must be named `user@domain.com.p7pem`.
* The certificate contains the public key and its certificate chain. The chain must be in PKCS#7 format and use PEM encoding and ASCII armor.
* The Key Access Control List (KACL) wrapped private key data files are in the `inkeydir <FilePath>` folder/directory.
* If this option is not specified, the directory is taken from `gam.cfg/gmail_cse_inkey_dir`.
* The files must be named `user@domain.com.wrap`.
* The files are in JSON format with two keys:
* `kacls_url` - The URI of the key access control list service that manages the private key.
* `wrapped_private_key` - Opaque data generated and used by the key access control list service.
By default, the `pem` and `kaclsdata` fields will not be displayed unless the corresponding `showpem` and `showkaclsdata` option is specified.
By default, Gam displays the new key pair as an indented list of keys and values; the following options cause the output to be displayed in alternate forms.
* `formatjson` - Display the fields in JSON format.
* `returnidonly` - Display just the new `<KeyPairID>`.
If 'addidentity` or `addidentity true` is specified, a client-side encryption identity is created with the new key pair.
If `kpemail <EmailAddress>` is not specified, the user's primary email address is used for the identity.
By default, Gam displays the identity as an indented list of keys and values; the following option causes the output to be in JSON format:
* `formatjson` - Display the fields in JSON format.
* `returnidonly` - Display just the new `<KeyPairID>-<EmailAddress>`.
## Action Gmail CSE Key Pairs
### Display pem and kaclsdata fields
By default, the `pem` and `kaclsdata` fields will not be displayed unless the corresponding `showpem` and `showkaclsdata` option is specified.
### Disable
Turns off a client-side encryption key pair. The authenticated user can no longer use the key pair to decrypt incoming CSE message texts or sign outgoing CSE mail.
```
gam <UserTypeEntity> disable csekeypair <KeyPairID>
[showpem] [showkaclsdata] [formatjson]
```
By default, Gam displays the disabled key pair as an indented list of keys and values; the following option causes the output to be displayed in alternate forms.
* `formatjson` - Display the fields in JSON format.
### Enable
Turn on a client-side encryption key pair that was turned off. The key pair becomes active again for any associated client-side encryption identities.
```
gam <UserTypeEntity> ensable csekeypair <KeyPairID>
[showpem] [showkaclsdata] [formatjson]
```
By default, Gam displays the enabled key pair as an indented list of keys and values; the following option causes the output to be displayed in alternate forms.
* `formatjson` - Display the fields in JSON format.
### Obliterate
Delete a client-side encryption key pair permanently and immediately. You can only permanently delete key pairs that have been turned off for more than 30 days.
To turn off a key pair, use `disable csekeypair`.
```
gam <UserTypeEntity> obliterate csekeypair <KeyPairID>
```
Gmail can't restore or decrypt any messages that were encrypted by an obliterated key. Authenticated users and Google Workspace administrators lose access to reading the encrypted messages.
## Display Gmail CSE Key Pairs
### Display pem and kaclsdata fields
By default, the `pem` and `kaclsdata` fields will not be displayed unless the corresponding `showpem` and `showkaclsdata` option is specified.
### Display an existing client-side encryption key pair.
```
gam <UserTypeEntity> info csekeypair <KeyPairID>
[showpem] [showkaclsdata] [formatjson]
```
By default, Gam displays the key pairs as an indented list of keys and values; the following option causes the output to be in JSON format:
* `formatjson` - Display the fields in JSON format.
### Display all client-side encryption key pairs for an authenticated user.
```
gam <UserTypeEntity> show csekeypairs
[showpem] [showkaclsdata] [formatjson]
```
By default, Gam displays the key pairs as an indented list of keys and values; the following option causes the output to be in JSON format:
* `formatjson` - Display the fields in JSON format.
```
gam <UserTypeEntity> print csekeypairs [todrive <ToDriveAttribute>*]
[showpem] [showkaclsdata] [formatjson [quotechar <Character>]]
```
By default, Gam displays the key pairs as columns of fields; the following option causes the output to be in JSON format:
* `formatjson` - Display the fields in JSON format.
By default, when writing CSV files, Gam uses a quote character of double quote `"`. The quote character is used to enclose columns that contain
the quote character itself, the column delimiter (comma by default) and new-line characters. Any quote characters within the column are doubled.
The `quotechar <Character>` option allows you to choose an alternate quote character, single quote for instance, that makes for readable/processable output.
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.

View File

@@ -10,7 +10,8 @@
## API documentation
* https://developers.google.com/gmail/api/v1/reference/users/settings/delegates
* https://support.google.com/a/answer/7223765?hl=en
* https://support.google.com/a/answer/7223765
* https://support.google.com/a/answer/11946994
## Definitions
* [`<UserTypeEntity>`](Collections-of-Users)

View File

@@ -88,11 +88,17 @@ all parent labels are created as necessary.
Example: `gam user user@domain.com add label "Top/Middle/Bottom" buildpath`
## Update a label's settings
The two commands are equivalent; in the first you specify a `<LabelName>`, in the second you specify a `<LabelId>`.
```
gam <UserTypeEntity> update labelsettings <LabelName> [name <String>]
[messagelistvisibility hide|show] [labellistvisibility hide|show|showifunread]
[backgroundcolor "<LabelColorHex>|<LabelBackgroundColorHex>|custom:<ColorHex>"]
[textcolor "<LabelColorHex>|<LabelTextColorHex>|custom:<ColorHex>"]
gam <UserTypeEntity> update labelid <LabelID> [name <String>]
[messagelistvisibility hide|show] [labellistvisibility hide|show|showifunread]
[backgroundcolor "<LabelColorHex>|<LabelBackgroundColorHex>|custom:<ColorHex>"]
[textcolor "<LabelColorHex>|<LabelTextColorHex>|custom:<ColorHex>"]
```
`<LabelColorHex>` values should be enclosed in " to keep the command shell on MacOS and Linux from mis-interpreting them.

View File

@@ -1,4 +1,5 @@
# Users - Gmail - Messages/Threads
- [Notes](#notes)
- [API documentation](#api-documentation)
- [Query documentation](#query-documentation)
- [Definitions](#definitions)
@@ -25,6 +26,13 @@
- [Display messages sent by delegates for delegator](#display-messages-sent-by-delegates-for-delegator)
- [User attribute `replace <Tag> <UserReplacement>` processing](Tag-Replace)
## Notes
Restrict email messages to authorized addresses or domains only
* https://support.google.com/a/answer/2640542
Block emails between specific user groups
* https://support.google.com/a/answer/9175444
## API documentation
* https://developers.google.com/gmail/api/v1/reference/users/messages
* https://developers.google.com/gmail/api/v1/reference/users/threads
@@ -172,6 +180,7 @@
(gdoc|ghtml <UserGoogleDoc>)|
(gcsdoc|gcshtml <StorageBucketObjectName>)|
(emlfile <FileName> [charset <Charset>]))
```
## Message queries with dates
```
query <QueryGmail> [querytime<String> <Date>]*
@@ -346,10 +355,10 @@ See below for message selection.
Export messages in EML format.
```
gam <UserTypeEntity> export message|messages
(((query <QueryGmail> [querytime<String> <Date>]*) (matchlabel <LabelName>) [or|and])+ [quick|notquick] [doit] [max_to_export <Number>])|(ids <MessageIDEntity>)
(((query <QueryGmail> [querytime<String> <Date>]*) (matchlabel <LabelName>) [or|and])+ [quick|notquick] [max_to_export <Number>])|(ids <MessageIDEntity>)
[targetfolder <FilePath>] [targetname <FileName>] [overwrite [<Boolean>]]
gam <UserTypeEntity> export thread|threads
(((query <QueryGmail> [querytime<String> <Date>]*) (matchlabel <LabelName>) [or|and])+ [quick|notquick] [doit] [max_to_export <Number>])|(ids <ThreadIDEntity>)
(((query <QueryGmail> [querytime<String> <Date>]*) (matchlabel <LabelName>) [or|and])+ [quick|notquick] [max_to_export <Number>])|(ids <ThreadIDEntity>)
[targetfolder <FilePath>] [targetname <FileName>] [overwrite [<Boolean>]]
```

View File

@@ -9,6 +9,7 @@
- [Manage vacation](#manage-vacation)
- [Display vacation](#display-vacation)
- [User attribute `replace <Tag> <UserReplacement>` processing](Tag-Replace)
- [Standardize user signatures](#standardize-user-signatures)
## API documentation
* https://developers.google.com/gmail/api/reference/rest/v1/users.settings.sendAs
@@ -245,3 +246,37 @@ Gam displays the information in CSV form.
* `compact` - Strip carriage returns and newlines in original HTML; this makes these values easier to process in the CSV file
and can be used as input to GAM.
* `enabledonly` - Do not display users with vacation autoreply disabled.
## Standardize user signatures
You can standardize user signatures by creating a signature template and a CSV file with data for each user.
You can create a signature template by defining the signature in the Gmail Settings GUI of a test user.
You must use the default signature `My signature`.
Use text like `{FirstName}` and `{Email}` in the locations where the actual values will go.
Once you're created the template signature, do the following:
```
$ gam user testuser@domain.com show signature compact > SimpleSig.html
$ more SimpleSig.html
SendAs Address: <testuser@domain.com>
IsPrimary: True
Default: True
Signature: <div dir="ltr">--<div>Name: {FirstName} {LastName}<div>Phone: {Phone}</div><div>Email: {Email}</div></div><div><br></div><div>Company Name</div><div>Company Address</div><div><br></div></div>\n
```
Edit SimpleSig.html and delete all text from `SendAs ` through `Signature: `.
The result should be:
```
<div dir="ltr">--<div>Name: {FirstName} {LastName}<div>Phone: {Phone}</div><div>Email: {Email}</div></div><div><br></div><div>Company Name</div><div>Company Address</div><div><br></div></div>\n
```
This is a sample Users.csv file.
```
email,first,last,phone
bsmith@domain.com,Bob,Smith,510-555-1212 x 123
mjones@domain.com,Mary,Jones,510-555-1212 x 456
```
This command will update the user's signatures.
```
gam csv Users.csv gam user "~email" signature htmlfile SimpleSig.html replace FirstName "~first" replace LastName "~last" replace Phone "~phone" replace Email "~email"
```

View File

@@ -131,13 +131,17 @@ Display all notes
```
gam <UserTypeEntity> show notes
[fields <NotesFieldList>] [filter <String>]
[role owner|writwer]
[role owner|writer]
[countsonly]
[compact|formatjson]
```
By default, GAM displays all non-trashed notes:
* `filter trashed` - Display notes in the trash
* `role owner|writer` - Display notes where the user has the specified role
When option `countsonly` is specified, the number of notes a user owns, the number of notes of user can edit
and the total number of notes is displayed.
By default, Gam displays the information as an indented list of keys and values; the note text is displayed as individual lines.
* `compact` - Display the note text with escaped carriage returns as \r and newlines as \n
* `formatjson` - Display the note in JSON format
@@ -145,7 +149,8 @@ By default, Gam displays the information as an indented list of keys and values;
```
gam <UserTypeEntity> print notes [todrive <ToDriveAttribute>*]
[fields <NotesFieldList>] [filter <String>]
[role owner|writwer]
[role owner|writer]
[countsonly]
[formatjson [quotechar <Character>]]
```
@@ -153,6 +158,9 @@ By default, GAM displays all non-trashed notes:
* `filter trashed` - Display notes in the trash
* `role owner|writer` - Display notes where the user has the specified role
When option `countsonly` is specified, the number of notes a user owns, the number of notes of user can edit
and the total number of notes is displayed.
By default, when writing CSV files, Gam uses a quote character of double quote `"`. The quote character is used to enclose columns that contain
the quote character itself, the column delimiter (comma by default) and new-line characters. Any quote characters within the column are doubled.
When using the `formatjson` option, double quotes are used extensively in the data resulting in hard to read/process output.

View File

@@ -69,6 +69,10 @@
```
<JSONData> ::= (json [charset <Charset>] <String>) | (json file <FileName> [charset <Charset>]) |
<OrgUnitID> ::= id:<String>
<OrgUnitPath> ::= /|(/<String>)+
<OrgUnitItem> ::= <OrgUnitID>|<OrgUnitPath>
<DriveFileACLRole> ::=
manager|organizer|owner|
contentmanager|fileorganizer|
@@ -277,16 +281,19 @@ gam <UserTypeEntity> unhide teamdrive <SharedDriveEntity>
```
gam <UserTypeEntity> show teamdriveinfo <SharedDriveEntity>
gam <UserTypeEntity> info teamdrive <SharedDriveEntity>
[fields <SharedDriveFieldNameList>] [formatjson]
[fields <SharedDriveFieldNameList>]
[guiroles [<Boolean>] [formatjson]
gam <UserTypeEntity> show teamdriveinfo <SharedDriveEntity>
[fields <SharedDriveFieldNameList>] [formatjson]
[fields <SharedDriveFieldNameList>]
[guiroles [<Boolean>] [formatjson]
gam <UserTypeEntity> show teamdrives
[matchname <RegularExpression>] (role|roles <SharedDriveACLRoleList>)*
[fields <SharedDriveFieldNameList>] [formatjson]
[fields <SharedDriveFieldNameList>]
[guiroles [<Boolean>] [formatjson]
```
By default, Gam displays all Teams Drives accessible by the user.
* `matchname <RegularExpression>` - Display Shared Drives with names that match a pattern.
* `(role|roles <SharedDriveACLRoleList>)* - Display Shared Drives where the user has one of the specified roles.
* `(role|roles <SharedDriveACLRoleList>)*` - Display Shared Drives where the user has one of the specified roles.
By default, Gam displays the information as an indented list of keys and values.
* `formatjson` - Display the fields in JSON format.
@@ -301,10 +308,20 @@ By default, Gam displays all Teams Drives accessible by the user.
The Google Drive API does not list roles for Shared Drives so GAM generates a role from the capabilities:
* `commenter - canComment: True, canEdit: False`
* `fileOrganizer - canAddChildren: True, canManageMembers: False`
* `reader - canComment: False, canEdit: False`
* `writer - canEdit: True, canTrashChildren: False`
* `fileOrganizer - canTrashChildren: True, canManageMembers: False`
* `organizer - canManageMembers: True`
* `reader - canCopy': True, canComment: False`
* `writer - canEdit: True, canManageMembers: False`
By default, the Drive API role names are displayed, use `guiroles` to display the Google Drive GUI role names.
```
API: GUI
commenter: Commenter
fileOrganizer: Content manager
organizer: Manager
reader: Viewer
writer: Contributor
```
By default, Gam displays the information as columns of fields; the following option causes the output to be in JSON format,
* `formatjson` - Display the fields in JSON format.

View File

@@ -69,6 +69,8 @@ gam <UserTypeEntity> create task <TasklistEntity>
<TaskAttribute>* [parent <TaskID>] [previous <TaskID>]
[compact|formatjson|returnidonly]
```
The API only supports all-day tasks; you should specify: `due YYYY-MM-DDT00:00:00Z`.
By default, Gam displays the created task as an indented list of keys and values; the task notes text is displayed as individual lines.
* `compact` - Display the task notes text with escaped carriage returns as \r and newlines as \n
* `formatjson` - Display the task in JSON format
@@ -100,6 +102,9 @@ By default, Gam displays the moved task as an indented list of keys and values;
* `formatjson` - Display the task in JSON format
## Display Tasks
All commands that display tasks display the due date in GMT as the time portion
is not supported by the API and converting the due date to local time may display the wrong date.
### Display selected tasks
```
gam <UserTypeEntity> info task <TasklistIDTaskIDEntity>
@@ -119,6 +124,13 @@ gam <UserTypeEntity> show tasks [tasklists <TasklistEntity>]
[orderby completed|due|updated]
[countsonly|compact|formatjson]
```
The API only supports dates in `duemin` and `duemax' but you must supply a null time:
* `duemin YYYY-MM-DDT00:00:00Z` - Specify the starting due date
* `duemax YYYY-MM-DDT00:00:00Z` - Specify one day beyond the ending due date
For example: `duemin 2024-05-01T00:00:00Z duemax 2024-05-02T00:00:00Z` will
display all tasks on 2024-05-01.
By default, tasks are displayed in hierarchical order.
* `orderby completed` - Display tasks in completed date order regardless of the hierarchy.
* `orderby due` - Display tasks in due date order regardless of the hierarchy.
@@ -142,6 +154,13 @@ gam <UserTypeEntity> print tasks [tasklists <TasklistEntity>] [todrive <ToDriveA
[orderby completed|due|updated]
[countsonly | (formatjson [quotechar <Character>])]
```
The API only supports dates in `duemin` and `duemax' but you must supply a null time:
* `duemin YYYY-MM-DDT00:00:00Z` - Specify the starting due date
* `duemax YYYY-MM-DDT00:00:00Z` - Specify one day beyond the ending due date
For example: `duemin 2024-05-01T00:00:00Z duemax 2024-05-02T00:00:00Z` will
display all tasks on 2024-05-01.
By default, tasks are displayed in hierarchical order.
* `orderby completed` - Display tasks in completed date order regardless of the hierarchy.
* `orderby due` - Display tasks in due date order regardless of the hierarchy.

View File

@@ -38,14 +38,15 @@
- [Print domain counts for users in a specific domain and/or selected by a query](#print-domain-counts-for-users-in-a-specific-domain-and-or-selected-by-a-query)
- [Print domain counts for users specified by `<UserTypeEntity>`](#print-domain-counts-for-users-specified-by-usertypeentity)
- [Print user list](#print-user-list)
- [Display user count](#display-user-count)
- [Display user counts](#display-user-counts)
- [Verify domain membership]($verify-domain-membership)
## API documentation
* https://developers.google.com/admin-sdk/directory/reference/rest/v1/users
* https://developers.google.com/admin-sdk/directory/reference/rest/v1/schemas
## Name guidelines
* https://support.google.com/a/answer/9193374?hl=en
* https://support.google.com/a/answer/9193374
## Query documentation
* https://developers.google.com/admin-sdk/directory/v1/guides/search-users
@@ -337,6 +338,10 @@ relation manager manageremail@domain.com
externalid organization "Employee ID"
```
`<UserMultiAttribute>.location.buildingid <String>` allows non-validated building IDs
by specifying `nv:` at the beginning of `<String>`; e.g., `nv:Building X' sets the building ID to `Building X`.
## Passwords
To set a user's password, you specify a `<Password>` string and a hash method that specifies how to interpret the string
* `password random|uniquerandom` - A 25 character plain text string of ASCII uppercase/lowecase letters, digits and punctuation
@@ -980,13 +985,17 @@ gam print users [todrive <ToDriveAttribute>*]
([domain|domains <DomainNameEntity>] [(query <QueryUser>)|(queries <QueryUserList>)]
[limittoou <OrgUnitItem>] [deleted_only|only_deleted])
[orderby <UserOrderByFieldName> [ascending|descending]]
[groups|groupsincolumns] [license|licenses|licence|licences]
[groups|groupsincolumns]
[license|licenses|licence|licences|licensebyuser|licensesbyuser|licencebyuser|licencesbyuser]
[onelicenseperrow|onelicenceperrow]
[(products|product <ProductIDList>)|(skus|sku <SKUIDList>)]
[schemas|custom|customschemas all|<SchemaNameList>]
[emailpart|emailparts|username]
[userview] [allfields|basic|full|(<UserFieldName>*|fields <UserFieldNameList>)]
[delimiter <Character>] [sortheaders [<Boolean>]] [scalarsfirst [<Boolean>]]
[formatjson [quotechar <Character>]] [quoteplusphonenumbers]
[issuspended <Boolean>] [aliasmatchpattern <RegularExpression>]
[showvalidcolumn] (addcsvdata <FieldName> <String>)*
```
By default, users in all domains in the account are selected; these options allow selection of subsets of users:
@@ -1003,24 +1012,29 @@ gam print users [todrive <ToDriveAttribute>*] select <UserTypeEntity>
[orderby <UserOrderByFieldName> [ascending|descending]]
[groups|groupsincolumns]
[license|licenses|licence|licences|licensebyuser|licensesbyuser|licencebyuser|licencesbyuser]
[onelicenseperrow|onelicenceperrow]
[(products|product <ProductIDList>)|(skus|sku <SKUIDList>)]
[schemas|custom|customschemas all|<SchemaNameList>]
[emailpart|emailparts|username][schemas|custom all|<SchemaNameList>]
[userview] [allfields|basic|full|(<UserFieldName>*|fields <UserFieldNameList>)]
[emailpart|emailparts|username]
[userview] [basic|full|allfields|(<UserFieldName>*|fields <UserFieldNameList>)]
[delimiter <Character>] [sortheaders [<Boolean>]] [scalarsfirst [<Boolean>]]
[formatjson [quotechar <Character>]] [quoteplusphonenumbers]
[issuspended <Boolean>] [aliasmatchpattern <RegularExpression>]
[showvalidcolumn] (addcsvdata <FieldName> <String>)*
gam <UserTypeEntity> print users [todrive <ToDriveAttribute>*]
[orderby <UserOrderByFieldName> [ascending|descending]]
[groups|groupsincolumns]
[license|licenses|licence|licences|licensebyuser|licensesbyuser|licencebyuser|licencesbyuser]
[onelicenseperrow|onelicenceperrow]
[(products|product <ProductIDList>)|(skus|sku <SKUIDList>)]
[schemas|custom|customschemas all|<SchemaNameList>]
[emailpart|emailparts|username]
[userview] [allfields|basic|full|(<UserFieldName>*|fields <UserFieldNameList>)]
[userview] [basic|full|allfields|(<UserFieldName>*|fields <UserFieldNameList>)]
[delimiter <Character>] [sortheaders [<Boolean>]] [scalarsfirst [<Boolean>]]
[formatjson [quotechar <Character>]] [quoteplusphonenumbers]
[issuspended <Boolean>] [aliasmatchpattern <RegularExpression>]
[showvalidcolumn] (addcsvdata <FieldName> <String>)*
```
By default, Gam gets no group membership information for each user. The `groups` and `groupsincolumns`
@@ -1058,12 +1072,34 @@ to limit the display of aliases to those that match `<RegularExpression>`.
By default, the entries in lists of groups and licenses are separated by the `csv_output_field_delimiter` from `gam.cfg`.
* `delimiter <Character>` - Separate list items with `<Character>`
By default, all licenses for a user are displayed in a list on one row:
```
primaryEmail,LicensesCount,Licenses,LicensesDisplay
user@domain.com,2,1010020020 1010330004,Google Workspace Enterprise Plus Google Voice Standard
```
With `onelicenseperrow|onelicenceperrow`, each license is on a separate row:
```
primaryEmail,License,LicenseDisplay
user@domain.com,1010020020,Google Workspace Enterprise Plus
user@domain.com 1010330004,Google Voice Standard
```
In the output, primaryEmail is the always the first column; these options control the sorting of the remaining columns.
* `allfields|basic|full` - All other columns are sorted by name.
* `sortheaders [true]` - All other columns are sorted by name.
* `<UserFieldName>*|fields <UserFieldNameList>` - The columns appear in the order that the fields are specified.
* `scalarsfirst [true]` - When columns are sorted by name, scalar fields appear before repeating fields.
By default, if `<UserTypeEntity>` includes an email address the is not a user member of the domain,
an error message is generated.
```
User: testuserxxx@domain.com, Does not exist
```
Using option `showvalidcolumn`, a new column `Found` indicates domain membership; no errors are generated
Add additional columns of data from the command line to the output
* `addcsvdata <FieldName> <String>`
By default, Gam displays the information as columns of fields; the following option causes the output to be in JSON format:
* `formatjson` - Display the fields in JSON format.
@@ -1206,9 +1242,62 @@ $ more UsersList.csv
["testuser1@domain.org", "testuser2@domain.org", "testuser3@domain.org", "testuser4@domain.org"]
```
## Display user count
## Display user counts
Display the number of users in an entity.
```
gam <UserTypeEntity> show count
gam <UserTypeEntity> print users showitemcountonly
gam print users select <UserTypeEntity> showitemcountonly
gam print users
([domain|domains <DomainNameEntity>] [(query <QueryUser>)|(queries <QueryUserList>)]
[limittoou <OrgUnitItem>] [deleted_only|only_deleted])|[select <UserTypeEntity>]
[issuspended <Boolean>]
showitemcountonly
```
Example
```
$ gam print users query "orgUnitPath='/Students/Middle School'" showitemcountonly
Getting all Users that match query (query="orgUnitPath='/Students/Middle School'"), may take some time on a large Google Workspace Account...
Got 221 Users: aaron-first@domain.com - zoe-last@domain.com
221
```
The `Getting` and `Got` messages are written to stderr, the count is writtem to stdout.
To retrieve the count with `showitemcountonly`:
```
Linux/MacOS
count=$(gam print users query "orgUnitPath='/Students/Middle School'" showitemcountonly)
Windows PowerShell
count = & gam print users query "orgUnitPath='/Students/Middle School'" showitemcountonly
```
## Verify domain membership
You have a CSV file of email addresses and want to verify of the addresses are valid users in your domain.
```
# Users.csv
$ more Users.csv
primaryEmail,name
testuser1@domain.com,Test User 1
testuserxxx@domain.com,Test User XXX
testuser2@domain.com,Test User 2
# Without showvalidcolumn, non-domain users generate an error
$ gam redirect csv - multiprocess csv Users.csv gam user "~primaryEmail" print users fields primaryemail,id addcsvdata name "~name"
2024-02-23T11:29:00.407-08:00,0/3,Using 3 processes...
2024-02-23T11:29:00.410-08:00,0,Processing item 3/3
User: testuserxxx@domain.com, Does not exist
2024-02-23T11:29:06.511-08:00,0/3,Processing complete
primaryEmail,id,name
testuser1@domain.com,118080758787650801331,Test User 1
testuser2@domain.com,107344800159717682514,Test User 2
# Using showvalidcolumn, a new column `Valid` indicates domain membership; no errors are generated
$ gam redirect csv - multiprocess csv Users.csv gam user "~primaryEmail" print users fields primaryemail,id addcsvdata name "~name" showvalidcolumn
2024-02-23T11:29:22.287-08:00,0/3,Using 3 processes...
2024-02-23T11:29:22.292-08:00,0,Processing item 3/3
2024-02-23T11:29:23.366-08:00,0/3,Processing complete
primaryEmail,id,Valid,name
testuser1@domain.com,118080758787650801331,True,Test User 1
testuserxxx@domain.com,,False,Test User XXX
testuser2@domain.com,107344800159717682514,True,Test User 2
```

View File

@@ -28,7 +28,7 @@ The YubiKey can be configured with a PIN that must be entered in order for it to
Yes but in practice this does not work very well with GAMADV-XTD3. The YubiKey will need to be touched every time there is a GAMADV-XTD3 command running which for batch or cron jobs may be constant. GAMADV-XTD3 can use a PIN configured on the YubiKey in order to offer an additional layer of protection.
### If I use a YubiKey, do I need to rotate the private key regularly?
No, because the YubiKey generated the private key it cannot be digitally exported from the YubiKey so there is not chance for it to be copied and stolen. Instead you should physically secure the YubiKey from theft.
No, because the YubiKey generated the private key it cannot be digitally exported from the YubiKey so there is no chance for it to be copied and stolen. Instead you should physically secure the YubiKey from theft.
### What data does the service account private key have access to?
When using domain-wide delegation with GAMADV-XTD3, the service account and anyone possessing the service account private key oauth2service.json file has access to the Gmail, Drive and Calendar data of ALL Workspace users in your domain. For this reason, whether using a YubiKey or not, you should take strong measures to protect the service account private key.

View File

@@ -219,7 +219,7 @@ gam create vaultexport|export matter <MatterItem> [name <String>] corpus calenda
[includeshareddrives <Boolean>] [driveversiondate <Date>|<Time>] [includeaccessinfo <Boolean>]
[includerooms <Boolean>]
[excludedrafts <Boolean>] [format mbox|pst]
[showconfidentialmodecontent <Boolean>] [usenewexport <Boolean>]
[showconfidentialmodecontent <Boolean>] [usenewexport <Boolean>] [exportlinkeddrivefiles <Boolean>]
[covereddata calllogs|textmessages|voicemails]
[region any|europe|us] [showdetails|returnidonly]
```
@@ -305,6 +305,10 @@ For `corpus mail`, you can specify whether to use the new export system:
* `usenewexport false` - Do not use the new export system
* `usenewexport true` - Use the new export system
For `corpus mail`, you can specify whether to enable exporting linked Drive files:
* `exportlinkeddrivefiles false` - Do not export linked Drive files
* `exportlinkeddrivefiles true` - Export linked Drive files
See: https://support.google.com/vault/answer/4388708#new_gmail_export&zippy=%2Cfebruary-new-gmail-export-system-available
For `corpus mail`, `corpus groups`, `corpus hangouts_chat`and `corpus voice`, you can specify the format of the exported data:
@@ -653,7 +657,11 @@ By default, Gam displays the information as an indented list of keys and values.
gam print vaultholds|holds [todrive <ToDriveAttributes>*] [matters <MatterItemList>]
[fields <VaultHoldFieldNameList>] [shownames]
[formatjson [quotechar <Character>]]
[oneitemperrow]
```
By default, all accounts for a hold are displayed on a single row;
use `oneitemperrow` to have each account displayed on a separate row.
By default, Gam displays the information as columns of fields; the following option causes the output to be in JSON format,
* `formatjson` - Display the fields in JSON format.

View File

@@ -1,44 +1,43 @@
\
# Version and Help
Print the current version of Gam with details
```
gam version
GAMADV-XTD3 6.67.10 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.75.02 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.0 64-bit final
MacOS Monterey 12.7 x86_64
Python 3.12.3 64-bit final
MacOS Sonoma 14.4.1 x86_64
Path: /Users/Admin/bin/gamadv-xtd3
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain.com
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
Time: 2023-06-02T21:10:00-07:00
```
Print the current version of Gam with details and time offset information
```
gam version timeoffset
GAMADV-XTD3 6.67.10 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.75.02 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.0 64-bit final
MacOS Monterey 12.7 x86_64
Python 3.12.3 64-bit final
MacOS Sonoma 14.4.1 x86_64
Path: /Users/Admin/bin/gamadv-xtd3
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain.com
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
Your system time differs from www.googleapis.com by less than 1 second
```
Print the current version of Gam with extended details and SSL information
```
gam version extended
GAMADV-XTD3 6.67.10 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.75.02 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.0 64-bit final
MacOS Monterey 12.7 x86_64
Python 3.12.3 64-bit final
MacOS Sonoma 14.4.1 x86_64
Path: /Users/Admin/bin/gamadv-xtd3
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain.com
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
Time: 2023-06-02T21:10:00-07:00
Your system time differs from admin.googleapis.com by less than 1 second
OpenSSL 3.1.1 30 May 2023
cryptography 41.0.1
filelock 3.12.0
filelock 3.12.3
google-api-python-client 2.88.0
google-auth-httplib2 0.1.0
google-auth-oauthlib 1.0.0
@@ -65,7 +64,7 @@ MacOS High Sierra 10.13.6 x86_64
Path: /Users/Admin/bin/gamadv-xtd3
Version Check:
Current: 5.35.08
Latest: 6.67.10
Latest: 6.75.02
echo $?
1
```
@@ -73,7 +72,7 @@ echo $?
Print the current version number without details
```
gam version simple
6.67.10
6.75.02
```
In Linux/MacOS you can do:
```
@@ -83,12 +82,12 @@ echo $VER
Print the current version of Gam and address of this Wiki
```
gam help
GAM 6.67.10 - https://github.com/taers232c/GAMADV-XTD3
GAM 6.75.02 - https://github.com/taers232c/GAMADV-XTD3
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.0 64-bit final
MacOS Monterey 12.7 x86_64
Python 3.12.3 64-bit final
MacOS Sonoma 14.4.1 x86_64
Path: /Users/Admin/bin/gamadv-xtd3
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain.com
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
Time: 2023-06-02T21:10:00-07:00
Help: Syntax in file /Users/Admin/bin/gamadv-xtd3/GamCommands.txt
Help: Documentation is at https://github.com/taers232c/GAMADV-XTD3/wiki

View File

@@ -129,6 +129,7 @@ Service Account Access
* [Users - Drive - File Selection](Drive-File-Selection)
* [Users - Drive - Activity/Settings](Users-Drive-Activity-Settings)
* [Users - Drive - Cleanup](Users-Drive-Cleanup)
* [Users - Drive - Comments](Users-Drive-Comments)
* [Users - Drive - Copy/Move](Users-Drive-Copy-Move)
* [Users - Drive - Files-Display](Users-Drive-Files-Display)
* [Users - Drive - Files-Manage](Users-Drive-Files-Manage)
@@ -141,6 +142,7 @@ Service Account Access
* [Users - Drive - Shortcuts](Users-Drive-Shortcuts)
* [Users - Drive - Transfer](Users-Drive-Transfer)
* [Users - Forms](Users-Forms)
* [Users - Gmail - Client Side Encryption](Users-Gmail-CSE)
* [Users - Gmail - Delegates](Users-Gmail-Delegates)
* [Users - Gmail - Filters](Users-Gmail-Filters)
* [Users - Gmail - Forwarding](Users-Gmail-Forwarding)

View File

@@ -123,7 +123,9 @@ clock_skew_in_seconds
Default: 10
Range: 10 - 3600
cmdlog
Path to GAM Log file; there is no logging if cmdlog is empty
Path to GAM Log file; there is no logging if cmdlog is empty.
If cmdlog specifies a relative path, e.g., just a filename, it is appended to GamConfigDir.
If cmdlog specifies a full path, it is used as is.
Default: ''
cmdlog_max_backups
Maximum number of backup log files
@@ -150,7 +152,7 @@ csv_input_column_delimiter
Default: ','
csv_input_no_escape_char
When reading a CSV file, should `\` be ignored as an escape character.
Set this to False if the input file data was written using `\` as an escape character.
Set this to False if the input file data was written using `\` as an escape character.
Default: True
csv_input_quote_char
A one-character string used to quote fields containing special characters,
@@ -213,14 +215,14 @@ csv_output_header_force
for inclusion in the CSV file written by a gam print command
Default: ''
csv_output_line_terminator
p Allowed values: cr, lf, crlf
Allowed values: cr, lf, crlf
Designates character(s) used to terminate the lines of a CSV file.
For Linux and Mac OS, this would typically be lf.
For Windows, this would typically be crlf.
Default: lf
csv_output_no_escape_char
When writing a CSV file, should `\` be ignored as an escape character.
Set this to True if the output file data is to be read by a non-Python program.
Set this to True if the output file data is to be read by a non-Python program.
Default: False
csv_output_quote_char
A one-character string used to quote fields containing special characters,
@@ -255,6 +257,10 @@ csv_output_row_filter_mode
csv_output_row_limit
A limit on the number of rows to write to a CSV file; a value of 0 sets no limit.
Default: 0
csv_output_sort_headers
A list of column headers that causes GAM to sort CSV output rows by those headers.
The column headers are case insensitive and if column header does not appear in the CSV output, it is ignored.
Default: Blank
csv_output_subfield_delimiter
Character used to delimit fields and subfields in headers when writing CSV files;
this must be a single character
@@ -322,6 +328,12 @@ extra_args
Path to extra_args.txt
Default: Blank
Data file: extra_args.txt
gmail_cse_incert_dir
Directory for the S/MIME certificate files used by Gmail Client Side Encryption.
Default: Blank
gmail_cse_inkey_dir
Directory for the Key Access Control List (KACL) wrapped private key data files used by Gmail Client Side Encryption.
Default: Blank
inter_batch_wait
When processing items in batches, how many seconds should GAM wait between batches
Default: 0
@@ -420,23 +432,23 @@ print_agu_domains
gam print groups
gam print|show group-members
gam print users
This allows predefining the list of domains so they don't have to be specified in each command.
This allows predefining the list of domains so they don't have to be specified in each command.
Default: Blank
print_cros_ous
A comma separated list of org unit that are used in these commands:
gam print cros
gam print crosactivity
This allows predefining the list of org units so they don't have to be specified in each command.
This allows predefining the list of org units so they don't have to be specified in each command.
Default: Blank
print_cros_ous_and_children
A comma separated list of org unit names that are used in these commands:
gam print cros
gam print crosactivity
This allows predefining the list of org units so they don't have to be specified in each command.
This allows predefining the list of org units so they don't have to be specified in each command.
Default: Blank
process_wait_limit
When processing batch/CSV files, how long (in seconds) GAM should wait for all batch|csv processes to complete
after all have been started. If the limit is reached, GAM terminates any remaining processes.
When processing batch/CSV files, how long (in seconds) GAM should wait for all batch|csv processes to complete
after all have been started. If the limit is reached, GAM terminates any remaining processes.
Default: 0: no limit
Range: 0 - Unlimited
quick_cros_move
@@ -461,7 +473,7 @@ section
Default section of gam.cfg.
Default: DEFAULT
show_api_calls_retry_data
Enable/disable display of Google API calls retry data and end of processing
Enable/disable display of Google API calls retry data at end of processing
Default: False
show_commands
Enable/disable display of commands to stderr when executing `gam batch|tbatch|csv|loop`.
@@ -568,11 +580,22 @@ todrive_upload_nodata
todrive_user
Email address of user to receive CSV files when todrive is specified
Default: '' which becomes admin user in admin_email or address from oauth2.txt
truncate_client_id
Prior to version 6.74.00, GAM stripped '.apps.googleusercontent.com' from the client_id in oauth2.txt
and passed the truncated value in API calls; this is no longer performed unless truncate_client_id is true
Default: False
update_cros_ou_with_id
Update the OU of a Chromebook with the OU ID rather than the OU path.
Set to true if you are getting the following error:
`400: invalidInput - Invalid Input: Inconsistent Orgunit id and path in request`
Default: False
use_classroom_owner_access
How is classroom member information obtained and how are classroom members deleted.
Client access does not provide complete information about non-domain students/teachers.
When False, GAM uses client access to get classroom member information and to delete members
When True, GAM uses service account access as the classroom owner.
An extra API call is required per course to authenticate the owner
Default: False
use_projectid_as_name
When False, new projects have a default project name of "GAM Project"
and a default app name of "GAM".
@@ -711,6 +734,7 @@ Section: DEFAULT
todrive_timezone = ''
todrive_upload_nodata = true
todrive_user = ''
truncate_client_id = false
update_cros_ou_with_id = false
use_projectid_as_name = false
user_max_results = 500
@@ -897,6 +921,7 @@ todrive_timestamp = false
todrive_timezone = ''
todrive_upload_nodata = true
todrive_user = ''
truncate_client_id = false
update_cros_ou_with_id = false
use_projectid_as_name = false

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,676 @@
Merged GAM-Team version
6.75.02
Updated `gam report <ActivityApplictionName>` to retry/handle the following error:
```
ERROR: 503: serviceNotAvailable - The service is currently unavailable.
```
6.75.01
Added option `admin <EmailAddress>` to `gam upload sakey`.
6.75.00
Updated `gam create project` to simplify handling the situation where your workspace is configured to disable service account private key uploads.
Added command `gam upload sakey` to aid in this process.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Authorization#upload-a-service-account-key-to-a-service-account-with-no-keys
6.74.02
Fixed bug in `gam <UserTypeEntity> print shareddrives ... formatjson` that caused a trap.
6.74.01
Updated `gam create|update drivefileacl <DriveFileEntity> ... expiration <Time>` to handle
the following error caused by tryig to add an expiration time to a member of a Shared Drive.
```
ERROR: 403: expirationDateNotAllowedForSharedDriveMembers - Expiration dates are not allowed for shared drive members.
```
6.74.00
Added `truncate_client_id` Boolean variable to `gam.cfg`. Prior to version 6.74.00, GAM stripped
'.apps.googleusercontent.com' from `client_id` in `oauth2.txt` and passed the truncated value in API calls.
At Jay's suggestion this is no longer performed by default; setting `truncate_client_id = true` restores the previous behavior.
Do `gam oauth delete` and `gam oauth create` to set the untruncated value of `client_id` in `oauth2.txt`.
6.73.00
The Google Chat API has been updated so that chat members can now have their role set to manager.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Users-Chat#manage-chat-members
6.72.16
Updated `emailaddressList <EmailAddressList>` and `domainlist|notdomainlist <DomainNameList>`
in `<PermissionMatch>` to perform case-insensitive matches as the API is returning mixed case
ACL email addresses in some cases.
6.72.15
Updated all commands that display tasks to display the due date in GMT as the time portion
is not supported by the API and converting the due date to local time may display the wrong date.
Renamed license SKU `1010400001` from `Beyond Corp Enterprise` to `Chrome Enterprise Premium`.
6.72.14
Upgraded to Python 3.12.3 where possible.
6.72.13
Added the following option to `<EventMatchProperty>` that can be used to select
events based on the domains of the attendees.
```
matchfield attendeesonlydomainlist <DomainNameList>
```
This returns true if all attendee's email addresses are in a domain in `<DomainNameList>`;
for example this lets you look for events with attendees only in your internal domains.
6.72.12
Added the following options to `<EventMatchProperty>` that can be used to select
events based on the domains of the attendees.
```
matchfield attendeesdomainlist <DomainNameList>
matchfield attendeesnotdomainlist <DomainNameList>
```
The first returns true if any attendee's email address is in a domain in `<DomainNameList>`;
for example this lets you look for events with attendees in specific external domains.
The second returns true if any attendee's email address is in a domain other than those in `<DomainNameList>`;
for example this lets you look for events with attendees not in your internal domains.
6.72.11
Added option `oneitemperrow` to 'gam print vaultholds` to have each of a
hold's accounts displayed on a separate row with all of the other hold fields.
6.72.10
Added `timeofdayrange=<HH:MM>/<HH:MM>` and `timeofdayrange!=<HH:MM>/<HH:MM>` to `<RowValueFilter>` that allows
CSV row filtering based on time-of-day.
6.72.09
Updated `countsonly` option of `gam <UserTypeEntity> print|show notes` to additionally display the total number of notes.
6.72.08
Added option `countsonly` to `gam <UserTypeEntity> print|show notes` that displays
the number of notes a user owns and the number of notes a user can edit.
6.72.07
Updated commands that send emails to not downshift `'First Last<firstlast@domain.com>'` to `'first last<firstlast@domain.com>'`.
6.72.06
Updated the following commands to properly handle emailaddress lists containing addresses of the form: `'First Last<firstlast@domain.com>'`.
```
gam <UserTypeEntity> sendemail recipient|to <RecipientEntity> [cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage]
gam create|update user ... notify <EmailAddressList>
```
6.72.05
Cleaned up code for all commands that display Chat objects.
6.72.04
Added commands to display Chat events.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Users-Chat#display-chat-events
6.72.03
Fixed bug in `gam <UserTypeEntity> create chatspace` that caused a trap.
6.72.02
Updated `gam delete admin <RoleAssignmentId>` to handle the following error that
occurs when `<RoleAssignmentId>` references a user that has been deleted.
```
ERROR: 404: resourceNotFound - Does not exist
```
6.72.01
Improved commands to display drive file comments.
6.72.00
Added commands to display drive file comments.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Users-Drive-Comments
6.71.18
Updated `<CrOSFieldName>` to include `cpuinfo` and `backlightinfo`.
6.71.17
Added `depth` column to output of `gam <UserTypeEntity> print diskusage <DriveFileEntity>` that can
be used to filter the depth of the folders displayed. Depth `-1` is the top level folder, depth `0`
are its immediate children, depth `2` are the children of depth `1` and so forth.
```
gam config csv_output_row_filter "depth:count<1" user organizer@domain.com print diskusage teamdriveid <TeamDriveID>
```
6.71.16
Updated `gam <UserTypeEntity> create|update sendas <EmailAddress> ... replyto <EmailAddress>`
to allow uppercase letters in `sendas <EmailAddress>` and `replyto <EmailAddress>`.
6.71.15
Updated `gam create project` to handle the following error:
```
ERROR: 403: permissionDenied - Authentication error: 7; Error Details: User not allowed to access GCP services.
```
This error occurs when the Google Workspace admin or GCP project manager email address used in the command
is in an OU where Google Cloud Platform is not enabled in Apps/Additional Google services.
6.71.14
Added a command to update a Gmail label's settings by specifying it's ID rather than it's name.
```
gam <UserTypeEntity> update labelid <LabelID> [name <String>]
[messagelistvisibility hide|show] [labellistvisibility hide|show|showifunread]
[backgroundcolor "<LabelColorHex>|<LabelBackgroundColorHex>|custom:<ColorHex>"]
[textcolor "<LabelColorHex>|<LabelTextColorHex>|custom:<ColorHex>"]
```
6.71.13
Updated `<UserMultiAttribute>.location.buildingid <String>` to allow non-validated building IDs
by specifying `nv:` at the beginning of `<String>`; e.g., `nv:Building X' sets the building ID to `Building X`.
6.71.12
Added option `showmimetype category <MimeTypeNameList>` to `gam <UserTypeEntity> print|show filecounts|filelist|filetree`
```
<MimeTypeName> ::= application|audio|font|image|message|model|multipart|text|video
<MimeTypeNameList> ::= "<MimeTypeName>(,<MimeTypeName>)*"
gam user user@domain.com print filelist fields id,name,mimetype showmimetype prefixes audio,video
```
Fix bug in `gam <UserTypeEntity> info|show sheet <DriveFileEntity>` that caused a trap.
6.71.11
Added option `addcsvdata <FieldName> <String>` to `gam print cros` that adds
additional columns of data to the CSV file output. Typically, you would read CSV file of device IDs/serial numbers
to generate a CSV file of results and copy data from the input CSV to the outout CSV.
6.71.10
Reverted change made in 6.71.09 to `gam <UserTypeEntity> print filelist` when `showmimetype` and `filepath|fullpath`
were both specified. The change improved the performance when `showmimetype` selected a small number of files;
the information for just those files was downloaded and then additional API calls were made to construct the file paths.
However, if a large number of files were selected, the additional APIs calls decreased performance.
Added option `mimetypeinquery` can be used when you expect the query to return a small number of files
relative to the total number of files.
6.71.09
Improved the performance of `gam <UserTypeEntity> print filelist` when `showmimetype` and `filepath|fullpath`
are both specified.
6.71.08
Added option `oneitemperrow` to 'gam print admins|adminroles` to have each of a
roles privileges displayed on a separate row with all of the other admin/role fields.
This produces a CSV file that can be used in subsequent commands without further script processing.
6.71.07
Added command to upload changes to Google Docs.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Users-Drive-Files-Manage#upload-changes-to-google-documents
6.71.06
Added additional error handling to Gmail Client Side Encryption commands.
Added license product Education Endpoint Management
* ProductID - 101049
Added license SKU Endpoint Education Upgrade
* ProductID - 101049
* SKUID - 1010490001 | eeu
6.71.05
Fixed a bug introduced in 6.71.00 that caused a trap in `gam <UserTypeEntity> print filelist`.
Added option `tdfrom <EmailAddress>` to `<ToDriveAttribute>` that causes GAM to use `<EmailAddress>` as the from address
in all emails sent. By default, the from address is the Google Workspace Admin in `gam oauth info`.o
6.71.04
Updated `gam <UserTypeEntity> create|update cseidentity` to accept either of the following key pair options:
* `primarykeypairid <KeyPairID>` - The configuration of a CSE identity that uses the same key pair for signing and encryption.
* `signingkeypairid <KeyPairID> encryptionkeypairid <KeyPairID>` - The configuration of a CSE identity that uses different key pairs for signing and encryption.
Updated CSV output row sorting to avoid a trap that occurred when a row was missing one of the sort fields.
6.71.03
Added option `tdalert <EmailAddress>` to `<ToDriveAttribute>`. When a todrive file is created or updated,
GAM will send notification emails to all `tdalert <EmailAddress>` users if `tdnotify` is true.
`<EmailAddress>` must be valid within your Google Workspace.
6.71.02
Added additional error handling to Gmail Client Side Encryption commands.
6.71.01
Fixed bug in `gam audit monitor create` that caused a trap.
6.71.00
Added `csv_output_sort_headers` string list variable to `gam.cfg` that causes GAM to sort CSV output
rows by the column headers specified in the variable. The column headers are case insensitive and
if column header does not appear in the CSV output, it is ignored.
Added `sortheaders <StringList>` to `redirect csv <FileName>` that has the same effect as above.
The sort keys specified in `redirect csv ... sortheaders <StringList>` take precedence over the values from `gam.cfg`.
Added option `tdsubject <String>` to `<ToDriveAttribute>` that causes GAM to use `<String>` as the subject
in all emails sent. In `<String>`, `#file#` will, be replaced by the file title and `#sheet#` will be replaced
by the sheet/tab title. By default, the subject is the file title.
6.70.09
Added additional error handling to Gmail Client Side Encryption commands.
Added options `showpem` and `showkaclsdata` to all Gmail CSE commands that process/display
CSE key pairs. By default, the `pem` and `kaclsdata` fields will not be displayed unless
the corresponding `show` option is specified.
6.70.08
Fixed bug in `gam <UserTypeEntity> create cseidentity <KeyPairID>` that caused an error.
6.70.07
Updated user instructions in `gam oauth create` and `gam <UserTypeEntity> update serviceaccount`
and changed `s` from selecting all scopes to selecting default scopes.
6.70.06
Updated `gam info users <UserTypeEntity>` to not include group tree infornation unless option `grouptree` is specified.
6.70.05
Added commands to create|delete|display Drive Label permissions.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Users-Drive-Labels
6.70.04
Added option `showvalidcolumn` to `gam print users` that can be used to identify whether
users are defined in the domain. Typically, you would read CSV file of email addresses
to verify as domain members.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Users#verify-domain-membership
Added option `addcsvdata <FieldName> <String>` to `gam print users` that adds
additional columns of data to the CSV file output. Typically, you would read CSV file of email addresses
to generate a CSV file of results and copy data from the input CSV to the outout CSV.
6.70.03
Renamed license product DuetAI to Gemini
* ProductID - 101047
Renamed license SKU DuetAI for Google Workspace to Gemini Enterprise
* ProductID - 101047
* SKUID - 1010470001 | geminient | duetai
Added support for license SKU Gemini Business
* ProductID - 101047
* SKUID - 1010470003 | geminibiz
6.70.02
In 6.69.00, GAM starting using course owner access when using `copyfrom` in `gam create|update course`
regardless of the value of `gam.cfg/use_course_owner_access`. This prevents copying from courses
with a deleted user. GAM now uses the value of `gam.cfg/use_course_owner_access` when `copyfrom` is used.
6.70.01
Added `gmail_cse_incert_dir` and `gmail_cse_inkey_dir` path variables to `gam.cfg` that provide
default values for the `incertdir <FilePath>` and `inkeydir <FilePath>` options in `gam <UserTypeEntity> create csekeypair`.
6.70.00
Added support for Gmail Client Side Encryption.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Users-Gmail-CSE
This is an initial, minimally tested release; proceed with care and report all issues.
6.69.00
Added `use_course_owner_access` Boolean variable to `gam.cfg` that controls how GAM gets
classroom member information and removes students/teachers. Client/admin access does not provide
complete information about non-domain students/teachers.
* `False` - Use client/admin access; this is the default. Use if you don't have non-domain members in your courses.
* `True` - Use service account access as the classroom owner. An extra API call is required per course to authenticate the owner; this will affect performance
Added the following command which must be used to delete classroom invitations for non-domain students/teachers.
```
gam delete classroominvitation courses <CourseEntity> (ids <ClassroomInvitationIDEntity>)|(role all|owner|student|teacher)
```
You can obtain the classroom invitation IDs with these commands:
```
gam show classroominvitations (course|class <CourseEntity>)*|([teacher <UserItem>] [student <UserItem>] [states <CourseStateList>])
[role all|owner|student|teacher] [formatjson]
gam print classroominvitations [todrive <ToDriveAttribute>*] (course|class <CourseEntity>)*|([teacher <UserItem>] [student <UserItem>] [states <CourseStateList>])
[role all|owner|student|teacher] [formatjson [quotechar <Character>]]
```
6.68.08
Updated `gam <UserTypeEntity> print filelist|drivefileacls|shareddriveacls ... oneitemperrow` to print
ACLs with multiple permission details on separate rows for each basic permission/permission detail combination.
This case occurs when a member of a Shared Drive has access to a file and also has explicitly granted access to the same file.
Added `permtype member|file` to `<PermissionMatch>` that allows determining whether an ACL on a Shared Drive file was
derived from membership or explicitly granted.
6.68.07
Updated `gam info user ... locations formatjson` to include the `buildingName` field in the
`locations` entries. If `gam.cfg` contains `quick_info_user = true` or the `quick` option
is included on the command line, add the option `buildingnames` to the command line.
6.68.06
Fixed bug in `gam <UserTypeEntity> copy drivefile <DriveFileID> ... mergewithparent` that incorrectly named
the copied file with the name of the parent folder.
Updated `gam <UserTypeEntity> copy|move drivefile` to avoid copying/moving the same file twice.
6.68.05
Updated `gam print groups ... ciallfields|(cifields <CIGroupFieldNameList>)` to account for an
API shortcoming that failed to get all of the Cloud Identity fields.
6.68.04
Added option `skiprows <Integer>` to `gam csv|loop` that causes GAM to skip processing the first `<Integer>` filtered rows.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Bulk-Processing#csv-files
6.68.03
Fixed bug in `gam <UserTypeEntity> create drivefileacl` that caused a trap.
6.68.02
Upgraded to Python 3.12.2 where possible.
Added options `restricted|(audience <String>)` to `gam <UserTypeEntity> create|update chatspace` that
sets the access options for the chat space. These options are in Developer Preview and will not be generally available.
6.68.01
Fixed `<PermissionMatch>` bug for real.
6.68.00
Fixed `<PermissionMatch>` bug introduced in 6.67.35 that caused a command error like the following or would
not properly match `type|nottype <DriveFileACLType>` and `role|notrole <DriveFileACLRole>`.
```
ERROR: permission attribute allowfilediscovery/withlink not allowed with type {'a', 'y', 'e', 'o', 'n'}
```
My sincere apologies.
6.67.39
Added option `wait <Integer> <Integer>` to `gam create datatransfer` that causes GAM to wait
for the transfer to complete. The first `<Integer>` must be in the range 5-60 and is the number
of seconds between checks to see if the transfer has completed. The second `<Integer>` is the maximum
number of checks to perform. By default, GAM does not wait for the transfer to complete.
6.67.38
Added option `tdnotify [<Boolean>]` to `<ToDriveAttribute>` that causes GAM to send notification
emails to all `tdshare <EmailAddress>` users when the file is uploaded/updated.
6.67.37
Fixed bug in `gam <UserTypeEntity> show messages ... showattachments` to avoid a trap when `text/plain` attachments
in character sets other than `UTF-8` are displayed.
6.67.36
Updated `gam batch <BatchContent>` and `gam tbatch <BatchContent>` commands to accept lines with the following form:
```
sleep <Integer>
```
Batch processing will suspend for `<Integer>` seconds before the next command line is processed.
6.67.35
Added the following options to `<PermissionMatch>` that allow more powerful matching.
```
nottype <DriveFileACLType>
typelist <DriveFileACLTypeList>
nottypelist <DriveFileACLTypeList>
rolelist <DriveFileACLRoleList>
notrolelist <DriveFileACLRoleList>
```
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Permission-Matches#define-a-match
6.67.34
Added option `movetoorgunitdelay <Integer>` to `gam <UserTypeEntity> create shareddrive <Name> ... ou|org|orgunit <OrgUnitItem>`.
GAM creates the Shared Drive, verifies that it has been created and then tries to move it to `<OrgUnitItem>`. Google seems to
require a delay or the following error is generated.
```
ERROR: 409: 409 - The operation was aborted.
```
`movetoorgunitdelay` defaults to 20 seconds which seems to work; `<Integer>` can range from 0 to 60.
6.67.33
Upgraded to OpenSSL 3.2.1 where possible.
Fixed bug in `gam <UserTypeEntity> print shareddrives` where `role` was improperly displayed as `fileOrganizer`
rather than `writer`.
Added option `guiroles [<Boolean>]` to `gam <UserTypeEntity> info|print|show shareddrive` that maps
the Drive API role names to the Google Drive GUI role names.
```
API: GUI
commenter: Commenter
fileOrganizer: Content manager
organizer: Manager
reader: Viewer
writer: Contributor
```
6.67.32
Updated `<ToDriveAttribute>` to allow multiple `tdshare <EmailAddress> commenter|reader|writer` options.
Fixed bug in `gam <UserTypeEntity> print shareddrives` where `role` was improperly displayed as `unknown`
rather than `reader` when `Allow viewers and commenters to download, print, and copy files` was unchecked for the Shared Drive.
6.67.31
Updated `gam <UserTypeEntity> claim|transfer ownership <DriveFileEntity>` to properly
handle the case where `<DriveFileEntity>` referencess a Drive shortcut.
6.67.30
Fixed bug where the `fullpath` option in various commands was not converting the generic shared drive name `Drive` to the drive's actual name.
6.67.29
Added optional argument `owneraccess` to `gam courses <CourseEntity> remove teachers|students [owneracccess] <UserTypeEntity` and
`gam course <CourseID> remove teacher|student [owneraccess] <EmailAddress>` in order to test a possible API change.
Updated code to avoid a trap when `gam config auto_batch_min 1 csv file.csv gam ...` was entered.
The `config auto_batch_min 1` is not appropriate in this context and will be ignored.
6.67.28
Improved handling of `Bad Request` error in `gam <UserTypeEntity> collect orphans`.
6.67.27
Updated `gam <UserTypeEntity> collect orphans` to handle the following error:
```
ERROR: 400: badRequest - Bad Request
```
6.67.26
Fixed bug in `gam print vaultexports ... formatjson` that caused a trap.
6.67.25
Added option `owneraccess` to `gam info courses <CourseEntity>` and `gam info course <CourseID>` in order
to test a possible API change.
6.67.24
Fixed bug that caused HTML password notification email messages to be displayed in raw form.
6.67.23
Use local copy of `googleapiclient` to remove static discovery documents to improve performance.
6.67.22
Added `permissionidlist <PermissionIDList>` to `<PermissionMatch>` that allows matching any permission ID in a list.
Added option `exportlinkeddrivefiles <Boolean>` to `gam create vaultexport` that is used with `corpus mail`.
6.67.21
Updated `gam remove aliases <EmailAddress> user|group <EmailAddressEntity>` to give a more informative
error message when the target/alias combination does not exist.
```
Old: User: testsimple@domain.com, User Alias: tsalias@domain.com, Remove Failed: Invalid Input: resource_id
New: User: testsimple@domain.com, User Alias: tsalias@domain.com, Remove Failed: Does not exist
```
6.67.20
Added option `onelicenseperrow|onelicenceperrow` to `gam print users ... licenses` that causes GAM to print
a seperate user information row for each license a user is assigned. This makes processing
the licenses in a script possible and allows better sorting in a CSV File.
By default, all licenses for a user are displayed in a list on one row:
```
primaryEmail,LicensesCount,Licenses,LicensesDisplay
user@domain.com,2,1010020020 1010330004,Google Workspace Enterprise Plus Google Voice Standard
```
With `onelicenseperrow|onelicenceperrow`, each license is on a separate row:
```
primaryEmail,License,LicenseDisplay
user@domain.com,1010020020,Google Workspace Enterprise Plus
user@domain.com 1010330004,Google Voice Standard
```
6.67.19
Updated `gam create|update user ... notify` to encode the characters `<>&` in the password
so that they display correctly when the notify message content is HTML.
6.67.18
Cleaned up `Getting/Got` messages for `gam print courses|course-participants`.
6.67.17
Added option `showitemcountonly` to various commands that causes GAM to display the
item count on stdout; no CSV file is written.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Cloud-Identity-Groups#display-group-counts
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Classroom-Courses#display-course-counts
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Classroom-Membership#display-course-membership-counts
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/ChromeOS-Devices#display-cros-device-counts
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Cloud-Identity-Devices#display-device-counts
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Cloud-Identity-Devices#display-device-user-counts
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Groups#display-group-counts
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Mobile-Devices#display-mobile-device-counts
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Organizational-Units#display-organizational-unit-counts
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Resources#display-resource-counts
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Users#display-user-counts
6.67.16
By default, `gam print group-members membernames` displays `Unknown` for members whose names can not be determined.
Added option `unknownname <String>` that let's you specify an alternative value.
Further improved performance of `gam print group-members membernames cachememberinfo`.
6.67.15
Update `gam print group-members membernames` to handle the following error:
```
ERROR: 400: failedPrecondition - Precondition check failed.
```
Added option `cachememberinfo [Boolean]` to `gam print group-members` that causes GAM to cache member info
so that only one API call is made to get information for each user/group. This consumes
more memory but dramatically reduces the number of API calls.
6.67.14
Updated reseller commands to handle the following error:
```
ERROR: 400: invalid - Customer domain [domain.com] is linked to one or more email verified customers, please provide a customer id.
```
6.67.13
Updated `gam create domain <DomainName>` to handle the following error:
```
ERROR: 409: conflict - Domain in request is in use by an email verified customer.
```
6.67.12
Added option `addcsvdata <FieldName> <String>` to `gam print datatransfers` that adds
additional columns of data to the CSV file output.
6.67.11
Updated various Gmail related commands to handle this error:
```
ERROR: 403: permissionDenied - Insufficient Permission
```
when the following service account scopes are selected:
```
[ ] 23) Gmail API - Basic Settings (Filters,IMAP, Language, POP, Vacation) - read/write, Sharing Settings (Delegates, Forwarding, SendAs) - read
[ ] 24) Gmail API - Full Access (Labels, Messages)
[ ] 25) Gmail API - Full Access (Labels, Messages) except delete message
[*] 26) Gmail API - Full Access - read only
[ ] 27) Gmail API - Send Messages - including todrive
[ ] 28) Gmail API - Sharing Settings (Delegates, Forwarding, SendAs) - write
```
6.67.10
Fixed bug that caused a trap when optional argument `charset <Charset>` was used with `emlfile <FileName>` in `gam <UserTypeEntity> draft|import|insert message`.

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2023 Ross Scroggs All Rights Reserved.
# Copyright (C) 2024 Ross Scroggs All Rights Reserved.
#
# All Rights Reserved.
#
@@ -77,6 +77,7 @@ class GamAction():
MOVE_MERGE = 'movm'
NOACTION = 'noac'
NOACTION_PREVIEW = 'noap'
OBLITERATE = 'obli'
PERFORM = 'perf'
PRE_PROVISIONED_DISABLE ='ppdi'
PRE_PROVISIONED_REENABLE ='ppre'
@@ -193,6 +194,7 @@ class GamAction():
MOVE_MERGE: ['Moved(Merge)', 'Move(Merge)'],
NOACTION: ['No Action', 'No Action'],
NOACTION_PREVIEW: ['No Action (Preview)', 'No Action (Preview)'],
OBLITERATE: ['Obliterated', 'Obliterate'],
PERFORM: ['Action Performed', 'Perform Action'],
PRE_PROVISIONED_DISABLE: ['PreProvisioned Disabled', 'PreProvisioned Disable'],
PRE_PROVISIONED_REENABLE: ['PreProvisioned Reenabled', 'PreProvisioned Reenable'],

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2023 Ross Scroggs All Rights Reserved.
# Copyright (C) 2024 Ross Scroggs All Rights Reserved.
#
# All Rights Reserved.
#
@@ -27,6 +27,7 @@ ANALYTICS_ADMIN = 'analyticsadmin'
CALENDAR = 'calendar'
CBCM = 'cbcm'
CHAT = 'chat'
CHAT_EVENTS = 'chatevents'
CHAT_MEMBERSHIPS = 'chatmemberships'
CHAT_MESSAGES = 'chatmessages'
CHAT_SPACES = 'chatspaces'
@@ -94,7 +95,9 @@ YOUTUBE = 'youtube'
CHROMEVERSIONHISTORY_URL = 'https://versionhistory.googleapis.com/v1/chrome/platforms'
DRIVE_SCOPE = 'https://www.googleapis.com/auth/drive'
GMAIL_SEND_SCOPE = 'https://www.googleapis.com/auth/gmail.send'
GOOGLE_OAUTH2_TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token"
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_TOKEN_ENDPOINT = 'https://oauth2.googleapis.com/token'
CLOUD_PLATFORM_SCOPE = 'https://www.googleapis.com/auth/cloud-platform'
IAM_SCOPE = 'https://www.googleapis.com/auth/iam'
PEOPLE_SCOPE = 'https://www.googleapis.com/auth/contacts'
@@ -190,6 +193,7 @@ _INFO = {
CALENDAR: {'name': 'Calendar API', 'version': 'v3', 'v2discovery': True, 'mappedAPI': 'calendar-json'},
CBCM: {'name': 'Chrome Browser Cloud Management API', 'version': 'v1.1beta1', 'v2discovery': True, 'localjson': True},
CHAT: {'name': 'Chat API', 'version': 'v1', 'v2discovery': True},
CHAT_EVENTS: {'name': 'Chat API - Events', 'version': 'v1', 'v2discovery': True, 'mappedAPI': CHAT},
CHAT_MEMBERSHIPS: {'name': 'Chat API - Memberships', 'version': 'v1', 'v2discovery': True, 'mappedAPI': CHAT},
CHAT_MESSAGES: {'name': 'Chat API - Messages', 'version': 'v1', 'v2discovery': True, 'mappedAPI': CHAT},
CHAT_SPACES: {'name': 'Chat API - Spaces', 'version': 'v1', 'v2discovery': True, 'mappedAPI': CHAT},
@@ -539,6 +543,10 @@ _SVCACCT_SCOPES = [
'api': CLASSROOM,
'subscopes': [],
'scope': 'https://www.googleapis.com/auth/classroom.profile.emails'},
{'name': 'Classroom API - Profile Photos',
'api': CLASSROOM,
'subscopes': [],
'scope': 'https://www.googleapis.com/auth/classroom.profile.photos'},
{'name': 'Classroom API - Rosters',
'api': CLASSROOM,
'subscopes': READONLY,
@@ -579,19 +587,19 @@ _SVCACCT_SCOPES = [
'api': FORMS,
'subscopes': [],
'scope': DRIVE_SCOPE},
{'name': 'Gmail API - Full Access',
{'name': 'Gmail API - Full Access (Labels, Messages)',
'api': GMAIL,
'subscopes': [],
'scope': 'https://mail.google.com/'},
{'name': 'Gmail API - Full Access except immediate delete',
{'name': 'Gmail API - Full Access (Labels, Messages) except delete message',
'api': GMAIL,
'subscopes': [],
'scope': 'https://www.googleapis.com/auth/gmail.modify'},
{'name': 'Gmail API - Basic Settings',
{'name': 'Gmail API - Basic Settings (Filters,IMAP, Language, POP, Vacation) - read/write, Sharing Settings (Delegates, Forwarding, SendAs) - read',
'api': GMAIL,
'subscopes': [],
'scope': 'https://www.googleapis.com/auth/gmail.settings.basic'},
{'name': 'Gmail API - Settings Sharing (Aliases, Delegates, Forwarding)',
{'name': 'Gmail API - Sharing Settings (Delegates, Forwarding, SendAs) - write',
'api': GMAIL,
'subscopes': [],
'scope': 'https://www.googleapis.com/auth/gmail.settings.sharing'},
@@ -638,6 +646,7 @@ _SVCACCT_SCOPES = [
{'name': 'Youtube API - read only',
'api': YOUTUBE,
'subscopes': [],
'offByDefault': True,
'scope': 'https://www.googleapis.com/auth/youtube.readonly'},
]
@@ -653,10 +662,12 @@ _SVCACCT_SPECIAL_SCOPES = [
{'name': 'Gmail API - Full Access - read only',
'api': GMAIL,
'subscopes': [],
'offByDefault': True,
'scope': 'https://www.googleapis.com/auth/gmail.readonly'},
{'name': 'Gmail API - Send Messages - including todrive',
'api': GMAIL,
'subscopes': [],
'offByDefault': True,
'scope': GMAIL_SEND_SCOPE},
{'name': 'Sheets API - todrive',
'api': SHEETSTD,

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2023 Ross Scroggs All Rights Reserved.
# Copyright (C) 2024 Ross Scroggs All Rights Reserved.
#
# All Rights Reserved.
#
@@ -131,6 +131,8 @@ CSV_OUTPUT_ROW_DROP_FILTER = 'csv_output_row_drop_filter'
CSV_OUTPUT_ROW_DROP_FILTER_MODE = 'csv_output_row_drop_filter_mode'
# Limit number of output rows
CSV_OUTPUT_ROW_LIMIT = 'csv_output_row_limit'
# Output sort headers
CSV_OUTPUT_SORT_HEADERS = 'csv_output_sort_headers'
# Column header subfield name delimiter in CSV output file
CSV_OUTPUT_SUBFIELD_DELIMITER = 'csv_output_subfield_delimiter'
# Add timestamp column to CSV output file
@@ -161,6 +163,10 @@ ENABLE_GCLOUD_REAUTH = 'enable_gcloud_reauth'
EVENT_MAX_RESULTS = 'event_max_results'
# Path to extra_args.txt
EXTRA_ARGS = 'extra_args'
# Gmail CSE certificates directory
GMAIL_CSE_INCERT_DIR = 'gmail_cse_incert_dir'
# Gmail CSE KACL wrapped key files
GMAIL_CSE_INKEY_DIR = 'gmail_cse_inkey_dir'
# When processing items in batches, how many seconds should GAM wait between batches
INTER_BATCH_WAIT = 'inter_batch_wait'
# When retrieving lists of licenses from API, how many should be retrieved in each chunk
@@ -282,8 +288,12 @@ TODRIVE_TIMEZONE = 'todrive_timezone'
TODRIVE_UPLOAD_NODATA = 'todrive_upload_nodata'
# User for todrive files
TODRIVE_USER = 'todrive_user'
# Truncate Client ID
TRUNCATE_CLIENT_ID = 'truncate_client_id'
# Update CrOS org unit with orgUnitId
UPDATE_CROS_OU_WITH_ID = 'update_cros_ou_with_id'
# Use course owner for course access
USE_COURSE_OWNER_ACCESS = 'use_course_owner_access'
# Use Project ID as Project Name and App Name
USE_PROJECTID_AS_NAME = 'use_projectid_as_name'
# When retrieving lists of Users from API, how many should be retrieved in each chunk
@@ -344,6 +354,7 @@ Defaults = {
CSV_OUTPUT_ROW_DROP_FILTER: '',
CSV_OUTPUT_ROW_DROP_FILTER_MODE: 'anymatch',
CSV_OUTPUT_ROW_LIMIT: '0',
CSV_OUTPUT_SORT_HEADERS: '',
CSV_OUTPUT_SUBFIELD_DELIMITER: '.',
CSV_OUTPUT_TIMESTAMP_COLUMN: '',
CSV_OUTPUT_USERS_AUDIT: FALSE,
@@ -359,6 +370,8 @@ Defaults = {
ENABLE_GCLOUD_REAUTH: FALSE,
EVENT_MAX_RESULTS: '250',
EXTRA_ARGS: '',
GMAIL_CSE_INCERT_DIR: '',
GMAIL_CSE_INKEY_DIR: '',
INTER_BATCH_WAIT: '0',
LICENSE_MAX_RESULTS: '100',
LICENSE_SKUS: '',
@@ -419,7 +432,9 @@ Defaults = {
TODRIVE_TIMEZONE: '',
TODRIVE_UPLOAD_NODATA: TRUE,
TODRIVE_USER: '',
TRUNCATE_CLIENT_ID: FALSE,
UPDATE_CROS_OU_WITH_ID: FALSE,
USE_COURSE_OWNER_ACCESS: FALSE,
USE_PROJECTID_AS_NAME: FALSE,
USER_MAX_RESULTS: '500',
USER_SERVICE_ACCOUNT_ACCESS_ONLY: FALSE,
@@ -445,6 +460,7 @@ TYPE_LOCALE = 'locl'
TYPE_PASSWORD = 'pass'
TYPE_ROWFILTER = 'rowf'
TYPE_STRING = 'stri'
TYPE_STRINGLIST = 'strl'
TYPE_TIMEZONE = 'tmzn'
VAR_TYPE = 'type'
@@ -499,6 +515,7 @@ VAR_INFO = {
CSV_OUTPUT_ROW_DROP_FILTER: {VAR_TYPE: TYPE_ROWFILTER},
CSV_OUTPUT_ROW_DROP_FILTER_MODE: {VAR_TYPE: TYPE_CHOICE, VAR_CHOICES: {'allmatch': True, 'anymatch': False}},
CSV_OUTPUT_ROW_LIMIT: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (0, None)},
CSV_OUTPUT_SORT_HEADERS: {VAR_TYPE: TYPE_STRINGLIST},
CSV_OUTPUT_SUBFIELD_DELIMITER: {VAR_TYPE: TYPE_CHARACTER},
CSV_OUTPUT_TIMESTAMP_COLUMN: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
CSV_OUTPUT_USERS_AUDIT: {VAR_TYPE: TYPE_BOOLEAN},
@@ -514,6 +531,8 @@ VAR_INFO = {
ENABLE_GCLOUD_REAUTH: {VAR_TYPE: TYPE_BOOLEAN},
EVENT_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 2500)},
EXTRA_ARGS: {VAR_TYPE: TYPE_FILE, VAR_SIGFILE: FN_EXTRA_ARGS_TXT, VAR_SFFT: ('', FN_EXTRA_ARGS_TXT), VAR_ACCESS: os.R_OK},
GMAIL_CSE_INCERT_DIR: {VAR_TYPE: TYPE_DIRECTORY},
GMAIL_CSE_INKEY_DIR: {VAR_TYPE: TYPE_DIRECTORY},
INTER_BATCH_WAIT: {VAR_TYPE: TYPE_FLOAT, VAR_LIMITS: (0.0, 60.0)},
LICENSE_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (10, 1000)},
LICENSE_SKUS: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
@@ -574,7 +593,9 @@ VAR_INFO = {
TODRIVE_TIMEZONE: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
TODRIVE_UPLOAD_NODATA: {VAR_TYPE: TYPE_BOOLEAN},
TODRIVE_USER: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
TRUNCATE_CLIENT_ID: {VAR_TYPE: TYPE_BOOLEAN},
UPDATE_CROS_OU_WITH_ID: {VAR_TYPE: TYPE_BOOLEAN},
USE_COURSE_OWNER_ACCESS: {VAR_TYPE: TYPE_BOOLEAN},
USE_PROJECTID_AS_NAME: {VAR_TYPE: TYPE_BOOLEAN},
USER_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 500)},
USER_SERVICE_ACCOUNT_ACCESS_ONLY: {VAR_TYPE: TYPE_BOOLEAN},

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2023 Ross Scroggs All Rights Reserved.
# Copyright (C) 2024 Ross Scroggs All Rights Reserved.
#
# All Rights Reserved.
#
@@ -379,6 +379,7 @@ class GamCLArgs():
PRINT_CMD = 'print'
SET_CMD = 'set'
CLEAR_CMD = 'clear'
SLEEP_CMD = 'sleep'
# Command line batch/csv/loop/tbatch keywords
BATCH_CMD = 'batch'
CSV_CMD = 'csv'
@@ -462,6 +463,8 @@ class GamCLArgs():
ARG_CHANNELSKU = 'channelsku'
ARG_CHANNELSKUS = 'channelskus'
ARG_CHAT = 'chat'
ARG_CHATEVENT = 'chatevent'
ARG_CHATEVENTS = 'chatevents'
ARG_CHATMEMBER = 'chatmember'
ARG_CHATMEMBERS = 'chatmembers'
ARG_CHATMESSAGE = 'chatmessage'
@@ -519,6 +522,10 @@ class GamCLArgs():
ARG_CROSES = 'croses'
ARG_CROSACTIVITY = 'crosactivity'
ARG_CROSTELEMETRY = 'crostelemetry'
ARG_CSEIDENTITY = 'cseidentity'
ARG_CSEIDENTITIES = 'cseidentities'
ARG_CSEKEYPAIR = 'csekeypair'
ARG_CSEKEYPAIRS = 'csekeypairs'
ARG_CURRENTPROJECTID = 'currentprojectid'
ARG_CUSTOMER = 'customer'
ARG_DATASTUDIOASSET = 'datastudioasset'
@@ -562,6 +569,8 @@ class GamCLArgs():
ARG_DRIVEFOLDERPATH = 'drivefolderpath'
ARG_DRIVELABEL = 'drivelabel'
ARG_DRIVELABELS = 'drivelabels'
ARG_DRIVELABELPERMISSION = 'drivelabelpermission'
ARG_DRIVELABELPERMISSIONS = 'drivelabelpermissions'
ARG_DRIVESETTINGS = 'drivesettings'
ARG_DRIVETRASH = 'drivetrash'
ARG_EMPTYDRIVEFOLDERS = 'emptydrivefolders'
@@ -571,6 +580,8 @@ class GamCLArgs():
ARG_EXPORTS = 'exports'
ARG_FEATURE = 'feature'
ARG_FEATURES = 'features'
ARG_FILECOMMENT = 'filecomment'
ARG_FILECOMMENTS = 'filecomments'
ARG_FILECOUNT = 'filecount'
ARG_FILECOUNTS = 'filecounts'
ARG_FILEDRIVELABEL = 'filedrivelabel'
@@ -809,6 +820,8 @@ class GamCLArgs():
OB_CHANNEL_CUSTOMER_ID = 'ChannelCustomerID'
OB_CHARACTER = 'Character'
OB_CHAR_SET = 'CharacterSet'
OG_CHAT_ATTACHMENT = 'ChatAttachment'
OB_CHAT_EVENT = 'ChatEvent'
OB_CHAT_MEMBER = 'ChatMember'
OB_CHAT_MESSAGE = 'ChatMessage'
OB_CHAT_MESSAGE_ID = 'ChatMessageID'
@@ -845,6 +858,7 @@ class GamCLArgs():
OB_COURSE_WORK_STATE_LIST = "CourseWorkStateList"
OB_CROS_DEVICE_ENTITY = 'CrOSDeviceEntity'
OB_CROS_ENTITY = 'CrOSEntity'
OB_CSE_KEYPAIR_ID = 'CSEKeyPairID'
OB_CUSTOMER_ID = 'CustomerID'
OB_CUSTOMER_AUTH_TOKEN = 'CustomerAuthToken'
OB_DEVICE_FILE_ENTITY = 'DeviceFileEntity'
@@ -871,6 +885,7 @@ class GamCLArgs():
OB_DRIVE_FOLDER_PATH = 'DriveFolderPath'
OB_DRIVE_LABEL_ID = 'DriveLabelID'
OB_DRIVE_LABEL_NAME = 'DriveLabelName'
OB_DRIVE_LABEL_PERMISSION_NAME = 'DriveLabelPermissionName'
OB_DRIVE_LABEL_FIELD_ID = 'DriveLabelFieldID'
OB_DRIVE_LABEL_SELECTION_ID_LIST = 'DriveLabelSelectionIDList'
OB_EMAIL_ADDRESS = 'EmailAddress'
@@ -930,6 +945,9 @@ class GamCLArgs():
OB_ORGUNIT_PATH = 'OrgUnitPath'
OB_PARAMETER_VALUE = 'ParameterValue'
OB_PASSWORD = 'Password'
OB_PERMISSION_ID_LIST = 'PermissionIDList'
OB_PERMISSION_ROLE_LIST = 'PermissionRoleList'
OB_PERMISSION_TYPE_LIST = 'PermissionTypeList'
OB_PHOTO_FILENAME_PATTERN = 'FilenameNamePattern'
OB_PRINTER_ID = 'PrinterID'
OB_PRIVILEGE_LIST = 'PrivilegeList'

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2023 Ross Scroggs All Rights Reserved.
# Copyright (C) 2024 Ross Scroggs All Rights Reserved.
#
# All Rights Reserved.
#
@@ -84,6 +84,7 @@ class GamEntity():
CHANNEL_PRODUCT = 'chpr'
CHANNEL_SKU = 'chsk'
CHAT_BOT = 'chbo'
CHAT_EVENT = 'chev'
CHAT_MANAGER_USER = 'chgu'
CHAT_MEMBER = 'chme'
CHAT_MEMBER_GROUP = 'chmg'
@@ -150,12 +151,14 @@ class GamEntity():
CRITERIA = 'crit'
CROS_DEVICE = 'cros'
CROS_SERIAL_NUMBER = 'crsn'
CSE_IDENTITY = 'csei'
CSE_KEYPAIR = 'csek'
CUSTOMER_DOMAIN = 'cudo'
CUSTOMER_ID = 'cuid'
DATE = 'date'
DEFAULT_LANGUAGE = 'dfla'
DELEGATE = 'dele'
DELETED_USER = 'del'
DELETED_USER = 'delu'
DELIVERY = 'deli'
DEVICE = 'devi'
DEVICE_FILE = 'devf'
@@ -163,7 +166,7 @@ class GamEntity():
DEVICE_USER = 'devu'
DEVICE_USER_CLIENT_STATE = 'ducs'
DISCOVERY_JSON_FILE = 'disc'
DOCUMENT = 'doc '
DOCUMENT = 'docu'
DOMAIN = 'doma'
DOMAIN_ALIAS = 'doal'
DOMAIN_CONTACT = 'doco'
@@ -171,6 +174,7 @@ class GamEntity():
DOMAIN_PROFILE = 'dopr'
DRIVE_DISK_USAGE = 'drdu'
DRIVE_FILE = 'dfil'
DRIVE_FILE_COMMENT = 'filc'
DRIVE_FILE_ID = 'fili'
DRIVE_FILE_NAME = 'filn'
DRIVE_FILE_RENAMED = 'firn'
@@ -189,6 +193,8 @@ class GamEntity():
DRIVE_LABEL_FIELD_ID = 'dlfi'
DRIVE_LABEL_ID = 'dlid'
DRIVE_LABEL_NAME = 'dlna'
DRIVE_LABEL_PERMISSION = 'dlpe'
DRIVE_LABEL_PERMISSION_NAME = 'dlpn'
DRIVE_ORPHAN_FILE_OR_FOLDER = 'orph'
DRIVE_PARENT_FOLDER = 'fipf'
DRIVE_PARENT_FOLDER_ID = 'fipi'
@@ -286,6 +292,7 @@ class GamEntity():
PERSONAL_DEVICE = 'pedv'
PHOTO = 'phot'
POP_ENABLED = 'popa'
PRESENTATION = 'pres'
PRINTER = 'prin'
PRINTER_ID = 'prid'
PRINTER_MODEL = 'prmd'
@@ -417,6 +424,7 @@ class GamEntity():
CHANNEL_PRODUCT: ['Channel Products', 'Channel Product'],
CHANNEL_SKU: ['Channel SKUs', 'Channel SKU'],
CHAT_BOT: ['Chat BOTs', 'Chat BOT'],
CHAT_EVENT: ['Chat Events', 'Chat Event'],
CHAT_MANAGER_USER: ['Chat User Managers', 'Chat User Manager'],
CHAT_MESSAGE: ['Chat Messages', 'Chat Message'],
CHAT_MESSAGE_ID: ['Chat Message IDs', 'Chat Message ID'],
@@ -483,6 +491,8 @@ class GamEntity():
CRITERIA: ['Criteria', 'Criteria'],
CROS_DEVICE: ['CrOS Devices', 'CrOS Device'],
CROS_SERIAL_NUMBER: ['CrOS Serial Numbers', 'CrOS Serial Numbers'],
CSE_IDENTITY: ['CSE Identities', 'CSE Identity'],
CSE_KEYPAIR: ['CSE KeyPairs', 'CSE KeyPair'],
CUSTOMER_DOMAIN: ['Customer Domains', 'Customer Domain'],
CUSTOMER_ID: ['Customer IDs', 'Customer ID'],
DATE: ['Dates', 'Date'],
@@ -504,6 +514,7 @@ class GamEntity():
DOMAIN_PROFILE: ['Domain Profiles', 'Domain Profile'],
DRIVE_DISK_USAGE: ['Drive Disk Usages', 'Drive Disk Usage'],
DRIVE_FILE: ['Drive Files', 'Drive File'],
DRIVE_FILE_COMMENT: ['Drive File Comments', 'Drive File Comment'],
DRIVE_FILE_ID: ['Drive File IDs', 'Drive File ID'],
DRIVE_FILE_NAME: ['Drive File Names', 'Drive File Name'],
DRIVE_FILE_REVISION: ['Drive File Revisions', 'Drive File Revision'],
@@ -522,6 +533,8 @@ class GamEntity():
DRIVE_LABEL_FIELD_ID: ['Drive Label Field IDs', 'Drive Label Field ID'],
DRIVE_LABEL_ID: ['Drive Label IDs', 'Drive Label ID'],
DRIVE_LABEL_NAME: ['Drive Label Names', 'Drive Label Name'],
DRIVE_LABEL_PERMISSION: ['Drive Label Permissions', 'Drive Label Permission'],
DRIVE_LABEL_PERMISSION_NAME: ['Drive Label Permission Names', 'Drive Label Permission Name'],
DRIVE_ORPHAN_FILE_OR_FOLDER: ['Drive Orphan Files/Folders', 'Drive Orphan File/Folder'],
DRIVE_PARENT_FOLDER: ['Drive Parent Folders', 'Drive Parent Folder'],
DRIVE_PARENT_FOLDER_ID: ['Drive Parent Folder IDs', 'Drive Parent Folder ID'],
@@ -619,6 +632,7 @@ class GamEntity():
PERSONAL_DEVICE: ['Personal Devices', 'Personal Device'],
PHOTO: ['Photos', 'Photo'],
POP_ENABLED: ['POP Enabled', 'POP Enabled'],
PRESENTATION: ['Presentations', 'Presentation'],
PRINTER: ['Printers', 'Printer'],
PRINTER_ID: ['Printer IDs', 'Printer ID'],
PRINTER_MODEL: ['Printer Models', 'Printer Model'],

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2023 Ross Scroggs All Rights Reserved.
# Copyright (C) 2024 Ross Scroggs All Rights Reserved.
#
# All Rights Reserved.
#
@@ -24,6 +24,7 @@ ABORTED = 'aborted'
ABUSIVE_CONTENT_RESTRICTION = 'abusiveContentRestriction'
ACCESS_NOT_CONFIGURED = 'accessNotConfigured'
ALREADY_EXISTS = 'alreadyExists'
APPLY_LABEL_FORBIDDEN = 'applyLabelForbidden'
AUTH_ERROR = 'authError'
BACKEND_ERROR = 'backendError'
BAD_GATEWAY = 'badGateway'
@@ -66,8 +67,10 @@ DOMAIN_CANNOT_USE_APIS = 'domainCannotUseApis'
DOMAIN_NOT_FOUND = 'domainNotFound'
DOMAIN_NOT_VERIFIED_SECONDARY = 'domainNotVerifiedSecondary'
DOMAIN_POLICY = 'domainPolicy'
DOWNLOAD_QUOTA_EXCEEDED = 'downloadQuotaExceeded'
DUPLICATE = 'duplicate'
EVENT_DURATION_EXCEEDS_LIMIT = 'eventDurationExceedsLimit'
EXPIRATION_DATE_NOT_ALLOWED_FOR_SHARED_DRIVE_MEMBERS = 'expirationDateNotAllowedForSharedDriveMembers'
FAILED_PRECONDITION = 'failedPrecondition'
FIELD_IN_USE = 'fieldInUse'
FIELD_NOT_WRITABLE = 'fieldNotWritable'
@@ -115,11 +118,13 @@ LIMIT_EXCEEDED = 'limitExceeded'
LOGIN_REQUIRED = 'loginRequired'
MALFORMED_WORKING_LOCATION_EVENT = 'malformedWorkingLocationEvent'
MEMBER_NOT_FOUND = 'memberNotFound'
MYDRIVE_HIERARCHY_DEPTH_LIMIT_EXCEEDED = 'myDriveHierarchyDepthLimitExceeded'
NO_LIST_TEAMDRIVES_ADMINISTRATOR_PRIVILEGE = 'noListTeamDrivesAdministratorPrivilege'
NO_MANAGE_TEAMDRIVE_ADMINISTRATOR_PRIVILEGE = 'noManageTeamDriveAdministratorPrivilege'
NOT_A_CALENDAR_USER = 'notACalendarUser'
NOT_FOUND = 'notFound'
NOT_IMPLEMENTED = 'notImplemented'
NUM_CHILDREN_IN_NON_ROOT_LIMIT_EXCEEDED = 'numChildrenInNonRootLimitExceeded'
OPERATION_NOT_SUPPORTED = 'operationNotSupported'
ORGANIZER_ON_NON_TEAMDRIVE_NOT_SUPPORTED = 'organizerOnNonTeamDriveNotSupported'
ORGANIZER_ON_NON_TEAMDRIVE_ITEM_NOT_SUPPORTED = 'organizerOnNonTeamDriveItemNotSupported'
@@ -181,8 +186,8 @@ SERVICE_NOT_AVAILABLE_RETRY_REASONS = [SERVICE_NOT_AVAILABLE]
ACTIVITY_THROW_REASONS = [SERVICE_NOT_AVAILABLE, BAD_REQUEST]
ALERT_THROW_REASONS = [SERVICE_NOT_AVAILABLE, AUTH_ERROR]
CALENDAR_THROW_REASONS = [SERVICE_NOT_AVAILABLE, AUTH_ERROR, NOT_A_CALENDAR_USER]
CIGROUP_CREATE_THROW_REASONS = [SERVICE_NOT_AVAILABLE, ALREADY_EXISTS, DOMAIN_NOT_FOUND, DOMAIN_CANNOT_USE_APIS, FORBIDDEN, INVALID, INVALID_ARGUMENT, PERMISSION_DENIED]
CIGROUP_GET_THROW_REASONS = [SERVICE_NOT_AVAILABLE, NOT_FOUND, DOMAIN_NOT_FOUND, DOMAIN_CANNOT_USE_APIS, FORBIDDEN, BAD_REQUEST, INVALID, SYSTEM_ERROR, PERMISSION_DENIED]
CIGROUP_CREATE_THROW_REASONS = [SERVICE_NOT_AVAILABLE, ALREADY_EXISTS, DOMAIN_NOT_FOUND, DOMAIN_CANNOT_USE_APIS, FORBIDDEN, INVALID, INVALID_ARGUMENT, PERMISSION_DENIED, FAILED_PRECONDITION]
CIGROUP_GET_THROW_REASONS = [SERVICE_NOT_AVAILABLE, NOT_FOUND, GROUP_NOT_FOUND, DOMAIN_NOT_FOUND, DOMAIN_CANNOT_USE_APIS, FORBIDDEN, BAD_REQUEST, INVALID, SYSTEM_ERROR, PERMISSION_DENIED]
CIGROUP_LIST_THROW_REASONS = [SERVICE_NOT_AVAILABLE, RESOURCE_NOT_FOUND, DOMAIN_NOT_FOUND, DOMAIN_CANNOT_USE_APIS, FORBIDDEN, BAD_REQUEST, INVALID, INVALID_ARGUMENT, SYSTEM_ERROR, PERMISSION_DENIED]
CIGROUP_LIST_USERKEY_THROW_REASONS = CIGROUP_LIST_THROW_REASONS+[INVALID_ARGUMENT]
CIGROUP_UPDATE_THROW_REASONS = [SERVICE_NOT_AVAILABLE, NOT_FOUND, GROUP_NOT_FOUND, DOMAIN_NOT_FOUND, DOMAIN_CANNOT_USE_APIS,
@@ -203,8 +208,9 @@ DRIVE_ACCESS_THROW_REASONS = DRIVE_USER_THROW_REASONS+[FILE_NOT_FOUND, FORBIDDEN
DRIVE_COPY_THROW_REASONS = DRIVE_ACCESS_THROW_REASONS+[CANNOT_COPY_FILE, BAD_REQUEST, RESPONSE_PREPARATION_FAILURE, TEAMDRIVES_SHARING_RESTRICTION_NOT_ALLOWED,
FIELD_NOT_WRITABLE, RATE_LIMIT_EXCEEDED, USER_RATE_LIMIT_EXCEEDED,
STORAGE_QUOTA_EXCEEDED, TEAMDRIVE_FILE_LIMIT_EXCEEDED, TEAMDRIVE_HIERARCHY_TOO_DEEP]
DRIVE_GET_THROW_REASONS = DRIVE_USER_THROW_REASONS+[FILE_NOT_FOUND]
DRIVE3_CREATE_ACL_THROW_REASONS = [BAD_REQUEST, INVALID, INVALID_SHARING_REQUEST, OWNERSHIP_CHANGE_ACROSS_DOMAIN_NOT_PERMITTED, CANNOT_SET_EXPIRATION,
DRIVE_GET_THROW_REASONS = DRIVE_USER_THROW_REASONS+[FILE_NOT_FOUND, DOWNLOAD_QUOTA_EXCEEDED]
DRIVE3_CREATE_ACL_THROW_REASONS = [BAD_REQUEST, INVALID, INVALID_SHARING_REQUEST, OWNERSHIP_CHANGE_ACROSS_DOMAIN_NOT_PERMITTED,
CANNOT_SET_EXPIRATION, EXPIRATION_DATE_NOT_ALLOWED_FOR_SHARED_DRIVE_MEMBERS,
NOT_FOUND, TEAMDRIVE_DOMAIN_USERS_ONLY_RESTRICTION, TEAMDRIVE_TEAM_MEMBERS_ONLY_RESTRICTION,
TARGET_USER_ROLE_LIMITED_BY_LICENSE_RESTRICTION, INSUFFICIENT_ADMINISTRATOR_PRIVILEGES, SHARING_RATE_LIMIT_EXCEEDED,
PUBLISH_OUT_NOT_PERMITTED, SHARE_IN_NOT_PERMITTED, SHARE_OUT_NOT_PERMITTED, SHARE_OUT_NOT_PERMITTED_TO_USER,
@@ -220,7 +226,8 @@ DRIVE3_CREATE_ACL_THROW_REASONS = [BAD_REQUEST, INVALID, INVALID_SHARING_REQUEST
DRIVE3_GET_ACL_REASONS = DRIVE_USER_THROW_REASONS+[FILE_NOT_FOUND, FORBIDDEN, INTERNAL_ERROR,
INSUFFICIENT_ADMINISTRATOR_PRIVILEGES, INSUFFICIENT_FILE_PERMISSIONS,
UNKNOWN_ERROR, INVALID]
DRIVE3_UPDATE_ACL_THROW_REASONS = [BAD_REQUEST, INVALID_OWNERSHIP_TRANSFER, CANNOT_REMOVE_OWNER, CANNOT_SET_EXPIRATION,
DRIVE3_UPDATE_ACL_THROW_REASONS = [BAD_REQUEST, INVALID_OWNERSHIP_TRANSFER, CANNOT_REMOVE_OWNER,
CANNOT_SET_EXPIRATION, EXPIRATION_DATE_NOT_ALLOWED_FOR_SHARED_DRIVE_MEMBERS,
OWNERSHIP_CHANGE_ACROSS_DOMAIN_NOT_PERMITTED,
NOT_FOUND, TEAMDRIVE_DOMAIN_USERS_ONLY_RESTRICTION, TEAMDRIVE_TEAM_MEMBERS_ONLY_RESTRICTION,
TARGET_USER_ROLE_LIMITED_BY_LICENSE_RESTRICTION, INSUFFICIENT_ADMINISTRATOR_PRIVILEGES, SHARING_RATE_LIMIT_EXCEEDED,
@@ -241,16 +248,19 @@ DRIVE3_DELETE_ACL_THROW_REASONS = [BAD_REQUEST, CANNOT_REMOVE_OWNER,
INSUFFICIENT_ADMINISTRATOR_PRIVILEGES, SHARING_RATE_LIMIT_EXCEEDED,
NOT_FOUND, PERMISSION_NOT_FOUND]
DRIVE3_MODIFY_LABEL_THROW_REASONS = DRIVE_USER_THROW_REASONS+[FILE_NOT_FOUND, NOT_FOUND, FORBIDDEN, INTERNAL_ERROR,
FILE_NEVER_WRITABLE, APPLY_LABEL_FORBIDDEN,
INSUFFICIENT_ADMINISTRATOR_PRIVILEGES, INSUFFICIENT_FILE_PERMISSIONS,
UNKNOWN_ERROR, INVALID_INPUT, BAD_REQUEST,
LABEL_MULTIPLE_VALUES_FOR_SINGULAR_FIELD, LABEL_MUTATION_FORBIDDEN,
LABEL_MUTATION_ILLEGAL_SELECTION, LABEL_MUTATION_UNKNOWN_FIELD]
DOCS_ACCESS_THROW_REASONS = DRIVE_USER_THROW_REASONS+[NOT_FOUND, PERMISSION_DENIED, FORBIDDEN, INTERNAL_ERROR, INSUFFICIENT_FILE_PERMISSIONS,
BAD_REQUEST, INVALID, INVALID_ARGUMENT, FAILED_PRECONDITION]
GMAIL_THROW_REASONS = [SERVICE_NOT_AVAILABLE, BAD_REQUEST]
GMAIL_LIST_THROW_REASONS = [FAILED_PRECONDITION, PERMISSION_DENIED, INVALID, INVALID_ARGUMENT]
GMAIL_SMIME_THROW_REASONS = [SERVICE_NOT_AVAILABLE, BAD_REQUEST, INVALID_ARGUMENT, FORBIDDEN, NOT_FOUND]
GMAIL_SMIME_THROW_REASONS = [SERVICE_NOT_AVAILABLE, BAD_REQUEST, INVALID_ARGUMENT, FORBIDDEN, NOT_FOUND, PERMISSION_DENIED]
GROUP_GET_THROW_REASONS = [GROUP_NOT_FOUND, DOMAIN_NOT_FOUND, DOMAIN_CANNOT_USE_APIS, FORBIDDEN, BAD_REQUEST, INVALID, SYSTEM_ERROR]
GROUP_GET_RETRY_REASONS = [INVALID, SYSTEM_ERROR, SERVICE_NOT_AVAILABLE]
GROUP_CREATE_THROW_REASONS = [DUPLICATE, DOMAIN_NOT_FOUND, DOMAIN_CANNOT_USE_APIS, FORBIDDEN, INVALID, INVALID_INPUT]
GROUP_GET_THROW_REASONS = [GROUP_NOT_FOUND, DOMAIN_NOT_FOUND, DOMAIN_CANNOT_USE_APIS, FORBIDDEN, BAD_REQUEST, INVALID, SYSTEM_ERROR]
GROUP_UPDATE_THROW_REASONS = [GROUP_NOT_FOUND, DOMAIN_NOT_FOUND, DOMAIN_CANNOT_USE_APIS, FORBIDDEN, INVALID, INVALID_INPUT]
GROUP_SETTINGS_THROW_REASONS = [NOT_FOUND, GROUP_NOT_FOUND, DOMAIN_NOT_FOUND, DOMAIN_CANNOT_USE_APIS, FORBIDDEN, SYSTEM_ERROR, PERMISSION_DENIED,
INVALID, INVALID_PARAMETER, INVALID_ATTRIBUTE_VALUE, INVALID_INPUT, SERVICE_LIMIT, SERVICE_NOT_AVAILABLE, AUTH_ERROR, REQUIRED]
@@ -263,6 +273,7 @@ MEMBERS_THROW_REASONS = [GROUP_NOT_FOUND, DOMAIN_NOT_FOUND, DOMAIN_CANNOT_USE_AP
MEMBERS_RETRY_REASONS = [SYSTEM_ERROR, SERVICE_NOT_AVAILABLE]
ORGUNIT_GET_THROW_REASONS = [INVALID_ORGUNIT, ORGUNIT_NOT_FOUND, BACKEND_ERROR, BAD_REQUEST, INVALID_CUSTOMER_ID, LOGIN_REQUIRED]
PEOPLE_ACCESS_THROW_REASONS = [SERVICE_NOT_AVAILABLE, FORBIDDEN, PERMISSION_DENIED]
RESELLER_THROW_REASONS = [BAD_REQUEST, RESOURCE_NOT_FOUND, FORBIDDEN, INVALID]
SHEETS_ACCESS_THROW_REASONS = DRIVE_USER_THROW_REASONS+[NOT_FOUND, PERMISSION_DENIED, FORBIDDEN, INTERNAL_ERROR, INSUFFICIENT_FILE_PERMISSIONS,
BAD_REQUEST, INVALID, INVALID_ARGUMENT, FAILED_PRECONDITION]
TASK_THROW_REASONS = [SERVICE_NOT_AVAILABLE, BAD_REQUEST, PERMISSION_DENIED, INVALID, NOT_FOUND, ACCESS_NOT_CONFIGURED]
@@ -273,6 +284,7 @@ YOUTUBE_THROW_REASONS = [SERVICE_NOT_AVAILABLE, AUTH_ERROR, UNSUPPORTED_SUPERVIS
REASON_MESSAGE_MAP = {
ABORTED: [
('Label name exists or conflicts', DUPLICATE),
('The operation was aborted', ABORTED),
],
CONDITION_NOT_MET: [
('Cyclic memberships not allowed', CYCLIC_MEMBERSHIPS_NOT_ALLOWED),
@@ -345,6 +357,8 @@ class accessNotConfigured(Exception):
pass
class alreadyExists(Exception):
pass
class applyLabelForbidden(Exception):
pass
class authError(Exception):
pass
class backendError(Exception):
@@ -425,10 +439,14 @@ class domainNotVerifiedSecondary(Exception):
pass
class domainPolicy(Exception):
pass
class downloadQuotaExceeded(Exception):
pass
class duplicate(Exception):
pass
class eventDurationExceedsLimit(Exception):
pass
class expirationDateNotAllowedForSharedDriveMembers(Exception):
pass
class failedPrecondition(Exception):
pass
class fieldInUse(Exception):
@@ -645,6 +663,7 @@ REASON_EXCEPTION_MAP = {
ABUSIVE_CONTENT_RESTRICTION: abusiveContentRestriction,
ACCESS_NOT_CONFIGURED: accessNotConfigured,
ALREADY_EXISTS: alreadyExists,
APPLY_LABEL_FORBIDDEN: applyLabelForbidden,
AUTH_ERROR: authError,
BACKEND_ERROR: backendError,
BAD_REQUEST: badRequest,
@@ -685,8 +704,10 @@ REASON_EXCEPTION_MAP = {
DOMAIN_NOT_FOUND: domainNotFound,
DOMAIN_NOT_VERIFIED_SECONDARY: domainNotVerifiedSecondary,
DOMAIN_POLICY: domainPolicy,
DOWNLOAD_QUOTA_EXCEEDED: downloadQuotaExceeded,
DUPLICATE: duplicate,
EVENT_DURATION_EXCEEDS_LIMIT: eventDurationExceedsLimit,
EXPIRATION_DATE_NOT_ALLOWED_FOR_SHARED_DRIVE_MEMBERS: expirationDateNotAllowedForSharedDriveMembers,
FAILED_PRECONDITION: failedPrecondition,
FIELD_IN_USE: fieldInUse,
FIELD_NOT_WRITABLE: fieldNotWritable,

View File

@@ -81,6 +81,8 @@ CSV_OUTPUT_ROW_FILTER_MODE = 'corm'
CSV_OUTPUT_ROW_LIMIT = 'corl'
# Add timestamp column to CSV output file
CSV_OUTPUT_TIMESTAMP_COLUMN = 'csv_output_timestamp_column'
# Output sort headers
CSV_OUTPUT_SORT_HEADERS = 'cosh'
# CSV todrive options
CSV_TODRIVE = 'todr'
# Current API services
@@ -236,6 +238,7 @@ Globals = {
CSV_OUTPUT_ROW_FILTER: [],
CSV_OUTPUT_ROW_FILTER_MODE: True,
CSV_OUTPUT_ROW_LIMIT: 0,
CSV_OUTPUT_SORT_HEADERS: [],
CSV_OUTPUT_TIMESTAMP_COLUMN: None,
CSV_TODRIVE: {},
CURRENT_API_SERVICES: {},

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2023 Ross Scroggs All Rights Reserved.
# Copyright (C) 2024 Ross Scroggs All Rights Reserved.
#
# All Rights Reserved.
#
@@ -91,7 +91,21 @@ Please go to:
8. Press enter here on the terminal once trust is complete.
'''
YOUR_GAM_PROJECT_IS_CREATED_AND_READY_TO_USE = 'That\'s it! Your GAM Project is created and ready to use.\n'
ENABLE_SERVICE_ACCOUNT_PRIVATE_KEY_UPLOAD = '''
Your workspace is configured to disable service account private key uploads.
Please go to:
https://github.com/taers232c/GAMADV-XTD3/wiki/Authorization#authorize-service-account-key-uploads
Follow the steps to allow a service account private key upload for the project ({0}) just created.
Once those steps are completed, you can continue with your project authentication.
'''
YOUR_GAM_PROJECT_IS_CREATED_AND_READY_TO_USE = '''
That\'s it! Your GAM Project is created and ready to use.
Proceed to the authentication steps.
'''
# check|update service messages in order of appearance
SYSTEM_TIME_STATUS = 'System time status'
@@ -150,6 +164,7 @@ API_CALLS_RETRY_DATA = 'API calls retry data\n'
API_CHECK_CLIENT_AUTHORIZATION = 'Please make sure the Client ID: {0} is authorized for the appropriate API or scopes:\n{1}\n\nRun: gam oauth create\n'
API_CHECK_SVCACCT_AUTHORIZATION = 'Please make sure the Service Account Client name: {0} is authorized for the appropriate API or scopes:\n{1}\n\nRun: gam user {2} check serviceaccount\n'
API_ERROR_SETTINGS = 'API error, some settings not set'
ARE_BOTH_REQUIRED = 'Arguments {0} and {1} are both required'
ARE_MUTUALLY_EXCLUSIVE = 'Arguments {0} and {1} are mutually exclusive'
AS = 'as'
ATTENDEES_ADD = 'Add Attendees'
@@ -191,6 +206,7 @@ CSV_DATA_ALREADY_SAVED = 'CSV data already saved'
CSV_FILE_HEADERS = 'The CSV file ({0}) has the following headers:\n'
CSV_SAMPLE_COMMANDS = 'Here are the first {0} commands {1} will run\n'
DATA_FIELD_MISMATCH = 'datafield {0} does not match saved datafield {1}'
DATA_TRANSFER_COMPLETED = 'Data Transfer completed: {0}\n'
DATA_UPLOADED_TO_DRIVE_FILE = 'Data uploaded to Drive File'
DEFAULT_SMIME = 'Default S/MIME'
DELETED = 'Deleted'
@@ -231,6 +247,7 @@ FAILED_PRECONDITION = 'Failed precondition'
FAILED_TO_PARSE_AS_JSON = 'Failed to parse as JSON'
FAILED_TO_PARSE_AS_LIST = 'Failed to parse as list'
FIELD_NOT_FOUND_IN_SCHEMA = 'Field {0} not found in schema {1}'
FILE_NOT_FOUND = 'File {0} not found'
FINISHED = 'Finished'
FILTER_CAN_ONLY_CONTAIN_ONE_CATEGORY_LABEL = 'Filter can only contain one CATEGORY label'
FILTER_CAN_ONLY_CONTAIN_ONE_USER_LABEL = 'Filter can only contain one USER label'
@@ -290,6 +307,7 @@ INVALID_SCHEMA_VALUE = 'Invalid Schema Value'
INVALID_SCOPE = 'Invalid Scope'
INVALID_SITE = 'Invalid Site ({0}), must match pattern ({1})'
INVALID_TAG_SPECIFICATION = 'Invalid tag, expected field.subfield or field.subfield.subfield.string'
INVALID_TIMEOFDAY_RANGE = '{0} must be less than/equal to {1}'
IN_SKIPIDS = 'In skipids'
IN_THE = ' in the {0}'
IN_TRASH_AND_EXCLUDE_TRASHED = 'In Trash and excludeTrashed'
@@ -299,6 +317,8 @@ IS_NOT_UNIQUE = 'Is not unique, {0}: {1}'
IS_REQD_TO_CHG_PWD_NO_DELEGATION = 'Is required to change password at next login. You must change password or clear changepassword flag for delegation.'
IS_SUSPENDED_NO_BACKUPCODES = 'User is suspended. You must unsuspend to process backupcodes'
IS_SUSPENDED_NO_DELEGATION = 'Is suspended. You must unsuspend for delegation.'
JSON_ERROR = 'JSON error "{0}" in file {1}'
JSON_KEY_NOT_FOUND = 'JSON key "{0}" not found in file {1}'
KIOSK_MODE_REQUIRED = ' This command ({0}) requires that the ChromeOS device be in Kiosk mode.'
LESS_THAN_1_SECOND = 'less than 1 second'
LIST_CHROMEOS_INVALID_INPUT_PAGE_TOKEN_RETRY = 'List ChromeOSdevices Invalid Input: pageToken retry'
@@ -479,6 +499,7 @@ USE_DOIT_ARGUMENT_TO_PERFORM_ACTION = 'Use the "doit" argument to perform action
USING_N_PROCESSES = '{0},0/{1},Using {2} {3}...\n'
VALUES_ARE_NOT_CONSISTENT = 'Values are not consistent'
VERSION_UPDATE_AVAILABLE = 'Version update available'
WAITING_FOR_DATA_TRANSFER_TO_COMPLETE_SLEEPING = 'Waiting for Data Transfer to complete. Sleeping {0} seconds\n'
WAITING_FOR_SERVICE_ACCOUNT_CREATION_TO_COMPLETE_SLEEPING = 'Waiting for Service Account creation to complete. Sleeping {0} seconds\n'
WAITING_FOR_SHARED_DRIVE_CREATION_TO_COMPLETE_SLEEPING = 'Waiting for Shared Drive creation to complete. Sleeping {0} seconds\n'
WHAT_IS_YOUR_PROJECT_ID = '\nWhat is your project ID? '

View File

@@ -32,9 +32,10 @@ _PRODUCTS = {
'101037': 'Google Workspace for Education',
'101038': 'AppSheet',
'101039': 'Assured Controls',
'101040': 'Beyond Corp Enterprise',
'101040': 'Chrome Enterprise',
'101043': 'Google Workspace Additional Storage',
'101047': 'Duet AI',
'101047': 'Gemini',
'101049': 'Education Endpoint Management',
'Google-Apps': 'Google Workspace',
'Google-Chrome-Device-Management': 'Google Chrome Device Management',
'Google-Drive-storage': 'Google Drive Storage',
@@ -61,12 +62,12 @@ _SKUS = {
'product': '101031', 'aliases': ['gwepstaff', 'workspaceeducationplusstaff'], 'displayName': 'Google Workspace for Education Plus (Staff)'},
'1010310010': {
'product': '101031', 'aliases': ['gwepstudent', 'workspaceeducationplusstudent'], 'displayName': 'Google Workspace for Education Plus (Extra Student)'},
'1010330002': {
'product': '101033', 'aliases': ['gvpremier', 'voicepremier', 'googlevoicepremier'], 'displayName': 'Google Voice Premier'},
'1010330003': {
'product': '101033', 'aliases': ['gvstarter', 'voicestarter', 'googlevoicestarter'], 'displayName': 'Google Voice Starter'},
'1010330004': {
'product': '101033', 'aliases': ['gvstandard', 'voicestandard', 'googlevoicestandard'], 'displayName': 'Google Voice Standard'},
'1010330002': {
'product': '101033', 'aliases': ['gvpremier', 'voicepremier', 'googlevoicepremier'], 'displayName': 'Google Voice Premier'},
'1010350001': {
'product': '101035', 'aliases': ['cloudsearch'], 'displayName': 'Cloud Search'},
'1010360001': {
@@ -82,13 +83,17 @@ _SKUS = {
'1010390001': {
'product': '101039', 'aliases': ['assuredcontrols'], 'displayName': 'Assured Controls'},
'1010400001': {
'product': '101040', 'aliases': ['beyondcorp', 'beyondcorpenterprise', 'bce'], 'displayName': 'Beyond Corp Enterprise'},
'product': '101040', 'aliases': ['beyondcorp', 'beyondcorpenterprise', 'bce', 'cep', 'chromeenterprisepremium'], 'displayName': 'Chrome Enterprise Premium'},
'1010430001': {
'product': '101043', 'aliases': ['gwas', 'plusstorage'], 'displayName': 'Google Workspace Additional Storage'},
'1010470001': {
'product': '101047', 'aliases': ['duetai'], 'displayName': 'Duet AI for Enterprise'},
'product': '101047', 'aliases': ['geminient', 'duetai'], 'displayName': 'Gemini Enterprise'},
'1010470002': {
'product': '101047', 'aliases': ['gwlabs', 'workspacelabs'], 'displayName': 'Google Workspace Labs'},
'1010470003': {
'product': '101047', 'aliases': ['geminibiz'], 'displayName': 'Gemini Business'},
'1010490001': {
'product': '101049', 'aliases': ['eeu'], 'displayName': 'Endpoint Education Upgrade'},
'Google-Apps': {
'product': 'Google-Apps', 'aliases': ['standard', 'free'], 'displayName': 'G Suite Legacy'},
'Google-Apps-For-Business': {

View File

@@ -0,0 +1,27 @@
# Copyright 2014 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Set default logging handler to avoid "No handler found" warnings.
import logging
try: # Python 2.7+
from logging import NullHandler
except ImportError:
class NullHandler(logging.Handler):
def emit(self, record):
pass
logging.getLogger(__name__).addHandler(NullHandler())

View File

@@ -0,0 +1,167 @@
# Copyright 2016 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Helpers for authentication using oauth2client or google-auth."""
import httplib2
try:
import google.auth
import google.auth.credentials
HAS_GOOGLE_AUTH = True
except ImportError: # pragma: NO COVER
HAS_GOOGLE_AUTH = False
try:
import google_auth_httplib2
except ImportError: # pragma: NO COVER
google_auth_httplib2 = None
try:
import oauth2client
import oauth2client.client
HAS_OAUTH2CLIENT = True
except ImportError: # pragma: NO COVER
HAS_OAUTH2CLIENT = False
def credentials_from_file(filename, scopes=None, quota_project_id=None):
"""Returns credentials loaded from a file."""
if HAS_GOOGLE_AUTH:
credentials, _ = google.auth.load_credentials_from_file(
filename, scopes=scopes, quota_project_id=quota_project_id
)
return credentials
else:
raise EnvironmentError(
"client_options.credentials_file is only supported in google-auth."
)
def default_credentials(scopes=None, quota_project_id=None):
"""Returns Application Default Credentials."""
if HAS_GOOGLE_AUTH:
credentials, _ = google.auth.default(
scopes=scopes, quota_project_id=quota_project_id
)
return credentials
elif HAS_OAUTH2CLIENT:
if scopes is not None or quota_project_id is not None:
raise EnvironmentError(
"client_options.scopes and client_options.quota_project_id are not supported in oauth2client."
"Please install google-auth."
)
return oauth2client.client.GoogleCredentials.get_application_default()
else:
raise EnvironmentError(
"No authentication library is available. Please install either "
"google-auth or oauth2client."
)
def with_scopes(credentials, scopes):
"""Scopes the credentials if necessary.
Args:
credentials (Union[
google.auth.credentials.Credentials,
oauth2client.client.Credentials]): The credentials to scope.
scopes (Sequence[str]): The list of scopes.
Returns:
Union[google.auth.credentials.Credentials,
oauth2client.client.Credentials]: The scoped credentials.
"""
if HAS_GOOGLE_AUTH and isinstance(credentials, google.auth.credentials.Credentials):
return google.auth.credentials.with_scopes_if_required(credentials, scopes)
else:
try:
if credentials.create_scoped_required():
return credentials.create_scoped(scopes)
else:
return credentials
except AttributeError:
return credentials
def authorized_http(credentials):
"""Returns an http client that is authorized with the given credentials.
Args:
credentials (Union[
google.auth.credentials.Credentials,
oauth2client.client.Credentials]): The credentials to use.
Returns:
Union[httplib2.Http, google_auth_httplib2.AuthorizedHttp]: An
authorized http client.
"""
from googleapiclient.http import build_http
if HAS_GOOGLE_AUTH and isinstance(credentials, google.auth.credentials.Credentials):
if google_auth_httplib2 is None:
raise ValueError(
"Credentials from google.auth specified, but "
"google-api-python-client is unable to use these credentials "
"unless google-auth-httplib2 is installed. Please install "
"google-auth-httplib2."
)
return google_auth_httplib2.AuthorizedHttp(credentials, http=build_http())
else:
return credentials.authorize(build_http())
def refresh_credentials(credentials):
# Refresh must use a new http instance, as the one associated with the
# credentials could be a AuthorizedHttp or an oauth2client-decorated
# Http instance which would cause a weird recursive loop of refreshing
# and likely tear a hole in spacetime.
refresh_http = httplib2.Http()
if HAS_GOOGLE_AUTH and isinstance(credentials, google.auth.credentials.Credentials):
request = google_auth_httplib2.Request(refresh_http)
return credentials.refresh(request)
else:
return credentials.refresh(refresh_http)
def apply_credentials(credentials, headers):
# oauth2client and google-auth have the same interface for this.
if not is_valid(credentials):
refresh_credentials(credentials)
return credentials.apply(headers)
def is_valid(credentials):
if HAS_GOOGLE_AUTH and isinstance(credentials, google.auth.credentials.Credentials):
return credentials.valid
else:
return (
credentials.access_token is not None
and not credentials.access_token_expired
)
def get_credentials_from_http(http):
if http is None:
return None
elif hasattr(http.request, "credentials"):
return http.request.credentials
elif hasattr(http, "credentials") and not isinstance(
http.credentials, httplib2.Credentials
):
return http.credentials
else:
return None

View File

@@ -0,0 +1,207 @@
# Copyright 2015 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Helper functions for commonly used utilities."""
import functools
import inspect
import logging
import urllib
logger = logging.getLogger(__name__)
POSITIONAL_WARNING = "WARNING"
POSITIONAL_EXCEPTION = "EXCEPTION"
POSITIONAL_IGNORE = "IGNORE"
POSITIONAL_SET = frozenset(
[POSITIONAL_WARNING, POSITIONAL_EXCEPTION, POSITIONAL_IGNORE]
)
positional_parameters_enforcement = POSITIONAL_WARNING
_SYM_LINK_MESSAGE = "File: {0}: Is a symbolic link."
_IS_DIR_MESSAGE = "{0}: Is a directory"
_MISSING_FILE_MESSAGE = "Cannot access {0}: No such file or directory"
def positional(max_positional_args):
"""A decorator to declare that only the first N arguments may be positional.
This decorator makes it easy to support Python 3 style keyword-only
parameters. For example, in Python 3 it is possible to write::
def fn(pos1, *, kwonly1=None, kwonly2=None):
...
All named parameters after ``*`` must be a keyword::
fn(10, 'kw1', 'kw2') # Raises exception.
fn(10, kwonly1='kw1') # Ok.
Example
^^^^^^^
To define a function like above, do::
@positional(1)
def fn(pos1, kwonly1=None, kwonly2=None):
...
If no default value is provided to a keyword argument, it becomes a
required keyword argument::
@positional(0)
def fn(required_kw):
...
This must be called with the keyword parameter::
fn() # Raises exception.
fn(10) # Raises exception.
fn(required_kw=10) # Ok.
When defining instance or class methods always remember to account for
``self`` and ``cls``::
class MyClass(object):
@positional(2)
def my_method(self, pos1, kwonly1=None):
...
@classmethod
@positional(2)
def my_method(cls, pos1, kwonly1=None):
...
The positional decorator behavior is controlled by
``_helpers.positional_parameters_enforcement``, which may be set to
``POSITIONAL_EXCEPTION``, ``POSITIONAL_WARNING`` or
``POSITIONAL_IGNORE`` to raise an exception, log a warning, or do
nothing, respectively, if a declaration is violated.
Args:
max_positional_arguments: Maximum number of positional arguments. All
parameters after this index must be
keyword only.
Returns:
A decorator that prevents using arguments after max_positional_args
from being used as positional parameters.
Raises:
TypeError: if a keyword-only argument is provided as a positional
parameter, but only if
_helpers.positional_parameters_enforcement is set to
POSITIONAL_EXCEPTION.
"""
def positional_decorator(wrapped):
@functools.wraps(wrapped)
def positional_wrapper(*args, **kwargs):
if len(args) > max_positional_args:
plural_s = ""
if max_positional_args != 1:
plural_s = "s"
message = (
"{function}() takes at most {args_max} positional "
"argument{plural} ({args_given} given)".format(
function=wrapped.__name__,
args_max=max_positional_args,
args_given=len(args),
plural=plural_s,
)
)
if positional_parameters_enforcement == POSITIONAL_EXCEPTION:
raise TypeError(message)
elif positional_parameters_enforcement == POSITIONAL_WARNING:
logger.warning(message)
return wrapped(*args, **kwargs)
return positional_wrapper
if isinstance(max_positional_args, int):
return positional_decorator
else:
args, _, _, defaults, _, _, _ = inspect.getfullargspec(max_positional_args)
return positional(len(args) - len(defaults))(max_positional_args)
def parse_unique_urlencoded(content):
"""Parses unique key-value parameters from urlencoded content.
Args:
content: string, URL-encoded key-value pairs.
Returns:
dict, The key-value pairs from ``content``.
Raises:
ValueError: if one of the keys is repeated.
"""
urlencoded_params = urllib.parse.parse_qs(content)
params = {}
for key, value in urlencoded_params.items():
if len(value) != 1:
msg = "URL-encoded content contains a repeated value:" "%s -> %s" % (
key,
", ".join(value),
)
raise ValueError(msg)
params[key] = value[0]
return params
def update_query_params(uri, params):
"""Updates a URI with new query parameters.
If a given key from ``params`` is repeated in the ``uri``, then
the URI will be considered invalid and an error will occur.
If the URI is valid, then each value from ``params`` will
replace the corresponding value in the query parameters (if
it exists).
Args:
uri: string, A valid URI, with potential existing query parameters.
params: dict, A dictionary of query parameters.
Returns:
The same URI but with the new query parameters added.
"""
parts = urllib.parse.urlparse(uri)
query_params = parse_unique_urlencoded(parts.query)
query_params.update(params)
new_query = urllib.parse.urlencode(query_params)
new_parts = parts._replace(query=new_query)
return urllib.parse.urlunparse(new_parts)
def _add_query_parameter(url, name, value):
"""Adds a query parameter to a url.
Replaces the current value if it already exists in the URL.
Args:
url: string, url to add the query parameter to.
name: string, query parameter name.
value: string, query parameter value.
Returns:
Updated query parameter. Does not update the url if value is None.
"""
if value is None:
return url
else:
return update_query_params(url, {name: value})

View File

@@ -0,0 +1,315 @@
# Copyright 2014 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Channel notifications support.
Classes and functions to support channel subscriptions and notifications
on those channels.
Notes:
- This code is based on experimental APIs and is subject to change.
- Notification does not do deduplication of notification ids, that's up to
the receiver.
- Storing the Channel between calls is up to the caller.
Example setting up a channel:
# Create a new channel that gets notifications via webhook.
channel = new_webhook_channel("https://example.com/my_web_hook")
# Store the channel, keyed by 'channel.id'. Store it before calling the
# watch method because notifications may start arriving before the watch
# method returns.
...
resp = service.objects().watchAll(
bucket="some_bucket_id", body=channel.body()).execute()
channel.update(resp)
# Store the channel, keyed by 'channel.id'. Store it after being updated
# since the resource_id value will now be correct, and that's needed to
# stop a subscription.
...
An example Webhook implementation using webapp2. Note that webapp2 puts
headers in a case insensitive dictionary, as headers aren't guaranteed to
always be upper case.
id = self.request.headers[X_GOOG_CHANNEL_ID]
# Retrieve the channel by id.
channel = ...
# Parse notification from the headers, including validating the id.
n = notification_from_headers(channel, self.request.headers)
# Do app specific stuff with the notification here.
if n.resource_state == 'sync':
# Code to handle sync state.
elif n.resource_state == 'exists':
# Code to handle the exists state.
elif n.resource_state == 'not_exists':
# Code to handle the not exists state.
Example of unsubscribing.
service.channels().stop(channel.body()).execute()
"""
from __future__ import absolute_import
import datetime
import uuid
from googleapiclient import _helpers as util
from googleapiclient import errors
# The unix time epoch starts at midnight 1970.
EPOCH = datetime.datetime(1970, 1, 1)
# Map the names of the parameters in the JSON channel description to
# the parameter names we use in the Channel class.
CHANNEL_PARAMS = {
"address": "address",
"id": "id",
"expiration": "expiration",
"params": "params",
"resourceId": "resource_id",
"resourceUri": "resource_uri",
"type": "type",
"token": "token",
}
X_GOOG_CHANNEL_ID = "X-GOOG-CHANNEL-ID"
X_GOOG_MESSAGE_NUMBER = "X-GOOG-MESSAGE-NUMBER"
X_GOOG_RESOURCE_STATE = "X-GOOG-RESOURCE-STATE"
X_GOOG_RESOURCE_URI = "X-GOOG-RESOURCE-URI"
X_GOOG_RESOURCE_ID = "X-GOOG-RESOURCE-ID"
def _upper_header_keys(headers):
new_headers = {}
for k, v in headers.items():
new_headers[k.upper()] = v
return new_headers
class Notification(object):
"""A Notification from a Channel.
Notifications are not usually constructed directly, but are returned
from functions like notification_from_headers().
Attributes:
message_number: int, The unique id number of this notification.
state: str, The state of the resource being monitored.
uri: str, The address of the resource being monitored.
resource_id: str, The unique identifier of the version of the resource at
this event.
"""
@util.positional(5)
def __init__(self, message_number, state, resource_uri, resource_id):
"""Notification constructor.
Args:
message_number: int, The unique id number of this notification.
state: str, The state of the resource being monitored. Can be one
of "exists", "not_exists", or "sync".
resource_uri: str, The address of the resource being monitored.
resource_id: str, The identifier of the watched resource.
"""
self.message_number = message_number
self.state = state
self.resource_uri = resource_uri
self.resource_id = resource_id
class Channel(object):
"""A Channel for notifications.
Usually not constructed directly, instead it is returned from helper
functions like new_webhook_channel().
Attributes:
type: str, The type of delivery mechanism used by this channel. For
example, 'web_hook'.
id: str, A UUID for the channel.
token: str, An arbitrary string associated with the channel that
is delivered to the target address with each event delivered
over this channel.
address: str, The address of the receiving entity where events are
delivered. Specific to the channel type.
expiration: int, The time, in milliseconds from the epoch, when this
channel will expire.
params: dict, A dictionary of string to string, with additional parameters
controlling delivery channel behavior.
resource_id: str, An opaque id that identifies the resource that is
being watched. Stable across different API versions.
resource_uri: str, The canonicalized ID of the watched resource.
"""
@util.positional(5)
def __init__(
self,
type,
id,
token,
address,
expiration=None,
params=None,
resource_id="",
resource_uri="",
):
"""Create a new Channel.
In user code, this Channel constructor will not typically be called
manually since there are functions for creating channels for each specific
type with a more customized set of arguments to pass.
Args:
type: str, The type of delivery mechanism used by this channel. For
example, 'web_hook'.
id: str, A UUID for the channel.
token: str, An arbitrary string associated with the channel that
is delivered to the target address with each event delivered
over this channel.
address: str, The address of the receiving entity where events are
delivered. Specific to the channel type.
expiration: int, The time, in milliseconds from the epoch, when this
channel will expire.
params: dict, A dictionary of string to string, with additional parameters
controlling delivery channel behavior.
resource_id: str, An opaque id that identifies the resource that is
being watched. Stable across different API versions.
resource_uri: str, The canonicalized ID of the watched resource.
"""
self.type = type
self.id = id
self.token = token
self.address = address
self.expiration = expiration
self.params = params
self.resource_id = resource_id
self.resource_uri = resource_uri
def body(self):
"""Build a body from the Channel.
Constructs a dictionary that's appropriate for passing into watch()
methods as the value of body argument.
Returns:
A dictionary representation of the channel.
"""
result = {
"id": self.id,
"token": self.token,
"type": self.type,
"address": self.address,
}
if self.params:
result["params"] = self.params
if self.resource_id:
result["resourceId"] = self.resource_id
if self.resource_uri:
result["resourceUri"] = self.resource_uri
if self.expiration:
result["expiration"] = self.expiration
return result
def update(self, resp):
"""Update a channel with information from the response of watch().
When a request is sent to watch() a resource, the response returned
from the watch() request is a dictionary with updated channel information,
such as the resource_id, which is needed when stopping a subscription.
Args:
resp: dict, The response from a watch() method.
"""
for json_name, param_name in CHANNEL_PARAMS.items():
value = resp.get(json_name)
if value is not None:
setattr(self, param_name, value)
def notification_from_headers(channel, headers):
"""Parse a notification from the webhook request headers, validate
the notification, and return a Notification object.
Args:
channel: Channel, The channel that the notification is associated with.
headers: dict, A dictionary like object that contains the request headers
from the webhook HTTP request.
Returns:
A Notification object.
Raises:
errors.InvalidNotificationError if the notification is invalid.
ValueError if the X-GOOG-MESSAGE-NUMBER can't be converted to an int.
"""
headers = _upper_header_keys(headers)
channel_id = headers[X_GOOG_CHANNEL_ID]
if channel.id != channel_id:
raise errors.InvalidNotificationError(
"Channel id mismatch: %s != %s" % (channel.id, channel_id)
)
else:
message_number = int(headers[X_GOOG_MESSAGE_NUMBER])
state = headers[X_GOOG_RESOURCE_STATE]
resource_uri = headers[X_GOOG_RESOURCE_URI]
resource_id = headers[X_GOOG_RESOURCE_ID]
return Notification(message_number, state, resource_uri, resource_id)
@util.positional(2)
def new_webhook_channel(url, token=None, expiration=None, params=None):
"""Create a new webhook Channel.
Args:
url: str, URL to post notifications to.
token: str, An arbitrary string associated with the channel that
is delivered to the target address with each notification delivered
over this channel.
expiration: datetime.datetime, A time in the future when the channel
should expire. Can also be None if the subscription should use the
default expiration. Note that different services may have different
limits on how long a subscription lasts. Check the response from the
watch() method to see the value the service has set for an expiration
time.
params: dict, Extra parameters to pass on channel creation. Currently
not used for webhook channels.
"""
expiration_ms = 0
if expiration:
delta = expiration - EPOCH
expiration_ms = (
delta.microseconds / 1000 + (delta.seconds + delta.days * 24 * 3600) * 1000
)
if expiration_ms < 0:
expiration_ms = 0
return Channel(
"web_hook",
str(uuid.uuid4()),
token,
url,
expiration=expiration_ms,
params=params,
)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,78 @@
# Copyright 2014 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Caching utility for the discovery document."""
from __future__ import absolute_import
import logging
import os
LOGGER = logging.getLogger(__name__)
DISCOVERY_DOC_MAX_AGE = 60 * 60 * 24 # 1 day
DISCOVERY_DOC_DIR = os.path.join(
os.path.dirname(os.path.realpath(__file__)), "documents"
)
def autodetect():
"""Detects an appropriate cache module and returns it.
Returns:
googleapiclient.discovery_cache.base.Cache, a cache object which
is auto detected, or None if no cache object is available.
"""
if "GAE_ENV" in os.environ:
try:
from . import appengine_memcache
return appengine_memcache.cache
except Exception:
pass
try:
from . import file_cache
return file_cache.cache
except Exception:
LOGGER.info(
"file_cache is only supported with oauth2client<4.0.0", exc_info=False
)
return None
def get_static_doc(serviceName, version):
"""Retrieves the discovery document from the directory defined in
DISCOVERY_DOC_DIR corresponding to the serviceName and version provided.
Args:
serviceName: string, name of the service.
version: string, the version of the service.
Returns:
A string containing the contents of the JSON discovery document,
otherwise None if the JSON discovery document was not found.
"""
content = None
doc_name = "{}.{}.json".format(serviceName, version)
try:
with open(os.path.join(DISCOVERY_DOC_DIR, doc_name), "r") as f:
content = f.read()
except FileNotFoundError:
# File does not exist. Nothing to do here.
pass
return content

View File

@@ -0,0 +1,55 @@
# Copyright 2014 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""App Engine memcache based cache for the discovery document."""
import logging
# This is only an optional dependency because we only import this
# module when google.appengine.api.memcache is available.
from google.appengine.api import memcache
from . import base
from ..discovery_cache import DISCOVERY_DOC_MAX_AGE
LOGGER = logging.getLogger(__name__)
NAMESPACE = "google-api-client"
class Cache(base.Cache):
"""A cache with app engine memcache API."""
def __init__(self, max_age):
"""Constructor.
Args:
max_age: Cache expiration in seconds.
"""
self._max_age = max_age
def get(self, url):
try:
return memcache.get(url, namespace=NAMESPACE)
except Exception as e:
LOGGER.warning(e, exc_info=True)
def set(self, url, content):
try:
memcache.set(url, content, time=int(self._max_age), namespace=NAMESPACE)
except Exception as e:
LOGGER.warning(e, exc_info=True)
cache = Cache(max_age=DISCOVERY_DOC_MAX_AGE)

View File

@@ -0,0 +1,46 @@
# Copyright 2014 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""An abstract class for caching the discovery document."""
import abc
class Cache(object):
"""A base abstract cache class."""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def get(self, url):
"""Gets the content from the memcache with a given key.
Args:
url: string, the key for the cache.
Returns:
object, the value in the cache for the given key, or None if the key is
not in the cache.
"""
raise NotImplementedError()
@abc.abstractmethod
def set(self, url, content):
"""Sets the given key and content in the cache.
Args:
url: string, the key for the cache.
content: string, the discovery document.
"""
raise NotImplementedError()

View File

@@ -0,0 +1,145 @@
# Copyright 2014 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""File based cache for the discovery document.
The cache is stored in a single file so that multiple processes can
share the same cache. It locks the file whenever accessing to the
file. When the cache content is corrupted, it will be initialized with
an empty cache.
"""
from __future__ import division
import datetime
import json
import logging
import os
import tempfile
try:
from oauth2client.contrib.locked_file import LockedFile
except ImportError:
# oauth2client < 2.0.0
try:
from oauth2client.locked_file import LockedFile
except ImportError:
# oauth2client > 4.0.0 or google-auth
raise ImportError(
"file_cache is unavailable when using oauth2client >= 4.0.0 or google-auth"
)
from . import base
from ..discovery_cache import DISCOVERY_DOC_MAX_AGE
LOGGER = logging.getLogger(__name__)
FILENAME = "google-api-python-client-discovery-doc.cache"
EPOCH = datetime.datetime(1970, 1, 1)
def _to_timestamp(date):
try:
return (date - EPOCH).total_seconds()
except AttributeError:
# The following is the equivalent of total_seconds() in Python2.6.
# See also: https://docs.python.org/2/library/datetime.html
delta = date - EPOCH
return (
delta.microseconds + (delta.seconds + delta.days * 24 * 3600) * 10**6
) / 10**6
def _read_or_initialize_cache(f):
f.file_handle().seek(0)
try:
cache = json.load(f.file_handle())
except Exception:
# This means it opens the file for the first time, or the cache is
# corrupted, so initializing the file with an empty dict.
cache = {}
f.file_handle().truncate(0)
f.file_handle().seek(0)
json.dump(cache, f.file_handle())
return cache
class Cache(base.Cache):
"""A file based cache for the discovery documents."""
def __init__(self, max_age):
"""Constructor.
Args:
max_age: Cache expiration in seconds.
"""
self._max_age = max_age
self._file = os.path.join(tempfile.gettempdir(), FILENAME)
f = LockedFile(self._file, "a+", "r")
try:
f.open_and_lock()
if f.is_locked():
_read_or_initialize_cache(f)
# If we can not obtain the lock, other process or thread must
# have initialized the file.
except Exception as e:
LOGGER.warning(e, exc_info=True)
finally:
f.unlock_and_close()
def get(self, url):
f = LockedFile(self._file, "r+", "r")
try:
f.open_and_lock()
if f.is_locked():
cache = _read_or_initialize_cache(f)
if url in cache:
content, t = cache.get(url, (None, 0))
if _to_timestamp(datetime.datetime.now()) < t + self._max_age:
return content
return None
else:
LOGGER.debug("Could not obtain a lock for the cache file.")
return None
except Exception as e:
LOGGER.warning(e, exc_info=True)
finally:
f.unlock_and_close()
def set(self, url, content):
f = LockedFile(self._file, "r+", "r")
try:
f.open_and_lock()
if f.is_locked():
cache = _read_or_initialize_cache(f)
cache[url] = (content, _to_timestamp(datetime.datetime.now()))
# Remove stale cache.
for k, (_, timestamp) in list(cache.items()):
if (
_to_timestamp(datetime.datetime.now())
>= timestamp + self._max_age
):
del cache[k]
f.file_handle().truncate(0)
f.file_handle().seek(0)
json.dump(cache, f.file_handle())
else:
LOGGER.debug("Could not obtain a lock for the cache file.")
except Exception as e:
LOGGER.warning(e, exc_info=True)
finally:
f.unlock_and_close()
cache = Cache(max_age=DISCOVERY_DOC_MAX_AGE)

View File

@@ -0,0 +1,197 @@
# Copyright 2014 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Errors for the library.
All exceptions defined by the library
should be defined in this file.
"""
from __future__ import absolute_import
__author__ = "jcgregorio@google.com (Joe Gregorio)"
import json
from googleapiclient import _helpers as util
class Error(Exception):
"""Base error for this module."""
pass
class HttpError(Error):
"""HTTP data was invalid or unexpected."""
@util.positional(3)
def __init__(self, resp, content, uri=None):
self.resp = resp
if not isinstance(content, bytes):
raise TypeError("HTTP content should be bytes")
self.content = content
self.uri = uri
self.error_details = ""
self.reason = self._get_reason()
@property
def status_code(self):
"""Return the HTTP status code from the response content."""
return self.resp.status
def _get_reason(self):
"""Calculate the reason for the error from the response content."""
reason = self.resp.reason
try:
try:
data = json.loads(self.content.decode("utf-8"))
except json.JSONDecodeError:
# In case it is not json
data = self.content.decode("utf-8")
if isinstance(data, dict):
reason = data["error"]["message"]
error_detail_keyword = next(
(
kw
for kw in ["detail", "details", "errors", "message"]
if kw in data["error"]
),
"",
)
if error_detail_keyword:
self.error_details = data["error"][error_detail_keyword]
elif isinstance(data, list) and len(data) > 0:
first_error = data[0]
reason = first_error["error"]["message"]
if "details" in first_error["error"]:
self.error_details = first_error["error"]["details"]
else:
self.error_details = data
except (ValueError, KeyError, TypeError):
pass
if reason is None:
reason = ""
return reason.strip()
def __repr__(self):
if self.error_details:
return '<HttpError %s when requesting %s returned "%s". Details: "%s">' % (
self.resp.status,
self.uri,
self.reason,
self.error_details,
)
elif self.uri:
return '<HttpError %s when requesting %s returned "%s">' % (
self.resp.status,
self.uri,
self.reason,
)
else:
return '<HttpError %s "%s">' % (self.resp.status, self.reason)
__str__ = __repr__
class InvalidJsonError(Error):
"""The JSON returned could not be parsed."""
pass
class UnknownFileType(Error):
"""File type unknown or unexpected."""
pass
class UnknownLinkType(Error):
"""Link type unknown or unexpected."""
pass
class UnknownApiNameOrVersion(Error):
"""No API with that name and version exists."""
pass
class UnacceptableMimeTypeError(Error):
"""That is an unacceptable mimetype for this operation."""
pass
class MediaUploadSizeError(Error):
"""Media is larger than the method can accept."""
pass
class ResumableUploadError(HttpError):
"""Error occurred during resumable upload."""
pass
class InvalidChunkSizeError(Error):
"""The given chunksize is not valid."""
pass
class InvalidNotificationError(Error):
"""The channel Notification is invalid."""
pass
class BatchError(HttpError):
"""Error occurred during batch operations."""
@util.positional(2)
def __init__(self, reason, resp=None, content=None):
self.resp = resp
self.content = content
self.reason = reason
def __repr__(self):
if getattr(self.resp, "status", None) is None:
return '<BatchError "%s">' % (self.reason)
else:
return '<BatchError %s "%s">' % (self.resp.status, self.reason)
__str__ = __repr__
class UnexpectedMethodError(Error):
"""Exception raised by RequestMockBuilder on unexpected calls."""
@util.positional(1)
def __init__(self, methodId=None):
"""Constructor for an UnexpectedMethodError."""
super(UnexpectedMethodError, self).__init__(
"Received unexpected call %s" % methodId
)
class UnexpectedBodyError(Error):
"""Exception raised by RequestMockBuilder on unexpected bodies."""
def __init__(self, expected, provided):
"""Constructor for an UnexpectedMethodError."""
super(UnexpectedBodyError, self).__init__(
"Expected: [%s] - Provided: [%s]" % (expected, provided)
)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,183 @@
# Copyright 2014 Joe Gregorio
#
# Licensed under the MIT License
"""MIME-Type Parser
This module provides basic functions for handling mime-types. It can handle
matching mime-types against a list of media-ranges. See section 14.1 of the
HTTP specification [RFC 2616] for a complete explanation.
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
Contents:
- parse_mime_type(): Parses a mime-type into its component parts.
- parse_media_range(): Media-ranges are mime-types with wild-cards and a 'q'
quality parameter.
- quality(): Determines the quality ('q') of a mime-type when
compared against a list of media-ranges.
- quality_parsed(): Just like quality() except the second parameter must be
pre-parsed.
- best_match(): Choose the mime-type with the highest quality ('q')
from a list of candidates.
"""
from __future__ import absolute_import
from functools import reduce
__version__ = "0.1.3"
__author__ = "Joe Gregorio"
__email__ = "joe@bitworking.org"
__license__ = "MIT License"
__credits__ = ""
def parse_mime_type(mime_type):
"""Parses a mime-type into its component parts.
Carves up a mime-type and returns a tuple of the (type, subtype, params)
where 'params' is a dictionary of all the parameters for the media range.
For example, the media range 'application/xhtml;q=0.5' would get parsed
into:
('application', 'xhtml', {'q', '0.5'})
"""
parts = mime_type.split(";")
params = dict(
[tuple([s.strip() for s in param.split("=", 1)]) for param in parts[1:]]
)
full_type = parts[0].strip()
# Java URLConnection class sends an Accept header that includes a
# single '*'. Turn it into a legal wildcard.
if full_type == "*":
full_type = "*/*"
(type, subtype) = full_type.split("/")
return (type.strip(), subtype.strip(), params)
def parse_media_range(range):
"""Parse a media-range into its component parts.
Carves up a media range and returns a tuple of the (type, subtype,
params) where 'params' is a dictionary of all the parameters for the media
range. For example, the media range 'application/*;q=0.5' would get parsed
into:
('application', '*', {'q', '0.5'})
In addition this function also guarantees that there is a value for 'q'
in the params dictionary, filling it in with a proper default if
necessary.
"""
(type, subtype, params) = parse_mime_type(range)
if (
"q" not in params
or not params["q"]
or not float(params["q"])
or float(params["q"]) > 1
or float(params["q"]) < 0
):
params["q"] = "1"
return (type, subtype, params)
def fitness_and_quality_parsed(mime_type, parsed_ranges):
"""Find the best match for a mime-type amongst parsed media-ranges.
Find the best match for a given mime-type against a list of media_ranges
that have already been parsed by parse_media_range(). Returns a tuple of
the fitness value and the value of the 'q' quality parameter of the best
match, or (-1, 0) if no match was found. Just as for quality_parsed(),
'parsed_ranges' must be a list of parsed media ranges.
"""
best_fitness = -1
best_fit_q = 0
(target_type, target_subtype, target_params) = parse_media_range(mime_type)
for (type, subtype, params) in parsed_ranges:
type_match = type == target_type or type == "*" or target_type == "*"
subtype_match = (
subtype == target_subtype or subtype == "*" or target_subtype == "*"
)
if type_match and subtype_match:
param_matches = reduce(
lambda x, y: x + y,
[
1
for (key, value) in target_params.items()
if key != "q" and key in params and value == params[key]
],
0,
)
fitness = (type == target_type) and 100 or 0
fitness += (subtype == target_subtype) and 10 or 0
fitness += param_matches
if fitness > best_fitness:
best_fitness = fitness
best_fit_q = params["q"]
return best_fitness, float(best_fit_q)
def quality_parsed(mime_type, parsed_ranges):
"""Find the best match for a mime-type amongst parsed media-ranges.
Find the best match for a given mime-type against a list of media_ranges
that have already been parsed by parse_media_range(). Returns the 'q'
quality parameter of the best match, 0 if no match was found. This function
bahaves the same as quality() except that 'parsed_ranges' must be a list of
parsed media ranges.
"""
return fitness_and_quality_parsed(mime_type, parsed_ranges)[1]
def quality(mime_type, ranges):
"""Return the quality ('q') of a mime-type against a list of media-ranges.
Returns the quality 'q' of a mime-type when compared against the
media-ranges in ranges. For example:
>>> quality('text/html','text/*;q=0.3, text/html;q=0.7,
text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5')
0.7
"""
parsed_ranges = [parse_media_range(r) for r in ranges.split(",")]
return quality_parsed(mime_type, parsed_ranges)
def best_match(supported, header):
"""Return mime-type with the highest quality ('q') from list of candidates.
Takes a list of supported mime-types and finds the best match for all the
media-ranges listed in header. The value of header must be a string that
conforms to the format of the HTTP Accept: header. The value of 'supported'
is a list of mime-types. The list of supported mime-types should be sorted
in order of increasing desirability, in case of a situation where there is
a tie.
>>> best_match(['application/xbel+xml', 'text/xml'],
'text/*;q=0.5,*/*; q=0.1')
'text/xml'
"""
split_header = _filter_blank(header.split(","))
parsed_header = [parse_media_range(r) for r in split_header]
weighted_matches = []
pos = 0
for mime_type in supported:
weighted_matches.append(
(fitness_and_quality_parsed(mime_type, parsed_header), pos, mime_type)
)
pos += 1
weighted_matches.sort()
return weighted_matches[-1][0][1] and weighted_matches[-1][2] or ""
def _filter_blank(i):
for s in i:
if s.strip():
yield s

View File

@@ -0,0 +1,409 @@
# Copyright 2014 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Model objects for requests and responses.
Each API may support one or more serializations, such
as JSON, Atom, etc. The model classes are responsible
for converting between the wire format and the Python
object representation.
"""
from __future__ import absolute_import
__author__ = "jcgregorio@google.com (Joe Gregorio)"
import json
import logging
import platform
import urllib
from googleapiclient import version as googleapiclient_version
from googleapiclient.errors import HttpError
_LIBRARY_VERSION = googleapiclient_version.__version__
_PY_VERSION = platform.python_version()
LOGGER = logging.getLogger(__name__)
dump_request_response = False
def _abstract():
raise NotImplementedError("You need to override this function")
class Model(object):
"""Model base class.
All Model classes should implement this interface.
The Model serializes and de-serializes between a wire
format such as JSON and a Python object representation.
"""
def request(self, headers, path_params, query_params, body_value):
"""Updates outgoing requests with a serialized body.
Args:
headers: dict, request headers
path_params: dict, parameters that appear in the request path
query_params: dict, parameters that appear in the query
body_value: object, the request body as a Python object, which must be
serializable.
Returns:
A tuple of (headers, path_params, query, body)
headers: dict, request headers
path_params: dict, parameters that appear in the request path
query: string, query part of the request URI
body: string, the body serialized in the desired wire format.
"""
_abstract()
def response(self, resp, content):
"""Convert the response wire format into a Python object.
Args:
resp: httplib2.Response, the HTTP response headers and status
content: string, the body of the HTTP response
Returns:
The body de-serialized as a Python object.
Raises:
googleapiclient.errors.HttpError if a non 2xx response is received.
"""
_abstract()
class BaseModel(Model):
"""Base model class.
Subclasses should provide implementations for the "serialize" and
"deserialize" methods, as well as values for the following class attributes.
Attributes:
accept: The value to use for the HTTP Accept header.
content_type: The value to use for the HTTP Content-type header.
no_content_response: The value to return when deserializing a 204 "No
Content" response.
alt_param: The value to supply as the "alt" query parameter for requests.
"""
accept = None
content_type = None
no_content_response = None
alt_param = None
def _log_request(self, headers, path_params, query, body):
"""Logs debugging information about the request if requested."""
if dump_request_response:
LOGGER.info("--request-start--")
LOGGER.info("-headers-start-")
for h, v in headers.items():
LOGGER.info("%s: %s", h, v)
LOGGER.info("-headers-end-")
LOGGER.info("-path-parameters-start-")
for h, v in path_params.items():
LOGGER.info("%s: %s", h, v)
LOGGER.info("-path-parameters-end-")
LOGGER.info("body: %s", body)
LOGGER.info("query: %s", query)
LOGGER.info("--request-end--")
def request(self, headers, path_params, query_params, body_value):
"""Updates outgoing requests with a serialized body.
Args:
headers: dict, request headers
path_params: dict, parameters that appear in the request path
query_params: dict, parameters that appear in the query
body_value: object, the request body as a Python object, which must be
serializable by json.
Returns:
A tuple of (headers, path_params, query, body)
headers: dict, request headers
path_params: dict, parameters that appear in the request path
query: string, query part of the request URI
body: string, the body serialized as JSON
"""
query = self._build_query(query_params)
headers["accept"] = self.accept
headers["accept-encoding"] = "gzip, deflate"
if "user-agent" in headers:
headers["user-agent"] += " "
else:
headers["user-agent"] = ""
headers["user-agent"] += "(gzip)"
if "x-goog-api-client" in headers:
headers["x-goog-api-client"] += " "
else:
headers["x-goog-api-client"] = ""
headers["x-goog-api-client"] += "gdcl/%s gl-python/%s" % (
_LIBRARY_VERSION,
_PY_VERSION,
)
if body_value is not None:
headers["content-type"] = self.content_type
body_value = self.serialize(body_value)
self._log_request(headers, path_params, query, body_value)
return (headers, path_params, query, body_value)
def _build_query(self, params):
"""Builds a query string.
Args:
params: dict, the query parameters
Returns:
The query parameters properly encoded into an HTTP URI query string.
"""
if self.alt_param is not None:
params.update({"alt": self.alt_param})
astuples = []
for key, value in params.items():
if type(value) == type([]):
for x in value:
x = x.encode("utf-8")
astuples.append((key, x))
else:
if isinstance(value, str) and callable(value.encode):
value = value.encode("utf-8")
astuples.append((key, value))
return "?" + urllib.parse.urlencode(astuples)
def _log_response(self, resp, content):
"""Logs debugging information about the response if requested."""
if dump_request_response:
LOGGER.info("--response-start--")
for h, v in resp.items():
LOGGER.info("%s: %s", h, v)
if content:
LOGGER.info(content)
LOGGER.info("--response-end--")
def response(self, resp, content):
"""Convert the response wire format into a Python object.
Args:
resp: httplib2.Response, the HTTP response headers and status
content: string, the body of the HTTP response
Returns:
The body de-serialized as a Python object.
Raises:
googleapiclient.errors.HttpError if a non 2xx response is received.
"""
self._log_response(resp, content)
# Error handling is TBD, for example, do we retry
# for some operation/error combinations?
if resp.status < 300:
if resp.status == 204:
# A 204: No Content response should be treated differently
# to all the other success states
return self.no_content_response
return self.deserialize(content)
else:
LOGGER.debug("Content from bad request was: %r" % content)
raise HttpError(resp, content)
def serialize(self, body_value):
"""Perform the actual Python object serialization.
Args:
body_value: object, the request body as a Python object.
Returns:
string, the body in serialized form.
"""
_abstract()
def deserialize(self, content):
"""Perform the actual deserialization from response string to Python
object.
Args:
content: string, the body of the HTTP response
Returns:
The body de-serialized as a Python object.
"""
_abstract()
class JsonModel(BaseModel):
"""Model class for JSON.
Serializes and de-serializes between JSON and the Python
object representation of HTTP request and response bodies.
"""
accept = "application/json"
content_type = "application/json"
alt_param = "json"
def __init__(self, data_wrapper=False):
"""Construct a JsonModel.
Args:
data_wrapper: boolean, wrap requests and responses in a data wrapper
"""
self._data_wrapper = data_wrapper
def serialize(self, body_value):
if (
isinstance(body_value, dict)
and "data" not in body_value
and self._data_wrapper
):
body_value = {"data": body_value}
return json.dumps(body_value)
def deserialize(self, content):
try:
content = content.decode("utf-8")
except AttributeError:
pass
try:
body = json.loads(content)
except json.decoder.JSONDecodeError:
body = content
else:
if self._data_wrapper and "data" in body:
body = body["data"]
return body
@property
def no_content_response(self):
return {}
class RawModel(JsonModel):
"""Model class for requests that don't return JSON.
Serializes and de-serializes between JSON and the Python
object representation of HTTP request, and returns the raw bytes
of the response body.
"""
accept = "*/*"
content_type = "application/json"
alt_param = None
def deserialize(self, content):
return content
@property
def no_content_response(self):
return ""
class MediaModel(JsonModel):
"""Model class for requests that return Media.
Serializes and de-serializes between JSON and the Python
object representation of HTTP request, and returns the raw bytes
of the response body.
"""
accept = "*/*"
content_type = "application/json"
alt_param = "media"
def deserialize(self, content):
return content
@property
def no_content_response(self):
return ""
class ProtocolBufferModel(BaseModel):
"""Model class for protocol buffers.
Serializes and de-serializes the binary protocol buffer sent in the HTTP
request and response bodies.
"""
accept = "application/x-protobuf"
content_type = "application/x-protobuf"
alt_param = "proto"
def __init__(self, protocol_buffer):
"""Constructs a ProtocolBufferModel.
The serialized protocol buffer returned in an HTTP response will be
de-serialized using the given protocol buffer class.
Args:
protocol_buffer: The protocol buffer class used to de-serialize a
response from the API.
"""
self._protocol_buffer = protocol_buffer
def serialize(self, body_value):
return body_value.SerializeToString()
def deserialize(self, content):
return self._protocol_buffer.FromString(content)
@property
def no_content_response(self):
return self._protocol_buffer()
def makepatch(original, modified):
"""Create a patch object.
Some methods support PATCH, an efficient way to send updates to a resource.
This method allows the easy construction of patch bodies by looking at the
differences between a resource before and after it was modified.
Args:
original: object, the original deserialized resource
modified: object, the modified deserialized resource
Returns:
An object that contains only the changes from original to modified, in a
form suitable to pass to a PATCH method.
Example usage:
item = service.activities().get(postid=postid, userid=userid).execute()
original = copy.deepcopy(item)
item['object']['content'] = 'This is updated.'
service.activities.patch(postid=postid, userid=userid,
body=makepatch(original, item)).execute()
"""
patch = {}
for key, original_value in original.items():
modified_value = modified.get(key, None)
if modified_value is None:
# Use None to signal that the element is deleted
patch[key] = None
elif original_value != modified_value:
if type(original_value) == type({}):
# Recursively descend objects
patch[key] = makepatch(original_value, modified_value)
else:
# In the case of simple types or arrays we just replace
patch[key] = modified_value
else:
# Don't add anything to patch if there's no change
pass
for key in modified:
if key not in original:
patch[key] = modified[key]
return patch

View File

@@ -0,0 +1,317 @@
# Copyright 2014 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Schema processing for discovery based APIs
Schemas holds an APIs discovery schemas. It can return those schema as
deserialized JSON objects, or pretty print them as prototype objects that
conform to the schema.
For example, given the schema:
schema = \"\"\"{
"Foo": {
"type": "object",
"properties": {
"etag": {
"type": "string",
"description": "ETag of the collection."
},
"kind": {
"type": "string",
"description": "Type of the collection ('calendar#acl').",
"default": "calendar#acl"
},
"nextPageToken": {
"type": "string",
"description": "Token used to access the next
page of this result. Omitted if no further results are available."
}
}
}
}\"\"\"
s = Schemas(schema)
print s.prettyPrintByName('Foo')
Produces the following output:
{
"nextPageToken": "A String", # Token used to access the
# next page of this result. Omitted if no further results are available.
"kind": "A String", # Type of the collection ('calendar#acl').
"etag": "A String", # ETag of the collection.
},
The constructor takes a discovery document in which to look up named schema.
"""
from __future__ import absolute_import
# TODO(jcgregorio) support format, enum, minimum, maximum
__author__ = "jcgregorio@google.com (Joe Gregorio)"
from collections import OrderedDict
from googleapiclient import _helpers as util
class Schemas(object):
"""Schemas for an API."""
def __init__(self, discovery):
"""Constructor.
Args:
discovery: object, Deserialized discovery document from which we pull
out the named schema.
"""
self.schemas = discovery.get("schemas", {})
# Cache of pretty printed schemas.
self.pretty = {}
@util.positional(2)
def _prettyPrintByName(self, name, seen=None, dent=0):
"""Get pretty printed object prototype from the schema name.
Args:
name: string, Name of schema in the discovery document.
seen: list of string, Names of schema already seen. Used to handle
recursive definitions.
Returns:
string, A string that contains a prototype object with
comments that conforms to the given schema.
"""
if seen is None:
seen = []
if name in seen:
# Do not fall into an infinite loop over recursive definitions.
return "# Object with schema name: %s" % name
seen.append(name)
if name not in self.pretty:
self.pretty[name] = _SchemaToStruct(
self.schemas[name], seen, dent=dent
).to_str(self._prettyPrintByName)
seen.pop()
return self.pretty[name]
def prettyPrintByName(self, name):
"""Get pretty printed object prototype from the schema name.
Args:
name: string, Name of schema in the discovery document.
Returns:
string, A string that contains a prototype object with
comments that conforms to the given schema.
"""
# Return with trailing comma and newline removed.
return self._prettyPrintByName(name, seen=[], dent=0)[:-2]
@util.positional(2)
def _prettyPrintSchema(self, schema, seen=None, dent=0):
"""Get pretty printed object prototype of schema.
Args:
schema: object, Parsed JSON schema.
seen: list of string, Names of schema already seen. Used to handle
recursive definitions.
Returns:
string, A string that contains a prototype object with
comments that conforms to the given schema.
"""
if seen is None:
seen = []
return _SchemaToStruct(schema, seen, dent=dent).to_str(self._prettyPrintByName)
def prettyPrintSchema(self, schema):
"""Get pretty printed object prototype of schema.
Args:
schema: object, Parsed JSON schema.
Returns:
string, A string that contains a prototype object with
comments that conforms to the given schema.
"""
# Return with trailing comma and newline removed.
return self._prettyPrintSchema(schema, dent=0)[:-2]
def get(self, name, default=None):
"""Get deserialized JSON schema from the schema name.
Args:
name: string, Schema name.
default: object, return value if name not found.
"""
return self.schemas.get(name, default)
class _SchemaToStruct(object):
"""Convert schema to a prototype object."""
@util.positional(3)
def __init__(self, schema, seen, dent=0):
"""Constructor.
Args:
schema: object, Parsed JSON schema.
seen: list, List of names of schema already seen while parsing. Used to
handle recursive definitions.
dent: int, Initial indentation depth.
"""
# The result of this parsing kept as list of strings.
self.value = []
# The final value of the parsing.
self.string = None
# The parsed JSON schema.
self.schema = schema
# Indentation level.
self.dent = dent
# Method that when called returns a prototype object for the schema with
# the given name.
self.from_cache = None
# List of names of schema already seen while parsing.
self.seen = seen
def emit(self, text):
"""Add text as a line to the output.
Args:
text: string, Text to output.
"""
self.value.extend([" " * self.dent, text, "\n"])
def emitBegin(self, text):
"""Add text to the output, but with no line terminator.
Args:
text: string, Text to output.
"""
self.value.extend([" " * self.dent, text])
def emitEnd(self, text, comment):
"""Add text and comment to the output with line terminator.
Args:
text: string, Text to output.
comment: string, Python comment.
"""
if comment:
divider = "\n" + " " * (self.dent + 2) + "# "
lines = comment.splitlines()
lines = [x.rstrip() for x in lines]
comment = divider.join(lines)
self.value.extend([text, " # ", comment, "\n"])
else:
self.value.extend([text, "\n"])
def indent(self):
"""Increase indentation level."""
self.dent += 1
def undent(self):
"""Decrease indentation level."""
self.dent -= 1
def _to_str_impl(self, schema):
"""Prototype object based on the schema, in Python code with comments.
Args:
schema: object, Parsed JSON schema file.
Returns:
Prototype object based on the schema, in Python code with comments.
"""
stype = schema.get("type")
if stype == "object":
self.emitEnd("{", schema.get("description", ""))
self.indent()
if "properties" in schema:
properties = schema.get("properties", {})
sorted_properties = OrderedDict(sorted(properties.items()))
for pname, pschema in sorted_properties.items():
self.emitBegin('"%s": ' % pname)
self._to_str_impl(pschema)
elif "additionalProperties" in schema:
self.emitBegin('"a_key": ')
self._to_str_impl(schema["additionalProperties"])
self.undent()
self.emit("},")
elif "$ref" in schema:
schemaName = schema["$ref"]
description = schema.get("description", "")
s = self.from_cache(schemaName, seen=self.seen)
parts = s.splitlines()
self.emitEnd(parts[0], description)
for line in parts[1:]:
self.emit(line.rstrip())
elif stype == "boolean":
value = schema.get("default", "True or False")
self.emitEnd("%s," % str(value), schema.get("description", ""))
elif stype == "string":
value = schema.get("default", "A String")
self.emitEnd('"%s",' % str(value), schema.get("description", ""))
elif stype == "integer":
value = schema.get("default", "42")
self.emitEnd("%s," % str(value), schema.get("description", ""))
elif stype == "number":
value = schema.get("default", "3.14")
self.emitEnd("%s," % str(value), schema.get("description", ""))
elif stype == "null":
self.emitEnd("None,", schema.get("description", ""))
elif stype == "any":
self.emitEnd('"",', schema.get("description", ""))
elif stype == "array":
self.emitEnd("[", schema.get("description"))
self.indent()
self.emitBegin("")
self._to_str_impl(schema["items"])
self.undent()
self.emit("],")
else:
self.emit("Unknown type! %s" % stype)
self.emitEnd("", "")
self.string = "".join(self.value)
return self.string
def to_str(self, from_cache):
"""Prototype object based on the schema, in Python code with comments.
Args:
from_cache: callable(name, seen), Callable that retrieves an object
prototype for a schema with the given name. Seen is a list of schema
names already seen as we recursively descend the schema definition.
Returns:
Prototype object based on the schema, in Python code with comments.
The lines of the code will all be properly indented.
"""
self.from_cache = from_cache
return self._to_str_impl(self.schema)

View File

@@ -0,0 +1,15 @@
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
__version__ = "2.124.0"