Compare commits

...

94 Commits

Author SHA1 Message Date
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
Ross Scroggs
90f9931dca Fixed bug that caused a trap when optional argument charset <Charset> was used with emlfile <FileName> 2024-01-09 15:21:06 -08:00
Ross Scroggs
4c357d5281 Added option maxevents <Number> to gam report <ActivityApplictionName> 2024-01-08 12:08:50 -08:00
Ross Scroggs
0abf2ceeca Added optional argument charset <Charset> to emlfile <FileName> 2024-01-07 21:38:02 -08:00
Ross Scroggs
3088570449 Handle gam <UserTypeEntity> delete message permission error 2024-01-07 10:29:15 -08:00
Ross Scroggs
800943c401 Updated commands that create ACLs to handle the following error:
ERROR: 400: abusiveContentRestriction - Bad Request. User message: "You cannot share this item because it has been flagged as inappropriate."
2024-01-04 15:04:24 -08:00
Ross Scroggs
3bedb57443 Handle Gmail sharing settings permission errors 2024-01-03 09:31:44 -08:00
Ross Scroggs
668ded91e2 Updated user attribute replace <Tag> <UserReplacement> to allow field:photourl 2024-01-02 21:08:35 -08:00
Ross Scroggs
293e1c1d9a Fixed bug introduced in 6.67.02 in gam <UserTypeEntity> claim ownership that caused a trap. 2024-01-02 12:32:13 -08:00
Ross Scroggs
7596215bbe Added option skipids <DriveFileEntity> to gam <UserTypeEntity> copy drivefile 2023-12-30 08:57:54 -08:00
Ross Scroggs
7c6bbaf107 Revert "Sdd skids to more command, handle Shared Drive limitations"
This reverts commit 5271368776.
2023-12-30 07:47:06 -08:00
Ross Scroggs
5271368776 Sdd skids to more command, handle Shared Drive limitations 2023-12-29 21:01:59 -08:00
Ross Scroggs
430a30e2d2 Fix vault corpus bug 2023-12-23 08:20:29 -08:00
Ross Scroggs
b0eae53f80 Improve print vaultcounts error message 2023-12-23 07:58:16 -08:00
Ross Scroggs
dd03bafaec Fixed bug in gam print vaultcounts that caused a trap. 2023-12-23 07:42:24 -08:00
Ross Scroggs
ded3ea104b Document sitesurl in vault 2023-12-22 19:55:50 -08:00
Ross Scroggs
0d9c6a77b6 Updates for API changes
Updated `gam <CrOSTypeEntity> update action <CrOSAction>` to use the new API function `batchChangeStatus`

Updated `gam create vaultexport matter <MatterItem>` to support `corpus calendar`.
2023-12-22 19:38:06 -08:00
Ross Scroggs
ae46ae8738 Added option convertcrnl to gam update chromepolicy 2023-12-20 19:59:18 -08:00
Ross Scroggs
06a4c7a8c9 Added option copysubfilesownedby any|me|others to `gam <UserTypeEntity> copy drivefile 2023-12-20 12:32:10 -08:00
Ross Scroggs
f89f730957 Handle issues in update alias/message 2023-12-19 20:04:06 -08:00
Ross Scroggs
80fc40a9c7 Updated functionality of option preservefiletimes in gam <UserTypeEntity> update drivefile <DriveFileEntity>. 2023-12-14 10:06:46 -08:00
Ross Scroggs
2bb0088ade Updated all drive commands to handle the following error:
```
ERROR: 401: Active session is invalid. Error code: 4 - authError
```
2023-12-12 10:25:59 -08:00
86 changed files with 11445 additions and 1650 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 }}-20240210
- 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
@@ -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

@@ -55,13 +55,18 @@ gam create alias bob[@yourdomain.com] user robert[@yourdomain.com]
The existing alias is deleted and a new alias is created.
```
gam update alias|aliases <EmailAddressEntity> user|group|target <UniqueID>|<EmailAddress>
[notargetverify]
[notargetverify] [waitafterdelete <Integer>]
```
`<EmailAddressEntity>` are the aliases, `<EmailAddress>` is the target.
By default, GAM makes additional API calls to verify that the target email address exists before updating the alias;
if you know that the target exists, you can suppress the verification with `notargetverify.
GAM updates an alias to point to a new target by deleting the alias and then recreates the alias pointing to the new target.
Unfortunately, if these commands are executed back-to-back; Google generates the `Update Failed: Duplicate` error.
Now, GAM waits 2 seconds between the delete and the insert which seems to eliminate the problem. If the problem persists,
use the option `waitafterdelete <Integer>` to increase the wait time to a maximum of 10 seconds.
## Delete an alias regardless of the target
```
gam delete alias|aliases [user|group|target] <EmailAddressEntity>
@@ -80,7 +85,7 @@ gam <UserTypeEntity> delete aliases
```
## Display aliases
Display a specific alise.
Display a specific alias.
```
gam info alias|aliases <EmailAddressEntity>
```
@@ -124,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

@@ -89,15 +89,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 +105,15 @@ 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
## 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.
@@ -404,60 +404,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 +497,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
@@ -817,24 +837,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 +896,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|
@@ -232,8 +232,11 @@
101035 |
101036 |
101037 |
101038 |
101039 |
101040 |
101043 |
101047 |
Google-Apps |
Google-Chrome-Device-Management |
Google-Drive-storage |
@@ -249,12 +252,17 @@
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 |
cdm | chrome | googlechromedevicemanagement | Google-Chrome-Device-Management |
cloudidentity | identity | 1010010001 |
cloudidentitypremium | identitypremium | 1010050001 |
cloudsearch | 1010350001 |
geminibiz | 1010470003 |
geminient| duetai | 1010470001 |
gsuitebasic | gafb | gafw | basic | Google-Apps-For-Business |
gsuitebusiness | gau | gsb | unlimited | Google-Apps-Unlimited |
gsuitebusinessarchived | gsbau | businessarchived | 1010340002 |
@@ -270,6 +278,7 @@
gwesstaff | workspaceeducationstandardstaff | 1010310006 |
gwesstudent | workspaceeducationstandardstudent | 1010310007 |
gwetlu | workspaceeducationupgrade | 1010370001 |
gwlabs | workspacelabs | 1010470002
meetdialing | googlemeetglobaldialing | 1010360001 |
postini | gams | gsuitegams | gsuitepostini | gsuitemessagesecurity | Google-Apps-For-Postini |
standard | free | Google-Apps |
@@ -278,16 +287,22 @@
voicepremier | gvpremier | googlevoicepremier | 1010330002 |
voicestandard | gvstandard | googlevoicestandard | 1010330004 |
voicestarter | gvstarter | googlevoicestarter | 1010330003 |
wsas | plusstorage | 1010430001 |
wsbizplus | workspacebusinessplus | 1010020025 |
wsbizplusarchived | workspacebusinessplusarchived | 1010340003 |
wsbizstan | workspacebusinessstandard | 1010020028 |
wsbizstanarchived | workspacebusinessstandardarchived | 1010340006 |
wsbizstarter | workspacebusinessstarter | wsbizstart | 1010020027 |
wsbizstarterarchived | workspacebusinessstarterarchived | 1010340005 |
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
wsessplus | workspaceessentialsplus | 1010060005 |
wsflw | workspacefrontline | workspacefrontlineworker | 1010020030 |
wsflwstan | workspacefrontlinestan | workspacefrontlineworkerstan | 1010020031
```
## Items built from primitives
```
@@ -346,7 +361,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 +436,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>
@@ -460,8 +476,7 @@
(message|textmessage|htmlmessage <String>)|
(file|textfile|htmlfile <FileName> [charset <Charset>])|
(gdoc|ghtml <UserGoogleDoc>)|
(gcsdoc|gcshtml <StorageBucketObjectName>)|
(emlfile <FileName>)
(gcsdoc|gcshtml <StorageBucketObjectName>)
<MessageID> ::= <String>
<Namespace> ::= <String>
<NotesName> ::= notes/<String>
@@ -535,7 +550,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>
@@ -590,8 +605,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

@@ -36,7 +36,7 @@ This Wiki page was built directly from Jay Lee's Wiki page; my sincere thanks fo
<ChatContent> ::=
((text <String>)|
(textfile <FileName> [charset <CharSet>])|
(textfile <FileName> [charset <Charset>])|
(gdoc <UserGoogleDoc>)|
(gcsdoc <StorageBucketObjectName>))
@@ -162,7 +162,7 @@ gam create chatmessage space <ChatSpace>
```
Specify the text of the message: `<ChatContent>`
* `text <String>` - The message is `<String>`
* `textfile <FileName> [charset <CharSet>]` - The message is read from a local file
* `textfile <FileName> [charset <Charset>]` - The message is read from a local file
* `gdoc <UserGoogleDoc>` - The message is read from a Google Doc.
* `gcsdoc <StorageBucketObjectName>` - The message is read from a Google Cloud Storage file.
@@ -207,7 +207,7 @@ gam update chatmessage name <ChatMessage>
```
Specify the source of the message:
* `text <String>` - The message is `<String>`
* `textfile <FileName> [charset <CharSet>]` - The message is read from a local file
* `textfile <FileName> [charset <Charset>]` - The message is read from a local file
* `gdoc <UserGoogleDoc>` - The message is read from a Google Doc.
* `gcsdoc <StorageBucketObjectName>` - The message is read from a Google Cloud Storage file.

View File

@@ -156,7 +156,7 @@ gam create chromepolicyimage <ChromePolicyImageSchemaName> <FileName>
## Update Chrome policy
You can update a policy for all devices/users within an OU or for a specific printer or application within an OU.
```
gam update chromepolicy (<SchemaName> (<Field> <Value>)+)+
gam update chromepolicy [convertcrnl]
(<SchemaName> ((<Field> <Value>)+ | <JSONData>))+
ou|org|orgunit <OrgUnitItem> [(printerid <PrinterID>)|(appid <AppID>)]
```
@@ -190,6 +190,14 @@ When specifying a value for this field you can enter the value in one of two way
* `DEFAULT_PRINT_COLOR_ENUM_MONOCHROME` - The full value
* `MONOCHROME` - The text following `..._ENUM_`
### TYPE_STRING fields with carriage returns (\r) and line feeds (\n)
Use the `convertcrnl` option to properly handle these characters
in value strings entered on the command line in the `<Field> <Value>` form.
```
gam update chromepolicy convertcrnl chrome.devices.DisabledDeviceReturnInstructions
deviceDisabledMessage "Please return device to:\nSchool\n123 Main Street\nAnytown US" ou /Path/to/OU
```
### Examples
Restrict student users from adding additional printers and set default printing to black and white.
```
@@ -322,12 +330,12 @@ Version `6.21.02` is required.
Display direct policies, update all
```
gam redirect csv ChromePolicies.csv print chromepolicies ou "/Path/To/OU1" show direct formatjson quotechar "'"
gam csv ChromePolicies.csv quotechar "'" gam update chromepolicy "~name" json "~JSON"
gam csv ChromePolicies.csv quotechar "'" gam update chromepolicy "~name" json "~JSON" ou "/Path/To/OU2"
```
Display all policies, select direct on update
```
gam redirect csv ChromePolicies.csv print chromepolicies ou "/Path/To/OU1" formatjson quotechar "'"
gam config csv_input_row_filter "direct:boolean:true" csv ChromePolicies.csv quotechar "'" gam update chromepolicy "~name" json "~JSON"
gam config csv_input_row_filter "direct:boolean:true" csv ChromePolicies.csv quotechar "'" gam update chromepolicy "~name" json "~JSON" ou "/Path/To/OU2"
```
## Create Chrome network

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)
@@ -160,8 +160,10 @@ The second form is backwards compatible with Standard GAM and selection with `<C
```
<CrOSAction> ::=
deprovision_different_model_replace|
deprovision_different_model_replacement|
deprovision_retiring_device|
deprovision_same_model_replace|
deprovision_same_model_replacement|
deprovision_upgrade_transfer|
disable|
reenable|
@@ -403,13 +405,15 @@ gam update ou csvkmd cros.csv keyfield OU datafield deviceId add croscsvdata dev
deprovision_same_model_replace|
deprovision_upgrade_transfer|
disable|
reenable|
pre_provisioned_disable|
pre_provisioned_reenable
reenable
gam <CrOSTypeEntity> update action <CrOSAction> [acknowledge_device_touch_requirement]
[actionbatchsize <Integer>]
gam update cros <CrOSEntity> action <CrOSAction> [acknowledge_device_touch_requirement]
[actionbatchsize <Integer>]
```
As of GAM version `6.67.00`, the new API function `batchChangeStatus` replaces the old API function `action`; ChromeOS devices are now processed in batches.
The batch size defaults to 10, the `actionbatchsize <Integer>` option can be used to set a batch size between 10 and 250.
As deprovisioning ChromeOS devices is not reversible, you must enter `acknowledge_device_touch_requirement`
when `<CrOSAction>` is `deprovision_same_model_replace`, `deprovision_different_model_replace`,
@@ -679,10 +683,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

@@ -35,7 +35,7 @@
<NoteContent> ::=
((<String>)|
(file <FileName> [charset <CharSet>])|
(file <FileName> [charset <Charset>])|
(gdoc <UserGoogleDoc>)|
(gcsdoc <StorageBucketObjectName>))

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

@@ -10,6 +10,589 @@ 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.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`.
### 6.67.09
Added option `maxevents <Number>` to `gam report <ActivityApplictionName>` that limits
the number of events displayed for each activity; the default is 0, no limit.
Setting options `maxactivities 1 maxevents 1 maxresults 1` can be used to as efficiently as possible
show the most recent activity/event; this can be useful when reporting drive activity for individual drive files.
### 6.67.08
Added optional argument `charset <Charset>` to `emlfile <FileName>` in `gam <UserTypeEntity> draft|import|insert message`;
the default value is `ascii`.
### 6.67.07
Updated `gam <UserTypeEntity> delete message` to handle this error:
```
ERROR: 403: permissionDenied - Insufficient Permission
```
when the following service account scopes are selected:
```
[ ] 24) Gmail API - Full Access (Labels, Messages)
[*] 25) Gmail API - Full Access (Labels, Messages) except delete message
```
### 6.67.06
Updated commands that create ACLs to handle the following error:
```
ERROR: 400: abusiveContentRestriction - Bad Request. User message: "You cannot share this item because it has been flagged as inappropriate."
```
### 6.67.05
Updated the following commands:
```
gam <UserTypeEntity> create|delete|update delegate
gam <UserTypeEntity> forward
gam <UserTypeEntity> create|delete forwardingaddresses
gam <UserTypeEntity> create|delete sendas
```
to handle this error:
```
ERROR: 403: permissionDenied - Insufficient Permission
```
when the following serice account scope is not enabled:
```
[ ] 28) Gmail API - Sharing Settings (Delegates, Forwarding, SendAs) - write
```
### 6.67.04
Updated user attribute `replace <Tag> <UserReplacement>` to allow `field:photourl` which allows
embedding a link to a user's photo in their signature. Formatting the signature HTML
to properly display the photo is left to the GAM admin.
### 6.67.03
Fixed bug introduced in 6.67.02 in `gam <UserTypeEntity> claim ownership` that caused a trap.
### 6.67.02
Added option `skipids <DriveFileEntity>` to `gam <UserTypeEntity> transfer drive` that handles special cases
where you want to prevent ownership from being transferred for selected files/folders.
Added option `skipids <DriveFileEntity>` to `gam <UserTypeEntity> copy drivefile` that handles special cases
where you want to prevent selected files/folders from being copied.
Updated commands that create files/folders on Shared Drives to handle the following errors:
```
storageQuotaExceeded
teamDriveFileLimitExceeded
teamDriveHierarchyTooDeep
```
* See: https://support.google.com/a/users/answer/7338880#shared_drives_file_folder_limits
### 6.67.01
Fixed bug in `gam print vaultcounts` that caused a trap.
### 6.67.00
Updated `gam <CrOSTypeEntity> update action <CrOSAction>` to use the new API function `batchChangeStatus`
that replaces the old API function `action`; ChromeOS devices are now processed in batches.
The batch size defaults to 10, the `actionbatchsize <Integer>` option can be used to set a batch size between 10 and 250.
Updated `gam create vaultexport matter <MatterItem>` to support `corpus calendar`.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Vault-Takeout#create-vault-exports
### 6.66.16
Added option `convertcrnl` to `gam update chromepolicy` to properly handle carriage returns (\r) and line feeds (\n)
in value strings entered on the command line in the `<Field> <Value>` form.
```
gam update chromepolicy convertcrnl chrome.devices.DisabledDeviceReturnInstructions
deviceDisabledMessage "Please return device to:\nSchool\n123 Main Street\nAnytown US" ou /Path/to/OU
```
### 6.66.15
Added option `copysubfilesownedby any|me|others` to `gam <UserTypeEntity> copy drivefile` that allows
specification of which source folder sub files to copy based on file ownership; the default is `any`.
This only applies when files are being copied from a 'My Drive'.
### 6.66.14
Updated `gam <UserTypeEntity> modify messages` to recognize the following error:
```
ERROR: 400: invalid - Invalid label: SENT
```
Updated `gam update alias <EmailAddressEntity> user|group|target <EmailAddress>`
to avoid the following problem.
```
$ gam update alias testalias@domain.com user testuser
User Alias: testalias@domain.com, Deleted
User Alias: testalias@domain.com, User: testuser@domain.com, Update Failed: Duplicate, Email Address: testalias@domain.com
```
GAM updates an alias to point to a new target by deleting the alias and then recreating the alias pointing to the new target.
Unfortunately, if these commands are executed back-to-back; Google generates the `Update Failed: Duplicate` error.
Now, GAM waits 2 seconds between the delete and the insert which seems to eliminate the problem. If the problem persists,
the option `waitafterdelete <Integer>` can be used to increase the wait time to a maximum of 10 seconds.
### 6.66.13
Updated functionality of option `preservefiletimes` in `gam <UserTypeEntity> update drivefile <DriveFileEntity>`.
* Current
* `preservefiletimes localfile <FileName>` - `modifiedTime` of `<DriveFileEntity>` is set to that of `localfile <FileName>`
* `preservefiletimes` - No effect
* Updated
* `preservefiletimes localfile <FileName>` - `modifiedTime` of `<DriveFileEntity>` is set to that of `localfile <FileName>`
* `preservefiletimes` - `modifiedTime` of `<DriveFileEntity>` retains its current value
### 6.66.12
Upgraded to Python 3.12.1 where possible.
Updated all drive commands to handle the following error:
```
ERROR: 401: Active session is invalid. Error code: 4 - authError
```
This is due to the Drive SDK API being disabled in the user's OU.
* See: https://support.google.com/a/answer/6105699
### 6.66.11
Fixed/improved handling of shortcuts in `gam <UserTypeEntity> transfer drive`.

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

@@ -334,18 +334,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.66.11 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.71.06 - 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.2 64-bit final
MacOS Sonoma 14.2.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 +350,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 +360,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 +390,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
@@ -1002,18 +1006,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.66.11 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.71.06 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
Ross Scroggs <ross.scroggs@gmail.com>
Python 3.12.0 64-bit final
Python 3.12.2 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 +1022,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 +1032,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 +1062,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

@@ -24,7 +24,8 @@
| 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 |
@@ -46,11 +47,13 @@
| 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 |

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>)*"
@@ -97,6 +99,7 @@
<TasklistIDTaskIDList> ::= "<TasklistIDTaskID>(,<TasklistIDTaskID>)*"
<ThreadIDList> ::= "<ThreadID>(,<ThreadID>)*"
<TimeList> ::= "<Time>(,<Time>)*"
<URLList> ::= "<URL>(,<URL>)*"
<UserList> ::= "<UserItem>(,<UserItem>)*"
<YouTubeChannelIDList> ::= "<YouTubeChannelID>(,<YouTubeChannelID>)*"
```

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)
@@ -237,6 +238,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

@@ -13,3 +13,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

@@ -58,7 +58,7 @@ gam report <ActivityApplicationName> [todrive <ToDriveAttributes>*]
[filtertime.* <Time>] [filter|filters <String>]
[event|events <EventNameList>] [ip <String>]
[groupidfilter <String>]
[maxactivities <Number>] [maxresults <Number>]
[maxactivities <Number>] [maxevents <Number>] [maxresults <Number>]
[countsonly [summary] [eventrowfilter]]
(addcsvdata <FieldName> <String>)* [shownoactivities]
```
@@ -100,9 +100,15 @@ Limit to those users that are a member of at least one of a list of groups.
Limit the total number of activites.
* `maxactivities <Number>`
Limit the number of events per activity; this only applies when `countsonly` is False.
* `maxevents <Number>`
Limit the number of activities downloaded per API call; infrequently used.
* `maxresults <Number>`
Setting options `maxactivities 1 maxevents 1 maxresults 1` can be used to as efficiently as possible
show the most recent activity/event; this can be useful when reporting drive activity for individual drive files.
Add additional columns of data from the command line to the output.
* `addcsvdata <FieldName> <String>`
@@ -333,6 +339,9 @@ Select the users for whom information is desired.
* `showorgunit` - Add a column labelled `orgUnitPath` to the output; an additional API call is made to get the email addresses of the users in `<OrgUnitPath>`
* `select <UserTypeEntity>` - A selected collection of users, e.g., `select group staff@domain.com`; there is one API call per user
By default, when `user all` is specified (or no user specification in supplied), GAM backs up looking for data with a (basically) random user. If the randaom
doesn't have any data, the command reports that no data was found. Use `allverifyuser <UserItem>` to specify a specific user to use to search for data.
Specify the report date; the default is today's date.
* `date <Date>` - A single date; there is one API call
* `range <Date> <Date>` - A range of dates; there is an API call per date

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>

View File

@@ -8,6 +8,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)
@@ -245,6 +246,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

@@ -54,8 +54,7 @@ Added the option `mailbox <EmailAddress>` to `gam sendemail` to allow specifying
(message|textmessage|htmlmessage <String>)|
(file|textfile|htmlfile <FileName> [charset <Charset>])|
(gdoc|ghtml <UserGoogleDoc>)|
(gcsdoc|gcshtml <StorageBucketObjectName>)|
(emlfile <FileName>)
(gcsdoc|gcshtml <StorageBucketObjectName>)
```
```
<Time> ::=
@@ -211,7 +210,7 @@ gam sendemail [recipient|to] <RecipientEntity>
[cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage]
[subject <String>]
[<MessageContent>] (replace <Tag> <String>)*
[html [<Boolean>]] (attach <FileName> [charset <CharSet>])*
[html [<Boolean>]] (attach <FileName> [charset <Charset>])*
(embedimage <FileName> <String>)*
[newuser <EmailAddress> firstname|givenname <String> lastname|familyname <string> password <Password>]
(<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)*
@@ -242,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.
@@ -267,7 +266,7 @@ gam sendemail [recipient|to] <RecipientEntity> [from <EmailAddress>]
[cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage]
[subject <String>]
[<MessageContent>] (replace <Tag> <String>)*
[html [<Boolean>]] (attach <FileName> [charset <CharSet>])*
[html [<Boolean>]] (attach <FileName> [charset <Charset>])*
(embedimage <FileName> <String>)*
[newuser <EmailAddress> firstname|givenname <String> lastname|familyname <string> password <Password>]
(<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)*
@@ -298,7 +297,7 @@ If `message` is not specified, the following value will be used:
* `Hello #givenname# #familyname#,\n\nYou have a new account at #domain#\nAccount details:\n\nUsername\n#user#\n\nPassword\n#password#\n\n
Start using your new account by signing in at\nhttps://www.google.com/accounts/AccountChooser?Email=#user#&continue=https://apps.google.com/user/hub\n`
If you want a language/organization specific message, use a template file: `message file <FileName> [charset <CharSet>]`
If you want a language/organization specific message, use a template file: `message file <FileName> [charset <Charset>]`
The `<SMTPDateHeader> <Time>` argument requires `<Time>` values which will be converted to RFC2822 dates. If you have these headers with values that
are not in `<Time>` format, use the argument `header <SMTPDateHeader> <String>`.
@@ -313,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;
@@ -346,7 +345,7 @@ gam <UserTypeEntity> sendemail recipient|to <RecipientEntity>
[cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage]
[subject <String>]
[<MessageContent>] (replace <Tag> <String>)*
[html [<Boolean>]] (attach <FileName> [charset <CharSet>])*
[html [<Boolean>]] (attach <FileName> [charset <Charset>])*
(embedimage <FileName> <String>)*
[newuser <EmailAddress> firstname|givenname <String> lastname|familyname <string> password <Password>]
(<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)*
@@ -377,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
```
@@ -386,7 +385,7 @@ gam <UserTypeEntity> sendemail [from <EmailAddress>]
[cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage]
[subject <String>]
[<MessageContent>] (replace <Tag> <String>)*
[html [<Boolean>]] (attach <FileName> [charset <CharSet>])*
[html [<Boolean>]] (attach <FileName> [charset <Charset>])*
(embedimage <FileName> <String>)*
[newuser <EmailAddress> firstname|givenname <String> lastname|familyname <string> password <Password>]
(<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)*
@@ -419,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

@@ -28,6 +28,7 @@
* https://developers.google.com/drive/api/v3/reference/teamdrives/list
* https://support.google.com/a/answer/7374057
* https://workspaceupdates.googleblog.com/2022/05/shared-drives-in-organizational-units-open-beta.html
* https://support.google.com/a/users/answer/7338880
## Query documentation
* https://developers.google.com/drive/api/v3/search-shareddrives
@@ -67,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

@@ -130,8 +130,11 @@
relation.<RelationSubfieldName>.<RelationSubfieldName>.<String>|
sshkeys.<SSHkeysSubfieldName>.<SSHkeysSubfieldName>.<String>|
website.<WebsiteSubfieldName>.<WebsiteSubfieldName>.<String>
<UserReplacementField> ::=
photourl
<Tag> ::= <String>
<UserReplacement> ::=
(field:<UserReplacementField>)|
(field:<UserReplacementFieldSubfield>)|
(field:<UserReplacementFieldSubfieldMatchSubfield>)|
(schema:<SchemaName>.<FieldName>)|
@@ -145,7 +148,7 @@ This command allows simple text replacement in the message.
```
gam sendemail <EmailAddressEntity> [from <UserItem>] [replyto <EmailAddress>]
[cc <EmailAddressEntity>] [bcc <EmailAddressEntity>] [singlemessage [<Boolean>]]
[subject <String>] [message <String>|(file <FileName> [charset <CharSet>])]
[subject <String>] [message <String>|(file <FileName> [charset <Charset>])]
(replace <Tag> <String>)* [html [<Boolean>]] (attach <FileName>)*
```
* Every instance of `{Tag}` in the message will be replaced by `<String>`.
@@ -162,30 +165,30 @@ These commands allow simple text replacement in the message/signature as well as
```
gam create user <EmailAddress> <UserAttribute>*
[notify <EmailAddress>] [subject <String>]
[message <String>|(file <FileName> [charset <CharSet>])] [html [<Boolean>]]
[message <String>|(file <FileName> [charset <Charset>])] [html [<Boolean>]]
(replace <Tag> <UserReplacement>)*
gam update user <UserItem> <UserAttribute>
[updateprimaryemail <RegularExpression> <EmailReplacement>]
[updateoufromgroup <FileName> [charset <CharSet>]
[updateoufromgroup <FileName> [charset <Charset>]
[columndelimiter <Character>] [noescapechar <Boolean>] [quotechar <Character>]
[fields <FieldNameList>] [keyfield <FieldName>] [datafield <FieldName>]]
[clearschema <SchemaName>] [clearschema <SchemaName>.<FieldName>]
[createifnotfound] [notify <EmailAddress>] [subject <String>]
[message <String>|(file <FileName> [charset <CharSet>])] [html [<Boolean>]]
[message <String>|(file <FileName> [charset <Charset>])] [html [<Boolean>]]
(replace <Tag> <UserReplacement>)*
gam <UserTypeEntity> draft message (<SMTPDateHeader> <Time>)*
(<SMTPHeader> <String>)* (header <String> <String>)*
(addlabel <LabelName>)* [labels <LabelNameList>]
(textmessage|message <String>)|(textfile|file <FileName> [charset <CharSet>])
(htmlmessage <String>)|(htmlfile <FileName> [charset <CharSet>])
(replace <Tag> <UserReplacement>)* (attach <FileName> [charset <CharSet>])*
(textmessage|message <String>)|(textfile|file <FileName> [charset <Charset>])
(htmlmessage <String>)|(htmlfile <FileName> [charset <Charset>])
(replace <Tag> <UserReplacement>)* (attach <FileName> [charset <Charset>])*
gam <UserTypeEntity> import message (<SMTPDateHeader> <Time>)*
(<SMTPHeader> <String>)*
(header <String> <String>)*
(addlabel <LabelName>)*
(textmessage <String>)|(textfile <FileName> [charset <CharSet>])
(htmlmessage <String>)|(htmlfile <FileName> [charset <CharSet>])
(textmessage <String>)|(textfile <FileName> [charset <Charset>])
(htmlmessage <String>)|(htmlfile <FileName> [charset <Charset>])
(replace <Tag> <UserReplacement>)* (attach <FileName>)*
[deleted [<Boolean>]] [nevermarkspam [<Boolean>]]
[processforcalendar [<Boolean>]]
@@ -194,19 +197,19 @@ gam <UserTypeEntity> insert message
(<SMTPHeader> <String>)*
(header <String> <String>)*
(addlabel <LabelName>)*
(textmessage <String>)|(textfile <FileName> [charset <CharSet>])
(htmlmessage <String>)|(htmlfile <FileName> [charset <CharSet>])
(textmessage <String>)|(textfile <FileName> [charset <Charset>])
(htmlmessage <String>)|(htmlfile <FileName> [charset <Charset>])
(replace <Tag> <UserReplacement>)* (attach <FileName>)*
[deleted [<Boolean>]]
gam <UserTypeEntity> [create|add] sendas <EmailAddress> <String>
[signature|sig <String>|(file <FileName> [charset <CharSet>])
[signature|sig <String>|(file <FileName> [charset <Charset>])
(replace <Tag> <UserReplacement>)*]
[html [<Boolean>]] [replyto <EmailAddress>]
[default] [treatasalias <Boolean>]
gam <UserTypeEntity> update sendas <EmailAddress>
[name <String>] [signature|sig <String>|(file <FileName> [charset <CharSet>])
[name <String>] [signature|sig <String>|(file <FileName> [charset <Charset>])
(replace <Tag> <UserReplacement>)*]
[html [<Boolean>]] [replyto <EmailAddress>] [default] [treatasalias <Boolean>]
@@ -216,7 +219,7 @@ gam <UserTypeEntity> signature|sig <String>|(file <FileName> [charset <Charset>]
[default] [primary] [treatasalias <Boolean>]
gam <UserTypeEntity> vacation <TrueValues> subject <String>
[message <String>|(file <FileName> [charset <CharSet>])]
[message <String>|(file <FileName> [charset <Charset>])]
(replace <Tag> <UserReplacement>)*
[html [<Boolean>]] [contactsonly [<Boolean>]] [domainonly [<Boolean>]]
[startdate <Date>|Started] [enddate <Date>|NotSpecified]

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

@@ -592,6 +592,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 +720,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 +771,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

@@ -59,7 +59,7 @@ Google requires that you have a Chat Bot configured in order to use the Chat API
<ChatContent> ::=
((text <String>)|
(textfile <FileName> [charset <CharSet>])|
(textfile <FileName> [charset <Charset>])|
(gdoc <UserGoogleDoc>)|
(gcsdoc <StorageBucketObjectName>))
@@ -82,7 +82,8 @@ Google requires that you have a Chat Bot configured in order to use the Chat API
```
gam <UserTypeEntity> create chatspace
[type <ChatSpaceType>]
[externalusersrallowed <Boolean>]
[restricted|(audience <String>)]
[externalusersallowed <Boolean>]
[members <UserTypeEntity>]
[displayname <String>]
[description <String>] [guidelines <String>]
@@ -117,20 +118,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.
@@ -301,7 +308,7 @@ gam <UserTypeEntity> create chatmessage <ChatSpace>
```
Specify the text of the message: `<ChatContent>`
* `text <String>` - The message is `<String>`
* `textfile <FileName> [charset <CharSet>]` - The message is read from a local file
* `textfile <FileName> [charset <Charset>]` - The message is read from a local file
* `gdoc <UserGoogleDoc>` - The message is read from a Google Doc.
* `gcsdoc <StorageBucketObjectName>` - The message is read from a Google Cloud Storage file.
@@ -344,7 +351,7 @@ gam <UserTypeEntity> update chatmessage name <ChatMessage>
```
Specify the text of the message: `<ChatContent>`
* `text <String>` - The message is `<String>`
* `textfile <FileName> [charset <CharSet>]` - The message is read from a local file
* `textfile <FileName> [charset <Charset>]` - The message is read from a local file
* `gdoc <UserGoogleDoc>` - The message is read from a Google Doc.
* `gcsdoc <StorageBucketObjectName>` - The message is read from a Google Cloud Storage file.

View File

@@ -55,7 +55,7 @@ As of version `6.14.04`, There is now support for managing "Other Contacts".
<NoteContent> ::=
((<String>)|
(file <FileName> [charset <CharSet>])|
(file <FileName> [charset <Charset>])|
(gdoc <UserGoogleDoc>)|
(gcsdoc <StorageBucketObjectName>)
```

View File

@@ -16,6 +16,7 @@
## API documentation
* https://developers.google.com/drive/api/v3/reference/files
* https://support.google.com/a/answer/7374057
* https://support.google.com/a/users/answer/7338880
## Definitions
* [`<DriveFileEntity>`](Drive-File-Selection)
@@ -66,11 +67,13 @@ gam <UserTypeEntity> copy drivefile <DriveFileEntity>
[summary [<Boolean>]] [showpermissionmessages [<Boolean>]]
[<DriveFileParentAttribute>]
[mergewithparent [<Boolean>]] [recursive [depth <Number>]]
<DriveFileCopyAttribute>*
[skipids <DriveFileEntity>]
[copysubfiles [<Boolean>]] [filenamematchpattern <RegularExpression>]
[filemimetype [not] <MimeTypeList>]
[copysubfilesownedby any|me|others]
[copysubfolders [<Boolean>]] [foldernamematchpattern <RegularExpression>]
[copysubshortcuts [<Boolean>]] [shortcutnamematchpattern <RegularExpression>]
<DriveFileCopyAttribute>*
[duplicatefiles overwriteolder|overwriteall|duplicatename|uniquename|skip]
[duplicatefolders merge|duplicatename|uniquename|skip]
[copiedshortcutspointtocopiedfiles [<Boolean>]]
@@ -124,6 +127,9 @@ The `depth <Number>` argument controls which files or folders within the top fol
* `depth 0` - the files or folders in the top folder are copied, no descendants of folders are copied.
* `depth N` - the files and folders within the top folder and those files and folders N levels below the top folder are copied.
### This option handles special cases where you want to prevent selected files/folders from being copied.
* `skipids <DriveFileEntity>` - Do not copy files/folders with the specified IDs.
### By default, when recursively copying a top folder, all sub files, folders and shortcuts are copied, subject to the `depth` option.
You can specify whether sub files, folders and shortcuts are copied. If sub folders are not copied, their contents are not copied.
* `copysubfiles false` - Sub files are not copied
@@ -148,6 +154,11 @@ You can specify `<RegularExpression>` patterns that limit the items copied based
* `foldernamematchpattern <RegularExpression>` - Only folders whose name matches `<RegularExpression>` are copied
* `shortcutnamematchpattern <RegularExpression>` - Only shortcuts whose name matches `<RegularExpression>` are copied
### By default, when copying sub files, all files, regardless of ownership, are copied.
* `copysubfilesownedby any` - All files, regardless of ownership, are copied.
* `copysubfilesownedby me` - Only files owned by `<UserTypeEntity>` are copied.
* `copysubfilesownedby others` - Only files not owned by `<UserTypeEntity>` are copied.
### Specify a new name for the file/folder
* `newfilename <DriveFileName>` - The copied file/folder will be named `<DriveFileName>`
* If `stripnameprefix <String>` is specified, `<String>` will be stripped from the front of `<DriveFileName>`
@@ -197,6 +208,7 @@ In previous versions, copying shortcuts caused an error because shortcuts can't
If a shortcut in the source structure points to a file/folder that is not in the source structure:
* The shortcut is re-created to point to the original file/folder.
If a shortcut in the source structure points to a file/folder that is in the source structure:
* `copiedshortcutspointtocopiedfiles` omitted or `copiedshortcutspointtocopiedfiles true` - The shortcut is re-created to point to the copied file/folder.
* `copiedshortcutspointtocopiedfiles false` - The shortcut is re-created to point to the original file/folder.

View File

@@ -34,6 +34,7 @@
## API documentation
* https://developers.google.com/drive/api/v3/reference/files
* https://support.google.com/a/answer/6105699
## Definitions
* [`<DriveFileEntity>`](Drive-File-Selection)
@@ -266,9 +267,10 @@
<DriveOwnersSubfieldName>|
parents|
<DriveParentsSubfieldName>|
permissionids|
permissiondetails|
permissions|
<DrivePermissionsSubfieldName>|
permissionids|
properties|
quotabytesused|quotaused|
resourcekey|
@@ -571,7 +573,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#'"
```
@@ -1004,7 +1006,7 @@ gam <UserTypeEntity> print|show filelist [todrive <ToDriveAttribute>*]
[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]

View File

@@ -25,7 +25,9 @@
* https://developers.google.com/drive/api/v3/ref-single-parent
* https://developers.google.com/drive/api/v3/shared-drives-diffs
* https://developers.google.com/drive/api/v3/shortcuts
* https://support.google.com/a/answer/6105699
* https://support.google.com/a/answer/7374057
* https://support.google.com/a/users/answer/7338880
* https://developers.google.com/docs/api/reference/rest
## Definitions
@@ -462,7 +464,7 @@ gam <UserTypeEntity> update drivefile <DriveFileEntity> [copy] [returnidonly|ret
[stripnameprefix <String>]
<DriveFileUpdateAttribute>*
[(gsheet|csvsheet <SheetEntity> [clearfilter])|(addsheet <String>)]
[charset <CharSet>] [columndelimiter <Character>]
[charset <Charset>] [columndelimiter <Character>]
```
By default, an existing file's attributes are updated.
@@ -492,7 +494,8 @@ From the Google Drive API documentation.
By default, Google assigns the current time to the attribute `modifiedTime`; you can assign your own value
with `modifiedtime <Time>`.
The option `preservefiletimes`, when used with `localfile <FileName>`, will set the `modifiedTime` attribute from the local file.
* `preservefiletimes localfile <FileName>` - `modifiedTime` of `<DriveFileEntity>` is set to that of `localfile <FileName>`
* `preservefiletimes` - `modifiedTime` of `<DriveFileEntity>` retains its current value
These are the naming rules when updating from a local file:
* `update drivefile drivefilename "GoogleFile.csv" localfile "NewLocalFile.csv"` - Google Drive file "GoogleFile.csv" is renamed "NewLocalFile.csv"
@@ -513,7 +516,7 @@ You can update a specific sheet within a Google spreadsheet or add a new sheet t
* `gsheet|csvsheet id:<Number>` - Specify a sheet by ID in a Google Sheets file to be updated
* `clearfilter` - When updating a sheet, this option causes GAM to clear the spreadsheet basic filter so hidden data will be overwritten
* `addsheet <String>` - Specify a sheet name to be added to the Google Sheets file
* `charset <CharSet>` - Specify the character set of the local file; if not specified, the value of `charset` from `gam.cfg` will be used
* `charset <Charset>` - Specify the character set of the local file; if not specified, the value of `charset` from `gam.cfg` will be used
* `columndelimiter <Character>` - Columns are separated by `<Character>`; if not specified, the value of `csv_input_column_delimiter` from `gam.cfg` will be used
If you want the Google spreadsheet to retain its name, specify: `retainname localfile LocalFile.csv`.

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

@@ -107,9 +107,9 @@ By default, files in the trash are not transferred.
Specify order of file processing.
* `(orderby <DriveOrderByFieldName> [ascending|descending])*`
These options handle special cases where you want to prevent ownership from being transferred for selected files.
* `skipids <DriveFileEntity>` - Do not transfer ownership for files with the specified IDs.
* `skipusers <UserTypeEntity>` - Do not transfer ownership for files owned by the specified users.
These options handle special cases where you want to prevent ownership from being transferred for selected files/folders.
* `skipids <DriveFileEntity>` - Do not transfer ownership for files/folders with the specified IDs.
* `skipusers <UserTypeEntity>` - Do not transfer ownership for files/folders owned by the specified users.
By default, only files owned by users in the same domain as the claiming user have their ownership transferred.
* `subdomains <DomainNameEntity>` - Transfer ownership for files in the selected sub-domains.

View File

@@ -7,6 +7,9 @@
- [Manage file permissions/sharing](#manage-file-permissionssharing)
- [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
* https://developers.google.com/drive/api/v3/ref-single-parent
@@ -303,3 +306,37 @@ Inspect Permissions.csv, verify that you want to proceed.
```
gam config csv_input_row_drop_filter "permission.role:regex:(owner)|(organizer)" csv ./Permissions.csv gam user "~Owner" delete drivefileacl "~id" "id:~~permission.id~~"
```
## Change shares to User1 to shares to User2
```
# Get files shared to User1
gam redirect csv ./FilesSharedWithU1.csv user user1@domain.com print filelist choose sharedwithme fields id,name,mimetype,owners.emailaddress
# For each of these files, get the sharing settings for U1
gam redirect csv ./FilesSharedWithU1Settings.csv multiprocess csv FilesSharedWithU1.csv gam user "~owners.0.emailAddress" print drivefileacls "~id" pm emailaddress "~Owner" em
# For each of these files, delete the share to User1
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
```

View File

@@ -37,8 +37,8 @@ gam <UserTypeEntity> transfer drive <UserItem> [select <DriveFileEntity>]
By default, all of the source users files will be transferred except those in the trash. If you want to transfer a subset of
the source users files, use the `select <DriveFileEntity>` option.
This option handles special cases where you want to prevent selected files from being transferred.
* `skipids <DriveFileEntity>` - Do not transfer files with the specified IDs.
This option handles special cases where you want to prevent selected files/folders from being transferred.
* `skipids <DriveFileEntity>` - Do not transfer files/folders with the specified IDs.
You can specify the access that the source user retains to the files that it owns.
If no option is specified, the source user retains no access to the transferred files.

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

@@ -171,7 +171,7 @@
(file|textfile|htmlfile <FileName> [charset <Charset>])|
(gdoc|ghtml <UserGoogleDoc>)|
(gcsdoc|gcshtml <StorageBucketObjectName>)|
(emlfile <FileName>)
(emlfile <FileName> [charset <Charset>]))
```
## Message queries with dates
```
@@ -204,21 +204,24 @@ You can also replace ` ` with `-` but it doesn't seem to be required.
* `query "label:Foo -Bar-"` - Select messages with label `Foo (Bar)`
You can have GAM do the substitutions for you with the `matchlabel <LabelName>` option.
* `matchlabel "Foo (Bar)"` is converted to `query "label:Foo -Bar-"`
## Draft messages
Add a draft message to a user's mailbox.
```
gam <UserTypeEntity> draft message
<MessageContent> (replace <Tag> <UserReplacement>)*
(<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)*
(attach <FileName> [charset <CharSet>])*
<MessageContent>
(attach <FileName> [charset <Charset>])*
(embedimage <FileName> <String>)*
```
`<MessageContent>` is the message, there are five ways to specify it:
* `message|textmessage|htmlmessage <String>` - Use `<String>` as the message
* `file|htmlfile <FileName> [charset <CharSet>]` - Read the message from `<FileName>`
* `file|htmlfile <FileName> [charset <Charset>]` - Read the message from `<FileName>`
* `gdoc|ghtml <UserGoogleDoc>` - Read the message from `<UserGoogleDoc>`
* `gcsdoc|gcshtml <StorageBucketObjectName>` - Read the message from the Google Cloud Storage file `<StorageBucketObjectName>`
* `emlfile <FileName>` - Read the message from the EML message file `<FileName>`. SMTP headers specified in the command will replace those in the message file.
* `emlfile <FileName> [charset <Charset>]` - Read the message from the EML message file `<FileName>`. SMTP headers specified in the command will replace those in the message file. The default `chatser` is `ascii`.
The `<SMTPDateHeader> <Time>` argument requires `<Time>` values which will be converted to RFC2822 dates. If you have these headers with values that
are not in `<Time>` format, use the argument `header <SMTPDateHeader> <String>`.
@@ -241,20 +244,20 @@ Your command line will have: `embedimage file1.jpg image1` embedimage file2.jpg
Import a message into a user's mailbox, with standard email delivery scanning and classification similar to receiving via SMTP.
```
gam <UserTypeEntity> import message
<MessageContent> (replace <Tag> <UserReplacement>)*
(<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)*
(addlabel <LabelName>)* [labels <LabelNameList>]
(attach <FileName> [charset <CharSet>])*
<MessageContent>
(attach <FileName> [charset <Charset>])*
(embedimage <FileName> <String>)*
[deleted [<Boolean>]] [checkspam [<Boolean>]] [processforcalendar [<Boolean>]]
```
`<MessageContent>` is the message, there are five ways to specify it:
* `message|textmessage|htmlmessage <String>` - Use `<String>` as the message
* `file|htmlfile <FileName> [charset <CharSet>]` - Read the message from `<FileName>`
* `file|htmlfile <FileName> [charset <Charset>]` - Read the message from `<FileName>`
* `gdoc|ghtml <UserGoogleDoc>` - Read the message from `<UserGoogleDoc>`
* `gcsdoc|gcshtml <StorageBucketObjectName>` - Read the message from the Google Cloud Storage file `<StorageBucketObjectName>`
* `emlfile <FileName>` - Read the message from the EML message file `<FileName>`. SMTP headers specified in the command will replace those in the message.
* `emlfile <FileName> [charset <Charset>]` - Read the message from the EML message file `<FileName>`. SMTP headers specified in the command will replace those in the message. The default `chatser` is `ascii`.
When `emlfile` is not specified:
* If `to` is not specified, it is set to the user email addresses in `<UserTypeEntity>`.
@@ -289,20 +292,20 @@ Your command line will have: `embedimage file1.jpg image1` embedimage file2.jpg
Insert a message into a user's mailbox similar to IMAP APPEND, bypassing most scanning and classification.
```
gam <UserTypeEntity> insert message
<MessageContent> (replace <Tag> <UserReplacement>)*
(<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)*
(addlabel <LabelName>)* [labels <LabelNameList>]
(attach <FileName> [charset <CharSet>])*
<MessageContent>
(attach <FileName> [charset <Charset>])*
(embedimage <FileName> <String>)*
[deleted [<Boolean>]]
```
`<MessageContent>` is the message, there are five ways to specify it:
* `message|textmessage|htmlmessage <String>` - Use `<String>` as the message
* `file|htmlfile <FileName> [charset <CharSet>]` - Read the message from `<FileName>`
* `file|htmlfile <FileName> [charset <Charset>]` - Read the message from `<FileName>`
* `gdoc|ghtml <UserGoogleDoc>` - Read the message from `<UserGoogleDoc>`
* `gcsdoc|gcshtml <StorageBucketObjectName>` - Read the message from the Google Cloud Storage file `<StorageBucketObjectName>`
* `emlfile <FileName>` - Read the message from the EML message file `<FileName>`. SMTP headers specified in the command will replace those in the message file.
* `emlfile <FileName> [charset <Charset>]` - Read the message from the EML message file `<FileName>`. SMTP headers specified in the command will replace those in the message file. The default `chatser` is `ascii`.
When `emlfile` is not specified:
* If `to` is not specified, it is set to the user email addresses in `<UserTypeEntity>`.

View File

@@ -13,6 +13,7 @@
## API documentation
* https://developers.google.com/gmail/api/reference/rest/v1/users.settings.sendAs
* https://developers.google.com/gmail/api/v1/reference/users/settings
* https://support.google.com/a/answer/1710338
## Definitions
* [`<UserTypeEntity>`](Collections-of-Users)
@@ -81,12 +82,14 @@ of the sendas address.
`<SendAsContent>` is the signature, there are four ways to specify it:
* `sig|signature|htmlsig <String>` - Use `<String>` as the signature
* `file|htmlfile <FileName> [charset <CharSet>]` - Read the signature from `<FileName>`
* `file|htmlfile <FileName> [charset <Charset>]` - Read the signature from `<FileName>`
* `gdoc|ghtml <UserGoogleDoc>` - Read the signature from `<UserGoogleDoc>`
* `gcsdoc|gcshtml <StorageBucketObjectName>` - Read the signature from the Google Cloud Storage file `<StorageBucketObjectName>`
The `default` option sets `<EmailAddress>` as the default sendas address for the user.
For `treatasalias`, see: https://support.google.com/a/answer/1710338
You can allow users to send mail through an external SMTP server when configuring a sendas address hosted outside your email domains. You must enable
this capability in Admin Console/Apps/Google Workspace/Gmail/Advanced settings/End User Access/Allow per-user outbound gateways.
@@ -139,12 +142,14 @@ gam <UserTypeEntity> signature|sig
```
`<SignatureContent>` is the signature, there are four ways to specify it:
* `<String>` - Use `<String>` as the signature
* `file|htmlfile <FileName> [charset <CharSet>]` - Read the signature from `<FileName>`
* `file|htmlfile <FileName> [charset <Charset>]` - Read the signature from `<FileName>`
* `gdoc|ghtml <UserGoogleDoc>` - Read the signature from `<UserGoogleDoc>`
* `gcsdoc|gcshtml <StorageBucketObjectName>` - Read the signature from the Google Cloud Storage file `<StorageBucketObjectName>`
The `default` option sets `<EmailAddress>` as the default sendas address for the user.
For `treatasalias`, see: https://support.google.com/a/answer/1710338
When `<UserTypeEntity>` specifies an alias, the `primary` option causes the primary
email address signature rather than the alias signature to be set.
@@ -211,7 +216,7 @@ gam <UserTypeEntity> vacation <Boolean> subject <String>
```
`<VacationMessageContent>` is the vacation message, there are four ways to specify it:
* `message|textmessage|htmlmessage <String>` - Use `<String>` as the vacation message
* `file|htmlfile <FileName> [charset <CharSet>]` - Read the vacation message from `<FileName>`
* `file|htmlfile <FileName> [charset <Charset>]` - Read the vacation message from `<FileName>`
* `gdoc|ghtml <UserGoogleDoc>` - Read the vacation message from `<UserGoogleDoc>`
* `gcsdoc|gcshtml <StorageBucketObjectName>` - Read the vacation message from the Google Cloud Storage file `<StorageBucketObjectName>`

View File

@@ -42,7 +42,7 @@ gam user user@domain.com check serviceaccount
<NoteContent> ::=
((text <String>)|
(textfile <FileName> [charset <CharSet>])|
(textfile <FileName> [charset <Charset>])|
(gdoc <UserGoogleDoc>)|
(gcsdoc <StorageBucketObjectName>)|
<JSONData>)
@@ -90,7 +90,7 @@ gam <UserTypeEntity> create note [title <String>]
```
`<NoteContent>` is the note text, there are four ways to specify it:
* `message|textmessage|htmlmessage <String>` - Use `<String>` as the note text
* `file|htmlfile <FileName> [charset <CharSet>]` - Read the note text from `<FileName>`
* `file|htmlfile <FileName> [charset <Charset>]` - Read the note text from `<FileName>`
* `gdoc|ghtml <UserGoogleDoc>` - Read the note text from `<UserGoogleDoc>`
* `gcsdoc|gcshtml <StorageBucketObjectName>` - Read the note text from the Google Cloud Storage file `<StorageBucketObjectName>`

View File

@@ -15,6 +15,7 @@
- [Display Shared Drive access](#display-shared-drive-access)
- [Display Shared Drive access for specific Shared Drives](#display-shared-drive-access-for-specific-shared-drives)
- [Display Shared Drive access for selected Shared Drives](#display-shared-drive-access-for-selected-shared-drives)
- [Change User1 Shared Drive access to User2](#change-user1-shared-drive-access-to-user2)
- [Display empty folders on a Shared Drive](#display-empty-folders-on-a-shared-drive)
- [Delete empty folders on a Shared Drive](#delete-empty-folders-on-a-shared-drive)
- [Empty the trash on a Shared Drive](#empty-the-trash-on-a-shared-drive)
@@ -25,6 +26,7 @@
* https://developers.google.com/drive/v3/web/manage-teamdrives#managing_team_drives_for_domain_administrators
* https://support.google.com/a/answer/7374057
* https://workspaceupdates.googleblog.com/2022/05/shared-drives-in-organizational-units-open-beta.html
* https://support.google.com/a/users/answer/7338880
## Query documentation
* https://developers.google.com/drive/api/v3/search-shareddrives
@@ -67,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|
@@ -275,12 +281,15 @@ 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.
@@ -299,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.
@@ -428,6 +447,17 @@ This command must be issued by a user with Shared Drive permission role organize
gam <UserTypeEntity> print emptydrivefolders [todrive <ToDriveAttribute>*]
select <SharedDriveEntity>
```
## Change User1 Shared Drive access to User2
```
# Get Shared Drives for User1
gam redirect csv ./U1SharedDrives.csv user user1@domain.com print shareddriveacls pm emailaddress user1@domain.com em oneitemperrow
# For each of those Shared Drives, delete User1 access
gam redirect stdout ./DeleteU1SharedDriveAccess.txt multiprocess redirect stderr stdout gam delete drivefileacl "~id" "~permission.emailAddress"
# For each of those Shared Drives, add User2 with the same role that User1 had
gam redirect stdout ./AddU2SharedDriveAccess.txt multiprocess redirect stderr stdout gam create drivefileacl "~id" user user2@domain.com role "~permission.role"
```
## Delete empty folders on a Shared Drive
This command must be issued by a user with Shared Drive permission role organizer.
```

View File

@@ -38,7 +38,8 @@
- [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
@@ -385,7 +386,7 @@ If subject is not specified, the following value will be used:
`<NotifyMessageContent>` is the message, there are four ways to specify it:
* `message|textmessage|htmlmessage <String>` - Use `<String>` as the message
* `file|htmlfile <FileName> [charset <CharSet>]` - Read the message from `<FileName>`
* `file|htmlfile <FileName> [charset <Charset>]` - Read the message from `<FileName>`
* `gdoc|ghtml <UserGoogleDoc>` - Read the message from `<UserGoogleDoc>`
* `gcsdoc|gcshtml <StorageBucketObjectName>` - Read the message from the Google Cloud Storage file `<StorageBucketObjectName>`
@@ -554,6 +555,14 @@ gam redirect stdout CreateUsers.log multiprocess redirect stderr stdout csv Crea
gam create user "~useremail" firstname "~firstname" lastname "~lastname" ou "~ou" password "~password"
notify "~~notifyemail~~,helpdesk@domain.com"
```
### Create users in bulk in OU with forced 2FA, notify each user and send a second email with backup codes. Log each step.
OU needs to be already set with forced 2FA, else you can't create backup codes in step 2.
These three commands should be run in sequence, as commands two and three are reliant on the previous command being run.
```
gam redirect stdout CreateUsers.log multiprocess redirect stderr stdout csv CreateUsers.csv gam create user "~useremail" firstname "~firstname" lastname "~lastname" ou "~ou" password random notify "~~notifyemail"
gam redirect stdout UpdateUsers.log multiprocess redirect stderr stdout csv CreateUsers.csv gam user ~useremail update backupcodes
gam redirect stdout SendBackupCodes.log multiprocess redirect stderr stdout csv CreateUsers.csv gam user ~useremail print backupcodes | gam csv - gam sendemail "~notifyemail" subject "Backup codes for 2FA login" message "~verificationCodes"
```
## Specify a user's attributes with JSON data
When creating a user, you may have a set of attributes that you'd like to assign to the user without having to specify
@@ -597,7 +606,7 @@ If the mailbox is setup, a zero return code is returned; if the retries are exha
gam update user <UserItem> [ignorenullpassword] <UserAttribute>*
[verifynotinvitable|alwaysevict] [noactionifalias]
[updateprimaryemail <RegularExpression> <EmailReplacement>]
[updateoufromgroup <FileName> [charset <CharSet>]
[updateoufromgroup <FileName> [charset <Charset>]
[columndelimiter <Character>] [noescapechar <Boolean>] [quotechar <Character>]
[fields <FieldNameList>] [keyfield <FieldName>] [datafield <FieldName>]]
[clearschema <SchemaName>] [clearschema <SchemaName>.<FieldName>]
@@ -618,7 +627,7 @@ gam update user <UserItem> [ignorenullpassword] <UserAttribute>*
gam update users <UserTypeEntity> [ignorenullpassword] <UserAttribute>*
[verifynotinvitable|alwaysevict] [noactionifalias]
[updateprimaryemail <RegularExpression> <EmailReplacement>]
[updateoufromgroup <FileName> [charset <CharSet>]
[updateoufromgroup <FileName> [charset <Charset>]
[columndelimiter <Character>] [noescapechar <Boolean>] [quotechar <Character>]
[fields <FieldNameList>] [keyfield <FieldName>] [datafield <FieldName>]]
[clearschema <SchemaName>] [clearschema <SchemaName>.<FieldName>]
@@ -639,7 +648,7 @@ gam update users <UserTypeEntity> [ignorenullpassword] <UserAttribute>*
gam <UserTypeEntity> update users [ignorenullpassword] <UserAttribute>*
[verifynotinvitable|alwaysevict] [noactionifalias]
[updateprimaryemail <RegularExpression> <EmailReplacement>]
[updateoufromgroup <FileName> [charset <CharSet>]
[updateoufromgroup <FileName> [charset <Charset>]
[columndelimiter <Character>] [noescapechar <Boolean>] [quotechar <Character>]
[fields <FieldNameList>] [keyfield <FieldName>] [datafield <FieldName>]]
[clearschema <SchemaName>] [clearschema <SchemaName>.<FieldName>]
@@ -813,7 +822,7 @@ groupz@domain.com,/Path/To/OUz
No update is performed if a user does not belong to any group in the CSV file or belongs to multiple groups in the CSV file.
```
[updateoufromgroup <FileName> [charset <CharSet>]
[updateoufromgroup <FileName> [charset <Charset>]
[columndelimiter <Character>] [noescapechar <Boolean>] [quotechar <Character>]
[fields <FieldNameList>] [keyfield <FieldName>] [datafield <FieldName>]]
```
@@ -972,13 +981,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:
@@ -995,24 +1008,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`
@@ -1039,7 +1057,7 @@ By default, Gam displays fields that only an adminstrator can view.
By default, Gam displays only the primary email address for each user.
* `allfields|basic` - Display all non custom schema fields for each user.
* `full` - Display all non custom schema fields and all custom schema fields for each user.
* `full` - Display all fields including all custom schema fields for each user.
* `<UserFieldName>* [fields <UserFieldNameList>]` - Only display selected fields.
* `schemas|custom all` - Get custom schema information for all schemas.
* `schemas|custom <SchemaNameList>` - Get custom schema information for a selected list of schemas.
@@ -1050,12 +1068,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.
@@ -1198,9 +1238,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

@@ -37,6 +37,7 @@
## Definitions
```
<AttendeeStatus> ::= accepted|declined|needsaction|tentative
<EmailItem> ::= <EmailAddress>|<UniqueID>|<String>
<EmailItemList> ::= "<EmailItem>(,<EmailItem>)*"
<EmailAddressList> ::= "<EmailAddess>(,<EmailAddress>)*"
@@ -52,6 +53,7 @@
<MatterItem> ::= <UniqueID>|<String>
<MatterState> ::= open|closed|deleted
<MatterStateList> ::= "<MatterState>(,<MatterState>)*"
<URLList> ::= "<URL>(,<URL>)*"
<QueryVaultCorpus> ::= <String>
See: https://developers.google.com/vault/reference/rest/v1/matters.holds#CorpusQuery
@@ -192,12 +194,9 @@ This command can be useful for discovering legacy former employee accounts which
gam print vaultcounts [todrive <ToDriveAttributes>*]
matter <MatterItem> corpus mail|groups
(accounts <EmailAddressEntity>) | (orgunit|org|ou <OrgUnitPath>) | everyone
(shareddrives|teamdrives <SharedDriveIDList>) | (rooms <RoomList>)
[scope <all_data|held_data|unprocessed_data>]
[terms <String>] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>] [timezone <TimeZone>]
[excludedrafts <Boolean>]
[includerooms <Boolean>]
[includeshareddrives|includeteamdrives <Boolean>] [driveversiondate <Date>|<Time>]
[wait <Integer>]
```
Check the status of a previous count operation with the name from a previous command.
@@ -210,16 +209,18 @@ gam print vaultcounts [todrive <ToDriveAttributes>*]
## Create Vault Exports
Create a Google Vault export request.
```
gam create vaultexport|export matter <MatterItem> [name <String>] corpus drive|mail|groups|hangouts_chat|voice
gam create vaultexport|export matter <MatterItem> [name <String>] corpus calendar|drive|mail|groups|hangouts_chat|voice
(accounts <EmailAddressEntity>) | (orgunit|org|ou <OrgUnitPath>) | everyone
(shareddrives|teamdrives <SharedDriveIDList>) | (rooms <RoomList>)
[scope <all_data|held_data|unprocessed_data>]
(shareddrives|teamdrives <SharedDriveIDList>) | (rooms <RoomList>) | (sitesurl <URLList>)
[scope all_data|held_data|unprocessed_data]
[terms <String>] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>] [timezone <TimeZone>]
[excludedrafts <Boolean>] [format mbox|pst]
[showconfidentialmodecontent <Boolean>] [usenewexport <Boolean>]
[includerooms <Boolean>]
[covereddata calllogs|textmessages|voicemails]
[locationquery <StringList>] [peoplequery <StringList>] [minuswords <StringList>]
[responsestatuses <AttendeeStatus>(,<AttendeeStatus>)*] [calendarversiondate <Date>|<Time>]
[includeshareddrives <Boolean>] [driveversiondate <Date>|<Time>] [includeaccessinfo <Boolean>]
[includerooms <Boolean>]
[excludedrafts <Boolean>] [format mbox|pst]
[showconfidentialmodecontent <Boolean>] [usenewexport <Boolean>] [exportlinkeddrivefiles <Boolean>]
[covereddata calllogs|textmessages|voicemails]
[region any|europe|us] [showdetails|returnidonly]
```
<MatterItem> specifies the matter name or ID the export should be associated with.
@@ -228,7 +229,8 @@ Specify the name of the export:
* `name <String>` - The export will be named `<String>`
* `default` - The export will be named `GAM <corpus> Export - <Time>`
Specify the corpus of data, this option is required::
Specify the corpus of data, this option is required:
* `calendar`
* `drive`
* `mail`
* `groups`
@@ -241,17 +243,43 @@ Specify the search method, this option is required:
* `everyone` - Search for all accounts in the organization
* `shareddrives|teamdrives <SharedDriveIDList>` - Search for all accounts in the Shared Drives specified in `<SharedDriveIDList>`
* `rooms <RoomList>` - Search in the Room specified in the chat rooms specified in `<RoomList>`
* `sitesurl <URLList>` - Search the published site URLs of new Google Sites
Specify the scope of data to include in the export:
* `all_data` - All available data; this is the default
* `held_data` - Data on Hold
* `unprocessed_data` - Data not processed
You can specify search terms to limit the scope of data:
* `terms <String>` - [Vault search](https://support.google.com/vault/answer/2474474)
Specify time limits on the scope of data:
* `start|starttime <Date>|<Time>` - The start time range for the search query. These timestamps are in GMT and rounded down to the start of the given date.
* `end|endtime <Date>|<Time>` - The end time range for the search query. These timestamps are in GMT and rounded down to the start of the given date.
* `timezone <TimeZone>` - The time zone name. It should be an IANA TZ name, such as "America/Los_Angeles"
For `corpus calendar`, you can specify advanced search options:
* `locationquery <StringList>`
* Matches only those events whose location contains all of the words in the given set.
* If the string contains quoted phrases, this method only matches those events whose location contain the exact phrase.
* Entries in the set are considered in "and".
* Word splitting example: ["New Zealand"] vs ["New","Zealand"] "New Zealand": matched by both "New and better Zealand": only matched by the latter.
* `peoplequery <StringList>`
* Matches only those events whose attendees contain all of the words in the given set.
* Entries in the set are considered in "and".
* `minuswords <StringList>`
* Matches only those events that do not contain any of the words in the given set in title, description, location, or attendees.
* Entries in the set are considered in "or".
* `responsestatuses <AttendeeStatus>(,<AttendeeStatus>)*
* Matches only events for which the custodian gave one of these responses. If the set is empty, there will be no filtering on responses.
* `calendarversiondate <Date>|<Time>`
* Search the current version of the Calendar event, but export the contents of the last version saved before 12:00 AM UTC on the specified date.
* Enter the date in UTC.
For `corpus calendar`, you can specify the format of the exported data:
* `format ics` - Export in ICS format, this is the default
* `format pst` - Export in PST format
For `corpus drive`, you can specify advanced search options:
* `driveversiondate <Date>|<Time>` - Search the versions of the Drive file as of the reference date. These timestamps are in GMT and rounded down to the given date.
* `includeshareddrives False` - Do not include Shared Drives in the search, this is the default.
@@ -265,9 +293,6 @@ For `corpus hangouts_chat` you can specify advanced search options:
* `includerooms False` - Do not include rooms, this is the default
* `includerooms True` - Include rooms
For `corpus mail`, you can specify search terms to limit the scope of data:
* `terms <String>` - [Vault search](https://support.google.com/vault/answer/2474474)
For `corpus mail`, you can specify whether to exclude draft messages:
* `excludedrafts False` - Do not exclude drafts, this is the default
* `excludedrafts True` - Exclude drafts
@@ -280,9 +305,13 @@ 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` and `corpus hangouts_chat`, you can specify the format of the exported data:
For `corpus mail`, `corpus groups`, `corpus hangouts_chat`and `corpus voice`, you can specify the format of the exported data:
* `format mbox` - Export in MBOX format, this is the default
* `format pst` - Export in PST format

View File

@@ -1,44 +1,43 @@
\
# Version and Help
Print the current version of Gam with details
```
gam version
GAMADV-XTD3 6.66.11 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.71.06 - 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.2 64-bit final
MacOS Sonoma 14.2.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.66.11 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.71.06 - 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.2 64-bit final
MacOS Sonoma 14.2.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.66.11 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
GAMADV-XTD3 6.71.06 - 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.2 64-bit final
MacOS Sonoma 14.2.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.2
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.66.11
Latest: 6.71.06
echo $?
1
```
@@ -73,7 +72,7 @@ echo $?
Print the current version number without details
```
gam version simple
6.66.11
6.71.06
```
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.66.11 - https://github.com/taers232c/GAMADV-XTD3
GAM 6.71.06 - 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.2 64-bit final
MacOS Sonoma 14.2.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

@@ -141,6 +141,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

@@ -150,7 +150,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 +213,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 +255,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 +326,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 +430,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
@@ -573,6 +583,13 @@ update_cros_ou_with_id
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".
@@ -1043,7 +1060,7 @@ $gam csv NewGooUsers.csv gam select goo create user ~Email firstname ~FirstName
```
The gam loop command and the select and redirect arguments can be combined to perform powerful operations in a single command line.
```
gam loop (-|<FileName>) [charset <CharSet>] (matchfield|skipfield <FieldName> <RegularExpression>)* gam <GAM argument list>
gam loop (-|<FileName>) [charset <Charset>] (matchfield|skipfield <FieldName> <RegularExpression>)* gam <GAM argument list>
```
Suppose you have the following CSV file, InfoDomains.csv:
```

View File

@@ -22,43 +22,6 @@ If an item contains spaces, it should be surrounded by ".
<FalseValues>= false|off|no|disabled|0
<TrueValues> ::= true|on|yes|enabled|1
<Charset> ::= ascii|latin1|mbcs|utf-8|utf-8-sig|utf-16|<String>
<CalendarColorIndex> ::= <Number in range 1-24>
<CalendarColorName> ::=
amethyst|avocado|banana|basil|birch|blueberry|
cherryblossom|citron|cobalt|cocoa|eucalyptus|flamingo|
grape|graphite|lavender|mango|peacock|pistachio|
pumpkin|radicchio|sage|tangerine|tomato|wisteria|
<ColorHex> ::= "#<Hex><Hex><Hex><Hex><Hex><Hex>"
<ColorNameGoogle> ::=
asparagus|bluevelvet|bubblegum|cardinal|chocolateicecream|denim|desertsand|
earthworm|macaroni|marsorange|mountaingray|mountaingrey|mouse|oldbrickred|
pool|purpledino|purplerain|rainysky|seafoam|slimegreen|spearmint|
toyeggplant|vernfern|wildstrawberries|yellowcab
<ColorNameWeb> ::=
aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|
blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|
cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|
darkgrey|darkgreen|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|
darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|
darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|
firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|
gray|grey|green|greenyellow|honeydew|hotpink|indianred|indigo|ivory|khaki|
lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|
lightgoldenrodyellow|lightgray|lightgrey|lightgreen|lightpink|lightsalmon|
lightseagreen|lightskyblue|lightslategray|lightslategrey|lightsteelblue|
lightyellow|lime|limegreen|linen|magenta|maroon|mediumaquamarine|mediumblue|
mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|
mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|
navajowhite|navy|oldlace|olive|olivedrab|orange|orangered|orchid|
palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|
peru|pink|plum|powderblue|purple|red|rosybrown|royalblue|saddlebrown|salmon|
sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|
slategrey|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|
wheat|white|whitesmoke|yellow|yellowgreen
<ColorName> ::= <ColorNameGoogle>|<ColorNameWeb>
<ColorValue> ::= <ColorName>|<ColorHex>
<DayOfWeek> ::= mon|tue|wed|thu|fri|sat|sun
<BCP47LanguageCode> ::=
ar-sa| # Arabic Saudi Arabia
cs-cz| # Czech Czech Republic
@@ -97,6 +60,43 @@ If an item contains spaces, it should be surrounded by ".
zh-cn| # Chinese China
zh-hk| # Chinese Hong Kong
zh-tw # Chinese Taiwan
<Charset> ::= ascii|latin1|mbcs|utf-8|utf-8-sig|utf-16|<String>
<CalendarColorIndex> ::= <Number in range 1-24>
<CalendarColorName> ::=
amethyst|avocado|banana|basil|birch|blueberry|
cherryblossom|citron|cobalt|cocoa|eucalyptus|flamingo|
grape|graphite|lavender|mango|peacock|pistachio|
pumpkin|radicchio|sage|tangerine|tomato|wisteria|
<ColorHex> ::= "#<Hex><Hex><Hex><Hex><Hex><Hex>"
<ColorNameGoogle> ::=
asparagus|bluevelvet|bubblegum|cardinal|chocolateicecream|denim|desertsand|
earthworm|macaroni|marsorange|mountaingray|mountaingrey|mouse|oldbrickred|
pool|purpledino|purplerain|rainysky|seafoam|slimegreen|spearmint|
toyeggplant|vernfern|wildstrawberries|yellowcab
<ColorNameWeb> ::=
aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|
blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|
cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|
darkgrey|darkgreen|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|
darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|
darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|
firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|
gray|grey|green|greenyellow|honeydew|hotpink|indianred|indigo|ivory|khaki|
lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|
lightgoldenrodyellow|lightgray|lightgrey|lightgreen|lightpink|lightsalmon|
lightseagreen|lightskyblue|lightslategray|lightslategrey|lightsteelblue|
lightyellow|lime|limegreen|linen|magenta|maroon|mediumaquamarine|mediumblue|
mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|
mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|
navajowhite|navy|oldlace|olive|olivedrab|orange|orangered|orchid|
palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|
peru|pink|plum|powderblue|purple|red|rosybrown|royalblue|saddlebrown|salmon|
sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|
slategrey|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|
wheat|white|whitesmoke|yellow|yellowgreen
<ColorName> ::= <ColorNameGoogle>|<ColorNameWeb>
<ColorValue> ::= <ColorName>|<ColorHex>
<DayOfWeek> ::= mon|tue|wed|thu|fri|sat|sun
<EventColorIndex> ::= <Number in range 1-11>
<EventColorName> ::=
banana|basil|blueberry|flamingo|graphite|grape|
@@ -243,6 +243,7 @@ If an item contains spaces, it should be surrounded by ".
101040 |
101043 |
101047 |
101049 |
Google-Apps |
Google-Chrome-Device-Management |
Google-Drive-storage |
@@ -258,56 +259,58 @@ If an item contains spaces, it should be surrounded by ".
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 | 1010400001 | Beyond Corp Enterprise |
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 |
wsbizstanarchived | workspacebusinessstandardarchived | 1010340006 |
wsbizstarter | workspacebusinessstarter | wsbizstart | 1010020027 |
wsbizstarterarchived | workspacebusinessstarterarchived | 1010340005 |
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 |
wsessplus | workspaceessentialsplus | 1010060005 |
wsflw | workspacefrontline | workspacefrontlineworker | 1010020030 |
wsflwstan | workspacefrontlinestan | workspacefrontlineworkerstan | 1010020031
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
## Items built from primitives
@@ -441,6 +444,7 @@ If an item contains spaces, it should be surrounded by ".
<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>
@@ -480,8 +484,7 @@ If an item contains spaces, it should be surrounded by ".
(message|textmessage|htmlmessage <String>)|
(file|textfile|htmlfile <FileName> [charset <Charset>])|
(gdoc|ghtml <UserGoogleDoc>)|
(gcsdoc|gcshtml <StorageBucketObjectName>)|
(emlfile <FileName>)
(gcsdoc|gcshtml <StorageBucketObjectName>)
<MessageID> ::= <String>
<Namespace> ::= <String>
<NotesName> ::= notes/<String>
@@ -584,6 +587,7 @@ If an item contains spaces, it should be surrounded by ".
<StorageObjectName> ::= <String>
<StorageBucketObjectName> ::=
https://storage.cloud.google.com/<StorageBucketName>/<StorageObjectName>|
https://storage.googleapis.com/<StorageBucketName>/<StorageObjectName>|
gs://<StorageBucketName>/<StorageObjectName>|
<StorageBucketName>/<StorageObjectName>
<Tag> ::= <String>
@@ -598,6 +602,7 @@ If an item contains spaces, it should be surrounded by ".
<Title> ::= <String>
<ToDriveAttribute> ::=
(tdaddsheet [<Boolean>])|
(tdalert <EmailAddress>)*|
(tdbackupsheet (id:<Number>)|<String>)|
(tdcellnumberformat text|number)|
(tdcellwrap clip|overflow|wrap)|
@@ -605,20 +610,23 @@ If an item contains spaces, it should be surrounded by ".
(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>)|
(tdsubject <String>)|
([tdsheetdaysoffset <Number>] [tdsheethoursoffset <Number>])|
(tdtimestamp [<Boolean>] [tdtimeformat <String>]
[tddaysoffset <Number>] [tdhoursoffset <Number>])|
([tddaysoffset <Number>] [tdhoursoffset <Number>])|
(tdtimezone <TimeZone>)|
(tdtitle <String>)|
(tdupdatesheet [<Boolean>])|
@@ -642,6 +650,7 @@ If an item contains spaces, it should be surrounded by ".
<AssetTagList> ::= "<AssetTag>(,<AssetTag>)*"
<CalendarACLScopeList> ::= "<CalendarACLScope>(,<CalendarACLScope>)*"
<CalendarList> ::= "<CalendarItem>(,<CalendarItem>)*"
<ChatSpaceList> ::= "<ChatSpace>(,<ChatSpace>)*"
<CIGroupAliasList> ::= "<CIGroupAlias>(,<CIGroupAlias>)*"
<CIGroupTypeList> ::= "<CIGroupType>(,<CIGroupType>)*"
<ClassroomInvitationIDList> ::= "<ClassroomInvitationID>(,<ClassroomInvitationID>)*"
@@ -665,6 +674,7 @@ If an item contains spaces, it should be surrounded by ".
<DeviceUserList> ::= "<DeviceUserID>(,<DeviceUserID>)*"
<DomainNameList> ::= "<DomainName>(,<DomainName>)*"
<DriveFileACLRoleList> ::= "<DriveFileACLRole>(,<DriveFileACLRole>)*"
<DriveFileACLTypeList> ::= "<DriveFileACLType>(,<DriveFileACLType>)*"
<DriveFileList> ::= "<DriveFileItem>(,<DriveFileItem>)*"
<DriveFilePermissionList> ::= "<DriveFilePermission>(,<DriveFilePermission>)*"
<DriveFilePermissionIDList> ::= "<DriveFilePermissionID>(,<DriveFilePermissionID>)*"
@@ -673,6 +683,7 @@ If an item contains spaces, it should be surrounded by ".
<DriveFolderNameList> ::= "<DriveFolderName>(,<DriveFolderName>)*"
<DriveLabelIDList> ::= "<DriveLabelID>(,<DriveLabelID>)*"
<DriveLabelNameList> ::= "<DriveLabelName>(,<DriveLabelName>)*"
<DriveLabelPermissionNameList> ::= "<DriveLabelPermissionName>(,<DriveLabelPermissionName>)*"
<DriveLabelFieldIDList> ::= "<DriveLabelFieldID>(,<DriveLabelFieldID>)*"
<DriveLabelSelectionIDList> ::= "<DriveLabelSelectionID>(,<DriveLabelSelectionID>)*"
<EmailAddressList> ::= "<EmailAddress>(,<EmailAddress>)*"
@@ -728,6 +739,7 @@ If an item contains spaces, it should be surrounded by ".
<TasklistIDTaskIDList> ::= "<TasklistIDTaskID>(,<TasklistIDTaskID>)*"
<ThreadIDList> ::= "<ThreadID>(,<ThreadID>)*"
<TimeList> ::= "<Time>(,<Time>)*"
<URLList> ::= "<URL>(,<URL>)*"
<UserList> ::= "<UserItem>(,<UserItem>)*"
<YouTubeChannelIDList> ::= "<YouTubeChannelID>(,<YouTubeChannelID>)*"
@@ -1094,6 +1106,8 @@ Specify a collection of items by directly specifying them; the item type is dete
<FileSelector> | <CSVFileSelector> | <CSVkmdSelector> | <CSVDataSelector>
<DriveLabelNameEntity> ::=
<DriveLabelNameList> | <FileSelector> | <CSVFileSelector> | <CSVDataSelector>
<DriveLabelPermissionNameEntity> ::=
<DriveLabelPermissionNameList> | <FileSelector> | <CSVFileSelector> | <CSVDataSelector>
<EmailAddressEntity> ::=
<EmailAddressList> | <FileSelector> | <CSVFileSelector> | <CSVkmdSelector> | <CSVDataSelector>
<FilterIDEntity> ::=
@@ -1239,7 +1253,7 @@ For redirect csv, the optional arguments must appear in the order shown.
<Redirect> ::=
redirect csv <FileName> [multiprocess] [append] [noheader] [charset <Charset>]
[columndelimiter <Character>] [noescapechar <Boolean>] [quotechar <Character>]
[timestampcolumn <String>]
[sortheaders <StringList>] [timestampcolumn <String>]
[todrive <ToDriveAttribute>*] |
redirect stdout <FileName> [multiprocess] [append] |
redirect stdout null [multiprocess] |
@@ -1287,13 +1301,13 @@ gam tbatch <BatchContent> [showcmds [<Boolean>]]
gam csv <CSVLoopContent> [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 <CSVLoopContent> [warnifnodata]
[columndelimiter <Character>] [noescapechar <Boolean>] [quotechar <Character>] [fields <FieldNameList>]
(matchfield|skipfield <FieldName> <RegularExpression>)* [showcmds [<Boolean>]]
[maxrows <Integer>]
[skiprows <Integer>] [maxrows <Integer>]
gam <GAMArgumentList>
You can make substitutions in <GAMArgumentList> with values from the CSV file.
@@ -1457,7 +1471,7 @@ gam print alertfeedback [todrive <ToDriveAttribute>*] [alert <AlertID>] [filter
gam create|add alias|aliases <EmailAddressEntity> user|group|target <UniqueID>|<EmailAddress>
[verifynotinvitable]
gam update alias|aliases <EmailAddressEntity> user|group|target <UniqueID>|<EmailAddress>
[notargetverify]
[notargetverify] [waitafterdelete <Integer>]
gam delete alias|aliases [user|group|target] <EmailAddressEntity>
gam remove aliases|nicknames <EmailAddress> user|group <EmailAddressEntity>
gam <UserTypeEntity> delete alias|aliases
@@ -2164,8 +2178,10 @@ gam print chromehistory releases [todrive <ToDriveAttribute>*]
<CrOSAction> ::=
deprovision_different_model_replace|
deprovision_different_model_replacement|
deprovision_retiring_device|
deprovision_same_model_replace|
deprovision_same_model_replacement|
deprovision_upgrade_transfer|
disable|
reenable|
@@ -2173,7 +2189,9 @@ gam print chromehistory releases [todrive <ToDriveAttribute>*]
pre_provisioned_reenable
gam update cros <CrOSEntity> action <CrOSAction> [acknowledge_device_touch_requirement]
[actionbatchsize <Integer>]
gam <CrOSTypeEntity> update action <CrOSAction> [acknowledge_device_touch_requirement]
[actionbatchsize <Integer>]
<CrOSCommand>
reboot|
@@ -2291,6 +2309,7 @@ gam print cros [todrive <ToDriveAttribute>*]
[timerangeorder ascending|descending] [showdvrsfp]
[sortheaders]
[formatjson [quotechar <Character>]]
[showitemcountonly]
Print fields for specified CrOS devices.
@@ -2303,6 +2322,7 @@ gam print cros [todrive <ToDriveAttribute>*] select <CrOSTypeEntity>
[timerangeorder ascending|descending] [showdvrsfp]
[sortheaders]
[formatjson [quotechar <Character>]]
[showitemcountonly]
gam <CrOSTypeEntity> print cros [todrive <ToDriveAttribute>*]
[orderby <CrOSOrderByFieldName> [ascending|descending]]
@@ -2313,6 +2333,7 @@ gam <CrOSTypeEntity> print cros [todrive <ToDriveAttribute>*]
[timerangeorder ascending|descending] [showdvrsfp]
[sortheaders]
[formatjson [quotechar <Character>]]
[showitemcountonly]
The first column will always be deviceId; the remaining field names will be sorted if allfields, basic, full or sortheaders is specified;
otherwise, the remaining field names will appear in the order specified.
@@ -2475,7 +2496,7 @@ gam print crostelemetry [todrive <ToDriveAttribute>*]
gam create chromepolicyimage <ChromePolicyImageSchemaName> <FileName>
gam update chromepolicy
gam update chromepolicy [convertcrnl]
(<SchemaName> ((<Field> <Value>)+ | <JSONData>))+
ou|org|orgunit <OrgUnitItem> [(printerid <PrinterID>)|(appid <AppID>)]
gam delete chromepolicy
@@ -2868,7 +2889,7 @@ gam course <CourseID> delete topic <CourseTopicID>
gam course <CourseID> create|add teachers [makefirstteacherowner] <UserItem>
gam course <CourseID> create|add students <UserItem>
gam course <CourseID> delete|remove teachers|students <UserItem>
gam course <CourseID> delete|remove teachers|students [owneraccess] <UserItem>
gam course <CourseID> clear teachers|students
gam course <CourseID> sync teachers [addonly|removeonly] [makefirstteacherowner] <UserTypeEntity>
gam course <CourseID> sync students [addonly|removeonly] <UserTypeEntity>
@@ -2881,15 +2902,17 @@ gam courses <CourseEntity> delete topic <CourseTopicIDEntity>
gam courses <CourseEntity> create|add teachers [makefirstteacherowner] <UserTypeEntity>
gam courses <CourseEntity> create|add students <UserTypeEntity>
gam courses <CourseEntity> delete|remove teachers|students <UserTypeEntity>
gam courses <CourseEntity> delete|remove teachers|students [owneraccess] <UserTypeEntity>
gam courses <CourseEntity> clear teachers|students
gam courses <CourseEntity> sync teachers [addonly|removeonly] [makefirstteacherowner] <UserTypeEntity>
gam courses <CourseEntity> sync students [addonly|removeonly] <UserTypeEntity>
gam info course <CourseID> [owneremail] [alias|aliases] [show all|students|teachers] [countsonly]
gam info course <CourseID> [owneraccess]
[owneremail] [alias|aliases] [show all|students|teachers] [countsonly]
[fields <CourseFieldNameList>] [skipfields <CourseFieldNameList>]
[formatjson]
gam info courses <CourseEntity> [owneremail] [alias|aliases] [show all|students|teachers] [countsonly]
gam info courses <CourseEntity> [owneraccess]
[owneremail] [alias|aliases] [show all|students|teachers] [countsonly]
[fields <CourseFieldNameList>] [skipfields <CourseFieldNameList>]
[formatjson]
gam print courses [todrive <ToDriveAttribute>*]
@@ -2897,13 +2920,16 @@ gam print courses [todrive <ToDriveAttribute>*]
[owneremail] [owneremailmatchpattern <RegularExpression>]
[alias|aliases|aliasesincolumns [delimiter <Character>]]
[show all|students|teachers] [countsonly]
[timefilter creationtime|updatetime] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>]
[fields <CourseFieldNameList>] [skipfields <CourseFieldNameList>]
[formatjson [quotechar <Character>]]
[timefilter creationtime|updatetime] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>]
[showitemcountonly]
gam print course-participants [todrive <ToDriveAttribute>*]
(course|class <CourseEntity>)*|([teacher <UserItem>] [student <UserItem>] [states <CourseStateList>])
[show all|students|teachers]
[formatjson [quotechar <Character>]]
[showitemcountonly]
<CourseAnnouncementFieldName> ::=
alternatelink|
@@ -3030,8 +3056,8 @@ gam print course-works [todrive <ToDriveAttribute>*]
# Classroom - Invitations
gam <UserTypeEntity> create classroominvitation courses <CourseEntity> [role owner|student|teacher]
[adminaccess|asadmin] [csv|csvformat] [todrive <ToDriveAttribute>*]
[formatjson [quotechar <Character>]]
[adminaccess|asadmin]
[csv|csvformat] [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]]
gam <UserTypeEntity> accept classroominvitation (ids <ClassroomInvitationIDEntity>)|([courses <CourseEntity>] [role all|owner|student|teacher])
gam <UserTypeEntity> delete classroominvitation (ids <ClassroomInvitationIDEntity>)|([courses <CourseEntity>] [role all|owner|student|teacher])
gam <UserTypeEntity> show classroominvitations [role all|owner|student|teacher]
@@ -3039,6 +3065,7 @@ gam <UserTypeEntity> show classroominvitations [role all|owner|student|teacher]
gam <UserTypeEntity> print classroominvitations [todrive <ToDriveAttribute>*] [role all|owner|student|teacher]
[formatjson [quotechar <Character>]]
gam delete classroominvitation courses <CourseEntity> (ids <ClassroomInvitationIDEntity>)|(role all|owner|student|teacher)
gam show classroominvitations (course|class <CourseEntity>)*|([teacher <UserItem>] [student <UserItem>] [states <CourseStateList>])
[role all|owner|student|teacher]
[formatjson]
@@ -3131,11 +3158,12 @@ gam print|show transferapps
gam create|add datatransfer|transfer <OldOwnerID> <DataTransferServiceList> <NewOwnerID>
[private|shared|all] [release_resources]
(<ParameterKey> <ParameterValue>)*
[wait <Integer> <Integer>]
gam info datatransfer|transfer <TransferID>
gam print datatransfers|transfers [todrive <ToDriveAttribute>*]
[olduser|oldowner <UserItem>] [newuser|newowner <UserItem>]
[status completed|failed|inprogress] [delimiter <Character>]
(addcsvdata <FieldName> <String>)*
gam show datatransfers|transfers
[olduser|oldowner <UserItem>] [newuser|newowner <UserItem>]
[status completed|failed|inprogress] [delimiter <Character>]
@@ -3351,16 +3379,16 @@ gam show peoplecontacts
[allfields|(fields <PeopleFieldNameList>)] [showmetadata]
[formatjson]
gam info people <PeopleResourceNameEntity>
gam info people|peopleprofile <PeopleResourceNameEntity>
[allfields|(fields <PeopleFieldNameList>)] [showmetadata]
[formatjson]
gam print people [todrive <ToDriveAttribute>*]
gam print people|peopleprofile [todrive <ToDriveAttribute>*]
[query <String>]
[mergesources <PeopleMergeSourceName>]
[coountsonly]
[allfields|(fields <PeopleFieldNameList>)] [showmetadata]
[formatjson [quotechar <Character>]]
gam show people
gam show people|peopleprofile
[query <String>]
[mergesources <PeopleMergeSourceName>]
[coountsonly]
@@ -3380,6 +3408,21 @@ gam [<UserTypeEntity>] show drivelabels
[basic|full] [languagecode <LanguageCode>]
[publishedonly [<Boolean>]] [minimumrole applier|editor|organizer|reader]
[formatjson] [adminaccess|asadmin]
`
gam [<UserTypeEntity>] create drivelabelpermission <DriveLabelNameEntity>
(user <UserItem>) | (group <GroupItem) | (audience <String>)
role applier|editor|organizer|reader
[formatjson] [adminaccess|asadmin]
gam [<UserTypeEntity>] delete drivelabelpermission <DriveLabelNameEntity>
(user <UserItem>) | (group <GroupItem) | (audience <String>)
[adminaccess|asadmin]
gam [<UserTypeEntity>] remove drivelabelpermission <DriveLabelPermissionNameEntity>
[adminaccess|asadmin]
gam [<UserTypeEntity>] print drivelabelpermissions <DriveLabelNameEntity> [todrive <ToDriveAttribute>*]
[formatjson [quotechar <Character>]] [adminaccess|asadmin]
gam [<UserTypeEntity>] show drivelabelpermissions <DriveLabelNameEntity>
[formatjson] [adminaccess|asadmin]
# Email Audit Monitor
@@ -3611,6 +3654,7 @@ gam print groups [todrive <ToDriveAttribute>*]
[memberemaildisplaypattern|memberemailskippattern <RegularExpression>]
[convertcrnl] [delimiter <Character>] [sortheaders]
[formatjson [quotechar <Character>]]
[showitemcountonly]
gam print grouptree <GroupEntity> [todrive <ToDriveAttribute>*]
[showparentsaslist [<Boolean>]] [delimiter <Character>]
@@ -3647,6 +3691,7 @@ gam print group-members [todrive <ToDriveAttribute>*]
[userfields <UserFieldNameList>]
[(recursive [noduplicates])|includederivedmembership] [nogroupemail]
[peoplelookup|(peoplelookupuser <EmailAddress>)]
[unknownname <String>] [cachememberinfo [Boolean]]
[formatjson [quotechar <Character>]]
gam show group-members
[([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryGroupList>)]))|
@@ -3730,6 +3775,7 @@ gam print cigroups [todrive <ToDriveAttribute>*]
[memberemaildisplaypattern|memberemailskippattern <RegularExpression>]
[convertcrnl] [delimiter <Character>]
[formatjson [quotechar <Character>]]
[showitemcountonly]
<CIGroupMembersFieldName> ::=
createtime
@@ -3854,6 +3900,7 @@ gam print devices [todrive <ToDriveAttribute>*]
[all|company|personal|nocompanydevices|nopersonaldevices]
[nodeviceusers]
[formatjson [quotechar <Character>]]
[showitemcountonly]
<DeviceUserAction> ::=
approve|
@@ -3877,6 +3924,7 @@ gam print deviceusers [todrive <ToDriveAttribute>*]
<DeviceUserFieldName>* [fields <DeviceUserFieldNameList>]
[orderby <DeviceOrderByFieldName> [ascending|descending]]
[formatjson [quotechar <Character>]]
[showitemcountonly]
gam info deviceuserstate <DeviceUserEntity> [clientid <String>]
gam update deviceuserstate <DeviceUserEntity> [clientid <String>]
@@ -4019,6 +4067,7 @@ gam print mobile [todrive <ToDriveAttribute>*]
[basic|full|allfields] <MobileFieldName>* [fields <MobileFieldNameList>]
[delimiter <Character>] [appslimit <Number>] [oneappperrow] [listlimit <Number>]
[formatjson [quotechar <Character>]]
[showitemcountonly]
# Organizational Units
@@ -4061,6 +4110,7 @@ gam print orgs|ous [todrive <ToDriveAttribute>*]
[allfields|<OrgUnitFieldName>*|(fields <OrgUnitFieldNameList>)] [convertcrnl] [batchsuborgs [<Boolean>]]
[mincroscount <Number>] [maxcroscount <Number>]
[minusercount <Number>] [maxusercount <Number>]
[showitemcountonly]
gam show orgtree [fromparent <OrgUnitItem>] [batchsuborgs [<Boolean>]]
# Printers
@@ -4162,7 +4212,7 @@ gam report <ActivityApplicationName> [todrive <ToDriveAttribute>*]
[filtertime.* <Time>] [filter|filters <String>]
[event|events <EventNameList>] [ip <String>]
[groupidfilter <String>]
[maxactivities <Number>] [maxresults <Number>]
[maxactivities <Number>] [maxevents <Number>] [maxresults <Number>]
[countsonly [summary] [eventrowfilter]]
(addcsvdata <FieldName> <String>)* [shownoactivities]
@@ -4354,6 +4404,7 @@ gam print resources [todrive <ToDriveAttribute>*] [allfields|<ResourceFieldName>
[query <String>]
[acls] [noselfowner] [calendar] [convertcrnl]
[formatjson [quotechar <Character>]]
[showitemcountonly]
<CalendarACLRole> ::= editor|freebusy|freebusyreader|owner|reader|writer|none
<CalendarACLScope> ::= <EmailAddress>|user:<EmailAdress>|group:<EmailAddress>|domain:<DomainName>|domain|default
@@ -4407,6 +4458,12 @@ gam print schema|schemas [todrive <ToDriveAttribute>*]
# Send Email
<MessageContent> ::=
(message|textmessage|htmlmessage <String>)|
(file|textfile|htmlfile <FileName> [charset <Charset>])|
(gdoc|ghtml <UserGoogleDoc>)|
(gcsdoc|gcshtml <StorageBucketObjectName>)|
gam sendemail [recipient|to] <RecipientEntity>
[from <EmailAddress>] [mailbox <EmailAddress>] [replyto <EmailAddress>]
[cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage]
@@ -4508,6 +4565,7 @@ gam create shareddrive <Name>
(<SharedDriveRestrictionsSubfieldName> <Boolean>)*
[hide|hidden <Boolean>] [ou|org|orgunit <OrgUnitItem>]
[errorretries <Integer>] [updateinitialdelay <Integer>] [updateretrydelay <Integer>]
[movetoorgunitdelay <Integer>]
[(csv [todrive <ToDriveAttribute>*] (addcsvdata <FieldName> <String>)*) | returnidonly]
gam update shareddrive <SharedDriveEntity> [name <Name>]
[(theme|themeid <String>) | ([customtheme <DriveFileID> <Float> <Float> <Float>] [color <ColorValue>])]
@@ -4517,9 +4575,11 @@ gam delete shareddrive <SharedDriveEntity>
[adminaccess|asadmin] [allowitemdeletion]
gam hide shareddrive <SharedDriveEntity>
gam unhide shareddrive <SharedDriveEntity>
gam info shareddrive <SharedDriveEntity> [fields <SharedDriveFieldNameList>]
gam info shareddrive <SharedDriveEntity>
[fields <SharedDriveFieldNameList>]
[formatjson]
gam show shareddriveinfo <SharedDriveEntity> [fields <SharedDriveFieldNameList>]
gam show shareddriveinfo <SharedDriveEntity>
[fields <SharedDriveFieldNameList>]
[formatjson]
gam print shareddrives [todrive <ToDriveAttribute>*]
[teamdriveadminquery|query <QueryTeamDrive>]
@@ -4546,16 +4606,19 @@ gam <UserTypeEntity> create shareddrive <Name> adminaccess
(<SharedDriveRestrictionsSubfieldName> <Boolean>)*
[hide|hidden <Boolean>] [ou|org|orgunit <OrgUnitItem>]
[errorretries <Integer>] [updateinitialdelay <Integer>] [updateretrydelay <Integer>]
[movetoorgunitdelay <Integer>]
[(csv [todrive <ToDriveAttribute>*] (addcsvdata <FieldName> <String>)*) | returnidonly]
gam update shareddrive <SharedDriveEntity> asadmin [name <Name>]
gam <UserTypeEntity> update shareddrive <SharedDriveEntity> adminaccess [name <Name>]
[(theme|themeid <String>) | ([customtheme <DriveFileID> <Float> <Float> <Float>] [color <ColorValue>])]
(<SharedDriveRestrictionsFieldName> <Boolean>)*
[hide|hidden <Boolean>] [ou|org|orgunit <OrgUnitItem>]
gam <UserTypeEntity> delete shareddrive <SharedDriveEntity>
adminaccess [allowitemdeletion]
gam <UserTypeEntity> info shareddrive <SharedDriveEntity> adminaccess [fields <SharedDriveFieldNameList>]
gam <UserTypeEntity> info shareddrive <SharedDriveEntity>
adminaccess [fields <SharedDriveFieldNameList>]
[formatjson]
gam <UserTypeEntity> show shareddriveinfo <SharedDriveEntity> adminaccess [fields <SharedDriveFieldNameList>]
gam <UserTypeEntity> show shareddriveinfo <SharedDriveEntity>
adminaccess [fields <SharedDriveFieldNameList>]
[formatjson]
gam <UserTypeEntity> print shareddrives [todrive <ToDriveAttribute>*]
adminaccess [teamdriveadminquery|query <QueryTeamDrive>]
@@ -4570,6 +4633,57 @@ gam <UserTypeEntity> show shareddrives
[fields <SharedDriveFieldNameList>]
[formatjson]
In these commands, you specify a user, administrator access is not used.
gam <UserTypeEntity> create shareddrive <Name>
[(theme|themeid <String>) | ([customtheme <DriveFileID> <Float> <Float> <Float>] [color <ColorValue>])]
(<SharedDriveRestrictionsSubfieldName> <Boolean>)*
[hide|hidden <Boolean>] [ou|org|orgunit <OrgUnitItem>]
[errorretries <Integer>] [updateinitialdelay <Integer>] [updateretrydelay <Integer>]
[movetoorgunitdelay <Integer>]
[(csv [todrive <ToDriveAttribute>*] (addcsvdata <FieldName> <String>)*) | returnidonly]
gam <UserTypeEntity> update shareddrive <SharedDriveEntity> [name <Name>]
[(theme|themeid <String>) | ([customtheme <DriveFileID> <Float> <Float> <Float>] [color <ColorValue>])]
(<SharedDriveRestrictionsFieldName> <Boolean>)*
[hide|hidden <Boolean>] [ou|org|orgunit <OrgUnitItem>]
gam <UserTypeEntity> delete shareddrive <SharedDriveEntity>
[allowitemdeletion]
gam <UserTypeEntity> info shareddrive <SharedDriveEntity>
[fields <SharedDriveFieldNameList>]
[guiroles [<Boolean>]] [formatjson]
gam <UserTypeEntity> show shareddriveinfo <SharedDriveEntity>
[fields <SharedDriveFieldNameList>]
[guiroles [<Boolean>]] [formatjson]
gam <UserTypeEntity> print shareddrives [todrive <ToDriveAttribute>*]
[teamdriveadminquery|query <QueryTeamDrive>]
[matchname <RegularExpression>] [orgunit|org|ou <OrgUnitPath>]
(role|roles <SharedDriveACLRoleList>)*
[fields <SharedDriveFieldNameList>]
[guiroles [<Boolean>]] [formatjson [quotechar <Character>]]
gam <UserTypeEntity> show shareddrives
[teamdriveadminquery|query <QueryTeamDrive>]
[matchname <RegularExpression>] [orgunit|org|ou <OrgUnitPath>]
(role|roles <SharedDriveACLRoleList>)*
[fields <SharedDriveFieldNameList>]
[guiroles [<Boolean>]] [formatjson]
<PermissionMatch> ::=
pm|permissionmatch [not]
[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>] [permtype member|file]
em|endmatch
<PermissionMatchMode> ::=
pmm|permissionmatchmode or|and
<PermissionMatchAction> ::=
pma|permissionmatchaction process|skip
These commands are used to manage the ACLs on the Team Drives themselves, not the files/folders on the Team Drives.
<DrivePermissionsFieldName> ::=
@@ -4853,8 +4967,11 @@ gam <UserTypeEntity> show teamdriveacls
relation.<RelationSubfieldName>.<RelationSubfieldName>.<String>|
sshkeys.<SSHkeysSubfieldName>.<SSHkeysSubfieldName>.<String>|
website.<WebsiteSubfieldName>.<WebsiteSubfieldName>.<String>
<UserReplacementField> ::=
photourl
<Tag> ::= <String>
<UserReplacement> ::=
(field:<UserReplacementField>)|
(field:<UserReplacementFieldSubfield>)|
(field:<UserReplacementFieldSubfieldMatchSubfield>)|
(schema:<SchemaName>.<FieldName>)|
@@ -4894,25 +5011,25 @@ gam show vaultmatters|matters [matterstate <MatterStateList>]
gam print vaultcounts [todrive <ToDriveAttributes>*]
matter <MatterItem> corpus mail|groups
(accounts <EmailAddressEntity>) | (orgunit|org|ou <OrgUnitPath>) | everyone
(shareddrives|teamdrives <SharedDriveIDList>) | (rooms <RoomList>)
[scope [all_data|held_data|unprocessed_data]]
[terms <String>] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>] [timezone <TimeZone>]
[excludedrafts <Boolean>]
[includerooms <Boolean>]
[includeshareddrives|includeteamdrives <Boolean>] [driveversiondate <Date>|<Time>]
[wait <Integer>]
gam print vaultcounts [todrive <ToDriveAttributes>*]
matter <MatterItem> operation <String> [wait <Integer>]
gam create vaultexport|export matter <MatterItem> [name <String>] corpus drive|mail|groups|hangouts_chat|voice
gam create vaultexport|export matter <MatterItem> [name <String>] corpus calendar|drive|mail|groups|hangouts_chat|voice
(accounts <EmailAddressEntity>) | (orgunit|org|ou <OrgUnitPath>) | everyone
(shareddrives|teamdrives <SharedDriveIDList>) | (rooms <RoomList>)
[scope [all_data|held_data|unprocessed_data]]
(shareddrives|teamdrives <SharedDriveIDList>) | (rooms <RoomList>) | (sitesurl <URLList>)
[scope all_data|held_data|unprocessed_data]
[terms <String>] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>] [timezone <TimeZone>]
[locationquery <StringList>] [peoplequery <StringList>] [minuswords <StringList>]
[responsestatuses <AttendeeStatus>(,<AttendeeStatus>)*] [calendarversiondate <Date>|<Time>]
[includeshareddrives <Boolean>] [driveversiondate <Date>|<Time>] [includeaccessinfo <Boolean>]
[includerooms <Boolean>]
[excludedrafts <Boolean>] [format mbox|pst]
[showconfidentialmodecontent <Boolean>] [usenewexport <Boolean>]
[includerooms <Boolean>] [covereddata calllogs|textmessages|voicemails]
[includeshareddrives|includeteamdrives <Boolean>] [driveversiondate <Date>|<Time>] [includeaccessinfo <Boolean>]
[showconfidentialmodecontent <Boolean>] [usenewexport <Boolean>] [exportlinkeddrivefiles <Boolean>]
[covereddata calllogs|textmessages|voicemails]
[region any|europe|us] [showdetails|returnidonly]
gam delete vaultexport|export <ExportItem> matter <MatterItem>
gam delete vaultexport|export <MatterItem> <ExportItem>
@@ -5320,13 +5437,16 @@ gam 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] [basic|full|allfields | <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>)*
[showitemcountonly]
Print fields for specified users.
@@ -5334,25 +5454,31 @@ 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]
[userview] [basic|full|allfields | <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>)*
[showitemcountonly]
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] [basic|full|allfields | <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>)*
[showitemcountonly]
The first column will always be primaryEmail; the remaining field names will be sorted if allfields, basic, full or sortheaders is specified;
otherwise, the remaining field names will appear in the order specified.
@@ -5736,18 +5862,19 @@ gam <UserTypeEntity> create focustime
[declinemessage <String>]
[summary <String>]
(timerange <Time> <Time> [recurrence <String>])+
[timezone <String>]
gam <UserTypeEntity> create outofoffice
[declinemode none|all|new]
[declinemessage <String>]
[summary <String>]
(timerange <Time> <Time> [recurrence <String>])+
[timezone <String>]
gam <UserTypeEntity> create workinglocation
(home|
(custom <String>)|
(office <String> [building|buildingid <String>] [floor|floorname <String>]
[section|floorsection <String>] [desk|deskcode <String>]))
[section|floorsection <String>] [desk|deskcode <String>]))
((date yyyy-mm-dd)|
(range yyyy-mm-dd yyyy-mm-dd)|
(daily yyyy-mm-dd N)|
@@ -5802,6 +5929,7 @@ gam <UserTypeEntity> print focustime|outofoffice|workinglocation
gam <UserTypeEntity> create chatspace
[type <ChatSpaceType>]
[restricted|(audience <String>)]
[externalusersrallowed <Boolean>]
[members <UserTypeEntity>]
[displayname <String>]
@@ -5810,10 +5938,11 @@ gam <UserTypeEntity> create chatspace
[<ChatContent>]
[formatjson|returnidonly]
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]
gam <UserTypeEntity> delete chatspace <ChatSpace>
@@ -6006,10 +6135,13 @@ gam <UserTypeEntity> copy drivefile <DriveFileEntity>
[summary [<Boolean>]] [showpermissionmessages [<Boolean>]]
[<DriveFileParentAttribute>]
[mergewithparent [<Boolean>]] [recursive [depth <Number>]]
[copysubfiles [<Boolean>]] [filenamematchpattern <RegularExpression>] [filemimetype [not] <MimeTypeList>]
<DriveFileCopyAttribute>*
[skipids <DriveFileEntity>]
[copysubfiles [<Boolean>]] [filenamematchpattern <RegularExpression>]
[filemimetype [not] <MimeTypeList>]
[copysubfilesownedby any|me|others]
[copysubfolders [<Boolean>]] [foldernamematchpattern <RegularExpression>]
[copysubshortcuts [<Boolean>]] [shortcutnamematchpattern <RegularExpression>]
<DriveFileCopyAttribute>*
[duplicatefiles overwriteolder|overwriteall|duplicatename|uniquename|skip]
[duplicatefolders merge|duplicatename|uniquename|skip]
[copiedshortcutspointtocopiedfiles [<Boolean>]]
@@ -6062,6 +6194,11 @@ gam <UserTypeEntity> get document <DriveFileEntity>
[targetfolder <FilePath>] [targetname <FileName>]
[donotfollowshortcuts [<Boolean>]] [overwrite [<Boolean>]]
gam <UserTypeEntity> update docuument <DriveFileEntity>
((json [charset <Charset>] <SpreadsheetJSONUpdateRequest>) |
(json file <FileName> [charset <Charset>]))
[formatjson]
gam <UserTypeEntity> get drivefile <DriveFileEntity> [revision <DriveFileRevisionID>]
[(format <FileFormatList>)|(gsheet|csvsheet <SheetEntity>)] [exportsheetaspdf <String>]
[targetfolder <FilePath>] [targetname <FileName>|-]
@@ -6469,9 +6606,10 @@ gam <UserTypeEntity> collect orphans
<DriveOwnersSubfieldName>|
parents|
<DriveParentsSubfieldName>|
permissionids|
permissiondetails|
permissions|
<DrivePermissionsSubfieldName>|
permissionids|
properties|
quotabytesused|quotaused|
resourcekey|
@@ -6508,19 +6646,6 @@ gam <UserTypeEntity> collect orphans
writerscanshare
<DriveFieldNameList> ::= "<DriveFieldName>(,<DriveFieldName>)*"
<PermissionMatch> ::=
permissionmatch|pm [not]
[type anyone|user|group|domain] [role|notrole <DriveFileACLRole>] [allowfilediscovery|withlink <Boolean>]
[emailaddress <RegularExpression>] [name|displayname <String>]
[domain|notdomain <RegularExpression>] [domainlist|notdomainlist <DomainNameList>]
[expirationstart <Time>] [expirationend <Time>]
[deleted <Boolean>] [inherited <Boolean>]
endmatch|em
<PermissionMatchMode> ::=
permissionmatchmode|pmm or|and
<PermissionMatchAction> ::=
permissionmatchaction|pma process|skip
gam <UserTypeEntity> show fileinfo <DriveFileEntity>
[returnidonly]
[filepath|fullpath] [pathdelimiter <Character>]
@@ -6921,22 +7046,29 @@ gam <UserTypeEntity> print labels|label [todrive <ToDriveAttribute>*]
x400-trace
<SMTPHeaderList> ::= "<SMTPDateHeader>|<SMTPHeader>(,<SMTPDateHeader>|<SMTPHeader>)*"
<MessageContent> ::=
(message|textmessage|htmlmessage <String>)|
(file|textfile|htmlfile <FileName> [charset <Charset>])|
(gdoc|ghtml <UserGoogleDoc>)|
(gcsdoc|gcshtml <StorageBucketObjectName>)|
(emlfile <FileName> [charset <Charset>])
gam <UserTypeEntity> draft message
(<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)*
<MessageContent> (replace <Tag> <UserReplacement>)*
(<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)*
(attach <FileName> [charset <CharSet>])*
(embedimage <FileName> <String>)*
gam <UserTypeEntity> import message
<MessageContent> (replace <Tag> <UserReplacement>)*
(<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)*
(addlabel <LabelName>)* [labels <LabelNameList>]
<MessageContent> (replace <Tag> <UserReplacement>)*
(attach <FileName> [charset <CharSet>])*
(embedimage <FileName> <String>)*
[deleted [<Boolean>]] [checkspam|nevermarkspam [<Boolean>]] [processforcalendar [<Boolean>]]
gam <UserTypeEntity> insert message
<MessageContent> (replace <Tag> <UserReplacement>)*
(<SMTPDateHeader> <Time>)* (<SMTPHeader> <String>)* (header <String> <String>)*
(addlabel <LabelName>)* [labels <LabelNameList>]
<MessageContent> (replace <Tag> <UserReplacement>)*
(attach <FileName> [charset <CharSet>])*
(embedimage <FileName> <String>)*
[deleted [<Boolean>]]
@@ -7030,6 +7162,15 @@ gam <UserTypeEntity> show signature|sig [compact|format|html]
gam <UserTypeEntity> print signature [compact]
[primary|default] [verifyonly] [todrive <ToDriveAttribute>*]
gam <UserTypeEntity> vacation <Boolean> subject <String>
[<VacationMessageContent> (replace <Tag> <UserReplacement>)*]
[html [<Boolean>]] [contactsonly [<Boolean>]] [domainonly [<Boolean>]]
[start|startdate <Date>|Started] [end|enddate <Date>|NotSpecified]
gam <UserTypeEntity> show vacation [compact|format|html] [enabledonly]
gam <UserTypeEntity> print vacation [compact] [enabledonly] [todrive <ToDriveAttribute>*]
# Users - Gmail - S/MIME
gam <UserTypeEntity> create|add smime file <FileName> [password <Password>]
[sendas|sendasemail <EmailAddress>] [default]
gam <UserTypeEntity> update smime default
@@ -7041,12 +7182,41 @@ gam <UserTypeEntity> show smimes
gam <UserTypeEntity> print smimes [todrive <ToDriveAttribute>*]
[primary|default|(sendas|sendasemail <EmailAddress>)]
gam <UserTypeEntity> vacation <Boolean> subject <String>
[<VacationMessageContent> (replace <Tag> <UserReplacement>)*]
[html [<Boolean>]] [contactsonly [<Boolean>]] [domainonly [<Boolean>]]
[start|startdate <Date>|Started] [end|enddate <Date>|NotSpecified]
gam <UserTypeEntity> show vacation [compact|format|html] [enabledonly]
gam <UserTypeEntity> print vacation [compact] [enabledonly] [todrive <ToDriveAttribute>*]
# Users - Gmail Client Side Encryption
gam <UserTypeEntity> create cseidentity
(primarykeypairid <KeyPairID>) | (signingkeypairid <KeyPairID> encryptionkeypairid <KeyPairID>)
[kpemail <EmailAddress>]
[formatjson]
gam <UserTypeEntity> update cseidentity
(primarykeypairid <KeyPairID>) | (signingkeypairid <KeyPairID> encryptionkeypairid <KeyPairID>)
[kpemail <EmailAddress>]
[formatjson]
gam <UserTypeEntity> delete cseidentity [kpemail <EmailAddress>]
gam <UserTypeEntity> info cseidentity [kpemail <EmailAddress>]
[formatjson]
gam <UserTypeEntity> show cseidentities
[formatjson]
gam <UserTypeEntity> print cseidentities [todrive <ToDriveAttribute>*]
[formatjson [quotechar <Character>]]
gam <UserTypeEntity> create csekeypair
[incertdir <FilePath>] [inkeydir <FilePath>]
[addidentity [<Boolean>]] [kpemail <EmailAddress>]
[showpem] [showkaclsdata] [formatjson|returnidonly]
gam <UserTypeEntity> disable csekeypair <KeyPairID>
[showpem] [showkaclsdata] [formatjson]
gam <UserTypeEntity> enable csekeypair <KeyPairID>
[showpem] [showkaclsdata] [formatjson]
gam <UserTypeEntity> obliterate csekeypair <KeyPairID>
gam <UserTypeEntity> info csekeypair <KeyPairID>
[showpem] [showkaclsdata] [formatjson]
gam <UserTypeEntity> show csekeypairs
[showpem] [showkaclsdata] [formatjson]
gam <UserTypeEntity> print csekeypairs [todrive <ToDriveAttribute>*]
[showpem] [showkaclsdata] [formatjson [quotechar <Character>]]
# Users - Gmail - Settings

View File

@@ -2,6 +2,592 @@
Merged GAM-Team version
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.
c6.69.00
Added `use_classroom_owner_access` Boolean variable to `gam.cfg` that controls how GAM gets
classroom member information and removes students/teachers. Client access does not provide
complete information about non-domain students/teachers.
* `False` - Use client 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@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`.
6.67.09
Added option `maxevents <Number>` to `gam report <ActivityApplictionName>` that limits
the number of events displayed for each activity; the default is 0, no limit.
Setting options `maxactivities 1 maxevents 1 maxresults 1` can be used to as efficiently as possible
show the most recent activity/event; this can be useful when reporting drive activity for individual drive files.
6.67.08
Added optional argument `charset <Charset>` to `emlfile <FileName>` in `gam <UserTypeEntity> draft|import|insert message`;
the default value is `ascii`.
6.67.07
Updated `gam <UserTypeEntity> delete message` to handle this error:
```
ERROR: 403: permissionDenied - Insufficient Permission
```
when the following service account scopes are selected:
```
[ ] 24) Gmail API - Full Access (Labels, Messages)
[*] 25) Gmail API - Full Access (Labels, Messages) except delete message
```
6.67.06
Updated commands that create ACLs to handle the following error:
```
ERROR: 400: abusiveContentRestriction - Bad Request. User message: "You cannot share this item because it has been flagged as inappropriate."
```
6.67.05
Updated the following commands:
```
gam <UserTypeEntity> create|delete|update delegate
gam <UserTypeEntity> forward
gam <UserTypeEntity> create|delete forwardingaddresses
gam <UserTypeEntity> create|delete sendas
```
to handle this error:
```
ERROR: 403: permissionDenied - Insufficient Permission
```
when the following serice account scope is not enabled:
```
[ ] 28) Gmail API - Sharing Settings (Delegates, Forwarding, SendAs) - write
```
6.67.04
Updated user attribute `replace <Tag> <UserReplacement>` to allow `field:photourl` which allows
embedding a link to a user's photo in their signature. Formatting the signature HTML
to properly display the photo is left to the GAM admin.
6.67.03
Fixed bug introduced in 6.67.02 in `gam <UserTypeEntity> claim ownership` that caused a trap.
6.67.02
Added option `skipids <DriveFileEntity>` to `gam <UserTypeEntity> copy drivefile` that handles special cases
where you want to prevent selected files/folders from being copied.
Updated commands that create files/folders on Shared Drives to handle the following errors:
```
storageQuotaExceeded
teamDriveFileLimitExceeded
teamDriveHierarchyTooDeep
```
* See: https://support.google.com/a/users/answer/7338880#shared_drives_file_folder_limits
6.67.01
Fixed bug in `gam print vaultcounts` that caused a trap.
6.67.00
Updated `gam <CrOSTypeEntity> update action <CrOSAction>` to use the new API function `batchChangeStatus`
that replaces the old API function `action`; ChromeOS devices are now processed in batches.
The batch size defaults to 10, the `actionbatchsize <Integer>` option can be used to set a batch size between 10 and 250.
Updated `gam create vaultexport matter <MatterItem>` to support `corpus calendar`.
* See: https://github.com/taers232c/GAMADV-XTD3/wiki/Vault-Takeout#create-vault-exports
6.66.16
Added option `convertcrnl` to `gam update chromepolicy` to properly handle carriage returns (\r) and line feeds (\n)
in value strings entered on the command line in the `<Field> <Value>` form.
```
gam update chromepolicy convertcrnl chrome.devices.DisabledDeviceReturnInstructions
deviceDisabledMessage "Please return device to:\nSchool\n123 Main Street\nAnytown US" ou /Path/to/OU
```
6.66.15
Added option `copysubfilesownedby any|me|others` to `gam <UserTypeEntity> copy drivefile` that allows
specification of which source folder sub files to copy based on file ownership; the default is `any`.
This only applies when files are being copied from a 'My Drive'.
6.66.14
Updated `gam <UserTypeEntity> modify messages` to recognize the following error:
```
ERROR: 400: invalid - Invalid label: SENT
```
Updated `gam update alias <EmailAddressEntity> user|group|target <EmailAddress>`
to avoid the following problem.
```
$ gam update alias testalias@domain.com user testuser
User Alias: testalias@domain.com, Deleted
User Alias: testalias@domain.com, User: testuser@domain.com, Update Failed: Duplicate, Email Address: testalias@domain.com
```
GAM updates an alias to point to a new target by deleting the alias and then recreating the alias pointing to the new target.
Unfortunately, if these commands are executed back-to-back; Google generates the `Update Failed: Duplicate` error.
Now, GAM waits 2 seconds between the delete and the insert which seems to eliminate the problem. If the problem persists,
the option `waitafterdelete <Integer>` can be used to increase the wait time to a maximum of 10 seconds.
6.66.13
Updated functionality of option `preservefiletimes` in `gam <UserTypeEntity> update drivefile <DriveFileEntity>`.
* Current
* `preservefiletimes localfile <FileName>` - `modifiedTime` of `<DriveFileEntity>` is set to that of `localfile <FileName>`
* `preservefiletimes` - No effect
* Updated
* `preservefiletimes localfile <FileName>` - `modifiedTime` of `<DriveFileEntity>` is set to that of `localfile <FileName>`
* `preservefiletimes` - `modifiedTime` of `<DriveFileEntity>` retains its current value
6.66.12
Upgraded to Python 3.12.1 where possible.
Updated all drive commands to handle the following error:
```
ERROR: 401: Active session is invalid. Error code: 4 - authError
```
This is due to the Drive SDK API being disabled in the user's OU.
* See: https://support.google.com/a/answer/6105699
6.66.11
Fixed/improved handling of shortcuts in `gam <UserTypeEntity> transfer drive`.

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'
@@ -166,9 +167,7 @@ class GamAction():
DELETE: ['Deleted', 'Delete'],
DELETE_EMPTY: ['Deleted', 'Delete Empty'],
DELETE_PREVIEW: ['Deleted (Preview)', 'Delete (Preview)'],
DEPROVISION: ['Deprovisioned', 'Deprovision'],
DELETE_SHORTCUT: ['Deleted Shortcut', 'Delete Shortcut'],
DISABLE: ['Disabled', 'Disable'],
DEPROVISION: ['Deprovisioned', 'Deprovision'],
DISABLE: ['Disabled', 'Disable'],
DOWNLOAD: ['Downloaded', 'Download'],
@@ -195,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.
#
@@ -539,6 +539,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 +583,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 +642,7 @@ _SVCACCT_SCOPES = [
{'name': 'Youtube API - read only',
'api': YOUTUBE,
'subscopes': [],
'offByDefault': True,
'scope': 'https://www.googleapis.com/auth/youtube.readonly'},
]
@@ -653,10 +658,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
@@ -284,6 +290,8 @@ TODRIVE_UPLOAD_NODATA = 'todrive_upload_nodata'
TODRIVE_USER = 'todrive_user'
# 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 +352,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 +368,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: '',
@@ -420,6 +431,7 @@ Defaults = {
TODRIVE_UPLOAD_NODATA: TRUE,
TODRIVE_USER: '',
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 +457,7 @@ TYPE_LOCALE = 'locl'
TYPE_PASSWORD = 'pass'
TYPE_ROWFILTER = 'rowf'
TYPE_STRING = 'stri'
TYPE_STRINGLIST = 'strl'
TYPE_TIMEZONE = 'tmzn'
VAR_TYPE = 'type'
@@ -499,6 +512,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 +528,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)},
@@ -575,6 +591,7 @@ VAR_INFO = {
TODRIVE_UPLOAD_NODATA: {VAR_TYPE: TYPE_BOOLEAN},
TODRIVE_USER: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
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'
@@ -519,6 +520,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 +567,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'
@@ -845,6 +852,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 +879,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 +939,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'
@@ -991,6 +1003,7 @@ class GamCLArgs():
OB_TRANSFER_ID = 'TransferID'
OB_URI = 'URI'
OB_URL = 'URL'
OB_URL_LIST = 'URLList'
OB_USER_ENTITY = 'UserEntity'
OB_USER_ITEM = 'UserItem'
OB_USER_NAME = 'UserName'

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_MANAGER_USER = 'chgu'
CHAT_MEMBER = 'chme'
CHAT_MEMBER_GROUP = 'chmg'
CHAT_MEMBER_USER = 'chmu'
@@ -149,12 +150,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'
@@ -162,7 +165,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'
@@ -188,6 +191,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'
@@ -416,6 +421,7 @@ class GamEntity():
CHANNEL_PRODUCT: ['Channel Products', 'Channel Product'],
CHANNEL_SKU: ['Channel SKUs', 'Channel SKU'],
CHAT_BOT: ['Chat BOTs', 'Chat BOT'],
CHAT_MANAGER_USER: ['Chat User Managers', 'Chat User Manager'],
CHAT_MESSAGE: ['Chat Messages', 'Chat Message'],
CHAT_MESSAGE_ID: ['Chat Message IDs', 'Chat Message ID'],
CHAT_MEMBER: ['Chat Members', 'Chat Member'],
@@ -481,6 +487,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'],
@@ -520,6 +528,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'],

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.
#
@@ -21,8 +21,10 @@
"""
# callGAPI throw reasons
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'
@@ -114,11 +116,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'
@@ -155,6 +159,7 @@ TARGET_USER_ROLE_LIMITED_BY_LICENSE_RESTRICTION = 'targetUserRoleLimitedByLicens
TEAMDRIVE_ALREADY_EXISTS = 'teamDriveAlreadyExists'
TEAMDRIVE_DOMAIN_USERS_ONLY_RESTRICTION = 'teamDriveDomainUsersOnlyRestriction'
TEAMDRIVE_TEAM_MEMBERS_ONLY_RESTRICTION = 'teamDriveTeamMembersOnlyRestriction'
TEAMDRIVE_FILE_LIMIT_EXCEEDED = 'teamDriveFileLimitExceeded'
TEAMDRIVE_HIERARCHY_TOO_DEEP = 'teamDriveHierarchyTooDeep'
TEAMDRIVE_MEMBERSHIP_REQUIRED = 'teamDriveMembershipRequired'
TEAMDRIVES_FOLDER_MOVE_IN_NOT_SUPPORTED = 'teamDrivesFolderMoveInNotSupported'
@@ -179,8 +184,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,
@@ -199,7 +204,8 @@ COURSE_ACCESS_THROW_REASONS = [NOT_FOUND, INSUFFICIENT_PERMISSIONS, PERMISSION_D
DRIVE_USER_THROW_REASONS = [SERVICE_NOT_AVAILABLE, AUTH_ERROR, DOMAIN_POLICY]
DRIVE_ACCESS_THROW_REASONS = DRIVE_USER_THROW_REASONS+[FILE_NOT_FOUND, FORBIDDEN, INTERNAL_ERROR, INSUFFICIENT_FILE_PERMISSIONS, UNKNOWN_ERROR, INVALID]
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]
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,
NOT_FOUND, TEAMDRIVE_DOMAIN_USERS_ONLY_RESTRICTION, TEAMDRIVE_TEAM_MEMBERS_ONLY_RESTRICTION,
@@ -213,7 +219,7 @@ DRIVE3_CREATE_ACL_THROW_REASONS = [BAD_REQUEST, INVALID, INVALID_SHARING_REQUEST
FILE_ORGANIZER_NOT_YET_ENABLED_FOR_THIS_TEAMDRIVE,
FILE_ORGANIZER_ON_FOLDERS_IN_SHARED_DRIVE_ONLY,
FILE_ORGANIZER_ON_NON_TEAMDRIVE_NOT_SUPPORTED,
TEAMDRIVES_FOLDER_SHARING_NOT_SUPPORTED, INVALID_LINK_VISIBILITY]
TEAMDRIVES_FOLDER_SHARING_NOT_SUPPORTED, INVALID_LINK_VISIBILITY, ABUSIVE_CONTENT_RESTRICTION]
DRIVE3_GET_ACL_REASONS = DRIVE_USER_THROW_REASONS+[FILE_NOT_FOUND, FORBIDDEN, INTERNAL_ERROR,
INSUFFICIENT_ADMINISTRATOR_PRIVILEGES, INSUFFICIENT_FILE_PERMISSIONS,
UNKNOWN_ERROR, INVALID]
@@ -238,16 +244,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]
@@ -260,6 +269,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]
@@ -270,6 +280,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),
@@ -336,10 +347,14 @@ REASON_MESSAGE_MAP = {
class aborted(Exception):
pass
class abusiveContentRestriction(Exception):
pass
class accessNotConfigured(Exception):
pass
class alreadyExists(Exception):
pass
class applyLabelForbidden(Exception):
pass
class authError(Exception):
pass
class backendError(Exception):
@@ -598,6 +613,8 @@ class teamDriveDomainUsersOnlyRestriction(Exception):
pass
class teamDriveTeamMembersOnlyRestriction(Exception):
pass
class teamDriveFileLimitExceeded(Exception):
pass
class teamDriveHierarchyTooDeep(Exception):
pass
class teamDriveMembershipRequired(Exception):
@@ -635,8 +652,10 @@ class userRateLimitExceeded(Exception):
REASON_EXCEPTION_MAP = {
ABORTED: aborted,
ABUSIVE_CONTENT_RESTRICTION: abusiveContentRestriction,
ACCESS_NOT_CONFIGURED: accessNotConfigured,
ALREADY_EXISTS: alreadyExists,
APPLY_LABEL_FORBIDDEN: applyLabelForbidden,
AUTH_ERROR: authError,
BACKEND_ERROR: backendError,
BAD_REQUEST: badRequest,
@@ -766,6 +785,7 @@ REASON_EXCEPTION_MAP = {
TEAMDRIVE_ALREADY_EXISTS: teamDriveAlreadyExists,
TEAMDRIVE_DOMAIN_USERS_ONLY_RESTRICTION: teamDriveDomainUsersOnlyRestriction,
TEAMDRIVE_TEAM_MEMBERS_ONLY_RESTRICTION: teamDriveTeamMembersOnlyRestriction,
TEAMDRIVE_FILE_LIMIT_EXCEEDED: teamDriveFileLimitExceeded,
TEAMDRIVE_HIERARCHY_TOO_DEEP: teamDriveHierarchyTooDeep,
TEAMDRIVE_MEMBERSHIP_REQUIRED: teamDriveMembershipRequired,
TEAMDRIVES_FOLDER_MOVE_IN_NOT_SUPPORTED: teamDrivesFolderMoveInNotSupported,

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.
#
@@ -150,6 +150,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 +192,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 +233,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'
@@ -299,6 +302,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'
@@ -431,7 +436,7 @@ SELECTED = 'Selected'
SERVICE_NOT_APPLICABLE = 'Service not applicable/Does not exist'
SERVICE_NOT_APPLICABLE_THIS_ADDRESS = 'Service not applicable for this address: {0}'
STARTING_THREAD = 'Starting thread'
STATISTICS_COPY_FILE = 'Total: {0}, Copied: {1}, Shortcut created {2}, Shortcut exists {3}, Duplicate: {4}, Copy Failed: {5}, Not copyable: {6}, Permissions Failed: {7}, Protected Ranges Failed: {8}'
STATISTICS_COPY_FILE = 'Total: {0}, Copied: {1}, Shortcut created {2}, Shortcut exists {3}, Duplicate: {4}, Copy Failed: {5}, Not copyable: {6}, In skipids: {7}, Permissions Failed: {8}, Protected Ranges Failed: {9}'
STATISTICS_COPY_FOLDER = 'Total: {0}, Copied: {1}, Shortcut created {2}, Shortcut exists {3}, Duplicate: {4}, Merged: {5}, Copy Failed: {6}, Not writable: {7}, Permissions Failed: {8}'
STATISTICS_MOVE_FILE = 'Total: {0}, Moved: {1}, Shortcut created {2}, Shortcut exists {3}, Duplicate: {4}, Move Failed: {5}, Not movable: {6}'
STATISTICS_MOVE_FOLDER = 'Total: {0}, Moved: {1}, Shortcut created {2}, Shortcut exists {3}, Duplicate: {4}, Merged: {5}, Move Failed: {6}, Not writable: {7}'
@@ -479,6 +484,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

@@ -34,7 +34,8 @@ _PRODUCTS = {
'101039': 'Assured Controls',
'101040': 'Beyond Corp 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': {
@@ -86,9 +87,13 @@ _SKUS = {
'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.114.0"