mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-04 06:11:39 +00:00
Compare commits
226 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa33dc83d4 | ||
|
|
4155e2bb64 | ||
|
|
9660cafa99 | ||
|
|
c55b9cfe96 | ||
|
|
78ea96767e | ||
|
|
d11d7a8ffc | ||
|
|
bec789d2fb | ||
|
|
0cda3fca31 | ||
|
|
4f8980184f | ||
|
|
ffa096d988 | ||
|
|
6e1b1ed9d5 | ||
|
|
48526b815e | ||
|
|
1ded893e7b | ||
|
|
c61bd01c0f | ||
|
|
f0adcc90c7 | ||
|
|
2abb13bb4c | ||
|
|
8f69c4c820 | ||
|
|
5652c52d96 | ||
|
|
ff29cc192e | ||
|
|
7428d0e734 | ||
|
|
caad9e999c | ||
|
|
24cb225381 | ||
|
|
2e3195c5ee | ||
|
|
bfa039a612 | ||
|
|
49f8988912 | ||
|
|
7e214dbe3b | ||
|
|
42ad12d8d8 | ||
|
|
842e6ef788 | ||
|
|
56b87039c2 | ||
|
|
1767a0889d | ||
|
|
3036366de5 | ||
|
|
a8f1031e0f | ||
|
|
054107c3b9 | ||
|
|
c478b22ab9 | ||
|
|
2f712499ea | ||
|
|
39b9622cdb | ||
|
|
59a3a68357 | ||
|
|
d920dbd79e | ||
|
|
49dd390c6b | ||
|
|
d20b4bc334 | ||
|
|
a973807e3e | ||
|
|
431b5f4f30 | ||
|
|
636799e567 | ||
|
|
d003e3fa1b | ||
|
|
07edbe6619 | ||
|
|
dc4a5a05fe | ||
|
|
a8c86eb53d | ||
|
|
8ef9a62dc9 | ||
|
|
29c9ed4135 | ||
|
|
0426a4ca0d | ||
|
|
af7bbe3cca | ||
|
|
3c55153752 | ||
|
|
a82ff4bb4e | ||
|
|
73759e9611 | ||
|
|
71d6d08f5a | ||
|
|
2f2be201a7 | ||
|
|
49aaca7172 | ||
|
|
f29c64984b | ||
|
|
a50329edf3 | ||
|
|
15d163abf4 | ||
|
|
852a116c9d | ||
|
|
e9c18d0c01 | ||
|
|
5e6d4ecb1c | ||
|
|
97635311db | ||
|
|
3c7ddfd236 | ||
|
|
e0aa0eb4a3 | ||
|
|
8eba9d252f | ||
|
|
5966680a31 | ||
|
|
eb1563b1e7 | ||
|
|
84f52668b7 | ||
|
|
cc60095344 | ||
|
|
5a1f237b30 | ||
|
|
934a671344 | ||
|
|
b81ea8e8c7 | ||
|
|
817920940e | ||
|
|
e0f7ebbcba | ||
|
|
8ce18960fe | ||
|
|
6a879927a7 | ||
|
|
9c22114aa5 | ||
|
|
add9bef046 | ||
|
|
64b6cfea93 | ||
|
|
c8e76d5727 | ||
|
|
4fda0b6aaa | ||
|
|
4634237879 | ||
|
|
5cc91fef53 | ||
|
|
4a3c408ec5 | ||
|
|
23f0c55053 | ||
|
|
e1cf21328a | ||
|
|
7ce5f982b3 | ||
|
|
8fb9439eff | ||
|
|
700e348dbd | ||
|
|
91a86a8663 | ||
|
|
293270c03d | ||
|
|
af13113161 | ||
|
|
59b9dca5ec | ||
|
|
bc5d4e8efb | ||
|
|
91111a62f6 | ||
|
|
5525324620 | ||
|
|
29bd632e45 | ||
|
|
369e3c7269 | ||
|
|
383fed7b3d | ||
|
|
849dd8d436 | ||
|
|
6340a35cf2 | ||
|
|
1b2ea1d4bd | ||
|
|
9087872bc2 | ||
|
|
d914e06f42 | ||
|
|
e12af0c870 | ||
|
|
b0b9e2a7de | ||
|
|
5b88d8b9ca | ||
|
|
b7010b099f | ||
|
|
5484d39d90 | ||
|
|
181a2f8949 | ||
|
|
cee0f97850 | ||
|
|
fd91388b7a | ||
|
|
beae08a99d | ||
|
|
aef614aeae | ||
|
|
c87bc39ad8 | ||
|
|
d0b78f8e81 | ||
|
|
f126cc1d6c | ||
|
|
2d8c3427c4 | ||
|
|
c2acd926af | ||
|
|
b904d77497 | ||
|
|
c093b92c0b | ||
|
|
734b8bfd40 | ||
|
|
e337d1f116 | ||
|
|
8434ac1e2f | ||
|
|
349cdbf582 | ||
|
|
693aeae9e5 | ||
|
|
e4ea8e156d | ||
|
|
ca2b7dd674 | ||
|
|
8a744aa7fc | ||
|
|
7f2beb4d80 | ||
|
|
103c421b31 | ||
|
|
0f4238e9a7 | ||
|
|
c9508d2dac | ||
|
|
5d293b4318 | ||
|
|
3f4b814c0b | ||
|
|
aca71d8db1 | ||
|
|
87dbe3c945 | ||
|
|
c254fc946f | ||
|
|
5a4718eae8 | ||
|
|
935c52f291 | ||
|
|
04fe93d3b8 | ||
|
|
22f279e309 | ||
|
|
a0cff87e5f | ||
|
|
943d327975 | ||
|
|
6c4aced95e | ||
|
|
ad80fd2a91 | ||
|
|
43d50734a4 | ||
|
|
52d6057365 | ||
|
|
8dd5a5dd8a | ||
|
|
4799b33e0e | ||
|
|
d25ae7de81 | ||
|
|
83cbbbf0b7 | ||
|
|
4794578688 | ||
|
|
446da392d9 | ||
|
|
07cbd4cdbb | ||
|
|
f17cbdc111 | ||
|
|
28c5d277a0 | ||
|
|
939840b702 | ||
|
|
78485ae4e9 | ||
|
|
cc47a93872 | ||
|
|
af122eeb5c | ||
|
|
ec118968ed | ||
|
|
2de416fe81 | ||
|
|
d10a3b91e3 | ||
|
|
cdadf68f30 | ||
|
|
900a123141 | ||
|
|
4b2f9488ce | ||
|
|
02d7f45988 | ||
|
|
cc34dbb88e | ||
|
|
5e9d99083c | ||
|
|
295bf74a1b | ||
|
|
7928437dc6 | ||
|
|
83283b7b6b | ||
|
|
09a289b4c4 | ||
|
|
6b940b9d01 | ||
|
|
cb492e0183 | ||
|
|
92799d57ae | ||
|
|
066100f218 | ||
|
|
cd4dd44004 | ||
|
|
40a2fdb7fd | ||
|
|
b60cf11668 | ||
|
|
0fa617c580 | ||
|
|
1cfa08612e | ||
|
|
2cebef9d4b | ||
|
|
c75313cdf4 | ||
|
|
ae1eaac037 | ||
|
|
01465a898a | ||
|
|
d6a7917ffd | ||
|
|
d9946088ab | ||
|
|
a2ad6a1037 | ||
|
|
34e240b40a | ||
|
|
ca1f33ade6 | ||
|
|
d8bddb1c21 | ||
|
|
64bab14483 | ||
|
|
2d1830f4fc | ||
|
|
ab00f2bd42 | ||
|
|
6d9505a4c0 | ||
|
|
40e3cb8ce5 | ||
|
|
4783ec6696 | ||
|
|
3bd746fe91 | ||
|
|
a91a82eecc | ||
|
|
a9564583cb | ||
|
|
f988c8879e | ||
|
|
825cad81a2 | ||
|
|
b9c0ea065a | ||
|
|
50ef633573 | ||
|
|
10202df7d7 | ||
|
|
a7815b41db | ||
|
|
cf467eb868 | ||
|
|
a3be19154f | ||
|
|
a7c19c689c | ||
|
|
1c78e3aac0 | ||
|
|
b9cc3d77b3 | ||
|
|
1feb81adf3 | ||
|
|
fd937758e6 | ||
|
|
fcc3d674c2 | ||
|
|
ce74264a01 | ||
|
|
aaf6448563 | ||
|
|
4a696635f5 | ||
|
|
beb14befca | ||
|
|
c91703364d | ||
|
|
597cea17cd | ||
|
|
9585f6c598 | ||
|
|
e356fe3e85 |
14
.github/ISSUE_TEMPLATE/aa-question.md
vendored
Normal file
14
.github/ISSUE_TEMPLATE/aa-question.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
name: Question about using GAM
|
||||
about: Help with using GAM or running it for the first time
|
||||
title: Please use the GAM discussion group
|
||||
labels: invalid
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
If you need help with GAM, please do not file an issue here, it will be closed and ignored.
|
||||
|
||||
Please post your question to the GAM discussion group where other admins are ready and willing to help:
|
||||
|
||||
https://groups.google.com/g/google-apps-manager
|
||||
23
.github/ISSUE_TEMPLATE/za-bug-report.md
vendored
Normal file
23
.github/ISSUE_TEMPLATE/za-bug-report.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: jay0lee
|
||||
|
||||
---
|
||||
|
||||
The issue tracker is for reporting product deficiencies. "How do I?" questions should be posted to the discussion forum at https://groups.google.com/group/google-apps-manager. When in doubt, start at the discussion forum and return here only when instructed to do so.
|
||||
|
||||
Please confirm the following:
|
||||
* I have upgraded to the latest GAM release from https://git.io/gamreleases and I still have this issue.
|
||||
* I am typing the command as described in the GAM Wiki at https://github.com/jay0lee/gam/wiki
|
||||
|
||||
Full steps to reproduce the issue:
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
Expected outcome (what are you trying to do?):
|
||||
|
||||
Actual outcome (what errors or bad behavior do you see instead?):
|
||||
20
.github/ISSUE_TEMPLATE/zz-feature-request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/zz-feature-request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for GAM
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: jay0lee
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
BIN
.github/actions/creds.tar.gpg
vendored
Normal file
BIN
.github/actions/creds.tar.gpg
vendored
Normal file
Binary file not shown.
16
.github/actions/decrypt.sh
vendored
Normal file
16
.github/actions/decrypt.sh
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
|
||||
gpgfile="$1"
|
||||
echo "source file is ${gpgfile}"
|
||||
credsfile="$2"
|
||||
echo "target file is ${credsfile}"
|
||||
if [ -z ${PASSCODE+x} ]; then
|
||||
echo "PASSCODE is unset";
|
||||
else
|
||||
echo "PASSCODE is set";
|
||||
fi
|
||||
|
||||
gpg --quiet --batch --yes --decrypt --passphrase="${PASSCODE}" \
|
||||
--output "${credsfile}" "${gpgfile}"
|
||||
|
||||
tar xf "${credsfile}" --directory "${gampath}"
|
||||
@@ -5,7 +5,7 @@ if [[ "$TRAVIS_JOB_NAME" == *"Testing" ]]; then
|
||||
echo "running tests with this version"
|
||||
else
|
||||
export whereibelong=$(pwd)
|
||||
echo "We are running on Ubuntu $TRAVIS_DIST $PLATFORM"
|
||||
echo "We are running on $ImageOS $ImageVersion"
|
||||
export LD_LIBRARY_PATH=~/ssl/lib:~/python/lib
|
||||
cpucount=$(nproc --all)
|
||||
echo "This device has $cpucount CPUs for compiling..."
|
||||
@@ -93,7 +93,7 @@ else
|
||||
python=~/python/bin/python3
|
||||
pip=~/python/bin/pip3
|
||||
|
||||
if ([ "${TRAVIS_DIST}" == "trusty" ] || [ "${TRAVIS_DIST}" == "xenial" ]) && [ "${PLATFORM}" == "x86_64" ]; then
|
||||
if ([ "${ImageOS}" == "ubuntu16" ]) && [ "${HOSTTYPE}" == "x86_64" ]; then
|
||||
echo "Installing deps for StaticX..."
|
||||
if [ ! -d patchelf-$PATCHELF_VERSION ]; then
|
||||
echo "Downloading PatchELF $PATCHELF_VERSION"
|
||||
@@ -108,11 +108,5 @@ else
|
||||
$pip install staticx
|
||||
fi
|
||||
|
||||
$pip install --upgrade git+git://github.com/pyinstaller/pyinstaller.git@$PYINSTALLER_COMMIT
|
||||
|
||||
cd $whereibelong
|
||||
fi
|
||||
|
||||
echo "Upgrading pip packages..."
|
||||
$pip list --outdated --format=freeze | grep -v '^\-e' | cut -d = -f 1 | xargs -n1 $pip install -U
|
||||
$pip install --upgrade -r src/requirements.txt
|
||||
32
.github/actions/linux-install.sh
vendored
Executable file
32
.github/actions/linux-install.sh
vendored
Executable file
@@ -0,0 +1,32 @@
|
||||
export gampath="dist/gam"
|
||||
rm -rf $gampath
|
||||
mkdir -p $gampath
|
||||
export gampath=$(readlink -e $gampath)
|
||||
$python -OO -m PyInstaller --clean --noupx --strip -F --distpath $gampath gam.spec
|
||||
export gam="${gampath}/gam"
|
||||
export GAMVERSION=`$gam version simple`
|
||||
cp LICENSE $gampath
|
||||
cp GamCommands.txt $gampath
|
||||
this_glibc_ver=$(ldd --version | awk '/ldd/{print $NF}')
|
||||
GAM_ARCHIVE="gam-${GAMVERSION}-${GAMOS}-${PLATFORM}-glibc${this_glibc_ver}.tar.xz"
|
||||
rm $gampath/lastupdatecheck.txt
|
||||
# tar will cd to dist and tar up gam/
|
||||
tar -C dist/ --create --file $GAM_ARCHIVE --xz gam
|
||||
echo "PyInstaller GAM info:"
|
||||
du -h $gam
|
||||
time $gam version extended
|
||||
if ([ "${ImageOS}" == "ubuntu16" ]) && [ "${HOSTTYPE}" == "x86_64" ]; then
|
||||
GAM_LEGACY_ARCHIVE=gam-${GAMVERSION}-${GAMOS}-${PLATFORM}-legacy.tar.xz
|
||||
$python -OO -m staticx -l /lib/x86_64-linux-gnu/libresolv.so.2 -l /lib/x86_64-linux-gnu/libnss_dns.so.2 $gam $gam-staticx
|
||||
strip $gam-staticx
|
||||
rm $gampath/gam
|
||||
mv $gam-staticx $gam
|
||||
chmod 755 $gam
|
||||
rm $gampath/lastupdatecheck.txt
|
||||
tar -C dist/ --create --file $GAM_LEGACY_ARCHIVE --xz gam
|
||||
echo "Legacy StaticX GAM info:"
|
||||
du -h $gam
|
||||
time $gam version extended
|
||||
fi
|
||||
echo "GAM packages:"
|
||||
ls -l gam-*.tar.xz
|
||||
133
.github/actions/macos-before-install.sh
vendored
Executable file
133
.github/actions/macos-before-install.sh
vendored
Executable file
@@ -0,0 +1,133 @@
|
||||
mypath=$HOME
|
||||
whereibelong=$(pwd)
|
||||
cpucount=$(sysctl -n hw.ncpu)
|
||||
echo "This device has $cpucount CPUs for compiling..."
|
||||
|
||||
#echo "Brew installing xz..."
|
||||
#brew install xz > /dev/null
|
||||
|
||||
#brew upgrade
|
||||
|
||||
brew install coreutils
|
||||
brew install bash
|
||||
|
||||
# prefer standard GNU tools like date over MacOS defaults
|
||||
export PATH="/usr/local/opt/coreutils/libexec/gnubin:$(brew --prefix)/opt/gnu-tar/libexec/gnubin:$PATH"
|
||||
|
||||
date --version
|
||||
gdate --version
|
||||
bash --version
|
||||
|
||||
cd ~
|
||||
|
||||
# Use official Python.org version of Python which is backwards compatible
|
||||
# with older MacOS versions
|
||||
if [ "$PLATFORM" == "x86_64" ]; then
|
||||
export pyfile=python-$BUILD_PYTHON_VERSION-macosx10.9.pkg
|
||||
else
|
||||
export pyfile=python-$BUILD_PYTHON_VERSION-macos11.0.pkg
|
||||
fi
|
||||
|
||||
wget https://www.python.org/ftp/python/$BUILD_PYTHON_VERSION/$pyfile
|
||||
echo "installing Python $BUILD_PYTHON_VERSION..."
|
||||
sudo installer -pkg ./$pyfile -target /
|
||||
|
||||
# This fixes https://github.com/pyinstaller/pyinstaller/issues/5062
|
||||
codesign --remove-signature /Library/Frameworks/Python.framework/Versions/3.9/Python
|
||||
|
||||
#if [ ! -f python-$MIN_PYTHON_VERSION-macosx10.9.pkg ]; then
|
||||
# wget --quiet https://www.python.org/ftp/python/$MIN_PYTHON_VERSION/python-$MIN_PYTHON_VERSION-macosx10.9.pkg
|
||||
#fi
|
||||
#sudo installer -pkg python-$MIN_PYTHON_VERSION-macosx10.9.pkg -target /
|
||||
|
||||
#brew install openssl@1.1
|
||||
#brew upgrade python
|
||||
|
||||
#export python=python3
|
||||
#export pip=pip3
|
||||
|
||||
#echo "Python location:"
|
||||
#which $python
|
||||
|
||||
cd ~
|
||||
|
||||
#export LD_LIBRARY_PATH=~/ssl/lib:~/python/lib
|
||||
#export openssl=~/ssl/bin/openssl
|
||||
#export python=~/python/bin/python3
|
||||
#export pip=~/python/bin/pip3
|
||||
|
||||
export python=/usr/local/bin/python3
|
||||
export pip=/usr/local/bin/pip3
|
||||
SSLVER=$($openssl version)
|
||||
SSLRESULT=$?
|
||||
PYVER=$($python -V)
|
||||
PYRESULT=$?
|
||||
|
||||
#wget --quiet https://www.python.org/ftp/python/$BUILD_PYTHON_VERSION/python-$BUILD_PYTHON_VERSION-macosx10.9.pkg
|
||||
|
||||
#if [ $SSLRESULT -ne 0 ] || [[ "$SSLVER" != "OpenSSL $BUILD_OPENSSL_VERSION "* ]] || [ $PYRESULT -ne 0 ] || [[ "$PYVER" != "Python $BUILD_PYTHON_VERSION"* ]]; then
|
||||
# echo "SSL Result: $SSLRESULT - SSL Ver: $SSLVER - Py Result: $PYRESULT - Py Ver: $PYVER"
|
||||
# if [ $SSLRESULT -ne 0 ]; then
|
||||
# echo "sslresult -ne 0"
|
||||
# fi
|
||||
# if [[ "$SSLVER" != "OpenSSL $BUILD_OPENSSL_VERSION "* ]]; then
|
||||
# echo "sslver not equal to..."
|
||||
# fi
|
||||
# if [ $PYRESULT -ne 0 ]; then
|
||||
# echo "pyresult -ne 0"
|
||||
# fi
|
||||
# if [[ "$PYVER" != "Python $BUILD_PYTHON_VERSION" ]]; then
|
||||
# echo "pyver not equal to..."
|
||||
# fi
|
||||
|
||||
# Start clean
|
||||
# rm -rf python
|
||||
# rm -rf ssl
|
||||
# mkdir python
|
||||
# mkdir ssl
|
||||
|
||||
# Compile latest OpenSSL
|
||||
# wget --quiet https://www.openssl.org/source/openssl-$BUILD_OPENSSL_VERSION.tar.gz
|
||||
# echo "Extracting OpenSSL..."
|
||||
# tar xf openssl-$BUILD_OPENSSL_VERSION.tar.gz
|
||||
# cd openssl-$BUILD_OPENSSL_VERSION
|
||||
# echo "Compiling OpenSSL $BUILD_OPENSSL_VERSION..."
|
||||
# ./config shared --prefix=$HOME/ssl
|
||||
# echo "Running make for OpenSSL..."
|
||||
# make -j$cpucount -s
|
||||
# echo "Running make install for OpenSSL..."
|
||||
# make install > /dev/null
|
||||
# cd ~
|
||||
|
||||
# Compile latest Python
|
||||
# echo "Downloading Python $BUILD_PYTHON_VERSION..."
|
||||
# curl -O https://www.python.org/ftp/python/$BUILD_PYTHON_VERSION/Python-$BUILD_PYTHON_VERSION.tar.xz
|
||||
# echo "Extracting Python..."
|
||||
# tar xf Python-$BUILD_PYTHON_VERSION.tar.xz
|
||||
# cd Python-$BUILD_PYTHON_VERSION
|
||||
# echo "Compiling Python $BUILD_PYTHON_VERSION..."
|
||||
# safe_flags="--with-openssl=$HOME/ssl --enable-shared --prefix=$HOME/python --with-ensurepip=upgrade"
|
||||
# unsafe_flags="--enable-optimizations --with-lto"
|
||||
# if [ ! -e Makefile ]; then
|
||||
# echo "running configure with safe and unsafe"
|
||||
# ./configure $safe_flags $unsafe_flags > /dev/null
|
||||
# fi
|
||||
# make -j$cpucount PROFILE_TASK="-m test.regrtest --pgo -j$(( $cpucount * 2 ))" -s
|
||||
# RESULT=$?
|
||||
# echo "First make exited with $RESULT"
|
||||
# if [ $RESULT != 0 ]; then
|
||||
# echo "Trying Python compile again without unsafe flags..."
|
||||
# make clean
|
||||
# ./configure $safe_flags > /dev/null
|
||||
# make -j$cpucount -s
|
||||
# echo "Sticking with safe Python for now..."
|
||||
# fi
|
||||
# echo "Installing Python..."
|
||||
# make install > /dev/null
|
||||
# cd ~
|
||||
#fi
|
||||
|
||||
$python -V
|
||||
|
||||
cd $whereibelong
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
cd src
|
||||
echo "MacOS Version Info According to Python:"
|
||||
python -c "import platform; print(platform.mac_ver())"
|
||||
echo "Xcode versionn:"
|
||||
53
.github/actions/windows-before-install.sh
vendored
Executable file
53
.github/actions/windows-before-install.sh
vendored
Executable file
@@ -0,0 +1,53 @@
|
||||
if [[ "$PLATFORM" == "x86_64" ]]; then
|
||||
export BITS="64"
|
||||
export PYTHONFILE_BITS="-amd64"
|
||||
export OPENSSL_BITS="-x64"
|
||||
elif [[ "$PLATFORM" == "x86" ]]; then
|
||||
export BITS="32"
|
||||
export PYTHONFILE_BITS=""
|
||||
export OPENSSL_BITS=""
|
||||
export CHOCOPTIONS="--forcex86"
|
||||
fi
|
||||
echo "This is a ${BITS}-bit build for ${PLATFORM}"
|
||||
|
||||
export mypath=$(pwd)
|
||||
cd ~
|
||||
|
||||
export python="python"
|
||||
export pip="pip"
|
||||
|
||||
# Python
|
||||
#echo "Installing Python..."
|
||||
#export python_file=python-${BUILD_PYTHON_VERSION}${PYTHONFILE_BITS}.exe
|
||||
#if [ ! -e $python_file ]; then
|
||||
# echo "Downloading $python_file..."
|
||||
# curl -O https://www.python.org/ftp/python/$BUILD_PYTHON_VERSION/$python_file
|
||||
#fi
|
||||
#until ./${python_file} /quiet InstallAllUsers=1 TargetDir=c:\\python; do echo "trying python again..."; done
|
||||
#export python=/c/python/python.exe
|
||||
#export pip=/c/python/scripts/pip.exe
|
||||
#until [ -f $python ]; do sleep 1; done
|
||||
#export PATH=$PATH:/c/python/scripts
|
||||
|
||||
# OpenSSL
|
||||
#echo "Installing OpenSSL..."
|
||||
#export exefile=Win${BITS}OpenSSL_Light-${BUILD_OPENSSL_VERSION//./_}.exe
|
||||
#if [ ! -e $exefile ]; then
|
||||
# echo "Downloading $exefile..."
|
||||
# curl -O https://slproweb.com/download/$exefile
|
||||
#fi
|
||||
#until ./${exefile} /silent /sp- /suppressmsgboxes /DIR=C:\\ssl; do echo "trying openssl again..."; done
|
||||
#until cp -v /c/ssl/libcrypto-1_1${OPENSSL_BITS}.dll /c/python/DLLs/; do echo "trying libcrypto copy again..."; sleep 3; done
|
||||
#until cp -v /c/ssl/libssl-1_1${OPENSSL_BITS}.dll /c/python/DLLs/; do echo "trying libssl copy again..."; done
|
||||
#if [[ "$PLATFORM" == "x86_64" ]]; then
|
||||
# cp -v /c/python/DLLs/libssl-1_1-x64.dll /c/python/DLLs/libssl-1_1.dll
|
||||
# cp -v /c/python/DLLs/libcrypto-1_1-x64.dll /c/python/DLLs/libcrypto-1_1.dll
|
||||
#fi
|
||||
|
||||
cd $mypath
|
||||
|
||||
echo "PATH: $PATH"
|
||||
cd ..
|
||||
$python setup.py install
|
||||
echo "cd to $mypath"
|
||||
cd $mypath
|
||||
@@ -1,4 +1,8 @@
|
||||
cd src
|
||||
if [[ "$PLATFORM" == "x86_64" ]]; then
|
||||
export WIX_BITS="x64"
|
||||
elif [[ "$PLATFORM" == "x86" ]]; then
|
||||
export WIX_BITS="x86"
|
||||
fi
|
||||
echo "compiling GAM with pyinstaller..."
|
||||
export gampath="dist/gam"
|
||||
rm -rf $gampath
|
||||
@@ -8,7 +12,7 @@ pyinstaller --clean --noupx -F --distpath $gampath gam.spec
|
||||
export gam="${gampath}/gam"
|
||||
echo "running compiled GAM..."
|
||||
$gam version
|
||||
export GAMVERSION=`$gam version simple`
|
||||
export GAMVERSION=$($gam version simple)
|
||||
rm $gampath/lastupdatecheck.txt
|
||||
cp LICENSE $gampath
|
||||
cp GamCommands.txt $gampath
|
||||
326
.github/workflows/build.yml
vendored
Normal file
326
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,326 @@
|
||||
name: Build and test GAM
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
schedule:
|
||||
- cron: '37 22 * * *'
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
working-directory: src
|
||||
|
||||
env:
|
||||
BUILD_PYTHON_VERSION: "3.9.1"
|
||||
MIN_PYTHON_VERSION: "3.9.1"
|
||||
BUILD_OPENSSL_VERSION: "1.1.1i"
|
||||
MIN_OPENSSL_VERSION: "1.1.1g"
|
||||
PATCHELF_VERSION: "0.12"
|
||||
#PYINSTALLER_COMMIT: "61d846d46bdc8b6d926bb57ae05e6c9bb884a144"
|
||||
PYINSTALLER_VERSION: "4.1"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-16.04
|
||||
jid: 1
|
||||
goal: "build"
|
||||
gamos: "linux"
|
||||
platform: "x86_64"
|
||||
- os: ubuntu-18.04
|
||||
jid: 2
|
||||
goal: "build"
|
||||
gamos: "linux"
|
||||
platform: "x86_64"
|
||||
- os: ubuntu-20.04
|
||||
jid: 3
|
||||
goal: "build"
|
||||
gamos: "linux"
|
||||
platform: "x86_64"
|
||||
# - os: [self-hosted, linux, ARM]
|
||||
# jid: 10
|
||||
# goal: "build"
|
||||
# gamos: "linux"
|
||||
# platform: "arm"
|
||||
# - os: [self-hosted, linux, ARM64]
|
||||
# jid: 11
|
||||
# goal: "build"
|
||||
# gamos: "linux"
|
||||
# platform: "arm64"
|
||||
- os: macos-10.15
|
||||
jid: 4
|
||||
goal: "build"
|
||||
gamos: "macos"
|
||||
platform: "x86_64"
|
||||
- os: macos-11.0
|
||||
jid: 12
|
||||
goal: "build"
|
||||
gamos: "macos"
|
||||
platform: "universal2"
|
||||
- os: windows-2019
|
||||
jid: 5
|
||||
goal: "build"
|
||||
gamos: "windows"
|
||||
python: 3.9.1
|
||||
pyarch: "x64"
|
||||
platform: "x86_64"
|
||||
- os: windows-2019
|
||||
jid: 6
|
||||
goal: "build"
|
||||
gamos: "windows"
|
||||
platform: "x86"
|
||||
python: 3.9.1
|
||||
pyarch: "x86"
|
||||
- os: ubuntu-20.04
|
||||
goal: "test"
|
||||
python: "3.6"
|
||||
jid: 7
|
||||
gamos: "linux"
|
||||
platform: "x86_64"
|
||||
- os: ubuntu-20.04
|
||||
goal: "test"
|
||||
python: "3.7"
|
||||
jid: 8
|
||||
gamos: "linux"
|
||||
platform: "x86_64"
|
||||
- os: ubuntu-20.04
|
||||
goal: "test"
|
||||
python: "3.8"
|
||||
jid: 9
|
||||
gamos: "linux"
|
||||
platform: "x86_64"
|
||||
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@master
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache multiple paths
|
||||
uses: actions/cache@v2
|
||||
if: matrix.goal != 'test'
|
||||
with:
|
||||
path: |
|
||||
~/python
|
||||
~/ssl
|
||||
key: ${{ matrix.os }}-${{ matrix.jid }}-20210103
|
||||
|
||||
- name: Set env variables
|
||||
env:
|
||||
GAMOS: ${{ matrix.gamos }}
|
||||
GOAL: ${{ matrix.goal }}
|
||||
JID: ${{ matrix.jid }}
|
||||
PLATFORM: ${{ matrix.platform }}
|
||||
run: |
|
||||
echo "GAMOS=${GAMOS}" >> $GITHUB_ENV
|
||||
echo "GOAL=${GOAL}" >> $GITHUB_ENV
|
||||
echo "JID=${JID}" >> $GITHUB_ENV
|
||||
echo "PLATFORM=${PLATFORM}" >> $GITHUB_ENV
|
||||
uname -a
|
||||
|
||||
- name: Use pre-compiled Python for testing and Windows
|
||||
if: matrix.python != ''
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
architecture: ${{ matrix.pyarch }}
|
||||
|
||||
- name: Set env variables for pre-compiled Python
|
||||
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\ngam: $gam\ngampath: $gampath"
|
||||
echo "python=${python}" >> $GITHUB_ENV
|
||||
echo "pip=${pip}" >> $GITHUB_ENV
|
||||
echo "gam=${gam}" >> $GITHUB_ENV
|
||||
echo "gampath=${gampath}" >> $GITHUB_ENV
|
||||
|
||||
- name: Build and install Python, OpenSSL and PyInstaller
|
||||
if: matrix.goal != 'test' && steps.cache-primes.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
set +e
|
||||
source ../.github/actions/${GAMOS}-before-install.sh
|
||||
echo "PATH=$PATH" >> $GITHUB_ENV # keep gnutools for MacOS
|
||||
echo "python=$python" >> $GITHUB_ENV
|
||||
echo "pip=$pip" >> $GITHUB_ENV
|
||||
echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH" >> $GITHUB_ENV
|
||||
echo -e "Python: $python\nPip: $pip\nLD_LIB...: $LD_LIBRARY_PATH"
|
||||
export url="https://codeload.github.com/pyinstaller/pyinstaller/tar.gz/v${PYINSTALLER_VERSION}"
|
||||
echo "Downloading ${url}"
|
||||
curl -o pyinstaller.tar.gz --compressed "${url}"
|
||||
tar xf pyinstaller.tar.gz
|
||||
cd "pyinstaller-${PYINSTALLER_VERSION}/bootloader"
|
||||
if [ "${PLATFORM}" == "x86" ]; then
|
||||
BITS="32"
|
||||
else
|
||||
BITS="64"
|
||||
fi
|
||||
$python ./waf all --target-arch=${BITS}bit
|
||||
cd ..
|
||||
$python setup.py install
|
||||
#$pip install pyinstaller
|
||||
|
||||
- name: Install pip requirements
|
||||
if: matrix.os != 'self-hosted'
|
||||
run: |
|
||||
set +e
|
||||
$pip list --outdated --format=freeze | grep -v '^\-e' | cut -d = -f 1 | xargs -n1 $pip install -U --force-reinstall
|
||||
$pip install --upgrade -r requirements.txt
|
||||
|
||||
- name: Build GAM with PyInstaller
|
||||
if: matrix.goal != 'test'
|
||||
run: |
|
||||
set +e
|
||||
source ../.github/actions/${GAMOS}-install.sh
|
||||
echo "gampath=$gampath" >> $GITHUB_ENV
|
||||
echo "gam=$gam" >> $GITHUB_ENV
|
||||
echo -e "GAM: ${gam}\nGAMPATH: ${gampath}\nGAMVERSION: ${GAMVERSION}"
|
||||
|
||||
- name: Basic Tests all jobs
|
||||
run: |
|
||||
echo -e "python: $python\npip: $pip\ngam: $gam\ngampath: $gampath\n"
|
||||
$python -m unittest discover --start-directory ./ --pattern "*_test.py" --buffer
|
||||
touch "${gampath}/nobrowser.txt"
|
||||
$gam version extended
|
||||
export GAMVERSION=$($gam version simple)
|
||||
echo "GAM Version ${GAMVERSION}"
|
||||
echo "GAMVERSION=${GAMVERSION}" >> $GITHUB_ENV
|
||||
|
||||
- name: Basic Tests build jobs only
|
||||
if: matrix.goal != 'test'
|
||||
run: |
|
||||
export vline=$($gam version | grep "Python ")
|
||||
export python_line=($vline)
|
||||
export this_python=${python_line[1]}
|
||||
$python tools/a_atleast_b.py "${this_python}" "${MIN_PYTHON_VERSION}"
|
||||
export vline=$($gam version extended | grep "OpenSSL ")
|
||||
export openssl_line=($vline)
|
||||
export this_openssl="${openssl_line[1]}"
|
||||
$python tools/a_atleast_b.py "${this_openssl}" "${MIN_OPENSSL_VERSION}"
|
||||
|
||||
|
||||
- name: Live API tests push only
|
||||
if: github.event_name == 'push'
|
||||
env: # Or as an environment variable
|
||||
PASSCODE: ${{ secrets.PASSCODE }}
|
||||
run: |
|
||||
source ../.github/actions/decrypt.sh ../.github/actions/creds.tar.gpg creds.tar
|
||||
export OAUTHFILE="oauth2.txt-gam-gha-${JID}"
|
||||
echo "OAUTHFILE=${OAUTHFILE}" >> $GITHUB_ENV
|
||||
export gam_user="gam-gha-${JID}@pdl.jaylee.us"
|
||||
echo "gam_user=${gam_user}" >> $GITHUB_ENV
|
||||
$gam oauth info
|
||||
$gam info domain
|
||||
$gam oauth refresh
|
||||
$gam info user
|
||||
export tstamp=$(date +%s%3N)
|
||||
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 GAM_THREADS=5
|
||||
echo email > sample.csv;
|
||||
for i in {01..10}; do
|
||||
echo "${newbase}-bulkuser-$i" >> sample.csv;
|
||||
done
|
||||
$gam create user $newuser firstname GHA lastname $JID password random recoveryphone 12125121110 recoveryemail jay0lee@gmail.com gha.jid $JID
|
||||
$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 create group $newgroup name "GHA $JID group" description "This is a description" isarchived true
|
||||
$gam user $newuser add license gsuitebusiness
|
||||
$gam update group $newgroup add owner $gam_user
|
||||
$gam update group $newgroup add member $newuser
|
||||
$gam csv sample.csv gam create user ~~email~~ firstname "GHA Bulk" lastname ~~email~~ gha.jid $JID
|
||||
$gam csv sample.csv gam update user ~~email~~ recoveryphone 12125121110 recoveryemail jay0lee@gmail.com password random
|
||||
$gam csv sample.csv gam update user ~~email~~ recoveryphone "" recoveryemail ""
|
||||
$gam csv sample.csv gam user ~email add license gsuitebusiness
|
||||
$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 user $gam_user check serviceaccount
|
||||
$gam user $newuser imap on
|
||||
$gam user $newuser show imap
|
||||
$gam csv sample.csv gam user $newuser delegate to ~email
|
||||
$gam user $newuser show delegates
|
||||
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 users "$gam_user $newbase-bulkuser-01 $newbase-bulkuser-02 $newbase-bulkuser-03" delete messages query in:anywhere maxtodelete 99999 doit
|
||||
$gam users "$newbase-bulkuser-04 $newbase-bulkuser-05 $newbase-bulkuser-06" trash messages query in:anywhere maxtotrash 99999 doit
|
||||
# disabling as we see a lot of errors here
|
||||
# $gam users "$newbase-bulkuser-07 $newbase-bulkuser-08 $newbase-bulkuser-09" modify messages query in:anywhere maxtomodify 99999 addlabel IMPORTANT addlabel STARRED doit
|
||||
$gam user $newuser delete label --ALL_LABELS--
|
||||
$gam create feature name Whiteboard-$newbase
|
||||
$gam create feature name VC-$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 show filelist
|
||||
$gam calendar $gam_user printacl | $gam csv - gam calendar $gam_user delete id ~id # clear ACLs
|
||||
$gam calendar $gam_user update read domain
|
||||
$gam calendar $gam_user update 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 ~id
|
||||
$gam calendar $gam_user addevent summary "GHA test event" start $(date '+%FT%T.%N%:z' -d "now + 1 hour") end $(date '+%FT%T.%N%:z' -d "now + 2 hours") attendee $newgroup hangoutsmeet guestscanmodify true sendupdates all
|
||||
$gam calendar $gam_user printevents after -0d
|
||||
matterid=uid:$($gam create vaultmatter name "GHA matter $newbase" description "test matter" collaborators $newuser | head -1 | cut -d ' ' -f 3)
|
||||
$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
|
||||
$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 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 create alias $newalias user $newuser
|
||||
$gam whatis $newuser
|
||||
$gam user $gam_user show tokens
|
||||
$gam print exports matter $matterid | $gam csv - gam download export $matterid id:~~id~~
|
||||
$gam delete hold "GHA hold $newbase" matter $matterid
|
||||
$gam update matter $matterid action close
|
||||
$gam update matter $matterid action delete
|
||||
$gam delete user $newuser
|
||||
$gam print users query "gha.jid=$JID" | $gam csv - gam delete user ~primaryEmail
|
||||
$gam print mobile
|
||||
$gam print devices
|
||||
$gam print browsers
|
||||
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 print cros allfields nolists
|
||||
$gam report usageparameters customer
|
||||
$gam report usage customer parameters gmail:num_emails_sent,accounts:num_1day_logins
|
||||
$gam report customer todrive
|
||||
$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
|
||||
$gam report admin start -3d todrive
|
||||
$gam print devices nopersonaldevices nodeviceusers filter "serial:$JID$JID$JID$JID-" | $gam csv - gam delete device id ~name
|
||||
|
||||
- name: Upload to Google Drive, build only.
|
||||
if: github.event_name == 'push' && matrix.goal != 'test'
|
||||
run: |
|
||||
ls gam-$GAMVERSION-*
|
||||
for gamfile in gam-$GAMVERSION-*; do
|
||||
echo "Uploading file ${gamfile} to Google Drive..."
|
||||
fileid=$($gam user $gam_user add drivefile localfile $gamfile drivefilename $GAMVERSION-${GITHUB_SHA:0:7}-$gamfile parentid 1N2zbO33qzUQFsGM49-m9AQC1ijzd_ru1 returnidonly)
|
||||
echo "file uploaded as ${fileid}, setting ACL..."
|
||||
$gam user $gam_user add drivefileacl $fileid anyone role reader withlink
|
||||
done
|
||||
253
.travis.yml
253
.travis.yml
@@ -1,253 +0,0 @@
|
||||
if: tag IS blank
|
||||
os: linux
|
||||
language: python
|
||||
dist: focal
|
||||
|
||||
env:
|
||||
global:
|
||||
- BUILD_PYTHON_VERSION=3.9.0
|
||||
- MIN_PYTHON_VERSION=3.9.0
|
||||
- BUILD_OPENSSL_VERSION=1.1.1h
|
||||
- MIN_OPENSSL_VERSION=1.1.1h
|
||||
- PATCHELF_VERSION=0.11
|
||||
- PYINSTALLER_COMMIT=7aa19839c171d898b5cf957739083c4bb901607e
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cache/pip
|
||||
- $HOME/python
|
||||
- $HOME/ssl
|
||||
|
||||
jobs:
|
||||
allow_failures:
|
||||
- python: nightly
|
||||
fast_finish: true
|
||||
include:
|
||||
- os: linux
|
||||
name: "Linux 64-bit Focal"
|
||||
dist: focal
|
||||
language: shell
|
||||
- os: linux
|
||||
name: "Linux 64-bit Bionic"
|
||||
dist: bionic
|
||||
language: shell
|
||||
- os: linux
|
||||
name: "Linux 64-bit Xenial"
|
||||
dist: xenial
|
||||
language: shell
|
||||
- os: linux
|
||||
name: "Linux ARM64 Focal"
|
||||
dist: focal
|
||||
language: shell
|
||||
arch: arm64
|
||||
filter_secrets: false
|
||||
- os: linux
|
||||
dist: bionic
|
||||
arch: arm64
|
||||
name: "Linux ARM64 Bionic"
|
||||
language: shell
|
||||
filter_secrets: false
|
||||
- os: linux
|
||||
dist: xenial
|
||||
arch: arm64
|
||||
name: "Linux ARM64 Xenial"
|
||||
language: shell
|
||||
filter_secrets: false
|
||||
- os: linux
|
||||
name: "Python 3.6 Source Testing"
|
||||
language: python
|
||||
python: 3.6
|
||||
- os: linux
|
||||
name: "Python 3.7 Source Testing"
|
||||
language: python
|
||||
python: 3.7
|
||||
- os: linux
|
||||
name: "Python 3.8 Source Testing"
|
||||
language: python
|
||||
python: 3.8
|
||||
- os: linux
|
||||
name: "Python 3.10 dev Source Testing"
|
||||
language: python
|
||||
python: 3.10-dev
|
||||
# - os: linux
|
||||
# name: "Python trunk nightly Source Testing"
|
||||
# language: python
|
||||
# python: nightly
|
||||
# - os: linux
|
||||
# name: "Python PyPi Source Testing"
|
||||
# language: python
|
||||
# python: pypy3
|
||||
- os: osx
|
||||
name: "MacOS 10.13"
|
||||
language: generic
|
||||
osx_image: xcode10.1
|
||||
- os: osx
|
||||
name: "MacOS 10.14"
|
||||
language: generic
|
||||
osx_image: xcode11.3
|
||||
- os: osx
|
||||
name: "MacOS 10.15"
|
||||
language: generic
|
||||
osx_image: xcode11.7
|
||||
- os: osx
|
||||
name: "MacOS 10.15 Universal Testing"
|
||||
language: generic
|
||||
osx_image: xcode12u
|
||||
- os: windows
|
||||
name: "Windows 64-bit"
|
||||
language: shell
|
||||
- os: windows
|
||||
name: "Windows 32-bit"
|
||||
language: shell
|
||||
|
||||
before_install:
|
||||
- if [ "${TRAVIS_OS_NAME}" == "osx" ]; then
|
||||
export GAMOS="macos";
|
||||
else
|
||||
export GAMOS="${TRAVIS_OS_NAME}";
|
||||
fi
|
||||
- if [ "${TRAVIS_JOB_NAME}" == "Windows 32-bit" ]; then
|
||||
export PLATFORM="x86";
|
||||
elif [ "${TRAVIS_CPU_ARCH}" == "amd64" ]; then
|
||||
export PLATFORM="x86_64";
|
||||
else
|
||||
export PLATFORM="${TRAVIS_CPU_ARCH}";
|
||||
fi
|
||||
- source src/travis/${TRAVIS_OS_NAME}-before-install.sh
|
||||
|
||||
install:
|
||||
- source src/travis/${TRAVIS_OS_NAME}-install.sh
|
||||
|
||||
script:
|
||||
# Discover and run all Python unit tests. Buffer output so that it's not sent to the build log.
|
||||
- $python -m unittest discover --start-directory ./ --pattern "*_test.py" --buffer
|
||||
- touch $gampath/nobrowser.txt
|
||||
- $gam version extended
|
||||
- $gam version | grep travis # travis should be part of the path (not /tmp or such)
|
||||
# determine which Python version GAM is built with and ensure it's at least build version from above.
|
||||
- if [[ "$TRAVIS_JOB_NAME" != *"Testing" ]]; then vline=$($gam version | grep "Python "); python_line=($vline); this_python=${python_line[1]}; $python tools/a_atleast_b.py $this_python $MIN_PYTHON_VERSION; fi
|
||||
# determine which OpenSSL version GAM is built with and ensure it's at least build version from above.
|
||||
- if [[ "$TRAVIS_JOB_NAME" != *"Testing" ]]; then vline=$($gam version extended | grep "OpenSSL "); openssl_line=($vline); this_openssl=${openssl_line[1]}; $python tools/a_atleast_b.py $this_openssl $MIN_OPENSSL_VERSION; fi
|
||||
- if [[ "$TRAVIS_JOB_NAME" != *"Testing" ]]; then $gam version extended | grep TLSv1\.[23]; fi # Builds should default TLS 1.2 or 1.3 to Google
|
||||
- if [[ "$TRAVIS_JOB_NAME" != *"Testing" ]]; then GAM_TLS_MIN_VERSION=TLSv1_2 $gam version extended location tls-v1-0.badssl.com:1010; [[ $? == 3 ]]; fi # expect fail since server doesn't support our TLS version
|
||||
- export jid="$(cut -d'.' -f2 <<<"$TRAVIS_JOB_NUMBER")"
|
||||
- if [ "$TRAVIS_EVENT_TYPE" != "pull_request" ]; then export e2e=true; fi
|
||||
- if [ "$e2e" = true ]; then export gam_user=gam-travis-$jid@pdl.jaylee.us; fi
|
||||
- if [ "$e2e" = true ]; then openssl aes-256-cbc -K $encrypted_6294a53f809d_key -iv $encrypted_6294a53f809d_iv -in travis/creds.tar.enc -out travis/creds.tar -d; fi
|
||||
- if [ "$e2e" = true ]; then tar xvf travis/creds.tar -C $gampath; fi
|
||||
- if [ "$e2e" = true ]; then export OAUTHFILE=oauth2.txt-gam-travis-$jid; fi
|
||||
- if [ "$e2e" = true ]; then $gam oauth info; fi
|
||||
- if [ "$e2e" = true ]; then $gam info domain; fi
|
||||
- if [ "$e2e" = true ]; then $gam oauth refresh; fi
|
||||
- if [ "$e2e" = true ]; then $gam info user; fi
|
||||
- if [ "$e2e" = true ]; then export tstamp=$(date +%s%3N);
|
||||
export newbase=travis-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 GAM_THREADS=5; fi
|
||||
- if [ "$e2e" = true ]; then echo email > sample.csv;
|
||||
for i in {01..20};
|
||||
do echo $newbase-bulkuser-$i >> sample.csv;
|
||||
done; fi
|
||||
- if [ "$e2e" = true ]; then $gam create user $newuser firstname Travis lastname $jid password random recoveryphone 12125121110 recoveryemail jay0lee@gmail.com travis.jid $jid; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $gam_user sendemail recipient $newuser subject "test message $newbase" message "Travis test message"; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $gam_user sendemail recipient exchange@pdl.jaylee.us subject "test ${tstamp}" message "test message"; fi
|
||||
- if [ "$e2e" = true ]; then $gam create group $newgroup name "Travis $jid group" description "This is a description" isarchived true; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $newuser add license gsuitebusiness; fi
|
||||
- if [ "$e2e" = true ]; then $gam update group $newgroup add owner $gam_user; fi
|
||||
- if [ "$e2e" = true ]; then $gam update group $newgroup add member $newuser; fi
|
||||
- if [ "$e2e" = true ]; then $gam csv sample.csv gam create user ~~email~~ firstname "Travis Bulk" lastname ~~email~~ travis.jid $jid; fi
|
||||
- if [ "$e2e" = true ]; then $gam csv sample.csv gam update user ~~email~~ recoveryphone 12125121110 recoveryemail jay0lee@gmail.com password random; fi
|
||||
- if [ "$e2e" = true ]; then $gam csv sample.csv gam update user ~~email~~ recoveryphone "" recoveryemail ""; fi
|
||||
- if [ "$e2e" = true ]; then $gam csv sample.csv gam user ~email add license gsuitebusiness; fi
|
||||
- if [ "$e2e" = true ]; then $gam csv sample.csv gam user $gam_user sendemail recipient ~~email~~@pdl.jaylee.us subject "test message $newbase" message "Travis test message"; fi
|
||||
- if [ "$e2e" = true ]; then $gam csv sample.csv gam update group $newgroup add member ~email; fi
|
||||
- if [ "$e2e" = true ]; then $gam info group $newgroup; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $gam_user check serviceaccount; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $newuser imap on; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $newuser show imap; fi
|
||||
- if [ "$e2e" = true ]; then $gam csv sample.csv gam user $newuser delegate to ~email; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $newuser show delegates; fi
|
||||
- if [ "$e2d" = true ]; then export biohazard=$(echo -e '\xe2\x98\xa3'); fi
|
||||
- if [ "$e2e" = true ]; then $gam user $newuser label "$biohazard unicode biohazard $biohazard"; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $newuser show labels; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $newuser show labels > labels.txt; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $gam_user importemail subject "Travis import $newbase" message "This is a test import" labels IMPORTANT,UNREAD,INBOX,STARRED; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $gam_user insertemail subject "Travis insert $newbase" file gam.py labels INBOX,UNREAD; fi # yep body is gam code
|
||||
- if [ "$e2e" = true ]; then $gam user $gam_user sendemail subject "Travis send $gam_user $newbase" file gam.py recipient admin@pdl.jaylee.us; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $gam_user draftemail subject "Travis draft $newbase" message "Draft message test"; fi
|
||||
- if [ "$e2e" = true ]; then $gam users "$gam_user $newbase-bulkuser-01 $newbase-bulkuser-02 $newbase-bulkuser-03" delete messages query in:anywhere maxtodelete 99999 doit; fi
|
||||
- if [ "$e2e" = true ]; then $gam users "$newbase-bulkuser-04 $newbase-bulkuser-05 $newbase-bulkuser-06" trash messages query in:anywhere maxtotrash 99999 doit; fi
|
||||
- if [ "$e2e" = true ]; then $gam users "$newbase-bulkuser-07 $newbase-bulkuser-08 $newbase-bulkuser-09" modify messages query in:anywhere maxtomodify 99999 addlabel IMPORTANT addlabel STARRED doit; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $newuser delete label --ALL_LABELS--; fi
|
||||
- if [ "$e2e" = true ]; then $gam create feature name Whiteboard-$newbase; fi
|
||||
- if [ "$e2e" = true ]; then $gam create feature name VC-$newbase; fi
|
||||
- if [ "$e2e" = true ]; then $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..."; fi
|
||||
- if [ "$e2e" = true ]; then $gam create resource $newresource "Resource Calendar $tstamp" capacity 25 features Whiteboard-$newbase,VC-$newbase building $newbuilding floor 15 type Room; fi
|
||||
- if [ "$e2e" = true ]; then $gam info resource $newresource; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $newuser show filelist; fi
|
||||
- if [ "$e2e" = true ]; then $gam calendar $gam_user printacl | $gam csv - gam calendar $gam_user delete id ~id; fi # clear ACLs
|
||||
- if [ "$e2e" = true ]; then $gam calendar $gam_user update read domain; fi
|
||||
- if [ "$e2e" = true ]; then $gam calendar $gam_user update freebusy default; fi
|
||||
- if [ "$e2e" = true ]; then $gam calendar $gam_user add editor $newuser; fi
|
||||
- if [ "$e2e" = true ]; then $gam calendar $gam_user showacl; fi
|
||||
- if [ "$e2e" = true ]; then $gam calendar $gam_user printacl | $gam csv - gam calendar $gam_user delete id ~id; fi
|
||||
- if [ "$e2e" = true ]; then $gam calendar $gam_user addevent summary "Travis test event" start $(date '+%FT%T.%N%:z' -d "now + 1 hour") end $(date '+%FT%T.%N%:z' -d "now + 2 hours") attendee $newgroup hangoutsmeet guestscanmodify true sendupdates all; fi
|
||||
- if [ "$e2e" = true ]; then $gam calendar $gam_user printevents after -0d; fi
|
||||
- if [ "$e2e" = true ]; then matterid=uid:$($gam create vaultmatter name "Travis matter $newbase" description "test matter" collaborators $newuser | head -1 | cut -d ' ' -f 3); fi
|
||||
- if [ "$e2e" = true ]; then $gam create vaulthold matter $matterid name "Travis hold $newbase" corpus mail accounts $newuser; fi
|
||||
- if [ "$e2e" = true ]; then $gam print vaultmatters matterstate open; fi
|
||||
- if [ "$e2e" = true ]; then $gam print vaultholds matter $matterid; fi
|
||||
- if [ "$e2e" = true ]; then $gam create vaultexport matter $matterid name "Travis export $newbase" corpus mail accounts $newuser; fi
|
||||
- if [ "$e2e" = true ]; then $gam print exports matter $matterid | $gam csv - gam info export $matterid id:~~id~~; fi
|
||||
- if [ "$e2e" = true ]; then $gam csv sample.csv gam user ~email add calendar id:$newresource; fi
|
||||
- if [ "$e2e" = true ]; then $gam delete resource $newresource; fi
|
||||
- if [ "$e2e" = true ]; then $gam delete feature Whiteboard-$newbase; fi
|
||||
- if [ "$e2e" = true ]; then $gam delete feature VC-$newbase; fi
|
||||
- if [ "$e2e" = true ]; then $gam delete building $newbuilding; fi
|
||||
- if [ "$e2e" = true ]; then $gam delete group $newgroup; fi
|
||||
- if [ "$e2e" = true ]; then $gam create alias $newalias user $newuser; fi
|
||||
- if [ "$e2e" = true ]; then $gam whatis $newuser; fi
|
||||
- if [ "$e2e" = true ]; then $gam user $gam_user show tokens; fi
|
||||
- if [ "$e2e" = true ]; then $gam print exports matter $matterid | $gam csv - gam download export $matterid id:~~id~~; fi
|
||||
- if [ "$e2e" = true ]; then $gam delete hold "Travis hold $newbase" matter $matterid; fi
|
||||
- if [ "$e2e" = true ]; then $gam update matter $matterid action close; fi
|
||||
- if [ "$e2e" = true ]; then $gam update matter $matterid action delete; fi
|
||||
- if [ "$e2e" = true ]; then $gam delete user $newuser; fi
|
||||
- if [ "$e2e" = true ]; then $gam print users query "travis.jid=$jid" | $gam csv - gam delete user ~primaryEmail; fi
|
||||
- if [ "$e2e" = true ]; then $gam print mobile; fi
|
||||
- if [ "$e2e" = true ]; then $gam print devices; fi
|
||||
- if [ "$e2e" = true ]; then export sn="$jid$jid$jid$jid$jid-$(openssl rand -base64 32 | sed 's/[^a-zA-Z0-9]//g')"; fi
|
||||
- if [ "$e2e" = true ]; then $gam create device serialnumber $sn devicetype android; fi
|
||||
- if [ "$e2e" = true ]; then $gam print cros allfields nolists; fi
|
||||
- if [ "$e2e" = true ]; then $gam report usageparameters customer; fi
|
||||
- if [ "$e2e" = true ]; then $gam report usage customer parameters gmail:num_emails_sent,accounts:num_1day_logins; fi
|
||||
- if [ "$e2e" = true ]; then $gam report customer todrive; fi
|
||||
- if [ "$e2e" = true ]; then $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; fi
|
||||
- if [ "$e2e" = true ]; then $gam report admin start -3d todrive; fi
|
||||
- if [ "$e2e" = true ]; then $gam print devices nopersonaldevices nodeviceusers filter "serial:$jid$jid$jid$jid$jid-" | $gam csv - gam delete device id ~name; fi
|
||||
- if ([ "$e2e" = true ] && [[ "$TRAVIS_JOB_NAME" != *"Testing" ]]); then
|
||||
for gamfile in gam-$GAMVERSION-*; do
|
||||
fileid=$($gam user $gam_user add drivefile localfile $gamfile drivefilename $GAMVERSION-${TRAVIS_COMMIT:0:7}-$gamfile parentid 1N2zbO33qzUQFsGM49-m9AQC1ijzd_ru1 returnidonly);
|
||||
$gam user $gam_user add drivefileacl $fileid anyone role reader withlink;
|
||||
done;
|
||||
fi
|
||||
|
||||
before_deploy:
|
||||
- export TRAVIS_TAG="preview"
|
||||
- unset LD_LIBRARY_PATH
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
token:
|
||||
secure: bzambMcQwyv/o5c5GrKGCsZHgE5R85tg8sNFvPfpISz3+uosCjnBXas7wvCKzT75XUFi2ztfbYak6HdKf4sGnNHk0saEicB3slH+ghPyZbYzp76yvvduhFO2nWW3/F01tL+Yfqqt4/q8wFaWGjrC5km+6GLVyB4lWA/Uyu49qKnz02uSwyhBD/VFbO7DOQ65a1iWk9HngyMsu0Oi7HIbSjSLtxTHedNfOf3waW0NivTTxYXiYGX/MCu3GWhgIGj47a+H3A6FcQ/9QWvnKgnoixdgPBUz7kDb7ktsWwQsILPGStgH7iMuG49ZlXdEFmqwifBri2wvzmFEevBGZjHcupy1IGrNFRG+IUGKMotio+OkLHlLjuv7ZJtqCz/Vf5SNFgNyMSanx6jKEUJuYvndVg99IRXmYVwHFwPu5BAcJACpU6C0AfyGmmSqqwxCd46uXL62ynxNFpHuRfOqlDnmCTfZgjOciJSlDDpf+Xz9fF7+oCoeCi3mrcZVFjhd3tT6Oxw5HrsDtm0ZNld1cdLidaq8H6vOFgHMd0A9yNYZzTzXTvpmxzkXT4Zc7s+PYKN6z5fRZ+pJeckUjRXblvVEfs5HFSymavcOc5AkRwxpvOsTQMNmlnaJCBo5UNs0K/rVmRi5cFmaiwTcBCY0kTllOBJ4zWsfq8seiokWwNUNK2g=
|
||||
file_glob: true
|
||||
overwrite: true
|
||||
file: gam-$GAMVERSION-*
|
||||
skip_cleanup: true
|
||||
draft: true
|
||||
on:
|
||||
repo: jay0lee/GAM
|
||||
condition: $TRAVIS_JOB_NAME != *"Testing"
|
||||
@@ -1,4 +1,6 @@
|
||||
GAM is a command line tool for Google Workspace (fka G Suite) Administrators to manage domain and user settings quickly and easily. [](https://travis-ci.org/jay0lee/GAM)
|
||||
GAM is a command line tool for Google Workspace (fka G Suite) Administrators to manage domain and user settings quickly and easily.
|
||||
|
||||

|
||||
# Quick Start
|
||||
## Linux / MacOS
|
||||
Open a terminal and run:
|
||||
@@ -12,8 +14,6 @@ Download the MSI Installer from the [GitHub Releases] page. Install the MSI and
|
||||
The GAM documentation is hosted in the [GitHub Wiki]
|
||||
# Mailing List / Discussion group
|
||||
The GAM mailing list / discussion group is hosted on [Google Groups]. You can join the list and interact via email, or just post from the web itself.
|
||||
# IM Room
|
||||
[](https://gitter.im/jay0lee-GAM/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
# Author
|
||||
GAM is maintained by <a href="mailto:jay0lee@gmail.com">Jay Lee</a>. Please direct "how do I?" questions to [Google Groups].
|
||||
|
||||
|
||||
@@ -67,21 +67,15 @@ If an item contains spaces, it should be surrounded by ".
|
||||
gpresentation|
|
||||
gscript|
|
||||
gsite|
|
||||
gsheet|gspreadsheet
|
||||
gsheet|gspreadsheet|
|
||||
gshortcut|
|
||||
g3pshortcut
|
||||
<ProductID> ::=
|
||||
Google-Apps|
|
||||
Google-Chrome-Device-Management|
|
||||
Google-Coordinate|
|
||||
Google-Drive-storage|
|
||||
Google-Vault|
|
||||
101001|101005|101031
|
||||
<ProductID> ::=
|
||||
Google-Apps|
|
||||
Google-Chrome-Device-Management|
|
||||
Google-Coordinate|
|
||||
Google-Drive-storage|
|
||||
Google-Vault|
|
||||
101001|101005|101006|101031|101033|101034
|
||||
101001|101005|101031|101033|101034
|
||||
<SKUID> ::=
|
||||
cloudidentity|identity|1010010001|
|
||||
cloudidentitypremium|identitypremium|1010050001|
|
||||
@@ -91,14 +85,18 @@ If an item contains spaces, it should be surrounded by ".
|
||||
gams|postini|gsuitegams|gsuitepostini|gsuitemessagesecurity|Google-Apps-For-Postini|
|
||||
gal|gsl|lite|gsuitelite|Google-Apps-Lite|
|
||||
gau|gsb|unlimited|gsuitebusiness|Google-Apps-Unlimited|
|
||||
gae|gse|enterprise|gsuiteenterprise|1010020020|
|
||||
wsentplus|workspaceenterpriseplus|gae|gse|enterprise|gsuiteenterprise|1010020020|
|
||||
wsbizplus|workspacebusinessplus|1010020025|
|
||||
wsentstan|workspaceenterprisestandard|'1010020026|
|
||||
wsbizstart|workspacebusinessstarter|1010020027|
|
||||
wsbizstan|workspacebusinessstandard|1010020028|
|
||||
gsefe|e4e|gsuiteenterpriseeducation|1010310002|
|
||||
gsefes|e4es|gsuiteenterpriseeducationstudent|1010310003|
|
||||
gsbau|businessarchived|gsuitebusinessarchived|
|
||||
gseau|enterprisearchived|gsuiteenterprisearchived|
|
||||
chrome|cdm|googlechromedevicemanagement|Google-Chrome-Device-Management|
|
||||
coordinate|googlecoordinate|Google-Coordinate|
|
||||
d4e|driveenterprise|drive4enterprise|
|
||||
wsess|workspaceesentials|gsuiteessentials|essentials|d4e|driveenterprise|drive4enterprise|1010060001|
|
||||
wsentess|workspaceenterpriseessentials|1010060003|
|
||||
drive20gb|20gb|googledrivestorage20gb|Google-Drive-storage-20GB|
|
||||
drive50gb|50gb|googledrivestorage50gb|Google-Drive-storage-50GB|
|
||||
drive200gb|200gb|googledrivestorage200gb|Google-Drive-storage-200GB|
|
||||
@@ -143,6 +141,7 @@ If an item contains spaces, it should be surrounded by ".
|
||||
<ACLScope> ::= [user:]<EmailAddress>|group:<EmailAddress>|domain[:<DomainName>]|default
|
||||
<APIScopeURL> ::= <String>
|
||||
<ASPID> ::= <String>
|
||||
<AssetTag> ::= <String>
|
||||
<BuildingID> ::= <String>|id:<String>
|
||||
<CalendarACLRole> ::= editor|freebusy|freebusyreader|owner|reader|writer
|
||||
<CalendarACLRuleID> ::= user:<EmailAddress>|group:<EmailAddress>|domain:<DomainName>|default
|
||||
@@ -202,12 +201,9 @@ If an item contains spaces, it should be surrounded by ".
|
||||
<ParameterValue> ::= <String>
|
||||
<Password> ::= <String>
|
||||
<PermissionID> ::= id:<String>|<EmailAddress>|anyone|anyonewithlink
|
||||
<PrinterID> ::= <String>
|
||||
<PrintJobAge> ::= <Number>[m|h|d]
|
||||
<PrintJobID> ::= <String>
|
||||
<PrintJobStatus> ::= done|error|held|in_progress|queued|submitted
|
||||
<PropertyKey> ::= <String>
|
||||
<PropertyValue> ::= <String>
|
||||
<QueryBrowser> ::= <String> See: https://support.google.com/chrome/a/answer/9681204#retrieve_all_chrome_devices_for_an_account
|
||||
<QueryCalendar> ::= <String>
|
||||
<QueryContact> ::= <String> See: https://developers.google.com/google-apps/contacts/v3/reference#contacts-query-parameters-reference
|
||||
<QueryCrOS> ::= <String> See: https://support.google.com/chrome/a/answer/1698333?hl=en
|
||||
@@ -215,7 +211,6 @@ If an item contains spaces, it should be surrounded by ".
|
||||
<QueryGmail> ::= <String> See: https://support.google.com/mail/answer/7190
|
||||
<QueryGroup> ::= <String> See: https://developers.google.com/admin-sdk/directory/v1/guides/search-groups
|
||||
<QueryMobile> ::= <String> See: https://support.google.com/a/answer/7549103
|
||||
<QueryPrinter> ::= <String> See: https://developers.google.com/cloud-print/docs/appInterfaces#search
|
||||
<QueryPrintJob> ::= <String> See: https://developers.google.com/cloud-print/docs/appInterfaces#parameters_3
|
||||
<QueryUser> ::= <String> See: https://developers.google.com/admin-sdk/directory/v1/guides/search-users
|
||||
<QueryVaultCorpus> ::= <String> See: https://developers.google.com/vault/reference/rest/v1/matters.holds#CorpusQuery
|
||||
@@ -308,6 +303,7 @@ If an item contains spaces, it should be surrounded by ".
|
||||
appdatacontents|
|
||||
cancomment|
|
||||
canreadrevisions|
|
||||
contentrestrictions|
|
||||
copyable|
|
||||
copyrequireswriterpermission|
|
||||
createddate|createdtime|
|
||||
@@ -560,6 +556,7 @@ Items, separated by spaces, with spaces, commas or single quotes in the items th
|
||||
<ACLList> ::= "<ACLScope>(,<ACLScope>)*"
|
||||
<APIScopeURLList> ::= "<APIScopeURL>(,<APIScopeURL>)*"
|
||||
<ASPIDList> ::= "<ASPID>(,<ASPID>)*"
|
||||
<AssetTagList> ::= "<AssetTag>(,<AssetTa>g)*"
|
||||
<CalendarList> ::= "<CalendarItem>(,<CalendarItem>)*"
|
||||
<ChatRoomList> ::= "<ChatRoom>(,<ChatRoom>)*"
|
||||
<CollaboratorItemList> ::= "<CollaboratorItem>(,<CollaboratorItem>)*"
|
||||
@@ -586,12 +583,10 @@ Items, separated by spaces, with spaces, commas or single quotes in the items th
|
||||
<MembersFieldNameList> ::= "<MembersFieldName>(,<MembersFieldName>)*"
|
||||
<MobileList> ::= "<MobileId>(,<MobileId>)*"
|
||||
<OrgUnitList> ::= "<OrgUnitPath>(,<OrgUnitPath>)*"
|
||||
<PrinterIDList> ::= "<PrinterID>(,<PrinterID>)*"
|
||||
<ProductIDList> ::= "(<ProductID>|SKUID>)(,<ProductID>|SKUID>)*"
|
||||
<PrintJobIDList> ::= "<PrintJobID>(,<PrintJobID>)*"
|
||||
<QueryCrOSList> ::= "<QueryCrOS>(,<QueryCrOS>)*"
|
||||
<QueryMobileList> ::= "<QueryMobile>(,<QueryMobile>)*"
|
||||
<QueryPrinterList> ::= "<QueryPrinter>(,<QueryPrinter>)*"
|
||||
<QueryUserList> ::= "<QueryUser>(,<QueryUser>)*"
|
||||
<ResourceIDList> ::= "<ResourceID>(,<ResourceID>)*"
|
||||
<SKUIDList> ="<SKUID>(,<SKUID>)*"
|
||||
@@ -645,7 +640,7 @@ Specify a collection of Users by directly specifying them or by specifiying item
|
||||
|
||||
## Item attributes
|
||||
|
||||
<BuildingAttributes> ::=
|
||||
<BuildingAttribute> ::=
|
||||
(description <String>)|
|
||||
(floors <FloorNameList>)|
|
||||
(id <String>)|
|
||||
@@ -653,7 +648,7 @@ Specify a collection of Users by directly specifying them or by specifiying item
|
||||
(longitude <Float>)|
|
||||
(name <String>)
|
||||
|
||||
<CalendarAttributes> ::=
|
||||
<CalendarAttribute> ::=
|
||||
(selected <Boolean>)|(hidden <Boolean>)|(summary <String>)|(colorindex|colorid <CalendarColorIndex>)|(backgroundcolor <ColorValue>)|(foregroundcolor <ColorValue>)|
|
||||
(reminder clear|(email|sms|pop <Number>))|
|
||||
(notification clear|(email|sms eventcreation|eventchange|eventcancellation|eventresponse|agenda))
|
||||
@@ -661,7 +656,7 @@ Specify a collection of Users by directly specifying them or by specifiying item
|
||||
<CalendarSettings> ::=
|
||||
(summary <String>)|(description <String>)|(location <String>)|(timezone <TimeZone>)
|
||||
|
||||
<CourseAttributes> ::=
|
||||
<CourseAttribute> ::=
|
||||
(description <String>)|
|
||||
(heading <String>)|
|
||||
(name <String>)|
|
||||
@@ -670,27 +665,37 @@ Specify a collection of Users by directly specifying them or by specifiying item
|
||||
(state|status <CourseState>)|
|
||||
(owner|ownerid|teacher <UserItem>)
|
||||
|
||||
<CrOSAttributes> ::=
|
||||
<CrOSAttribute> ::=
|
||||
(asset|assetid|tag <String>)|
|
||||
(location <String>)|
|
||||
(notes <String>)|
|
||||
(org|ou <OrgUnitPath>)|
|
||||
(user <Name>)
|
||||
|
||||
<DriveFileAddAttributes> ::=
|
||||
<CIGroupAttribute> ::=
|
||||
(description <String>)|
|
||||
(name <String>)
|
||||
|
||||
<DriveFileAddAttribute> ::=
|
||||
(localfile <FileName>)|
|
||||
(convert)|(ocr)|(ocrlanguage <Language>)|
|
||||
(restricted|restrict)|(starred|star)|(trashed|trash)|(viewed|view)|
|
||||
(contentrestrictions readonly false)|
|
||||
(contentrestrictions readonly true [reason <String>])|
|
||||
copyrequireswriterpermission|
|
||||
(lastviewedbyme <Time>)|(modifieddate|modifiedtime <Time>)|(description <String>)|(mimetype <MimeType>)|
|
||||
(parentid <DriveFolderID>)|(parentname <DriveFolderName>)|(anyownerparentname <DriveFolderName>)|writerscantshare
|
||||
<DriveFileUpdateAttributes> ::=
|
||||
(parentid <DriveFolderID>)|(parentname <DriveFolderName>)|(anyownerparentname <DriveFolderName>)|writerscantshare|
|
||||
(shortcut <DriveFileID>)
|
||||
<DriveFileUpdateAttribute> ::=
|
||||
(localfile <FileName>)|
|
||||
(convert)|(ocr)|(ocrlanguage <Language>)|
|
||||
(restricted|restrict <Boolean>)|(starred|star <Boolean>)|(trashed|trash <Boolean>)|(viewed|view <Boolean>)|
|
||||
(contentrestrictions readonly false)|
|
||||
(contentrestrictions readonly true [reason <String>])|
|
||||
(copyrequireswriterpermission <Boolean>)|
|
||||
(lastviewedbyme <Time>)|(modifieddate <Time>)|(description <String>)|(mimetype <MimeType>)|
|
||||
(parentid <DriveFolderID>)|(parentname <DriveFolderName>)|(anyownerparentname <DriveFolderName>)|writerscantshare
|
||||
(parentid <DriveFolderID>)|(parentname <DriveFolderName>)|(anyownerparentname <DriveFolderName>)|writerscantshare|
|
||||
(shortcut <DriveFileID>)
|
||||
<GroupSettingsAttribute> ::=
|
||||
(allowexternalmembers <Boolean>)|
|
||||
(allowwebposting <Boolean>)|
|
||||
@@ -759,13 +764,7 @@ Specify a collection of Users by directly specifying them or by specifiying item
|
||||
<MobileAction> ::=
|
||||
admin_remote_wipe|wipe|admin_account_wipe|accountwipe|wipeaccount|approve|block|cancel_remote_wipe_then_activate|cancel_remote_wipe_then_block
|
||||
|
||||
<PrinterAttributes> ::= (currentquota <Number>)|(dailyquota <Number>)|
|
||||
(defaultdisplayname <String>)|(description <String>)|(displayname <String>)|(firmware <String>)|(gcpversion <String>)|
|
||||
(istosaccepted <Boolean>)|(manufacturer <String>)|(model <String>)|(name <String>)|(ownerid <EmailAddress>)|(proxy <String>)|(public <Boolean>)|
|
||||
(quotaenabled <Boolean>)|(status <Number>)|(type <String>)|(uuid <String>)|
|
||||
(setupurl <URL>)|(supporturl <URL>)|(updateurl <URL>)
|
||||
|
||||
<ResourceAttributes> ::=
|
||||
<ResourceAttribute> ::=
|
||||
(buildingid <BuildingID>)|
|
||||
(capacity <Number>)|
|
||||
(category other|room|conference_room|category_unknown)|
|
||||
@@ -799,7 +798,7 @@ Specify a collection of Users by directly specifying them or by specifiying item
|
||||
(recoveryphone <string>)|
|
||||
(suspended <Boolean>)|
|
||||
(<SchemaName>.<FieldName> [multivalued|multivalue|value|multinonempty [type home|other|work|(custom <String>)]] <String>)
|
||||
<UserMultiAttributes> ::=
|
||||
<UserMultiAttribute> ::=
|
||||
(address clear|(type home|other|work|(custom <String>) [unstructured|formatted <String>] [pobox <String>] [extendedaddress <String>] [streetaddress <String>]
|
||||
[locality <String>] [region <String>] [postalcode <String>] [country <String>] [countrycode <String>] notprimary|primary))|
|
||||
(otheremail clear|(home|other|work|<String> <String>))|
|
||||
@@ -858,7 +857,7 @@ gam <UserTypeEntity> check serviceaccount [scope|scopes <APIScopeURLList>]
|
||||
|
||||
gam whatis <EmailItem>
|
||||
|
||||
<ResoldCustomerAttributes> ::=
|
||||
<ResoldCustomerAttribute> ::=
|
||||
(email|alternateemail <EmailAddress>)|
|
||||
(contact|contactname <String>)|
|
||||
(phone|phonenumber <String>)|
|
||||
@@ -871,7 +870,7 @@ gam whatis <EmailItem>
|
||||
(zipcode|postal|postalcode <String>)|
|
||||
(country|countrycode <String>)
|
||||
|
||||
gam create resoldcustomer <CustomerDomain> (customer_auth_token <String>) <ResoldCustomerAttributes>+
|
||||
gam create resoldcustomer <CustomerDomain> (customer_auth_token <String>) <ResoldCustomerAttribute>+
|
||||
gam update resoldcustomer <CustomerID> [customer_auth_token <String>] <ResoldCustomerAttribues>+
|
||||
gam info resoldcustomer <CustomerID>
|
||||
|
||||
@@ -962,7 +961,7 @@ gam delete domainalias|aliasdomain <DomainAlias>
|
||||
gam info domainalias|aliasdomain <DomainAlias>
|
||||
gam print domainaliases|aliasdomains [todrive]
|
||||
|
||||
<CustomerAttributes> ::=
|
||||
<CustomerAttribute> ::=
|
||||
(primary <DomainName>)|
|
||||
(adminsecondaryemail|alternateemail <EmailAddress>)|
|
||||
(contact|contactname <String>)|
|
||||
@@ -977,7 +976,7 @@ gam print domainaliases|aliasdomains [todrive]
|
||||
(zipcode|postal|postalcode <String>)|
|
||||
(country|countrycode <String>)
|
||||
|
||||
gam update customer <CustomerAttributes>*
|
||||
gam update customer <CustomerAttribute>*
|
||||
|
||||
gam info customer
|
||||
|
||||
@@ -1020,7 +1019,7 @@ The following attributes are equivalent:
|
||||
sendnotifications false - sendupdates none
|
||||
sendnotifications true - sendupdates all
|
||||
|
||||
<EventAttributes> ::=
|
||||
<EventAttribute> ::=
|
||||
anyonecanaddself|
|
||||
(attendee <EmailAddress>)|
|
||||
available|
|
||||
@@ -1046,8 +1045,8 @@ The following attributes are equivalent:
|
||||
(timezone <Timezone>)|
|
||||
(visibility default|public|prvate)
|
||||
|
||||
<EventUpdateAttributes> ::=
|
||||
<EventAttributes>|
|
||||
<EventUpdateAttribute> ::=
|
||||
<EventAttribute>|
|
||||
(removeattendee <EmailAddress>)|
|
||||
(replacedescription <RegularExpression> <String>)
|
||||
|
||||
@@ -1062,10 +1061,10 @@ The following attributes are equivalent:
|
||||
<EventDisplayProperty> ::=
|
||||
(timezone <TimeZone>)
|
||||
|
||||
gam calendar <CalendarItem> addevent [id <String>] <EventAttributes>+ [<EventNotificationAttribute>]
|
||||
gam calendar <CalendarItem> addevent [id <String>] <EventAttribute>+ [<EventNotificationAttribute>]
|
||||
gam calendar <CalendarItem> deleteevent id|eventid <EventID> [doit] [<EventNotificationAttribute>]
|
||||
gam calendar <CalendarItem> moveevent id|eventid <EventID> [doit] [<EventNotificationAttribute>]
|
||||
gam calendar <CalendarItem> updateevent <EventID> <EventUpdateAttributes>+ [<EventNotificationAttribute>]
|
||||
gam calendar <CalendarItem> updateevent <EventID> <EventUpdateAttribute>+ [<EventNotificationAttribute>]
|
||||
gam calendar <CalendarItem> wipe
|
||||
gam calendar <CalendarItem> printevents <EventSelectProperty>* <EventDisplayProperty>* [todrive]
|
||||
|
||||
@@ -1077,6 +1076,60 @@ gam calendar <CalendarItem> printevents <EventSelectProperty>* <EventDisplayProp
|
||||
|
||||
gam calendar <CalendarItem> modify <CalendarSettings>+
|
||||
|
||||
<BrowserAttribute> ::=
|
||||
(assetid <String>)|
|
||||
(location <String>)|
|
||||
(notes <String>)|
|
||||
(user <String>
|
||||
|
||||
<BrowserFieldName> ::=
|
||||
annotatedAssetId|
|
||||
annotatedLocation|
|
||||
annotatedNotes|
|
||||
annotatedUser|
|
||||
browsers|
|
||||
browserVersions|
|
||||
deviceId|
|
||||
extensionCount|
|
||||
installedBrowserVersion|
|
||||
lastActivityTime|
|
||||
lastDeviceUser|
|
||||
lastDeviceUsers|
|
||||
lastPolicyFetchTime|
|
||||
lastRegistrationTime|
|
||||
lastStatusReportTime|
|
||||
machineName|
|
||||
machinePolicies|
|
||||
orgUnitPath|
|
||||
osArchitecture|
|
||||
osPlatform|
|
||||
osPlatformVersion|
|
||||
osVersion|
|
||||
orgUnitPath|
|
||||
policyCount|
|
||||
safeBrowsingClickThroughCount|
|
||||
serialNumber|
|
||||
virtualDeviceId
|
||||
<BrowserFieldNameList> ::= "<BrowseFieldName>(,<BrowserFieldName>)*"
|
||||
|
||||
gam move browsers ou|org|orgunit <OrgUnitPath>
|
||||
((ids <DeviceIDList>) |
|
||||
(query <QueryBrowser>) |
|
||||
(file <FileName>) |
|
||||
(csvfile <FileName>:<FieldName>))
|
||||
[batchsize <Integer>]
|
||||
gam update browser <DeviceID> <BrowserAttibute>+
|
||||
|
||||
gam info browser <DeviceID>
|
||||
[basic|full]
|
||||
[fields <BrowserFieldNameList>]
|
||||
|
||||
gam print browsers [todrive]
|
||||
[query <QueryBrowser>]
|
||||
[projection basic|full]
|
||||
[fields <BrowserFieldNameList>]
|
||||
[sortheaders]
|
||||
|
||||
<CrOSAction> ::=
|
||||
deprovision_same_model_replace|
|
||||
deprovision_different_model_replace|
|
||||
@@ -1085,7 +1138,19 @@ gam calendar <CalendarItem> modify <CalendarSettings>+
|
||||
disable|
|
||||
reenable
|
||||
|
||||
gam update cros <CrOSEntity> (<CrOSAttributes>+)|(action <CrOSAction> [acknowledge_device_touch_requirement])
|
||||
gam update cros <CrOSEntity> action <CrOSAction> [acknowledge_device_touch_requirement]
|
||||
|
||||
<CrOSCommand>
|
||||
wipe_users|
|
||||
remote_powerwash|
|
||||
reboot|
|
||||
set_volume <0-100>|
|
||||
take_a_screenshot
|
||||
|
||||
gam issuecommand cros <CrOSEntity> command <CrOSCommand> [times_to_check_status <0-1000+>] [doit]
|
||||
gam getcommand cros <CrOSEntity> commandid <CommandID> [times_to_check_status <0-1000+>]
|
||||
|
||||
gam update cros <CrOSEntity> <CrOSAttribute>+
|
||||
gam info cros <CrOSEntity> [nolists] [listlimit <Number>] [start <Date>] [end <Date>]
|
||||
[basic|full|allfields] <CrOSFieldName>* [fields <CrOSFieldNameList>] [downloadfile latest|<Time>] [targetfolder <FilePath>]
|
||||
|
||||
@@ -1144,11 +1209,6 @@ gam info device [id] <DeviceID>
|
||||
gam delete device [id] <DeviceID>
|
||||
gam cancelwipe device [id] <DeviceID>
|
||||
gam wipe device [id] <DeviceID>
|
||||
gam approve deviceuser [id] <DeviceUserID>
|
||||
gam block deviceuser [id] <DeviceUserID>
|
||||
gam delete deviceuser [id] <DeviceUserID>
|
||||
gam cancelwipe deviceuser [id] <DeviceUserID>
|
||||
gam wipe deviceuser [id] <DeviceUserID>
|
||||
gam print devices [todrive] [filter|query <QueryDevice>]
|
||||
[orderby <DeviceOrderByFieldName> [ascending|descending]]
|
||||
[company|personal|nocompanydevices|nopersonaldevices]
|
||||
@@ -1161,14 +1221,48 @@ gam sync devices [filter|query <QueryDevice>]
|
||||
[unassigned_missing_action delete|wipe|donothing]
|
||||
[assigned_missing_action delete|wipe|donothing]
|
||||
|
||||
gam approve deviceuser [id] <DeviceUserID>
|
||||
gam block deviceuser [id] <DeviceUserID>
|
||||
gam delete deviceuser [id] <DeviceUserID>
|
||||
gam cancelwipe deviceuser [id] <DeviceUserID>
|
||||
gam wipe deviceuser [id] <DeviceUserID>
|
||||
|
||||
gam info deviceuserstate [id] <DeviceUserID> [clientid <String>]
|
||||
gam update deviceuserstate [id] <DeviceUserID> [clientid <String>]
|
||||
[customid <String>] [assettags clear|<AssetTagList>]
|
||||
[compliantstate|compliancestate compliant|noncompliant] [managedstate clear|managed|unmanaged]
|
||||
[healthscore very_poor|poor|neutral|good|very_good] [scorereason clear|<String>]
|
||||
(customvalue (bool <Boolean>)|(number <Integer>)|(string <String>))*
|
||||
|
||||
gam update mobile <MobileID>|query:<QueryMobile> action <MobileAction> [doit] [if_users|match_users <UserTypeEntity>]
|
||||
gam delete mobile <MobileID>
|
||||
gam info mobile <MobileID>
|
||||
gam print mobile [todrive] [(query <QueryMobile>)|(queries <QueryMobileList>)] [basic|full] [orderby <MobileOrderByFieldName> [ascending|descending]]
|
||||
fields <MobileFieldNameList>] [delimiter <Character>] [appslimit <Number>] [listlimit <Number>]
|
||||
|
||||
gam create group <EmailAddress> <GroupAttributes>*
|
||||
gam update group <GroupItem> [email <EmailAddress>] <GroupAttributes>*
|
||||
gam create cigroup <EmailAddress> <CIGroupAttribute>*
|
||||
[makeowner] [alias|aliases <AliasList>] [dynamic <QueryDynamicGroup>]
|
||||
gam update cigroup <GroupItem> [email <EmailAddress>] <CIGroupAttribute>* [security]
|
||||
gam update cigroup <GroupItem> add [owner|manager|member] [notsuspended|suspended] [expires never|<Time>] <UserTypeEntity>
|
||||
gam update cigroup <GroupItem> delete|remove [owner|manager|member] [notsuspended|suspended] <UserTypeEntity>
|
||||
gam update cigroup <GroupItem> sync [owner|manager|member] [notsuspended|suspended] [expires never|<Time>] <UserTypeEntity>
|
||||
gam update cigroup <GroupItem> update [owner|manager|member] [notsuspended|suspended] [expires never|<Time>] <UserTypeEntity>
|
||||
gam update cigroup <GroupItem> clear [member] [manager] [owner] [notsuspended|suspended]
|
||||
gam delete cigroup <GroupItem>
|
||||
gam info cigroup <GroupItem> [nousers] [nojoindate] [showupdatedate]
|
||||
|
||||
gam print cigroups [todrive]
|
||||
[enterprisemember <UserItem>]
|
||||
[members|memberscount] [managers|managerscount] [owners|ownerscount]
|
||||
[delimiter <Character>] [sortheaders]
|
||||
|
||||
gam info cimember <UserItem> <GroupItem>
|
||||
gam print cigroup-members|cigroups-members [todrive]
|
||||
[(enterprisemember <UserItem>)|(cigroup <GroupItem>)]
|
||||
[roles <GroupRoleList>]
|
||||
|
||||
gam create group <EmailAddress> <GroupAttribute>*
|
||||
gam update group <GroupItem> [email <EmailAddress>] <GroupAttribute>*
|
||||
gam update group <GroupItem> add [owner|manager|member] [notsuspended|suspended] [allmail|daily|digest|none|nomail] <UserTypeEntity>
|
||||
gam update group <GroupItem> delete|remove [owner|manager|member] <UserTypeEntity>
|
||||
gam update group <GroupItem> sync [owner|manager|member] [notsuspended|suspended] [allmail|daily|digest|none|nomail] <UserTypeEntity>
|
||||
@@ -1191,8 +1285,8 @@ gam print group-members|groups-members [todrive]
|
||||
gam print licenses [todrive] [(products|product <ProductIDList>)|(skus|sku <SKUIDList>)|allskus|gsuite] [countsonly]
|
||||
gam show license|licenses|licence|licences [(products|product <ProductIDList>)|(skus|sku <SKUIDList>)|allskus|gsuite]
|
||||
|
||||
gam create building <Name> <BuildingAttributes>*
|
||||
gam update building <BuildIngID> <BuildingAttributes>*
|
||||
gam create building <Name> <BuildingAttribute>*
|
||||
gam update building <BuildIngID> <BuildingAttribute>*
|
||||
gam delete building <BuildingID>
|
||||
gam info building <BuildingID>
|
||||
gam print buildings [todrive]
|
||||
@@ -1202,8 +1296,8 @@ gam update feature <Name> name <Name>
|
||||
gam delete feature <Name>
|
||||
gam print features [todrive]
|
||||
|
||||
gam create resource <ResourceID> <Name> <ResourceAttributes>*
|
||||
gam update resource <ResourceID> <ResourceAttributes>*
|
||||
gam create resource <ResourceID> <Name> <ResourceAttribute>*
|
||||
gam update resource <ResourceID> <ResourceAttribute>*
|
||||
gam delete resource <ResourceID>
|
||||
gam info resource <ResourceID>
|
||||
gam print resources [todrive] [allfields] <ResourceFieldName>* [query <String>]
|
||||
@@ -1215,8 +1309,8 @@ gam info schema <SchemaName>
|
||||
gam show schema|schemas
|
||||
gam print schema|schemas
|
||||
|
||||
gam create user <EmailAddress> <UserAttributes>*
|
||||
gam update user <UserItem> <UserAttributes>* [clearschema <SchemaName>] [clearschema <SchemaName>.<FieldName>]
|
||||
gam create user <EmailAddress> <UserAttribute>*
|
||||
gam update user <UserItem> <UserAttribute>* [clearschema <SchemaName>] [clearschema <SchemaName>.<FieldName>]
|
||||
gam delete user <UserItem>
|
||||
gam undelete user <UserItem> [org|ou <OrgUnitPath>]
|
||||
gam info user [<UserItem>] [noaliases] [nogroups] [nolicenses|nolicences] [noschemas] [schemas|custom <SchemaNameList>] [userview] [skus|sku <SKUIDList>]
|
||||
@@ -1237,8 +1331,8 @@ gam create verify|verification <DomainName>
|
||||
gam update verify|verification <DomainName> cname|txt|text|site|file
|
||||
gam info verify|verification
|
||||
|
||||
gam create course [id|alias <CourseAlias>] <CourseAttributes>*
|
||||
gam update course <CourseID> <CourseAttributes>+
|
||||
gam create course [id|alias <CourseAlias>] <CourseAttribute>*
|
||||
gam update course <CourseID> <CourseAttribute>+
|
||||
gam delete course <CourseID>
|
||||
gam info course <CourseID>
|
||||
gam print courses [todrive] [teacher <UserItem>] [student <UserItem>] [states <CourseStateList>]
|
||||
@@ -1258,32 +1352,6 @@ gam show guardian|guardians [invitedguardian <EmailAddress>] [student <StudentIt
|
||||
gam print guardian|guardians [todrive] [invitedguardian <EmailAddress>] [student <StudentItem>] [invitations [states <GuardianStateList>]] [<UserTypeEntity>]
|
||||
gam cancel guardianinvitation|guardianinvitations <GuardianInvitationID> <StudentItem>
|
||||
|
||||
gam update printer <PrinterID> <PrinterAttributes>+
|
||||
gam delete printer <PrinterID>
|
||||
gam info printer <PrinterID> [everything]
|
||||
gam print printers [todrive] [(query <QueryPrinter>)|(queries <QueryPrinterList>)] [type <String>] [status <String>] [extrafields <String>]
|
||||
|
||||
gam printer <PrinterID> add user|manager|owner <EmailAddress>|[domain:]<DomainName>|public [notify]
|
||||
gam printer <PrinterID> delete <EmailAddress>|[domain:]<DomainName>|public
|
||||
gam printer <PrinterID> showacl
|
||||
gam printjob <PrintJobID> cancel
|
||||
gam printjob <PrintJobID> delete
|
||||
gam printjob <PrintJobID> resubmit <PrinterID>
|
||||
|
||||
gam printjob <PrinterID>|any fetch
|
||||
[olderthan|newerthan <PrintJobAge>] [query <QueryPrintJob>]
|
||||
[status <PrintJobStatus>]
|
||||
[orderby <PrintJobOrderByFieldName> [ascending|descending]]
|
||||
[owner|user <EmailAddress>]
|
||||
[limit <Number>] [drivedir|(targetfolder <FilePath>)]
|
||||
gam printjob <PrinterID> submit <FileName>|<URL> [name|title <String>] (tag <String>)*
|
||||
gam print printjobs [todrive] [printer|printerid <PrinterID>]
|
||||
[olderthan|newerthan <PrintJobAge>] [query <QueryPrintJob>]
|
||||
[status <PrintJobStatus>]
|
||||
[orderby <PrintJobOrderByFieldName> [ascending|descending]]
|
||||
[owner|user <EmailAddress>]
|
||||
[limit <Number>]
|
||||
|
||||
gam create vaultexport|export matter <MatterItem> [name <name>] corpus <drive|mail|groups|hangouts_chat>
|
||||
(accounts <EmailAddressList>) | (orgunit|ou <OrgUnitPath>) | (teamdrives <TeamDriveList>) | (rooms <ChatRoomList>) | everyone
|
||||
[scope <all_data|held_data|unprocessed_data>]
|
||||
@@ -1319,6 +1387,18 @@ gam undelete vaultmatter|matter <MatterItem>
|
||||
gam info vaultmatter|matter <MatterItem>
|
||||
gam print vaultmatters|matters [todrive] [basic|full] [matterstate open|closed|deleted]
|
||||
|
||||
gam print vaultcounts [todrive]
|
||||
matter <MatterItem> corpus mail|groups
|
||||
(accounts <EmailAddressEntity>) | (orgunit|org|ou <OrgUnitPath>) | everyone
|
||||
[scope <all_data|held_data|unprocessed_data>]
|
||||
[terms <terms>] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>] [timezone <TimeZone>]
|
||||
[excludedrafts <Boolean>]
|
||||
[wait <Integer>]
|
||||
|
||||
gam print vaultcounts [todrive]
|
||||
matter <MatterItem> operation <String>
|
||||
[wait <Integer>]
|
||||
|
||||
gam <UserTypeEntity> delete|del asp|asps|applicationspecificpasswords all|<ASPIDList>
|
||||
gam <UserTypeEntity> show asps|asp|applicationspecificpasswords
|
||||
|
||||
@@ -1326,8 +1406,8 @@ gam <UserTypeEntity> update backupcodes|backupcode|verificationcodes
|
||||
gam <UserTypeEntity> delete|del backupcodes|backupcode|verificationcodes
|
||||
gam <UserTypeEntity> show backupcodes|backupcode|verificationcodes
|
||||
|
||||
gam <UserTypeEntity> add calendar <CalendarItem> <CalendarAttributes>*
|
||||
gam <UserTypeEntity> update calendar <CalendarItem>|primary <CalendarAttributes>+
|
||||
gam <UserTypeEntity> add calendar <CalendarItem> <CalendarAttribute>*
|
||||
gam <UserTypeEntity> update calendar <CalendarItem>|primary <CalendarAttribute>+
|
||||
gam <UserTypeEntity> delete|del calendar <CalendarItem>
|
||||
gam <UserTypeEntity> show calendars
|
||||
gam <UserTypeEntity> info calendar <CalendarItem>|primary
|
||||
@@ -1346,8 +1426,8 @@ gam <UserTypeEntity> show fileinfo <DriveFileID> [allfields|<DriveFieldName>*]
|
||||
gam <UserTypeEntity> show filerevisions <DriveFileID>
|
||||
gam <UserTypeEntity> show filetree [anyowner] (orderby <DriveOrderByFieldName> [ascending|descending])*
|
||||
|
||||
gam <UserTypeEntity> create|add drivefile [drivefilename <DriveFileName>] <DriveFileAddAttributes>* [csv] [todrive] [returnidonly]
|
||||
gam <UserTypeEntity> update drivefile (id <DriveFileID)|(drivefilename <DriveFileName>)|(query <QueryDriveFile) [copy] [newfilename <DriveFileName>] <DriveFileUpdateAttributes>*
|
||||
gam <UserTypeEntity> create|add drivefile [drivefilename <DriveFileName>] <DriveFileAddAttribute>* [csv] [todrive] [returnidonly]
|
||||
gam <UserTypeEntity> update drivefile (id <DriveFileID)|(drivefilename <DriveFileName>)|(query <QueryDriveFile) [copy] [newfilename <DriveFileName>] <DriveFileUpdateAttribute>*
|
||||
gam <UserTypeEntity> get drivefile (id <DriveFileID>)|(drivefilename <DriveFileName>)|(query <QueryDriveFile>)
|
||||
[revision <Number>] [(format <FileFormatList>)|(csvsheet <String>)]
|
||||
[targetfolder <FilePath>] [targetname -|<FileName>] [overwrite] [showprogress]
|
||||
@@ -1384,7 +1464,7 @@ gam <UserTypeEntity> show tokens|token [clientid <ClientID>]
|
||||
gam <UserTypeEntity> print tokens|token [todrive] [clientid <ClientID>]
|
||||
gam print tokens|token [todrive] [clientid <ClientID>] [<UserTypeEntity>]
|
||||
|
||||
gam <UserTypeEntity> update user <UserAttributes>
|
||||
gam <UserTypeEntity> update user <UserAttribute>
|
||||
|
||||
gam <UserTypeEntity> deprovision|deprov
|
||||
|
||||
@@ -1424,6 +1504,11 @@ gam <UserTypeEntity> delete|del delegate|delegates <EmailAddress>
|
||||
gam <UserTypeEntity> show delegates|delegate [csv]
|
||||
gam <UserTypeEntity> print delegates [todrive]
|
||||
|
||||
gam <UserTypeEntity> create|add contactdelegate <EmailAddress>
|
||||
gam <UserTypeEntity> delete|del contactdelegate <EmailAddress>
|
||||
gam <UserTypeEntity> show contactdelegates [csv]
|
||||
gam <UserTypeEntity> print contactdelegates [todrive]
|
||||
|
||||
gam <UserTypeEntity> [create|add] filter [from <EmailAddress>] [to <EmailAddress>] [subject <String>] [haswords|query <List>] [nowords|negatedquery <List>] [musthaveattachment|hasattachment] [excludechats] [size larger|smaller <ByteCount>]
|
||||
[label <LabelID>] [important|notimportant] [star] [trash] [markread] [archive] [neverspam] [forward <EmailAddress>]
|
||||
gam <UserTypeEntity> delete filters <FilterIDEntity>
|
||||
@@ -1491,4 +1576,3 @@ gam <UserTypeEntity> show vacation [format]
|
||||
|
||||
gam <UserTypeEntity> signout
|
||||
gam <UserTypeEntity> turnoff2sv
|
||||
|
||||
|
||||
393
src/cbcm-v1.1beta1.json
Normal file
393
src/cbcm-v1.1beta1.json
Normal file
@@ -0,0 +1,393 @@
|
||||
{
|
||||
"auth": {
|
||||
"oauth2": {
|
||||
"scopes": {
|
||||
"https://www.googleapis.com/auth/admin.directory.device.chromebrowsers": {
|
||||
"description": "View and manage your Chrome browsers registered with Cloud Management"
|
||||
},
|
||||
"https://www.googleapis.com/auth/admin.directory.device.chromebrowsers.readonly": {
|
||||
"description": "View your Chrome browsers registered with Cloud Management"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"basePath": "",
|
||||
"baseUrl": "https://www.googleapis.com/admin/directory/v1.1beta1/customer/",
|
||||
"batchPath": "batch",
|
||||
"canonicalName": "cbcm",
|
||||
"discoveryVersion": "v1",
|
||||
"documentationLink": "https://support.google.com/chrome/a/answer/9681204",
|
||||
"fullyEncodeReservedExpansion": true,
|
||||
"icons": {
|
||||
"x16": "http://www.google.com/images/icons/product/search-16.gif",
|
||||
"x32": "http://www.google.com/images/icons/product/search-32.gif"
|
||||
},
|
||||
"id": "cbcm:v1.1beta1",
|
||||
"kind": "discovery#restDescription",
|
||||
"mtlsRootUrl": "https://admin.mtls.googleapis.com/",
|
||||
"name": "cbcm",
|
||||
"ownerDomain": "google.com",
|
||||
"ownerName": "Jay Lee",
|
||||
"packagePath": "cbcm",
|
||||
"parameters": {
|
||||
"$.xgafv": {
|
||||
"description": "V1 error format.",
|
||||
"enum": [
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"enumDescriptions": [
|
||||
"v1 error format",
|
||||
"v2 error format"
|
||||
],
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"access_token": {
|
||||
"description": "OAuth access token.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"alt": {
|
||||
"default": "json",
|
||||
"description": "Data format for response.",
|
||||
"enum": [
|
||||
"json",
|
||||
"media",
|
||||
"proto"
|
||||
],
|
||||
"enumDescriptions": [
|
||||
"Responses with Content-Type of application/json",
|
||||
"Media download with context-dependent Content-Type",
|
||||
"Responses with Content-Type of application/x-protobuf"
|
||||
],
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"callback": {
|
||||
"description": "JSONP",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"fields": {
|
||||
"description": "Selector specifying which fields to include in a partial response.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"key": {
|
||||
"description": "API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"oauth_token": {
|
||||
"description": "OAuth 2.0 token for the current user.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"prettyPrint": {
|
||||
"default": "true",
|
||||
"description": "Returns response with indentations and line breaks.",
|
||||
"location": "query",
|
||||
"type": "boolean"
|
||||
},
|
||||
"quotaUser": {
|
||||
"description": "Available to use for quota purposes for server-side applications. Can be any arbitrary string assigned to a user, but should not exceed 40 characters.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"uploadType": {
|
||||
"description": "Legacy upload protocol for media (e.g. \"media\", \"multipart\").",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"upload_protocol": {
|
||||
"description": "Upload protocol for media (e.g. \"raw\", \"multipart\").",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"protocol": "rest",
|
||||
"resources": {
|
||||
"chromebrowsers": {
|
||||
"methods": {
|
||||
"delete": {
|
||||
"description": "Deletes a browser.",
|
||||
"flatPath": "{customer}/devices/chromebrowsers/{deviceId}",
|
||||
"httpMethod": "DELETE",
|
||||
"id": "cbcm.chromebrowsers.delete",
|
||||
"parameterOrder": [
|
||||
"customer",
|
||||
"deviceId"
|
||||
],
|
||||
"parameters": {
|
||||
"customer": {
|
||||
"description": "Immutable ID of the G Suite account.",
|
||||
"location": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
"deviceId": {
|
||||
"description": "Immutable ID of the browser.",
|
||||
"location": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"path": "{customer}/devices/chromebrowsers/{deviceId}",
|
||||
"scopes": [
|
||||
"https://www.googleapis.com/auth/admin.directory.device.chromebrowsers"
|
||||
]
|
||||
},
|
||||
"get": {
|
||||
"description": "Retrieves a browser.",
|
||||
"flatPath": "{customer}/devices/chromebrowsers/{deviceId}",
|
||||
"httpMethod": "GET",
|
||||
"id": "cbcm.chromebrowsers.get",
|
||||
"parameterOrder": [
|
||||
"customer",
|
||||
"deviceId"
|
||||
],
|
||||
"parameters": {
|
||||
"customer": {
|
||||
"description": "Immutable ID of the G Suite account.",
|
||||
"location": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
"deviceId": {
|
||||
"description": "Immutable ID of the browser.",
|
||||
"location": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
"projection": {
|
||||
"description": "Restrict information returned to a set of selected fields. FULL or BASIC.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"path": "{customer}/devices/chromebrowsers/{deviceId}",
|
||||
"response": {
|
||||
"$ref": "ChromeBrowser"
|
||||
},
|
||||
"scopes": [
|
||||
"https://www.googleapis.com/auth/admin.directory.device.chromebrowsers",
|
||||
"https://www.googleapis.com/auth/admin.directory.device.chromebrowsers.readonly"
|
||||
]
|
||||
},
|
||||
"list": {
|
||||
"description": "Retrieves a paginated list of all the browsers in a domain.",
|
||||
"flatPath": "{customer}/devices/chromebrowsers",
|
||||
"httpMethod": "GET",
|
||||
"id": "cbcm.chromebrowsers.list",
|
||||
"parameterOrder": [
|
||||
"customer"
|
||||
],
|
||||
"parameters": {
|
||||
"customer": {
|
||||
"description": "Immutable ID of the G Suite account.",
|
||||
"location": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
"maxResults": {
|
||||
"description": "Maximum number of results to return.",
|
||||
"format": "int32",
|
||||
"location": "query",
|
||||
"maximum": "100",
|
||||
"minimum": "1",
|
||||
"type": "integer"
|
||||
},
|
||||
"orderBy": {
|
||||
"description": "property to use for sorting results.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"orgUnitPath": {
|
||||
"description": "The full path of the organizational unit or its unique ID.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"pageToken": {
|
||||
"description": "Token to specify the next page in the list.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"projection": {
|
||||
"description": "Restrict information returned to a set of selected fields. FULL or BASIC.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"query": {
|
||||
"description": "Search string using the list page query language.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"sortOrder": {
|
||||
"description": "Whether to return results in ascending or descending order. Must be used with the orderBy parameter.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"path": "{customer}/devices/chromebrowsers",
|
||||
"response": {
|
||||
"$ref": "ChromeBrowsers"
|
||||
},
|
||||
"scopes": [
|
||||
"https://www.googleapis.com/auth/admin.directory.device.chromebrowsers",
|
||||
"https://www.googleapis.com/auth/admin.directory.device.chromebrowsers.readonly"
|
||||
]
|
||||
},
|
||||
"moveChromeBrowsersToOu": {
|
||||
"description": "Move Chrome Browsers Device between Organization Units",
|
||||
"flatPath": "{customer}/devices/chromebrowsers/moveChromeBrowsersToOu",
|
||||
"httpMethod": "POST",
|
||||
"id": "cbcm.chromebrowsers.moveChromeBrowsersToOu",
|
||||
"parameterOrder": [
|
||||
"customer"
|
||||
],
|
||||
"parameters": {
|
||||
"customer": {
|
||||
"description": "Immutable ID of the G Suite account.",
|
||||
"location": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"path": "{customer}/devices/chromebrowsers/moveChromeBrowsersToOu",
|
||||
"request": {
|
||||
"$ref": "MoveChromeBrowsersRequest"
|
||||
},
|
||||
"scopes": [
|
||||
"https://www.googleapis.com/auth/admin.directory.device.chromebrowsers"
|
||||
]
|
||||
},
|
||||
"update": {
|
||||
"description": "Updates a browser.",
|
||||
"flatPath": "{customer}/devices/chromebrowsers/{deviceId}",
|
||||
"httpMethod": "PUT",
|
||||
"id": "cbcm.chromebrowsers.update",
|
||||
"parameterOrder": [
|
||||
"customer",
|
||||
"deviceId"
|
||||
],
|
||||
"parameters": {
|
||||
"customer": {
|
||||
"description": "Immutable ID of the G Suite account.",
|
||||
"location": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
"deviceId": {
|
||||
"description": "Immutable ID of the browser.",
|
||||
"location": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
"projection": {
|
||||
"description": "BASIC or FULL",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"path": "{customer}/devices/chromebrowsers/{deviceId}",
|
||||
"request": {
|
||||
"$ref": "ChromeBrowser"
|
||||
},
|
||||
"response": {
|
||||
"$ref": "ChromeBrowser"
|
||||
},
|
||||
"scopes": [
|
||||
"https://www.googleapis.com/auth/admin.directory.device.chromebrowsers"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"revision": "20201203",
|
||||
"rootUrl": "https://www.googleapis.com/admin/directory/v1.1beta1/customer/",
|
||||
"schemas": {
|
||||
"ChromeBrowser": {
|
||||
"id": "ChromeBrowser",
|
||||
"properties": {
|
||||
"annotatedAssetId": {
|
||||
"description": "Asset identifier as annotated by the administrator or specified during enrollment.",
|
||||
"type": "string"
|
||||
},
|
||||
"annotatedLocation": {
|
||||
"description": "Address or location of the device as annotated by the administrator.",
|
||||
"type": "string"
|
||||
},
|
||||
"annotatedNotes": {
|
||||
"description": "Notes about this device as annotated by the administrator",
|
||||
"type": "string"
|
||||
},
|
||||
"annotatedUser": {
|
||||
"description": "User of the device as annotated by the administrator.",
|
||||
"type": "string"
|
||||
},
|
||||
"deviceId": {
|
||||
"annotations": {
|
||||
"required": [
|
||||
"cbcm.chromebrowsers.update"
|
||||
]
|
||||
},
|
||||
"description": "The unique ID of the device.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"ChromeBrowsers": {
|
||||
"id": "ChromeBrowsers",
|
||||
"properties": {
|
||||
"browsers": {
|
||||
"description": "List of Chrome browser objects.",
|
||||
"items": {
|
||||
"$ref": "ChromeBrowser"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"etag": {
|
||||
"description": "ETag of the resource.",
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"default": "admin#directory#chromeosdevices",
|
||||
"description": "Kind of resource this is.",
|
||||
"type": "string"
|
||||
},
|
||||
"nextPageToken": {
|
||||
"description": "Token used to access the next page of this result. To access the next page, use this token's value in the `pageToken` query string of this request.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"MoveChromeBrowsersRequest": {
|
||||
"properties": {
|
||||
"org_unit_path": {
|
||||
"annotations": {
|
||||
"required": [
|
||||
"cbcm.chromebrowsers.moveChromeBrowsersToOu"
|
||||
]
|
||||
},
|
||||
"description": "Destination organization unit to move devices to. Full path of the organizational unit or its ID prefixed with id:",
|
||||
"type": "string"
|
||||
},
|
||||
"resource_ids": {
|
||||
"annotations": {
|
||||
"required": [
|
||||
"cbcm.chromebrowsers.moveChromeBrowsersToOu"
|
||||
]
|
||||
},
|
||||
"description": "List of unique device IDs of Chrome Browser Devices to move. A maximum of 600 browsers may be moved per request.",
|
||||
"type": "array"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"servicePath": "",
|
||||
"title": "Admin SDK API",
|
||||
"version": "cbcm_v1.1beta1"
|
||||
}
|
||||
249
src/contactdelegation-v1.json
Normal file
249
src/contactdelegation-v1.json
Normal file
@@ -0,0 +1,249 @@
|
||||
{
|
||||
"auth": {
|
||||
"oauth2": {
|
||||
"scopes": {
|
||||
"https://www.googleapis.com/auth/admin.contact.delegation": {
|
||||
"description": "View and manage your Contact Delegation"
|
||||
},
|
||||
"https://www.googleapis.com/auth/admin.contact.delegation.readonly": {
|
||||
"description": "View your Contact Delegation"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"basePath": "",
|
||||
"baseUrl": "https://admin.googleapis.com/admin/contacts/v1/",
|
||||
"batchPath": "batch",
|
||||
"canonicalName": "contactdelegation",
|
||||
"description": "The Contact Delegation API allows Admins to delegate access of one user's, called the delegator, contacts to another user, called the delegate.",
|
||||
"discoveryVersion": "v1",
|
||||
"documentationLink": "https://developers.google.com/admin-sdk/contact-delegation",
|
||||
"fullyEncodeReservedExpansion": true,
|
||||
"icons": {
|
||||
"x16": "http://www.google.com/images/icons/product/search-16.gif",
|
||||
"x32": "http://www.google.com/images/icons/product/search-32.gif"
|
||||
},
|
||||
"id": "contactdelegation:v1",
|
||||
"kind": "discovery#restDescription",
|
||||
"name": "contactdelegation",
|
||||
"ownerDomain": "google.com",
|
||||
"ownerName": "Google",
|
||||
"packagePath": "admin",
|
||||
"parameters": {
|
||||
"$.xgafv": {
|
||||
"description": "V1 error format.",
|
||||
"enum": [
|
||||
"1",
|
||||
"2"
|
||||
],
|
||||
"enumDescriptions": [
|
||||
"v1 error format",
|
||||
"v2 error format"
|
||||
],
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"access_token": {
|
||||
"description": "OAuth access token.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"alt": {
|
||||
"default": "json",
|
||||
"description": "Data format for response.",
|
||||
"enum": [
|
||||
"json",
|
||||
"media",
|
||||
"proto"
|
||||
],
|
||||
"enumDescriptions": [
|
||||
"Responses with Content-Type of application/json",
|
||||
"Media download with context-dependent Content-Type",
|
||||
"Responses with Content-Type of application/x-protobuf"
|
||||
],
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"callback": {
|
||||
"description": "JSONP",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"fields": {
|
||||
"description": "Selector specifying which fields to include in a partial response.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"key": {
|
||||
"description": "API key. Your API key identifies your project and provides you with API access, quota, and reports. Required unless you provide an OAuth 2.0 token.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"oauth_token": {
|
||||
"description": "OAuth 2.0 token for the current user.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"prettyPrint": {
|
||||
"default": "true",
|
||||
"description": "Returns response with indentations and line breaks.",
|
||||
"location": "query",
|
||||
"type": "boolean"
|
||||
},
|
||||
"quotaUser": {
|
||||
"description": "Available to use for quota purposes for server-side applications. Can be any arbitrary string assigned to a user, but should not exceed 40 characters.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"uploadType": {
|
||||
"description": "Legacy upload protocol for media (e.g. \"media\", \"multipart\").",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"upload_protocol": {
|
||||
"description": "Upload protocol for media (e.g. \"raw\", \"multipart\").",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"protocol": "rest",
|
||||
"resources": {
|
||||
"delegates": {
|
||||
"methods": {
|
||||
"create": {
|
||||
"description": "Creates a contact delegations",
|
||||
"flatPath": "users/{user}/delegates",
|
||||
"httpMethod": "POST",
|
||||
"id": "contactdelegations.delegates.create",
|
||||
"parameterOrder": [
|
||||
"user"
|
||||
],
|
||||
"parameters": {
|
||||
"user": {
|
||||
"description": "Email address of the delegator.",
|
||||
"location": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"path": "users/{user}/delegates/{delegate}",
|
||||
"request": {
|
||||
"$ref": "Delegate"
|
||||
},
|
||||
"scopes": [
|
||||
"https://www.googleapis.com/auth/admin.contact.delegation"
|
||||
]
|
||||
},
|
||||
"delete": {
|
||||
"description": "Deletes a contact delegation.",
|
||||
"flatPath": "users/{user}/delegates/{delegate}",
|
||||
"httpMethod": "DELETE",
|
||||
"id": "contactdelegations.delegates.delete",
|
||||
"parameterOrder": [
|
||||
"user",
|
||||
"delegate"
|
||||
],
|
||||
"parameters": {
|
||||
"delegate": {
|
||||
"description": "Email address of the delegate",
|
||||
"location": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
},
|
||||
"user": {
|
||||
"description": "Email address of the delegator.",
|
||||
"location": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"path": "users/{user}/delegates/{delegate}",
|
||||
"scopes": [
|
||||
"https://www.googleapis.com/auth/admin.contact.delegation"
|
||||
]
|
||||
},
|
||||
"list": {
|
||||
"description": "Lists contact delegates for a user",
|
||||
"flatPath": "users/{user}/delegates",
|
||||
"httpMethod": "GET",
|
||||
"id": "contactdelegations.delegates.list",
|
||||
"parameterOrder": [
|
||||
"user"
|
||||
],
|
||||
"parameters": {
|
||||
"pageSize": {
|
||||
"description": "Determines how many delegates are returned in each response. ",
|
||||
"format": "int32",
|
||||
"location": "query",
|
||||
"minimum": "1",
|
||||
"type": "integer"
|
||||
},
|
||||
"pageToken": {
|
||||
"description": "Token to specify the next page in the list.",
|
||||
"location": "query",
|
||||
"type": "string"
|
||||
},
|
||||
"user": {
|
||||
"description": "Email address of the delegator.",
|
||||
"location": "path",
|
||||
"required": true,
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"path": "users/{user}/delegates",
|
||||
"response": {
|
||||
"$ref": "Delegates"
|
||||
},
|
||||
"scopes": [
|
||||
"https://www.googleapis.com/auth/admin.contact.delegation",
|
||||
"https://www.googleapis.com/auth/admin.contact.delegation.readonly"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"rootUrl": "https://admin.googleapis.com/admin/contacts/v1/",
|
||||
"schemas": {
|
||||
"Delegate": {
|
||||
"description": "JSON template for a delegate.",
|
||||
"id": "Delegate",
|
||||
"properties": {
|
||||
"email": {
|
||||
"description": "Email of the delegate.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
},
|
||||
"Delegates": {
|
||||
"id": "Delegates",
|
||||
"properties": {
|
||||
"delegates": {
|
||||
"description": "List of delegates.",
|
||||
"items": {
|
||||
"$ref": "Delegate"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"etag": {
|
||||
"description": "ETag of the resource.",
|
||||
"type": "string"
|
||||
},
|
||||
"kind": {
|
||||
"default": "",
|
||||
"description": "Kind of resource this is.",
|
||||
"type": "string"
|
||||
},
|
||||
"nextPageToken": {
|
||||
"description": "Token used to access the next page of this result. To access the next page, use this token's value in the `pageToken` query string of this request.",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"servicePath": "",
|
||||
"title": "Contact Delegation API",
|
||||
"version": "v1",
|
||||
"version_module": true
|
||||
}
|
||||
@@ -28,7 +28,7 @@ upgrade_only=false
|
||||
gamversion="latest"
|
||||
adminuser=""
|
||||
regularuser=""
|
||||
gam_glibc_vers="2.27 2.23"
|
||||
gam_glibc_vers="2.31 2.27 2.23"
|
||||
gam_macos_vers="10.15.6 10.14.6 10.13.6"
|
||||
|
||||
while getopts "hd:a:o:b:lp:u:r:v:" OPTION
|
||||
@@ -140,7 +140,8 @@ case $gamos in
|
||||
echo_red "Sorry, you need to be running at least MacOS $gam_macos_ver to run GAM"
|
||||
exit
|
||||
fi
|
||||
gamfile="macos-x86_64-$use_macos_ver.tar.xz"
|
||||
#gamfile="macos-x86_64-$use_macos_ver.tar.xz"
|
||||
gamfile="macos-x86_64.tar.xz"
|
||||
;;
|
||||
*)
|
||||
echo_red "Sorry, this installer currently only supports Linux and MacOS. Looks like you're runnning on $gamos. Exiting."
|
||||
|
||||
@@ -12,6 +12,7 @@ proot = os.path.dirname(importlib.import_module('httplib2').__file__)
|
||||
extra_files = [(os.path.join(proot, 'cacerts.txt'), 'httplib2')]
|
||||
|
||||
extra_files += copy_metadata('google-api-python-client')
|
||||
extra_files += [('cbcm-v1.1beta1.json', '.')]
|
||||
|
||||
a = Analysis(['gam/__main__.py'],
|
||||
hiddenimports=[],
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""Main behavioral methods and argument routing for GAM."""
|
||||
|
||||
|
||||
import base64
|
||||
import configparser
|
||||
import csv
|
||||
@@ -49,8 +50,10 @@ from gam import display
|
||||
from gam import fileutils
|
||||
from gam.gapi import calendar as gapi_calendar
|
||||
from gam.gapi import cloudidentity as gapi_cloudidentity
|
||||
from gam.gapi import cbcm as gapi_cbcm
|
||||
from gam.gapi.cloudidentity import devices as gapi_cloudidentity_devices
|
||||
from gam.gapi.cloudidentity import groups as gapi_cloudidentity_groups
|
||||
from gam.gapi import contactdelegation as gapi_contactdelegation
|
||||
from gam.gapi.directory import asps as gapi_directory_asps
|
||||
from gam.gapi.directory import cros as gapi_directory_cros
|
||||
from gam.gapi.directory import customer as gapi_directory_customer
|
||||
@@ -1091,7 +1094,7 @@ def buildAlertCenterGAPIObject(user):
|
||||
|
||||
def buildActivityGAPIObject(user):
|
||||
userEmail = convertUIDtoEmailAddress(user)
|
||||
return (userEmail, buildGAPIServiceObject('appsactivity', userEmail))
|
||||
return (userEmail, buildGAPIServiceObject('driveactivity', userEmail))
|
||||
|
||||
|
||||
def buildDriveGAPIObject(user):
|
||||
@@ -2718,7 +2721,7 @@ def deletePhoto(users):
|
||||
for user in users:
|
||||
i += 1
|
||||
print(f'Deleting photo for {user}{currentCount(i, count)}')
|
||||
gapi.call(cd.users().photos(), 'delete', userKey=user)
|
||||
gapi.call(cd.users().photos(), 'delete', userKey=user, soft_errors=True)
|
||||
|
||||
|
||||
def printDriveSettings(users):
|
||||
@@ -2786,23 +2789,58 @@ def getTeamDriveThemes(users):
|
||||
|
||||
|
||||
def printDriveActivity(users):
|
||||
drive_ancestorId = 'root'
|
||||
drive_fileId = None
|
||||
def _get_user_info(user_id):
|
||||
if user_id.startswith('people/'):
|
||||
user_id = user_id[7:]
|
||||
entry = user_info.get(user_id)
|
||||
if entry is None:
|
||||
result = gapi.call(cd.users(), 'get',
|
||||
soft_errors=True,
|
||||
userKey=user_id, fields='primaryEmail,name.fullName')
|
||||
if result:
|
||||
entry = (result['primaryEmail'], result['name']['fullName'])
|
||||
else:
|
||||
entry = (f'uid:{user_id}', 'Unknown')
|
||||
user_info[user_id] = entry
|
||||
return entry
|
||||
|
||||
def _update_known_users(structure):
|
||||
if isinstance(structure, list):
|
||||
for v in structure:
|
||||
if isinstance(v, (dict, list)):
|
||||
_update_known_users(v)
|
||||
elif isinstance(structure, dict):
|
||||
for k, v in sorted(iter(structure.items())):
|
||||
if k != 'knownUser':
|
||||
if isinstance(v, (dict, list)):
|
||||
_update_known_users(v)
|
||||
else:
|
||||
entry = _get_user_info(v['personName'])
|
||||
v['emailAddress'] = entry[0]
|
||||
v['personName'] = entry[1]
|
||||
break
|
||||
|
||||
cd = buildGAPIObject('directory')
|
||||
drive_key = 'ancestorName'
|
||||
drive_fileId = 'root'
|
||||
user_info = {}
|
||||
todrive = False
|
||||
titles = [
|
||||
'user.name', 'user.permissionId', 'target.id', 'target.name',
|
||||
'target.mimeType'
|
||||
'user.name', 'user.emailAddress', 'target.id', 'target.name',
|
||||
'target.mimeType', 'eventTime'
|
||||
]
|
||||
sort_titles = titles[:]
|
||||
csvRows = []
|
||||
i = 5
|
||||
while i < len(sys.argv):
|
||||
activity_object = sys.argv[i].lower().replace('_', '')
|
||||
if activity_object == 'fileid':
|
||||
drive_fileId = sys.argv[i + 1]
|
||||
drive_ancestorId = None
|
||||
drive_key = 'itemName'
|
||||
i += 2
|
||||
elif activity_object == 'folderid':
|
||||
drive_ancestorId = sys.argv[i + 1]
|
||||
drive_fileId = sys.argv[i + 1]
|
||||
drive_key = 'ancestorName'
|
||||
i += 2
|
||||
elif activity_object == 'todrive':
|
||||
todrive = True
|
||||
@@ -2810,23 +2848,57 @@ def printDriveActivity(users):
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i],
|
||||
'gam <users> show driveactivity')
|
||||
|
||||
for user in users:
|
||||
user, activity = buildActivityGAPIObject(user)
|
||||
if not activity:
|
||||
continue
|
||||
page_token = None
|
||||
total_items = 0
|
||||
kwargs = {drive_key: f'items/{drive_fileId}',
|
||||
'pageToken': page_token}
|
||||
page_message = gapi.got_total_items_msg(f'Activities for {user}', '')
|
||||
feed = gapi.get_all_pages(activity.activities(),
|
||||
'list',
|
||||
'activities',
|
||||
page_message=page_message,
|
||||
source='drive.google.com',
|
||||
userId='me',
|
||||
drive_ancestorId=drive_ancestorId,
|
||||
groupingStrategy='none',
|
||||
drive_fileId=drive_fileId)
|
||||
for item in feed:
|
||||
display.add_row_titles_to_csv_file(
|
||||
utils.flatten_json(item['combinedEvent']), csvRows, titles)
|
||||
while True:
|
||||
feed = gapi.call(activity.activity(), 'query', body=kwargs)
|
||||
page_token, total_items = gapi.process_page(feed, 'activities', None, total_items, page_message, None)
|
||||
kwargs['pageToken'] = page_token
|
||||
if feed:
|
||||
for activity_event in feed.get('activities', []):
|
||||
event_row = {}
|
||||
actors = activity_event.get('actors', [])
|
||||
if actors:
|
||||
userId = actors[0].get('user', {}).get('knownUser', {}).get('personName', '')
|
||||
if not userId:
|
||||
userId = actors[0].get('impersonation', {}).get('impersonatedUser', {}).get('knownUser', {}).get('personName', '')
|
||||
if userId:
|
||||
entry = _get_user_info(userId)
|
||||
event_row['user.name'] = entry[1]
|
||||
event_row['user.emailAddress'] = entry[0]
|
||||
targets = activity_event.get('targets', [])
|
||||
if targets:
|
||||
driveItem = targets[0].get('driveItem')
|
||||
if driveItem:
|
||||
event_row['target.id'] = driveItem['name'][6:]
|
||||
event_row['target.name'] = driveItem['title']
|
||||
event_row['target.mimeType'] = driveItem['mimeType']
|
||||
else:
|
||||
teamDrive = targets[0].get('teamDrive')
|
||||
if teamDrive:
|
||||
event_row['target.id'] = teamDrive['name'][11:]
|
||||
event_row['target.name'] = teamDrive['title']
|
||||
if 'timestamp' in activity_event:
|
||||
event_row['eventTime'] = activity_event.pop('timestamp')
|
||||
elif 'timeRange' in activity_event:
|
||||
timeRange = activity_event.pop('timeRange')
|
||||
event_row['eventTime'] = f'{timeRange["startTime"]}-{timeRange["endTime"]}'
|
||||
_update_known_users(activity_event)
|
||||
display.add_row_titles_to_csv_file(
|
||||
utils.flatten_json(activity_event, flattened=event_row), csvRows, titles)
|
||||
del feed
|
||||
if not page_token:
|
||||
gapi.finalize_page_message(page_message)
|
||||
break
|
||||
display.sort_csv_titles(sort_titles, titles)
|
||||
display.write_csv_file(csvRows, titles, 'Drive Activity', todrive)
|
||||
|
||||
|
||||
@@ -3197,10 +3269,21 @@ def printDriveFileList(users):
|
||||
'kind', 'etag', 'selfLink'
|
||||
]:
|
||||
continue
|
||||
x_attrib = f'{attrib}.{j}.{list_attrib}'
|
||||
if x_attrib not in titles:
|
||||
titles.append(x_attrib)
|
||||
a_file[x_attrib] = l_attrib[list_attrib]
|
||||
if not isinstance(l_attrib[list_attrib], dict):
|
||||
x_attrib = f'{attrib}.{j}.{list_attrib}'
|
||||
if x_attrib not in titles:
|
||||
titles.append(x_attrib)
|
||||
a_file[x_attrib] = l_attrib[list_attrib]
|
||||
else:
|
||||
for sl_attrib in l_attrib[list_attrib]:
|
||||
if sl_attrib in [
|
||||
'kind', 'etag', 'selfLink'
|
||||
]:
|
||||
continue
|
||||
x_attrib = f'{attrib}.{j}.{list_attrib}.{sl_attrib}'
|
||||
if x_attrib not in titles:
|
||||
titles.append(x_attrib)
|
||||
a_file[x_attrib] = l_attrib[list_attrib][sl_attrib]
|
||||
elif isinstance(f_file[attrib], (str, int, bool)):
|
||||
if attrib not in titles:
|
||||
titles.append(attrib)
|
||||
@@ -3538,7 +3621,11 @@ def getDriveFileAttribute(i, body, parameters, myarg, update=False):
|
||||
sys.argv[i+2], f'gam <users> {operation} drivefile')
|
||||
i += 3
|
||||
if len(sys.argv) > i and sys.argv[i].lower() == 'reason':
|
||||
body['contentRestrictions'][0]['reason'] = sys.argv[i+1]
|
||||
if body['contentRestrictions'][0]['readOnly']:
|
||||
body['contentRestrictions'][0]['reason'] = sys.argv[i+1]
|
||||
else:
|
||||
controlflow.invalid_argument_exit(
|
||||
'reason', 'contentrestrictions readonly false')
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(
|
||||
@@ -3553,6 +3640,10 @@ def getDriveFileAttribute(i, body, parameters, myarg, update=False):
|
||||
return i
|
||||
|
||||
|
||||
def has_multiple_parents(body):
|
||||
return len(body.get('parents', [])) > 1
|
||||
|
||||
|
||||
def doUpdateDriveFile(users):
|
||||
fileIdSelection = {'fileIds': [], 'query': None}
|
||||
media_body = None
|
||||
@@ -3601,6 +3692,9 @@ def doUpdateDriveFile(users):
|
||||
body.setdefault('parents', [])
|
||||
for a_parent in more_parents:
|
||||
body['parents'].append({'id': a_parent})
|
||||
if has_multiple_parents(body):
|
||||
sys.stderr.write(f"Multiple parents ({len(body['parents'])}) specified for {user}, only one is allowed.\n")
|
||||
continue
|
||||
if fileIdSelection['query']:
|
||||
fileIdSelection['fileIds'] = doDriveSearch(
|
||||
drive, query=fileIdSelection['query'])
|
||||
@@ -3688,6 +3782,9 @@ def createDriveFile(users):
|
||||
body.setdefault('parents', [])
|
||||
for a_parent in more_parents:
|
||||
body['parents'].append({'id': a_parent})
|
||||
if has_multiple_parents(body):
|
||||
sys.stderr.write(f"Multiple parents ({len(body['parents'])}) specified for {user}, only one is allowed.\n")
|
||||
continue
|
||||
if parameters[DFA_LOCALFILEPATH]:
|
||||
media_body = googleapiclient.http.MediaFileUpload(
|
||||
parameters[DFA_LOCALFILEPATH],
|
||||
@@ -10097,6 +10194,11 @@ def doRequestOAuth(login_hint=None, scopes=None):
|
||||
|
||||
|
||||
OAUTH2_SCOPES = [
|
||||
{
|
||||
'name': 'Chrome Browser Cloud Management',
|
||||
'subscopes': ['readonly'],
|
||||
'scopes': 'https://www.googleapis.com/auth/admin.directory.device.chromebrowsers',
|
||||
},
|
||||
{
|
||||
'name':
|
||||
'Classroom API - counts as 5 scopes',
|
||||
@@ -10114,14 +10216,18 @@ OAUTH2_SCOPES = [
|
||||
'subscopes': ['readonly'],
|
||||
'scopes': 'https://www.googleapis.com/auth/cloud-identity.groups'
|
||||
},
|
||||
{
|
||||
'name': 'Contact Delegation',
|
||||
'subscopes': ['readonly'],
|
||||
'scopes': 'https://www.googleapis.com/auth/admin.contact.delegation'
|
||||
},
|
||||
{
|
||||
'name': 'Data Transfer API',
|
||||
'subscopes': ['readonly'],
|
||||
'scopes': 'https://www.googleapis.com/auth/admin.datatransfer'
|
||||
},
|
||||
{
|
||||
'name':
|
||||
'Directory API - Chrome OS Devices',
|
||||
'name': 'Directory API - Chrome OS Devices',
|
||||
'subscopes': ['readonly'],
|
||||
'scopes':
|
||||
'https://www.googleapis.com/auth/admin.directory.device.chromeos'
|
||||
@@ -11062,6 +11168,10 @@ def ProcessGAMCommand(args):
|
||||
gapi_directory_resource.updateFeature()
|
||||
elif argument in ['adminrole']:
|
||||
gapi_directory_roles.update()
|
||||
elif argument == 'deviceuserstate':
|
||||
gapi_cloudidentity_devices.update_state()
|
||||
elif argument in ['browser', 'browsers']:
|
||||
gapi_cbcm.update()
|
||||
else:
|
||||
controlflow.invalid_argument_exit(argument, 'gam update')
|
||||
sys.exit(0)
|
||||
@@ -11120,6 +11230,10 @@ def ProcessGAMCommand(args):
|
||||
gapi_directory_resource.getBuildingInfo()
|
||||
elif argument in ['device']:
|
||||
gapi_cloudidentity_devices.info()
|
||||
elif argument == 'deviceuserstate':
|
||||
gapi_cloudidentity_devices.info_state()
|
||||
elif argument in ['browser', 'browsers']:
|
||||
gapi_cbcm.info()
|
||||
else:
|
||||
controlflow.invalid_argument_exit(argument, 'gam info')
|
||||
sys.exit(0)
|
||||
@@ -11182,6 +11296,8 @@ def ProcessGAMCommand(args):
|
||||
doDeleteServiceAccountKeys()
|
||||
elif argument in ['adminrole']:
|
||||
gapi_directory_roles.delete()
|
||||
elif argument in ['browser', 'browsers']:
|
||||
gapi_cbcm.delete()
|
||||
else:
|
||||
controlflow.invalid_argument_exit(argument, 'gam delete')
|
||||
sys.exit(0)
|
||||
@@ -11272,6 +11388,10 @@ def ProcessGAMCommand(args):
|
||||
doPrintShowAlerts()
|
||||
elif argument in ['alertfeedback', 'alertsfeedback']:
|
||||
doPrintShowAlertFeedback()
|
||||
elif argument in ['browser', 'browsers']:
|
||||
gapi_cbcm.print_()
|
||||
elif argument in ['vaultcount']:
|
||||
gapi_vault.print_count()
|
||||
else:
|
||||
controlflow.invalid_argument_exit(argument, 'gam print')
|
||||
sys.exit(0)
|
||||
@@ -11290,6 +11410,11 @@ def ProcessGAMCommand(args):
|
||||
else:
|
||||
controlflow.invalid_argument_exit(argument, 'gam show')
|
||||
sys.exit(0)
|
||||
elif command == 'move':
|
||||
argument = sys.argv[2].lower()
|
||||
if argument in ['browser', 'browsers']:
|
||||
gapi_cbcm.move()
|
||||
sys.exit(0)
|
||||
elif command in ['oauth', 'oauth2']:
|
||||
argument = sys.argv[2].lower()
|
||||
if argument in ['request', 'create']:
|
||||
@@ -11391,6 +11516,14 @@ def ProcessGAMCommand(args):
|
||||
elif command == 'block':
|
||||
gapi_cloudidentity_devices.block_user()
|
||||
sys.exit(0)
|
||||
elif command in ['issuecommand', 'getcommand']:
|
||||
target = sys.argv[2].lower().replace('_', '')
|
||||
if target == 'cros':
|
||||
if command == 'issuecommand':
|
||||
gapi_directory_cros.issue_command()
|
||||
elif command == 'getcommand':
|
||||
gapi_directory_cros.get_command()
|
||||
sys.exit(0)
|
||||
users = getUsersToModify()
|
||||
command = sys.argv[3].lower()
|
||||
if command == 'print' and len(sys.argv) == 4:
|
||||
@@ -11469,6 +11602,8 @@ def ProcessGAMCommand(args):
|
||||
printShowTeamDrives(users, False)
|
||||
elif showWhat in ['teamdriveinfo']:
|
||||
doGetTeamDriveInfo(users)
|
||||
elif showWhat in ['contactdelegate', 'contactdelegates']:
|
||||
gapi_contactdelegation.print_(users, False)
|
||||
else:
|
||||
controlflow.invalid_argument_exit(showWhat, 'gam <users> show')
|
||||
elif command == 'print':
|
||||
@@ -11497,6 +11632,8 @@ def ProcessGAMCommand(args):
|
||||
printShowTokens(5, 'users', users, True)
|
||||
elif printWhat in ['teamdrive', 'teamdrives']:
|
||||
printShowTeamDrives(users, True)
|
||||
elif printWhat in ['contactdelegate', 'contactdelegates']:
|
||||
gapi_contactdelegation.print_(users, True)
|
||||
else:
|
||||
controlflow.invalid_argument_exit(printWhat,
|
||||
'gam <users> print')
|
||||
@@ -11577,6 +11714,8 @@ def ProcessGAMCommand(args):
|
||||
deleteSmime(users)
|
||||
elif delWhat == 'teamdrive':
|
||||
doDeleteTeamDrive(users)
|
||||
elif delWhat == 'contactdelegate':
|
||||
gapi_contactdelegation.delete(users)
|
||||
else:
|
||||
controlflow.invalid_argument_exit(delWhat, 'gam <users> delete')
|
||||
elif command in ['add', 'create']:
|
||||
@@ -11609,6 +11748,8 @@ def ProcessGAMCommand(args):
|
||||
addSmime(users)
|
||||
elif addWhat == 'teamdrive':
|
||||
doCreateTeamDrive(users)
|
||||
elif addWhat == 'contactdelegate':
|
||||
gapi_contactdelegation.create(users)
|
||||
else:
|
||||
controlflow.invalid_argument_exit(addWhat,
|
||||
f'gam <users> {command}')
|
||||
|
||||
@@ -66,7 +66,7 @@ def csv_field_error_exit(field_name, field_names):
|
||||
|
||||
|
||||
def invalid_json_exit(file_name):
|
||||
"""Raises a sysyem exit when invalid JSON content is encountered."""
|
||||
"""Raises a system exit when invalid JSON content is encountered."""
|
||||
system_error_exit(17, MESSAGE_INVALID_JSON.format(file_name))
|
||||
|
||||
|
||||
|
||||
@@ -288,7 +288,7 @@ def print_json(object_value, spacing=''):
|
||||
sys.stdout.write(f' {spacing}{i+1}) ')
|
||||
print_json(a_value, f' {spacing}')
|
||||
elif isinstance(object_value, dict):
|
||||
for key in ['kind', 'etag', 'etags']:
|
||||
for key in ['kind', 'etag', 'etags', '@type']:
|
||||
object_value.pop(key, None)
|
||||
for another_object, another_value in object_value.items():
|
||||
sys.stdout.write(f' {spacing}{another_object}: ')
|
||||
|
||||
@@ -218,6 +218,61 @@ def got_total_items_first_last_msg(items):
|
||||
return f'Got {TOTAL_ITEMS_MARKER} {items}: {FIRST_ITEM_MARKER} - {LAST_ITEM_MARKER}' + '\n'
|
||||
|
||||
|
||||
def process_page(page, items, all_items, total_items, page_message, message_attribute):
|
||||
"""Process one page of a Google service function response.
|
||||
|
||||
Append a list of items to the aggregate list of items
|
||||
|
||||
Args:
|
||||
page: list of items
|
||||
items: see get_all_pages
|
||||
all_items: aggregate list of items
|
||||
total_items: length of all_items
|
||||
page_message: see get_all_pages
|
||||
message_attribute: get_all_pages
|
||||
Returns:
|
||||
The page token and total number of items
|
||||
"""
|
||||
if page:
|
||||
page_token = page.get('nextPageToken')
|
||||
page_items = page.get(items, [])
|
||||
num_page_items = len(page_items)
|
||||
total_items += num_page_items
|
||||
if all_items is not None:
|
||||
all_items.extend(page_items)
|
||||
else:
|
||||
page_token = None
|
||||
num_page_items = 0
|
||||
|
||||
# Show a paging message to the user that indicates paging progress
|
||||
if page_message:
|
||||
show_message = page_message.replace(TOTAL_ITEMS_MARKER,
|
||||
str(total_items))
|
||||
if message_attribute:
|
||||
first_item = page_items[0] if num_page_items > 0 else {}
|
||||
last_item = page_items[-1] if num_page_items > 1 else first_item
|
||||
if isinstance(message_attribute, str):
|
||||
first_item = str(first_item.get(message_attribute, ''))
|
||||
last_item = str(last_item.get(message_attribute, ''))
|
||||
else:
|
||||
for attr in message_attribute:
|
||||
first_item = first_item.get(attr, {})
|
||||
last_item = last_item.get(attr, {})
|
||||
first_item = str(first_item)
|
||||
last_item = str(last_item)
|
||||
show_message = show_message.replace(FIRST_ITEM_MARKER, first_item)
|
||||
show_message = show_message.replace(LAST_ITEM_MARKER, last_item)
|
||||
sys.stderr.write('\r')
|
||||
sys.stderr.flush()
|
||||
sys.stderr.write(show_message)
|
||||
return (page_token, total_items)
|
||||
|
||||
def finalize_page_message(page_message):
|
||||
""" Issue final page_message """
|
||||
if page_message and (page_message[-1] != '\n'):
|
||||
sys.stderr.write('\r\n')
|
||||
sys.stderr.flush()
|
||||
|
||||
def get_all_pages(service,
|
||||
function,
|
||||
items='items',
|
||||
@@ -274,46 +329,12 @@ def get_all_pages(service,
|
||||
soft_errors=soft_errors,
|
||||
throw_reasons=throw_reasons,
|
||||
retry_reasons=retry_reasons,
|
||||
pageToken=page_token,
|
||||
**kwargs)
|
||||
if page:
|
||||
page_token = page.get('nextPageToken')
|
||||
page_items = page.get(items, [])
|
||||
num_page_items = len(page_items)
|
||||
total_items += num_page_items
|
||||
all_items.extend(page_items)
|
||||
else:
|
||||
page_token = None
|
||||
num_page_items = 0
|
||||
|
||||
# Show a paging message to the user that indicates paging progress
|
||||
if page_message:
|
||||
show_message = page_message.replace(TOTAL_ITEMS_MARKER,
|
||||
str(total_items))
|
||||
if message_attribute:
|
||||
first_item = page_items[0] if num_page_items > 0 else {}
|
||||
last_item = page_items[-1] if num_page_items > 1 else first_item
|
||||
if type(message_attribute) is str:
|
||||
first_item = str(first_item.get(message_attribute, ''))
|
||||
last_item = str(last_item.get(message_attribute, ''))
|
||||
else:
|
||||
for attr in message_attribute:
|
||||
first_item = first_item.get(attr, {})
|
||||
last_item = last_item.get(attr, {})
|
||||
first_item = str(first_item)
|
||||
last_item = str(last_item)
|
||||
show_message = show_message.replace(FIRST_ITEM_MARKER, first_item)
|
||||
show_message = show_message.replace(LAST_ITEM_MARKER, last_item)
|
||||
sys.stderr.write('\r')
|
||||
sys.stderr.flush()
|
||||
sys.stderr.write(show_message)
|
||||
|
||||
page_token, total_items = process_page(page, items, all_items, total_items, page_message, message_attribute)
|
||||
if not page_token:
|
||||
# End the paging status message and return all items.
|
||||
if page_message and (page_message[-1] != '\n'):
|
||||
sys.stderr.write('\r\n')
|
||||
sys.stderr.flush()
|
||||
finalize_page_message(page_message)
|
||||
return all_items
|
||||
kwargs['pageToken'] = page_token
|
||||
|
||||
|
||||
# TODO: Make this private once all execution related items that use this method
|
||||
|
||||
197
src/gam/gapi/cbcm.py
Normal file
197
src/gam/gapi/cbcm.py
Normal file
@@ -0,0 +1,197 @@
|
||||
"""Chrome Browser Cloud Management API calls"""
|
||||
|
||||
import csv
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
import gam
|
||||
from gam.var import *
|
||||
from gam import controlflow
|
||||
from gam import display
|
||||
from gam import fileutils
|
||||
from gam import gapi
|
||||
from gam.gapi.directory import orgunits as gapi_directory_orgunits
|
||||
from gam import utils
|
||||
|
||||
|
||||
def build():
|
||||
return gam.buildGAPIObject('cbcm')
|
||||
|
||||
|
||||
def delete():
|
||||
cbcm = build()
|
||||
device_id = sys.argv[3]
|
||||
gapi.call(cbcm.chromebrowsers(), 'delete', deviceId=device_id,
|
||||
customer=GC_Values[GC_CUSTOMER_ID])
|
||||
print(f'Deleted browser {device_id}')
|
||||
|
||||
|
||||
def info():
|
||||
cbcm = build()
|
||||
device_id = sys.argv[3]
|
||||
projection = 'BASIC'
|
||||
fields = None
|
||||
i = 4
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg in ['basic', 'full']:
|
||||
projection = myarg.upper()
|
||||
i += 1
|
||||
elif myarg == 'fields':
|
||||
fields = sys.argv[i+1]
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam info browser')
|
||||
browser = gapi.call(cbcm.chromebrowsers(), 'get',
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
fields=fields, deviceId=device_id,
|
||||
projection=projection)
|
||||
display.print_json(browser)
|
||||
|
||||
|
||||
def move():
|
||||
cbcm = build()
|
||||
body = {'resource_ids': []}
|
||||
i = 3
|
||||
resource_ids = []
|
||||
batch_size = 600
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg == 'ids':
|
||||
resource_ids.extend(sys.argv[i + 1].split(','))
|
||||
i += 2
|
||||
elif myarg == 'query':
|
||||
query = sys.argv[i + 1]
|
||||
page_message = gapi.got_total_items_msg('Browsers', '...\n')
|
||||
browsers = gapi.get_all_pages(cbcm.chromebrowsers(), 'list',
|
||||
'browsers', page_message=page_message,
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
query=query, projection='BASIC',
|
||||
fields='browsers(deviceId),nextPageToken')
|
||||
ids = [browser['deviceId'] for browser in browsers]
|
||||
resource_ids.extend(ids)
|
||||
i += 2
|
||||
elif myarg == 'file':
|
||||
with fileutils.open_file(sys.argv[i+1], strip_utf_bom=True) as filed:
|
||||
for row in filed:
|
||||
rid = row.strip()
|
||||
if rid:
|
||||
resource_ids.append(rid)
|
||||
i += 2
|
||||
elif myarg == 'csvfile':
|
||||
drive, fname_column = os.path.splitdrive(sys.argv[i+1])
|
||||
if fname_column.find(':') == -1:
|
||||
controlflow.system_error_exit(
|
||||
2, 'Expected csvfile FileName:FieldName')
|
||||
(filename, column) = fname_column.split(':')
|
||||
with fileutils.open_file(drive + filename) as filed:
|
||||
input_file = csv.DictReader(filed, restval='')
|
||||
if column not in input_file.fieldnames:
|
||||
controlflow.csv_field_error_exit(column,
|
||||
input_file.fieldnames)
|
||||
for row in input_file:
|
||||
rid = row[column].strip()
|
||||
if rid:
|
||||
resource_ids.append(rid)
|
||||
i += 2
|
||||
elif myarg in ['ou', 'orgunit', 'org']:
|
||||
org_unit = gapi_directory_orgunits.getOrgUnitItem(sys.argv[i + 1])
|
||||
body['org_unit_path'] = org_unit
|
||||
i += 2
|
||||
elif myarg == 'batchsize':
|
||||
batch_size = int(sys.argv[i+1])
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i],
|
||||
'gam move browsers')
|
||||
if 'org_unit_path' not in body:
|
||||
controlflow.missing_argument_exit('ou', 'gam move browsers')
|
||||
elif not resource_ids:
|
||||
controlflow.missing_argument_exit('query or ids',
|
||||
'gam move browsers')
|
||||
# split moves into max 600 devices per batch
|
||||
for chunk in range(0, len(resource_ids), batch_size):
|
||||
body['resource_ids'] = resource_ids[chunk:chunk + batch_size]
|
||||
print(f' moving {len(body["resource_ids"])} browsers to ' \
|
||||
f'{body["org_unit_path"]}')
|
||||
gapi.call(cbcm.chromebrowsers(), 'moveChromeBrowsersToOu',
|
||||
customer=GC_Values[GC_CUSTOMER_ID], body=body)
|
||||
|
||||
|
||||
def print_():
|
||||
cbcm = build()
|
||||
projection = 'BASIC'
|
||||
query = None
|
||||
fields = None
|
||||
titles = []
|
||||
csv_rows = []
|
||||
todrive = False
|
||||
sort_headers = False
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg == 'query':
|
||||
query = sys.argv[i+1]
|
||||
i += 2
|
||||
elif myarg == 'projection':
|
||||
projection = sys.argv[i + 1].upper()
|
||||
i += 2
|
||||
elif myarg == 'todrive':
|
||||
todrive = True
|
||||
i += 1
|
||||
elif myarg == 'sortheaders':
|
||||
sort_headers = True
|
||||
i += 1
|
||||
elif myarg == 'fields':
|
||||
fields = sys.argv[i + 1]
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i],
|
||||
'gam print browsers')
|
||||
if fields:
|
||||
fields = f'browsers({fields}),nextPageToken'
|
||||
page_message = gapi.got_total_items_msg('Browsers', '...\n')
|
||||
browsers = gapi.get_all_pages(cbcm.chromebrowsers(), 'list',
|
||||
'browsers', page_message=page_message,
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
query=query, projection=projection,
|
||||
fields=fields)
|
||||
for browser in browsers:
|
||||
browser = utils.flatten_json(browser)
|
||||
for a_key in browser:
|
||||
if a_key not in titles:
|
||||
titles.append(a_key)
|
||||
csv_rows.append(browser)
|
||||
if sort_headers:
|
||||
display.sort_csv_titles(['name',], titles)
|
||||
display.write_csv_file(csv_rows, titles, 'Browsers', todrive)
|
||||
|
||||
attributes = {
|
||||
'assetid': 'annotatedAssetId',
|
||||
'location': 'annotatedLocation',
|
||||
'notes': 'annotatedNotes',
|
||||
'user': 'annotatedUser'
|
||||
}
|
||||
attribute_fields = ','.join(list(attributes.values()))
|
||||
|
||||
def update():
|
||||
cbcm = build()
|
||||
device_id = sys.argv[3]
|
||||
body = {'deviceId': device_id}
|
||||
i = 4
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg in attributes:
|
||||
body[attributes[myarg]] = sys.argv[i+1]
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i],
|
||||
'gam update browser')
|
||||
browser = gapi.call(cbcm.chromebrowsers(), 'get', deviceId=device_id,
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
projection='BASIC', fields=attribute_fields)
|
||||
browser.update(body)
|
||||
result = gapi.call(cbcm.chromebrowsers(), 'update', deviceId=device_id,
|
||||
customer=GC_Values[GC_CUSTOMER_ID], body=browser,
|
||||
projection='BASIC', fields="deviceId")
|
||||
print(f'Updated browser {result["deviceId"]}')
|
||||
@@ -3,6 +3,7 @@ import sys
|
||||
|
||||
import googleapiclient
|
||||
|
||||
import gam
|
||||
from gam.var import *
|
||||
from gam import controlflow
|
||||
from gam import display
|
||||
@@ -11,7 +12,7 @@ from gam import gapi
|
||||
from gam import utils
|
||||
from gam.gapi import errors as gapi_errors
|
||||
from gam.gapi import cloudidentity as gapi_cloudidentity
|
||||
|
||||
from gam.gapi.directory import customer as gapi_directory_customer
|
||||
|
||||
def _get_device_customerid():
|
||||
customer = GC_Values[GC_CUSTOMER_ID]
|
||||
@@ -49,6 +50,7 @@ def create():
|
||||
result = gapi.call(ci.devices(), 'create', customer=customer, body=body)
|
||||
print(f'Created device {result["response"]["name"]}')
|
||||
|
||||
|
||||
def _get_device_name():
|
||||
name = sys.argv[3]
|
||||
if name == 'id':
|
||||
@@ -65,10 +67,16 @@ def info():
|
||||
device = gapi.call(ci.devices(), 'get', name=name, customer=customer)
|
||||
device_users = gapi.get_all_pages(ci.devices().deviceUsers(), 'list',
|
||||
'deviceUsers', parent=name, customer=customer)
|
||||
for device_user in device_users:
|
||||
parent = device_user['name']
|
||||
device_user['client_states'] = gapi.get_all_pages(
|
||||
ci.devices().deviceUsers().clientStates(),
|
||||
'list', 'clientStates', parent=parent, customer=customer)
|
||||
display.print_json(device)
|
||||
print('Device Users:')
|
||||
display.print_json(device_users)
|
||||
|
||||
|
||||
def _generic_action(action, device_user=False):
|
||||
ci = gapi_cloudidentity.build_dwd()
|
||||
customer = _get_device_customerid()
|
||||
@@ -87,30 +95,153 @@ def _generic_action(action, device_user=False):
|
||||
op = gapi.call(endpoint, action, name=name, **kwargs)
|
||||
print(op)
|
||||
|
||||
|
||||
def delete():
|
||||
_generic_action('delete')
|
||||
|
||||
|
||||
def cancel_wipe():
|
||||
_generic_action('cancelWipe')
|
||||
|
||||
|
||||
def wipe():
|
||||
_generic_action('wipe')
|
||||
|
||||
|
||||
def approve_user():
|
||||
_generic_action('approve', True)
|
||||
|
||||
|
||||
def block_user():
|
||||
_generic_action('block', True)
|
||||
|
||||
|
||||
def cancel_wipe_user():
|
||||
_generic_action('cancelWipe', True)
|
||||
|
||||
|
||||
def delete_user():
|
||||
_generic_action('delete', True)
|
||||
|
||||
|
||||
def wipe_user():
|
||||
_generic_action('wipe', True)
|
||||
|
||||
|
||||
def _get_deviceuser_name():
|
||||
i = 3
|
||||
name = sys.argv[i]
|
||||
if name == 'id':
|
||||
i += 1
|
||||
name = sys.argv[i]
|
||||
if not name.startswith('devices/'):
|
||||
name = f'devices/{name}'
|
||||
return (i+1, name)
|
||||
|
||||
def info_state():
|
||||
ci = gapi_cloudidentity.build_dwd()
|
||||
gapi_directory_customer.setTrueCustomerId()
|
||||
customer = _get_device_customerid()
|
||||
customer_id = customer[10:]
|
||||
client_id = f'{customer_id}-gam'
|
||||
i, deviceuser = _get_deviceuser_name()
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg == 'clientid':
|
||||
client_id = f'{customer_id}-{sys.argv[i+1]}'
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam info deviceuserstate')
|
||||
name = f'{deviceuser}/clientStates/{client_id}'
|
||||
result = gapi.call(ci.devices().deviceUsers().clientStates(), 'get',
|
||||
name=name, customer=customer)
|
||||
display.print_json(result)
|
||||
|
||||
|
||||
def update_state():
|
||||
ci = gapi_cloudidentity.build_dwd()
|
||||
gapi_directory_customer.setTrueCustomerId()
|
||||
customer = _get_device_customerid()
|
||||
customer_id = customer[10:]
|
||||
client_id = f'{customer_id}-gam'
|
||||
body = {}
|
||||
i, deviceuser = _get_deviceuser_name()
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg == 'clientid':
|
||||
client_id = f'{customer_id}-{sys.argv[i+1]}'
|
||||
i += 2
|
||||
elif myarg in ['assettag', 'assettags']:
|
||||
body['assetTags'] = gam.shlexSplitList(sys.argv[i+1])
|
||||
if body['assetTags'] == ['clear']:
|
||||
# TODO: this doesn't work to clear
|
||||
# existing values. Figure out why.
|
||||
body['assetTags'] = [None]
|
||||
i += 2
|
||||
elif myarg in ['compliantstate', 'compliancestate']:
|
||||
comp_states = gapi.get_enum_values_minus_unspecified(
|
||||
ci._rootDesc['schemas']['GoogleAppsCloudidentityDevicesV1ClientState']['properties']['complianceState']['enum'])
|
||||
body['complianceState'] = sys.argv[i+1].upper()
|
||||
if body['complianceState'] not in comp_states:
|
||||
controlflow.expected_argument_exit('compliant_state',
|
||||
', '.join(comp_states),
|
||||
sys.argv[i+1])
|
||||
i += 2
|
||||
elif myarg == 'customid':
|
||||
body['customId'] = sys.argv[i+1]
|
||||
i += 2
|
||||
elif myarg == 'healthscore':
|
||||
health_scores = gapi.get_enum_values_minus_unspecified(
|
||||
ci._rootDesc['schemas']['GoogleAppsCloudidentityDevicesV1ClientState']['properties']['healthScore']['enum'])
|
||||
body['healthScore'] = sys.argv[i+1].upper()
|
||||
if body['healthScore'] == 'CLEAR':
|
||||
body['healthScore'] = None
|
||||
if body['healthScore'] and body['healthScore'] not in health_scores:
|
||||
controlflow.expected_argument_exit('health_score',
|
||||
', '.join(health_scores),
|
||||
sys.argv[i+1])
|
||||
i += 2
|
||||
elif myarg == 'customvalue':
|
||||
allowed_types = ['bool', 'number', 'string']
|
||||
value_type = sys.argv[i+1].lower()
|
||||
if value_type not in allowed_types:
|
||||
controlflow.expected_argument_exit('custom_value',
|
||||
', '.join(allowed_types),
|
||||
sys.argv[i+1])
|
||||
key = sys.argv[i+2]
|
||||
value = sys.argv[i+3]
|
||||
if value_type == 'bool':
|
||||
value = gam.getBoolean(value, key)
|
||||
elif value_type == 'number':
|
||||
value = int(value)
|
||||
body.setdefault('keyValuePairs', {})
|
||||
body['keyValuePairs'][key] = {f'{value_type}Value': value}
|
||||
i += 4
|
||||
elif myarg in ['managedstate']:
|
||||
managed_states = gapi.get_enum_values_minus_unspecified(
|
||||
ci._rootDesc['schemas']['GoogleAppsCloudidentityDevicesV1ClientState']['properties']['managed']['enum'])
|
||||
body['managed'] = sys.argv[i+1].upper()
|
||||
if body['managed'] == 'CLEAR':
|
||||
body['managed'] = None
|
||||
if body['managed'] and body['managed'] not in managed_states:
|
||||
controlflow.expected_argument_exit('managed_state',
|
||||
', '.join(managed_states),
|
||||
sys.argv[i+1])
|
||||
i += 2
|
||||
elif myarg in ['scorereason']:
|
||||
body['scoreReason'] = sys.argv[i+1]
|
||||
if body['scoreReason'] == 'clear':
|
||||
body['scoreReason'] = None
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam update deviceuserstate')
|
||||
name = f'{deviceuser}/clientStates/{client_id}'
|
||||
updateMask = ','.join(body.keys())
|
||||
result = gapi.call(ci.devices().deviceUsers().clientStates(), 'patch',
|
||||
name=name, customer=customer, updateMask=updateMask, body=body)
|
||||
display.print_json(result)
|
||||
|
||||
|
||||
def print_():
|
||||
ci = gapi_cloudidentity.build_dwd()
|
||||
customer = _get_device_customerid()
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import sys
|
||||
|
||||
import googleapiclient
|
||||
|
||||
import gam
|
||||
from gam.var import *
|
||||
from gam import controlflow
|
||||
@@ -160,6 +164,7 @@ def print_():
|
||||
members = membersCountOnly = managers = managersCountOnly = owners = ownersCountOnly = False
|
||||
gapi_directory_customer.setTrueCustomerId()
|
||||
parent = f'customers/{GC_Values[GC_CUSTOMER_ID]}'
|
||||
usemember = None
|
||||
memberDelimiter = '\n'
|
||||
todrive = False
|
||||
titles = []
|
||||
@@ -171,6 +176,10 @@ def print_():
|
||||
if myarg == 'todrive':
|
||||
todrive = True
|
||||
i += 1
|
||||
elif myarg == 'enterprisemember':
|
||||
member = gam.convertUIDtoEmailAddress(sys.argv[i + 1], email_types=['user', 'group'])
|
||||
usemember = f"member_key_id == '{member}' && 'cloudidentity.googleapis.com/groups.discussion_forum' in labels"
|
||||
i += 2
|
||||
elif myarg == 'delimiter':
|
||||
memberDelimiter = sys.argv[i + 1]
|
||||
i += 2
|
||||
@@ -222,16 +231,36 @@ def print_():
|
||||
display.add_titles_to_csv_file([
|
||||
'Owners',
|
||||
], titles)
|
||||
gam.printGettingAllItems('Groups', None)
|
||||
gam.printGettingAllItems('Groups', usemember)
|
||||
page_message = gapi.got_total_items_first_last_msg('Groups')
|
||||
entityList = gapi.get_all_pages(ci.groups(),
|
||||
'list',
|
||||
'groups',
|
||||
page_message=page_message,
|
||||
message_attribute=['groupKey', 'id'],
|
||||
parent=parent,
|
||||
view='FULL',
|
||||
pageSize=500)
|
||||
if usemember:
|
||||
try:
|
||||
result = gapi.get_all_pages(ci.groups().memberships(),
|
||||
'searchTransitiveGroups',
|
||||
'memberships',
|
||||
throw_reasons=[gapi_errors.ErrorReason.FOUR_O_O],
|
||||
page_message=page_message,
|
||||
message_attribute=['groupKey', 'id'],
|
||||
parent='groups/-', query=usemember,
|
||||
fields='nextPageToken,memberships(group,groupKey(id),relationType)',
|
||||
pageSize=1000)
|
||||
except googleapiclient.errors.HttpError:
|
||||
controlflow.system_error_exit(
|
||||
2,
|
||||
f'enterprisemember requires Enterprise license')
|
||||
entityList = []
|
||||
for entity in result:
|
||||
if entity['relationType'] == 'DIRECT':
|
||||
entityList.append(gapi.call(ci.groups(), 'get', name=entity['group']))
|
||||
else:
|
||||
entityList = gapi.get_all_pages(ci.groups(),
|
||||
'list',
|
||||
'groups',
|
||||
page_message=page_message,
|
||||
message_attribute=['groupKey', 'id'],
|
||||
parent=parent,
|
||||
view='FULL',
|
||||
pageSize=500)
|
||||
i = 0
|
||||
count = len(entityList)
|
||||
for groupEntity in entityList:
|
||||
@@ -319,6 +348,7 @@ def print_members():
|
||||
todrive = False
|
||||
gapi_directory_customer.setTrueCustomerId()
|
||||
parent = f'customers/{GC_Values[GC_CUSTOMER_ID]}'
|
||||
usemember = None
|
||||
roles = []
|
||||
titles = ['group']
|
||||
csvRows = []
|
||||
@@ -339,6 +369,10 @@ def print_members():
|
||||
f'{role} is not a valid role for "gam print group-members {myarg}"'
|
||||
)
|
||||
i += 2
|
||||
elif myarg == 'enterprisemember':
|
||||
member = gam.convertUIDtoEmailAddress(sys.argv[i + 1], email_types=['user', 'group'])
|
||||
usemember = f"member_key_id == '{member}' && 'cloudidentity.googleapis.com/groups.discussion_forum' in labels"
|
||||
i += 2
|
||||
elif myarg in ['cigroup', 'cigroups']:
|
||||
group_email = gam.normalizeEmailAddressOrUID(sys.argv[i + 1])
|
||||
groups_to_get = [group_email]
|
||||
@@ -347,19 +381,36 @@ def print_members():
|
||||
controlflow.invalid_argument_exit(sys.argv[i],
|
||||
'gam print cigroup-members')
|
||||
if not groups_to_get:
|
||||
gam.printGettingAllItems('Groups', None)
|
||||
gam.printGettingAllItems('Groups', usemember)
|
||||
page_message = gapi.got_total_items_first_last_msg('Groups')
|
||||
groups_to_get = gapi.get_all_pages(
|
||||
ci.groups(),
|
||||
'list',
|
||||
'groups',
|
||||
message_attribute=['groupKey', 'id'],
|
||||
page_message=page_message,
|
||||
parent=parent,
|
||||
view='BASIC',
|
||||
pageSize=1000,
|
||||
fields='nextPageToken,groups(groupKey(id))')
|
||||
groups_to_get = [group['groupKey']['id'] for group in groups_to_get]
|
||||
if usemember:
|
||||
try:
|
||||
groups_to_get = gapi.get_all_pages(ci.groups().memberships(),
|
||||
'searchTransitiveGroups',
|
||||
'memberships',
|
||||
throw_reasons=[gapi_errors.ErrorReason.FOUR_O_O],
|
||||
message_attribute=['groupKey', 'id'],
|
||||
page_message=page_message,
|
||||
parent='groups/-', query=usemember,
|
||||
pageSize=1000,
|
||||
fields='nextPageToken,memberships(groupKey(id),relationType)')
|
||||
except googleapiclient.errors.HttpError:
|
||||
controlflow.system_error_exit(
|
||||
2,
|
||||
f'enterprisemember requires Enterprise license')
|
||||
groups_to_get = [group['groupKey']['id'] for group in groups_to_get if group['relationType'] == 'DIRECT']
|
||||
else:
|
||||
groups_to_get = gapi.get_all_pages(
|
||||
ci.groups(),
|
||||
'list',
|
||||
'groups',
|
||||
message_attribute=['groupKey', 'id'],
|
||||
page_message=page_message,
|
||||
parent=parent,
|
||||
view='BASIC',
|
||||
pageSize=1000,
|
||||
fields='nextPageToken,groups(groupKey(id))')
|
||||
groups_to_get = [group['groupKey']['id'] for group in groups_to_get]
|
||||
i = 0
|
||||
count = len(groups_to_get)
|
||||
for group_email in groups_to_get:
|
||||
@@ -411,7 +462,7 @@ def update():
|
||||
|
||||
def _getRoleAndUsers():
|
||||
checkSuspended = None
|
||||
role = None
|
||||
role = ROLE_MEMBER
|
||||
expireTime = None
|
||||
i = 5
|
||||
if sys.argv[i].lower() in GROUP_ROLES_MAP:
|
||||
@@ -421,7 +472,10 @@ def update():
|
||||
checkSuspended = sys.argv[i].lower() == 'suspended'
|
||||
i += 1
|
||||
if sys.argv[i].lower() in ['expire', 'expires']:
|
||||
expireTime = sys.argv[i+1]
|
||||
if role != ROLE_MEMBER:
|
||||
controlflow.invalid_argument_exit(
|
||||
sys.argv[i], f'role {role}')
|
||||
expireTime = utils.get_time_or_delta_from_now(sys.argv[i+1])
|
||||
i += 2
|
||||
if sys.argv[i].lower() in usergroup_types:
|
||||
users_email = gam.getUsersToModify(entity_type=sys.argv[i].lower(),
|
||||
@@ -449,8 +503,6 @@ def update():
|
||||
return
|
||||
if myarg == 'add':
|
||||
role, expireTime, users_email = _getRoleAndUsers()
|
||||
if not role:
|
||||
role = ROLE_MEMBER
|
||||
if len(users_email) > 1:
|
||||
sys.stderr.write(
|
||||
f'Group: {group}, Will add {len(users_email)} {role}s.\n')
|
||||
@@ -473,7 +525,7 @@ def update():
|
||||
}
|
||||
if role != ROLE_MEMBER:
|
||||
body['roles'].append({'name': role})
|
||||
if expireTime:
|
||||
elif expireTime not in {None, NEVER_TIME}:
|
||||
for role in body['roles']:
|
||||
if role['name'] == ROLE_MEMBER:
|
||||
role['expiryDetail'] = {'expireTime': expireTime}
|
||||
@@ -544,7 +596,7 @@ def update():
|
||||
for user in to_add:
|
||||
item = ['gam', 'update', 'cigroup', f'id:{parent}', 'add',
|
||||
role,]
|
||||
if expireTime:
|
||||
if role == ROLE_MEMBER and expireTime not in {None, NEVER_TIME}:
|
||||
item.extend(['expires', expireTime])
|
||||
item.append(user)
|
||||
items.append(item)
|
||||
@@ -580,8 +632,6 @@ def update():
|
||||
)
|
||||
elif myarg == 'update':
|
||||
role, expireTime, users_email = _getRoleAndUsers()
|
||||
if not role:
|
||||
role = ROLE_MEMBER
|
||||
if len(users_email) > 1:
|
||||
sys.stderr.write(
|
||||
f'Group: {group}, Will update {len(users_email)} {role}s.\n'
|
||||
@@ -596,36 +646,48 @@ def update():
|
||||
items.append(item)
|
||||
elif len(users_email) > 0:
|
||||
name = membership_email_to_id(ci, parent, users_email[0])
|
||||
preUpdateRoles = []
|
||||
addRoles = []
|
||||
removeRoles = []
|
||||
current_roles = gapi.call(ci.groups().memberships(),
|
||||
'get',
|
||||
name=name,
|
||||
fields='roles').get('roles', [])
|
||||
current_roles = [role['name'] for role in current_roles]
|
||||
postUpdateRoles = []
|
||||
member_roles = gapi.call(ci.groups().memberships(),
|
||||
'get',
|
||||
name=name,
|
||||
fields='roles').get('roles', [{'name': ROLE_MEMBER}])
|
||||
current_roles = [crole['name'] for crole in member_roles]
|
||||
# When upgrading role, strip any expiryDetail from member before role changes
|
||||
if role != ROLE_MEMBER:
|
||||
for crole in member_roles:
|
||||
if 'expiryDetail' in crole:
|
||||
preUpdateRoles.append(
|
||||
{'fieldMask': 'expiryDetail.expireTime',
|
||||
'membershipRole': {'name': ROLE_MEMBER,
|
||||
'expiryDetail': {'expireTime': None}}})
|
||||
break
|
||||
# When downgrading role or simply updating member expireTime, update expiryDetail after role changes
|
||||
elif expireTime:
|
||||
postUpdateRoles.append(
|
||||
{'fieldMask': 'expiryDetail.expireTime',
|
||||
'membershipRole': {'name': role,
|
||||
'expiryDetail': {'expireTime': expireTime if expireTime != NEVER_TIME else None}}})
|
||||
for crole in current_roles:
|
||||
if crole not in {ROLE_MEMBER, role}:
|
||||
removeRoles.append(crole)
|
||||
if role not in current_roles:
|
||||
new_role = {'name': role}
|
||||
if role == ROLE_MEMBER and expireTime:
|
||||
if role == ROLE_MEMBER and expireTime not in {None, NEVER_TIME}:
|
||||
new_role['expiryDetail'] = {'expireTime': expireTime}
|
||||
expireTime = None
|
||||
postUpdateRoles = []
|
||||
addRoles.append(new_role)
|
||||
bodys = []
|
||||
if preUpdateRoles:
|
||||
bodys.append({'updateRolesParams': preUpdateRoles})
|
||||
if addRoles:
|
||||
bodys.append({'addRoles': addRoles})
|
||||
if removeRoles:
|
||||
bodys.append({'removeRoles': removeRoles})
|
||||
if expireTime:
|
||||
bodys.append({
|
||||
'name': ROLE_MEMBER,
|
||||
# Note this doesn't actually work for some reason. Only known method to change
|
||||
# expire time right now is to remove/re-add member.
|
||||
'expiryDetail': {
|
||||
'expireTime': expireTime
|
||||
}
|
||||
})
|
||||
if postUpdateRoles:
|
||||
bodys.append({'updateRolesParams': postUpdateRoles})
|
||||
for body in bodys:
|
||||
try:
|
||||
gapi.call(ci.groups().memberships(),
|
||||
|
||||
96
src/gam/gapi/contactdelegation.py
Normal file
96
src/gam/gapi/contactdelegation.py
Normal file
@@ -0,0 +1,96 @@
|
||||
"""Contact Delegation API calls"""
|
||||
|
||||
import sys
|
||||
|
||||
import gam
|
||||
from gam.gapi.directory import users as gapi_directory_users
|
||||
from gam import controlflow
|
||||
from gam import display
|
||||
from gam import gapi
|
||||
|
||||
|
||||
def build():
|
||||
return gam.buildGAPIObject('contactdelegation')
|
||||
|
||||
|
||||
def create(users):
|
||||
condel = build()
|
||||
delegate = gam.normalizeEmailAddressOrUID(sys.argv[5])
|
||||
delegate = gapi_directory_users.get_primary(delegate)
|
||||
if not delegate:
|
||||
controlflow.system_error_exit(5,
|
||||
f'{sys.argv[5]} is not the primary address of a user.')
|
||||
body = {'email': delegate}
|
||||
i = 0
|
||||
count = len(users)
|
||||
for user in users:
|
||||
i += 1
|
||||
print(
|
||||
f'Granting {delegate} contact delegate access to {user}{gam.currentCount(i, count)}'
|
||||
)
|
||||
gapi.call(condel.delegates(),
|
||||
'create',
|
||||
soft_errors=True,
|
||||
user=user,
|
||||
body=body)
|
||||
|
||||
|
||||
def delete(users):
|
||||
condel = build()
|
||||
delegate = gam.normalizeEmailAddressOrUID(sys.argv[5])
|
||||
delegate = gapi_directory_users.get_primary(delegate)
|
||||
if not delegate:
|
||||
controlflow.system_error_exit(5,
|
||||
f'{sys.argv[5]} is not the primary address of a user.')
|
||||
i = 0
|
||||
count = len(users)
|
||||
for user in users:
|
||||
i += 1
|
||||
print(
|
||||
f'Deleting {delegate} contact delegate access to {user}{gam.currentCount(i, count)}'
|
||||
)
|
||||
gapi.call(condel.delegates(),
|
||||
'delete',
|
||||
soft_errors=True,
|
||||
user=user,
|
||||
delegate=delegate)
|
||||
|
||||
|
||||
def print_(users, csvFormat):
|
||||
condel = build()
|
||||
if csvFormat:
|
||||
todrive = False
|
||||
csv_rows = []
|
||||
titles = ['User', 'delegateAddress']
|
||||
else:
|
||||
csvStyle = False
|
||||
i = 5
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if not csvFormat and myarg == 'csv':
|
||||
csvStyle = True
|
||||
i += 1
|
||||
elif csvFormat and myarg == 'todrive':
|
||||
todrive = True
|
||||
i += 1
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i],
|
||||
'gam print contactdelegation')
|
||||
page_message = gapi.got_total_items_msg('Contact Delegates', '...\n')
|
||||
for user in users:
|
||||
delegates = gapi.get_all_pages(condel.delegates(), 'list',
|
||||
'delegates',
|
||||
page_message=page_message,
|
||||
user=user)
|
||||
for delegate in delegates:
|
||||
if csvFormat:
|
||||
csv_rows.append({'User': user, 'delegateAddress': delegate['email']})
|
||||
else:
|
||||
if csvStyle:
|
||||
print(f'{user},{delegate["email"]}')
|
||||
else:
|
||||
print(
|
||||
f'Delegator: {user}\n Delegate Email: {delegate["email"]}\n'
|
||||
)
|
||||
if csvFormat:
|
||||
display.write_csv_file(csv_rows, titles, 'Contact Delegates', todrive)
|
||||
@@ -1,4 +1,8 @@
|
||||
import datetime
|
||||
import json
|
||||
import time
|
||||
|
||||
import googleapiclient
|
||||
|
||||
from gam.var import *
|
||||
import gam
|
||||
@@ -7,10 +11,97 @@ from gam import display
|
||||
from gam import fileutils
|
||||
from gam import gapi
|
||||
from gam.gapi import directory as gapi_directory
|
||||
from gam.gapi import errors as gapi_errors
|
||||
from gam.gapi.directory import orgunits as gapi_directory_orgunits
|
||||
from gam import utils
|
||||
|
||||
|
||||
def _display_cros_command_result(cd, device_id, command_id, times_to_check_status):
|
||||
print(f'deviceId: {device_id}, commandId: {command_id}')
|
||||
final_states = {'EXPIRED', 'CANCELLED', 'EXECUTED_BY_CLIENT'}
|
||||
for _ in range(0, times_to_check_status):
|
||||
time.sleep(2)
|
||||
result = gapi.call(cd.customer().devices().chromeos().commands(), 'get',
|
||||
customerId=GC_Values[GC_CUSTOMER_ID], deviceId=device_id,
|
||||
commandId=command_id)
|
||||
display.print_json(result)
|
||||
if result.get('state') in final_states:
|
||||
return
|
||||
|
||||
def issue_command():
|
||||
cd = gapi_directory.build()
|
||||
i, devices = getCrOSDeviceEntity(3, cd)
|
||||
body = {}
|
||||
valid_commands = gapi.get_enum_values_minus_unspecified(
|
||||
cd._rootDesc['schemas']
|
||||
['DirectoryChromeosdevicesIssueCommandRequest']
|
||||
['properties']['commandType']['enum'])
|
||||
command_map = {}
|
||||
for valid_command in valid_commands:
|
||||
v = valid_command.lower().replace('_', '')
|
||||
command_map[v] = valid_command
|
||||
times_to_check_status = 1
|
||||
doit = False
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg == 'command':
|
||||
command = sys.argv[i+1].lower().replace('_', '')
|
||||
if command not in command_map:
|
||||
controlflow.system_error_exit(2, f'expected command of ' \
|
||||
f'{", ".join(valid_commands)} got {command}')
|
||||
body['commandType'] = command_map[command]
|
||||
i += 2
|
||||
if command == 'setvolume':
|
||||
body['payload'] = json.dumps({'volume': sys.argv[i]})
|
||||
i += 1
|
||||
elif myarg == 'timestocheckstatus':
|
||||
times_to_check_status = int(sys.argv[i+1])
|
||||
i += 2
|
||||
elif myarg == 'doit':
|
||||
doit = True
|
||||
i += 1
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam issuecommand cros')
|
||||
if body['commandType'] == 'WIPE_USERS' and not doit:
|
||||
controlflow.system_error_exit(2, 'wipe_users command requires admin ' \
|
||||
'acknowledge user data will be destroyed with the ' \
|
||||
'doit argument')
|
||||
if body['commandType'] == 'REMOTE_POWERWASH' and not doit:
|
||||
controlflow.system_error_exit(2, 'remote_powerwash command requires ' \
|
||||
'admin acknowledge user data will be destroyed, device will need' \
|
||||
' to be reconnected to WiFi and re-enrolled with the doit argument')
|
||||
for device_id in devices:
|
||||
try:
|
||||
result = gapi.call(cd.customer().devices().chromeos(), 'issueCommand',
|
||||
customerId=GC_Values[GC_CUSTOMER_ID], deviceId=device_id,
|
||||
throw_reasons=[gapi_errors.ErrorReason.FOUR_O_O],
|
||||
body=body)
|
||||
except googleapiclient.errors.HttpError:
|
||||
controlflow.system_error_exit(4, '400 response from Google. This ' \
|
||||
'usually indicates the devices was not in a state where it will' \
|
||||
' accept the command. For example, reboot, set_volume and take_a_screenshot' \
|
||||
' require the device to be in auto-start kiosk app mode.')
|
||||
command_id = result.get('commandId')
|
||||
_display_cros_command_result(cd, device_id, command_id, times_to_check_status)
|
||||
|
||||
def get_command():
|
||||
cd = gapi_directory.build()
|
||||
i, devices = getCrOSDeviceEntity(3, cd)
|
||||
command_id = None
|
||||
times_to_check_status = 1
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg == 'commandid':
|
||||
command_id = sys.argv[i+1]
|
||||
i += 2
|
||||
elif myarg == 'timestocheckstatus':
|
||||
times_to_check_status = int(sys.argv[i+1])
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam getcommand cros')
|
||||
for device_id in devices:
|
||||
_display_cros_command_result(cd, device_id, command_id, times_to_check_status)
|
||||
|
||||
def doUpdateCros():
|
||||
cd = gapi_directory.build()
|
||||
i, devices = getCrOSDeviceEntity(3, cd)
|
||||
|
||||
@@ -15,6 +15,10 @@ def GroupIsAbuseOrPostmaster(emailAddr):
|
||||
return emailAddr.startswith('abuse@') or emailAddr.startswith('postmaster@')
|
||||
|
||||
|
||||
def mapGroupEmailForSettings(emailAddr):
|
||||
return emailAddr.replace('/', '%2F')
|
||||
|
||||
|
||||
def create():
|
||||
cd = gapi_directory.build()
|
||||
body = {'email': gam.normalizeEmailAddressOrUID(sys.argv[3], noUid=True)}
|
||||
@@ -67,7 +71,7 @@ def create():
|
||||
gapi_errors.ErrorReason.SERVICE_LIMIT,
|
||||
gapi_errors.ErrorReason.NOT_FOUND
|
||||
],
|
||||
groupUniqueId=body['email'],
|
||||
groupUniqueId=mapGroupEmailForSettings(body['email']),
|
||||
fields='*')
|
||||
if current_settings is not None:
|
||||
gs_body = dict(
|
||||
@@ -75,7 +79,7 @@ def create():
|
||||
if gs_body:
|
||||
gapi.call(gs.groups(),
|
||||
'update',
|
||||
groupUniqueId=body['email'],
|
||||
groupUniqueId=mapGroupEmailForSettings(body['email']),
|
||||
retry_reasons=[
|
||||
gapi_errors.ErrorReason.SERVICE_LIMIT,
|
||||
gapi_errors.ErrorReason.NOT_FOUND
|
||||
@@ -170,7 +174,7 @@ def info(group_name=None):
|
||||
'get',
|
||||
throw_reasons=[gapi_errors.ErrorReason.AUTH_ERROR],
|
||||
retry_reasons=[gapi_errors.ErrorReason.SERVICE_LIMIT],
|
||||
groupUniqueId=basic_info['email']
|
||||
groupUniqueId=mapGroupEmailForSettings(basic_info['email'])
|
||||
) # Use email address retrieved from cd since GS API doesn't support uid
|
||||
if settings is None:
|
||||
settings = {}
|
||||
@@ -589,7 +593,7 @@ def print_():
|
||||
gapi_errors.ErrorReason.SERVICE_LIMIT,
|
||||
gapi_errors.ErrorReason.INVALID
|
||||
],
|
||||
groupUniqueId=groupEmail,
|
||||
groupUniqueId=mapGroupEmailForSettings(groupEmail),
|
||||
fields=gsfields)
|
||||
if settings:
|
||||
for key in settings:
|
||||
@@ -1174,7 +1178,7 @@ def update():
|
||||
gs.groups(),
|
||||
'get',
|
||||
retry_reasons=[gapi_errors.ErrorReason.SERVICE_LIMIT],
|
||||
groupUniqueId=group,
|
||||
groupUniqueId=mapGroupEmailForSettings(group),
|
||||
fields='*')
|
||||
if current_settings is not None:
|
||||
gs_body = dict(
|
||||
@@ -1185,7 +1189,7 @@ def update():
|
||||
gs.groups(),
|
||||
'update',
|
||||
retry_reasons=[gapi_errors.ErrorReason.SERVICE_LIMIT],
|
||||
groupUniqueId=group,
|
||||
groupUniqueId=mapGroupEmailForSettings(group),
|
||||
body=gs_body)
|
||||
print(f'updated group {group}')
|
||||
|
||||
|
||||
@@ -27,13 +27,13 @@ def info():
|
||||
'get',
|
||||
customerId=GC_Values[GC_CUSTOMER_ID],
|
||||
resourceId=resourceId)
|
||||
if 'deviceId' in info:
|
||||
if 'deviceId' in device_info:
|
||||
device_info['deviceId'] = device_info['deviceId'].encode('unicode-escape').decode(
|
||||
UTF8)
|
||||
attrib = 'securityPatchLevel'
|
||||
if attrib in info and int(device_info[attrib]):
|
||||
if attrib in device_info and int(device_info[attrib]):
|
||||
device_info[attrib] = utils.formatTimestampYMDHMS(device_info[attrib])
|
||||
display.print_json(info)
|
||||
display.print_json(device_info)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,19 @@ import gam
|
||||
from gam import gapi
|
||||
from gam.gapi import directory as gapi_directory
|
||||
|
||||
|
||||
def get_primary(email):
|
||||
'''returns primary email of user or empty if email is not a user primary or
|
||||
alias address.'''
|
||||
cd = gapi_directory.build()
|
||||
result = gapi.call(cd.users(), 'get', userKey=email,
|
||||
projection='basic', fields='primaryEmail',
|
||||
soft_errors=True)
|
||||
if not result:
|
||||
return ''
|
||||
return result.get('primaryEmail', '').lower()
|
||||
|
||||
|
||||
def signout(users):
|
||||
cd = gapi_directory.build()
|
||||
i = 0
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import datetime
|
||||
import json
|
||||
import sys
|
||||
from time import sleep
|
||||
|
||||
import googleapiclient.http
|
||||
|
||||
@@ -11,6 +12,7 @@ from gam import display
|
||||
from gam import fileutils
|
||||
from gam import gapi
|
||||
from gam.gapi import storage as gapi_storage
|
||||
from gam.gapi.directory import orgunits as gapi_directory_orgunits
|
||||
from gam import utils
|
||||
|
||||
|
||||
@@ -84,21 +86,120 @@ VAULT_SEARCH_METHODS_MAP = {
|
||||
VAULT_SEARCH_METHODS_LIST = [
|
||||
'accounts', 'orgunit', 'shareddrives', 'rooms', 'everyone'
|
||||
]
|
||||
QUERY_ARGS = ['corpus', 'scope', 'terms', 'start', 'starttime',
|
||||
'end', 'endtime', 'timezone', 'excludedrafts',
|
||||
'driveversiondate', 'includeshareddrives', 'includeteamdrives',
|
||||
'includerooms'] + list(VAULT_SEARCH_METHODS_MAP.keys())
|
||||
|
||||
def _build_query(query, myarg, i, query_discovery):
|
||||
if not query:
|
||||
query = {'dataScope': 'ALL_DATA'}
|
||||
if myarg == 'corpus':
|
||||
query['corpus'] = sys.argv[i + 1].upper()
|
||||
allowed_corpuses = gapi.get_enum_values_minus_unspecified(
|
||||
query_discovery['properties']['corpus']['enum'])
|
||||
if query['corpus'] not in allowed_corpuses:
|
||||
controlflow.expected_argument_exit('corpus',
|
||||
', '.join(allowed_corpuses),
|
||||
sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg in VAULT_SEARCH_METHODS_MAP:
|
||||
if query.get('searchMethod'):
|
||||
message = f'Multiple search methods ' \
|
||||
f'({", ".join(VAULT_SEARCH_METHODS_LIST)})' \
|
||||
f'specified, only one is allowed'
|
||||
controlflow.system_error_exit(3, message)
|
||||
searchMethod = VAULT_SEARCH_METHODS_MAP[myarg]
|
||||
query['searchMethod'] = searchMethod
|
||||
if searchMethod == 'ACCOUNT':
|
||||
query['accountInfo'] = {
|
||||
'emails': sys.argv[i + 1].split(',')
|
||||
}
|
||||
i += 2
|
||||
elif searchMethod == 'ORG_UNIT':
|
||||
query['orgUnitInfo'] = {
|
||||
'orgUnitId': gapi_directory_orgunits.getOrgUnitId(sys.argv[i + 1])[1]
|
||||
}
|
||||
i += 2
|
||||
elif searchMethod == 'SHARED_DRIVE':
|
||||
query['sharedDriveInfo'] = {
|
||||
'sharedDriveIds': sys.argv[i + 1].split(',')
|
||||
}
|
||||
i += 2
|
||||
elif searchMethod == 'ROOM':
|
||||
query['hangoutsChatInfo'] = {
|
||||
'roomId': sys.argv[i + 1].split(',')
|
||||
}
|
||||
i += 2
|
||||
else:
|
||||
i += 1
|
||||
elif myarg == 'scope':
|
||||
query['dataScope'] = sys.argv[i + 1].upper()
|
||||
allowed_scopes = gapi.get_enum_values_minus_unspecified(
|
||||
query_discovery['properties']['dataScope']['enum'])
|
||||
if query['dataScope'] not in allowed_scopes:
|
||||
controlflow.expected_argument_exit('scope',
|
||||
', '.join(allowed_scopes),
|
||||
sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg in ['terms']:
|
||||
query['terms'] = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg in ['start', 'starttime']:
|
||||
query['startTime'] = utils.get_date_zero_time_or_full_time(
|
||||
sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg in ['end', 'endtime']:
|
||||
query['endTime'] = utils.get_date_zero_time_or_full_time(
|
||||
sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg in ['timezone']:
|
||||
query['timeZone'] = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg in ['excludedrafts']:
|
||||
query['mailOptions'] = {
|
||||
'excludeDrafts': gam.getBoolean(sys.argv[i + 1], myarg)
|
||||
}
|
||||
i += 2
|
||||
elif myarg in ['driveversiondate']:
|
||||
query.setdefault('driveOptions', {})['versionDate'] = \
|
||||
utils.get_date_zero_time_or_full_time(sys.argv[i+1])
|
||||
i += 2
|
||||
elif myarg in ['includeshareddrives', 'includeteamdrives']:
|
||||
query.setdefault(
|
||||
'driveOptions', {})['includeSharedDrives'] = gam.getBoolean(
|
||||
sys.argv[i + 1], myarg)
|
||||
i += 2
|
||||
elif myarg in ['includerooms']:
|
||||
query['hangoutsChatOptions'] = {
|
||||
'includeRooms': gam.getBoolean(sys.argv[i + 1], myarg)
|
||||
}
|
||||
i += 2
|
||||
return (query, i)
|
||||
|
||||
def _validate_query(query, query_discovery):
|
||||
if 'corpus' not in query:
|
||||
allowed_corpuses = gapi.get_enum_values_minus_unspecified(
|
||||
query_discovery['properties']['corpus']['enum'])
|
||||
controlflow.system_error_exit(3, 'you must specify a corpus. ' \
|
||||
f'Choose one of {", ".join(allowed_corpuses)}')
|
||||
if 'searchMethod' not in query:
|
||||
controlflow.system_error_exit(3, f'you must specify a search method. ' \
|
||||
'Choose one of ' \
|
||||
f'{", ".join(VAULT_SEARCH_METHODS_LIST)}')
|
||||
|
||||
|
||||
def createExport():
|
||||
v = buildGAPIObject()
|
||||
allowed_corpuses = gapi.get_enum_values_minus_unspecified(
|
||||
v._rootDesc['schemas']['Query']['properties']['corpus']['enum'])
|
||||
allowed_scopes = gapi.get_enum_values_minus_unspecified(
|
||||
v._rootDesc['schemas']['Query']['properties']['dataScope']['enum'])
|
||||
query_discovery = v._rootDesc['schemas']['Query']
|
||||
allowed_formats = gapi.get_enum_values_minus_unspecified(
|
||||
v._rootDesc['schemas']['MailExportOptions']['properties']
|
||||
['exportFormat']['enum'])
|
||||
export_format = 'MBOX'
|
||||
showConfidentialModeContent = None # default to not even set
|
||||
matterId = None
|
||||
body = {'query': {'dataScope': 'ALL_DATA'}, 'exportOptions': {}}
|
||||
query = None
|
||||
body = {'exportOptions': {}}
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
@@ -109,83 +210,8 @@ def createExport():
|
||||
elif myarg == 'name':
|
||||
body['name'] = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg == 'corpus':
|
||||
body['query']['corpus'] = sys.argv[i + 1].upper()
|
||||
if body['query']['corpus'] not in allowed_corpuses:
|
||||
controlflow.expected_argument_exit('corpus',
|
||||
', '.join(allowed_corpuses),
|
||||
sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg in VAULT_SEARCH_METHODS_MAP:
|
||||
if body['query'].get('searchMethod'):
|
||||
message = f'Multiple search methods ' \
|
||||
f'({", ".join(VAULT_SEARCH_METHODS_LIST)})' \
|
||||
f'specified, only one is allowed'
|
||||
controlflow.system_error_exit(3, message)
|
||||
searchMethod = VAULT_SEARCH_METHODS_MAP[myarg]
|
||||
body['query']['searchMethod'] = searchMethod
|
||||
if searchMethod == 'ACCOUNT':
|
||||
body['query']['accountInfo'] = {
|
||||
'emails': sys.argv[i + 1].split(',')
|
||||
}
|
||||
i += 2
|
||||
elif searchMethod == 'ORG_UNIT':
|
||||
body['query']['orgUnitInfo'] = {
|
||||
'orgUnitId': gam.getOrgUnitId(sys.argv[i + 1])[1]
|
||||
}
|
||||
i += 2
|
||||
elif searchMethod == 'SHARED_DRIVE':
|
||||
body['query']['sharedDriveInfo'] = {
|
||||
'sharedDriveIds': sys.argv[i + 1].split(',')
|
||||
}
|
||||
i += 2
|
||||
elif searchMethod == 'ROOM':
|
||||
body['query']['hangoutsChatInfo'] = {
|
||||
'roomId': sys.argv[i + 1].split(',')
|
||||
}
|
||||
i += 2
|
||||
else:
|
||||
i += 1
|
||||
elif myarg == 'scope':
|
||||
body['query']['dataScope'] = sys.argv[i + 1].upper()
|
||||
if body['query']['dataScope'] not in allowed_scopes:
|
||||
controlflow.expected_argument_exit('scope',
|
||||
', '.join(allowed_scopes),
|
||||
sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg in ['terms']:
|
||||
body['query']['terms'] = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg in ['start', 'starttime']:
|
||||
body['query']['startTime'] = utils.get_date_zero_time_or_full_time(
|
||||
sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg in ['end', 'endtime']:
|
||||
body['query']['endTime'] = utils.get_date_zero_time_or_full_time(
|
||||
sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg in ['timezone']:
|
||||
body['query']['timeZone'] = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg in ['excludedrafts']:
|
||||
body['query']['mailOptions'] = {
|
||||
'excludeDrafts': gam.getBoolean(sys.argv[i + 1], myarg)
|
||||
}
|
||||
i += 2
|
||||
elif myarg in ['driveversiondate']:
|
||||
body['query'].setdefault('driveOptions', {})['versionDate'] = \
|
||||
utils.get_date_zero_time_or_full_time(sys.argv[i+1])
|
||||
i += 2
|
||||
elif myarg in ['includeshareddrives', 'includeteamdrives']:
|
||||
body['query'].setdefault(
|
||||
'driveOptions', {})['includeSharedDrives'] = gam.getBoolean(
|
||||
sys.argv[i + 1], myarg)
|
||||
i += 2
|
||||
elif myarg in ['includerooms']:
|
||||
body['query']['hangoutsChatOptions'] = {
|
||||
'includeRooms': gam.getBoolean(sys.argv[i + 1], myarg)
|
||||
}
|
||||
i += 2
|
||||
elif myarg in QUERY_ARGS:
|
||||
query, i = _build_query(query, myarg, i, query_discovery)
|
||||
elif myarg in ['format']:
|
||||
export_format = sys.argv[i + 1].upper()
|
||||
if export_format not in allowed_formats:
|
||||
@@ -216,13 +242,8 @@ def createExport():
|
||||
if not matterId:
|
||||
controlflow.system_error_exit(
|
||||
3, 'you must specify a matter for the new export.')
|
||||
if 'corpus' not in body['query']:
|
||||
controlflow.system_error_exit(3, f'you must specify a corpus for the ' \
|
||||
f'new export. Choose one of {", ".join(allowed_corpuses)}')
|
||||
if 'searchMethod' not in body['query']:
|
||||
controlflow.system_error_exit(3, f'you must specify a search method ' \
|
||||
'for the new export. Choose one of ' \
|
||||
f'{", ".join(VAULT_SEARCH_METHODS_LIST)}')
|
||||
_validate_query(query, query_discovery)
|
||||
body['query'] = query
|
||||
if 'name' not in body:
|
||||
corpus_name = body['query']['corpus']
|
||||
corpus_date = datetime.datetime.now()
|
||||
@@ -270,6 +291,81 @@ def getExportInfo():
|
||||
display.print_json(export)
|
||||
|
||||
|
||||
def print_count():
|
||||
v = buildGAPIObject()
|
||||
query_discovery = v._rootDesc['schemas']['Query']
|
||||
matterId = None
|
||||
operation_wait = 15
|
||||
query = None
|
||||
body = {'view': 'ALL'}
|
||||
name = None
|
||||
todrive = False
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg == 'matter':
|
||||
matterId = getMatterItem(v, sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg == 'operation':
|
||||
name = sys.argv[i+1]
|
||||
i += 2
|
||||
elif myarg == 'todrive':
|
||||
todrive = True
|
||||
i += 1
|
||||
elif myarg in QUERY_ARGS:
|
||||
query, i = _build_query(query, myarg, i, query_discovery)
|
||||
elif myarg == 'wait':
|
||||
operation_wait = int(sys.argv[i + 1])
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam create export')
|
||||
if not matterId:
|
||||
controlflow.system_error_exit(
|
||||
3, 'you must specify a matter for the count.')
|
||||
if name:
|
||||
operation = {'name': name}
|
||||
else:
|
||||
_validate_query(query, query_discovery)
|
||||
body['query'] = query
|
||||
operation = gapi.call(v.matters(), 'count', matterId=matterId, body=body)
|
||||
print(f'Watching operation {operation["name"]}...')
|
||||
while not operation.get('done'):
|
||||
print(f' operation {operation["name"]} is not done yet. Checking again in {operation_wait} seconds')
|
||||
sleep(operation_wait)
|
||||
operation = gapi.call(v.operations(), 'get', name=operation['name'])
|
||||
response = operation.get('response', {})
|
||||
query = operation['metadata']['query']
|
||||
search_method = query.get('searchMethod')
|
||||
# ARGH count results don't include accounts with zero items.
|
||||
# so we keep track of which accounts we searched and can report
|
||||
# zero data for them.
|
||||
if search_method == 'ACCOUNT':
|
||||
query_accounts = query.get('accountInfo', [])
|
||||
elif search_method == 'ENTIRE_ORG':
|
||||
query_accounts = gam.getUsersToModify('all', 'users')
|
||||
elif search_method == 'ORG_UNIT':
|
||||
org_unit = query['orgUnitInfo']['orgUnitId']
|
||||
query_accounts = gam.getUsersToModify('ou', org_unit)
|
||||
mailcounts = response.get('mailCountResult', {})
|
||||
groupcounts = response.get('groupsCountResult', {})
|
||||
csv_rows = []
|
||||
for a_count in [mailcounts, groupcounts]:
|
||||
for errored_account in a_count.get('accountCountErrors', []):
|
||||
account = errored_account.get('account')
|
||||
csv_rows.append({'account': account, 'error': errored_account.get('errorType')})
|
||||
if account in query_accounts: query_accounts.remove(account)
|
||||
for account in a_count.get('nonQueryableAccounts', []):
|
||||
csv_rows.append({'account': account, 'error': 'Not queried because not on hold'})
|
||||
if account in query_accounts: query_accounts.remove(account)
|
||||
for account in a_count.get('accountCounts', []):
|
||||
email = account.get('account', {}).get('email', '')
|
||||
csv_rows.append({'account': email, 'count': account.get('count')})
|
||||
if email in query_accounts: query_accounts.remove(email)
|
||||
for account in query_accounts:
|
||||
csv_rows.append({'account': account, 'count': 0})
|
||||
titles = ['account', 'count', 'error']
|
||||
display.write_csv_file(csv_rows, titles, 'Vault Counts', todrive)
|
||||
|
||||
def createHold():
|
||||
v = buildGAPIObject()
|
||||
allowed_corpuses = gapi.get_enum_values_minus_unspecified(
|
||||
@@ -301,7 +397,7 @@ def createHold():
|
||||
i += 2
|
||||
elif myarg in ['orgunit', 'ou']:
|
||||
body['orgUnit'] = {
|
||||
'orgUnitId': gam.getOrgUnitId(sys.argv[i + 1])[1]
|
||||
'orgUnitId': gapi_directory_orgunits.getOrgUnitId(sys.argv[i + 1])[1]
|
||||
}
|
||||
i += 2
|
||||
elif myarg in ['start', 'starttime']:
|
||||
@@ -407,7 +503,7 @@ def getHoldInfo():
|
||||
acct_email = gam.convertUIDtoEmailAddress(uid, cd, [account_type])
|
||||
results['accounts'][i]['email'] = acct_email
|
||||
if 'orgUnit' in results:
|
||||
results['orgUnit']['orgUnitPath'] = gam.doGetOrgInfo(
|
||||
results['orgUnit']['orgUnitPath'] = gapi_directory_orgunits.info(
|
||||
results['orgUnit']['orgUnitId'], return_attrib='orgUnitPath')
|
||||
display.print_json(results)
|
||||
|
||||
@@ -496,7 +592,7 @@ def updateHold():
|
||||
i += 2
|
||||
elif myarg in ['orgunit', 'ou']:
|
||||
body['orgUnit'] = {
|
||||
'orgUnitId': gam.getOrgUnitId(sys.argv[i + 1])[1]
|
||||
'orgUnitId': gapi_directory_orgunits.getOrgUnitId(sys.argv[i + 1])[1]
|
||||
}
|
||||
i += 2
|
||||
elif myarg in ['start', 'starttime']:
|
||||
|
||||
@@ -228,12 +228,14 @@ def get_yyyymmdd(argstr, minLen=1, returnTimeStamp=False, returnDateTime=False):
|
||||
def get_time_or_delta_from_now(time_string):
|
||||
"""Get an ISO 8601 time or a positive/negative delta applied to now.
|
||||
Args:
|
||||
time_string (string): The time or delta (e.g. '2017-09-01T12:34:56Z' or '-4h')
|
||||
time_string (string): The time or delta (e.g. '2017-09-01T12:34:56Z' or '-4h') or never
|
||||
Returns:
|
||||
string: iso8601 formatted datetime in UTC.
|
||||
"""
|
||||
time_string = time_string.strip().upper()
|
||||
if time_string:
|
||||
if time_string == 'NEVER':
|
||||
return NEVER_TIME
|
||||
if time_string[0] not in ['+', '-']:
|
||||
return time_string
|
||||
return (datetime.datetime.utcnow() +
|
||||
|
||||
@@ -8,7 +8,7 @@ import platform
|
||||
import re
|
||||
|
||||
GAM_AUTHOR = 'Jay Lee <jay0lee@gmail.com>'
|
||||
GAM_VERSION = '5.23'
|
||||
GAM_VERSION = '5.31'
|
||||
GAM_LICENSE = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
||||
|
||||
GAM_URL = 'https://git.io/gam'
|
||||
@@ -114,10 +114,44 @@ SKUS = {
|
||||
'aliases': ['gau', 'gsb', 'unlimited', 'gsuitebusiness'],
|
||||
'displayName': 'G Suite Business'
|
||||
},
|
||||
'1010020027': {
|
||||
'product': 'Google-Apps',
|
||||
'aliases': ['wsbizstart', 'workspacebusinessstarter'],
|
||||
'displayName': 'Workspace Business Starter'
|
||||
},
|
||||
'1010020028': {
|
||||
'product': 'Google-Apps',
|
||||
'aliases': ['wsbizstan', 'workspacebusinessstandard'],
|
||||
'displayName': 'Workspace Business Standard'
|
||||
},
|
||||
'1010020025': {
|
||||
'product': 'Google-Apps',
|
||||
'aliases': ['wsbizplus', 'workspacebusinessplus'],
|
||||
'displayName': 'Workspace Business Plus'
|
||||
},
|
||||
'1010060001': {
|
||||
'product': 'Google-Apps',
|
||||
'aliases': [
|
||||
'gsuiteessentials', 'essentials', 'd4e', 'driveenterprise',
|
||||
'drive4enterprise', 'wsess', 'workspaceesentials'
|
||||
],
|
||||
'displayName': 'Google Workspace Essentials'
|
||||
},
|
||||
'1010060003': {
|
||||
'product': 'Google-Apps',
|
||||
'aliases': ['wsentess', 'workspaceenterpriseessentials'],
|
||||
'displayName': 'Workspace Enterprise Essentials'
|
||||
},
|
||||
'1010020026': {
|
||||
'product': 'Google-Apps',
|
||||
'aliases': ['wsentstan', 'workspaceenterprisestandard'],
|
||||
'displayName': 'Workspace Enterprise Standard'
|
||||
},
|
||||
'1010020020': {
|
||||
'product': 'Google-Apps',
|
||||
'aliases': ['gae', 'gse', 'enterprise', 'gsuiteenterprise'],
|
||||
'displayName': 'Google Workspace Enterprise Plus (fka G Suite Enterprise)'
|
||||
'aliases': ['gae', 'gse', 'enterprise', 'gsuiteenterprise',
|
||||
'wsentplus', 'workspaceenterpriseplus'],
|
||||
'displayName': 'Workspace Enterprise Plus'
|
||||
},
|
||||
'1010340002': {
|
||||
'product': '101034',
|
||||
@@ -129,14 +163,6 @@ SKUS = {
|
||||
'aliases': ['gseau', 'enterprisearchived', 'gsuiteenterprisearchived'],
|
||||
'displayName': 'Google Workspace Enterprise Plus Archived'
|
||||
},
|
||||
'1010060001': {
|
||||
'product': '101006',
|
||||
'aliases': [
|
||||
'gsuiteessentials', 'essentials', 'd4e', 'driveenterprise',
|
||||
'drive4enterprise'
|
||||
],
|
||||
'displayName': 'Google Workspace Essentials'
|
||||
},
|
||||
'Google-Drive-storage-20GB': {
|
||||
'product': 'Google-Drive-storage',
|
||||
'aliases': ['drive20gb', '20gb', 'googledrivestorage20gb'],
|
||||
@@ -192,11 +218,6 @@ SKUS = {
|
||||
'aliases': ['vfe', 'googlevaultformeremployee'],
|
||||
'displayName': 'Google Vault Former Employee'
|
||||
},
|
||||
'Google-Coordinate': {
|
||||
'product': 'Google-Coordinate',
|
||||
'aliases': ['coordinate', 'googlecoordinate'],
|
||||
'displayName': 'Google Coordinate'
|
||||
},
|
||||
'Google-Chrome-Device-Management': {
|
||||
'product': 'Google-Chrome-Device-Management',
|
||||
'aliases': ['chrome', 'cdm', 'googlechromedevicemanagement'],
|
||||
@@ -212,7 +233,6 @@ PRODUCTID_NAME_MAPPINGS = {
|
||||
'101034': 'G Suite Archived',
|
||||
'Google-Apps': 'Google Workspace',
|
||||
'Google-Chrome-Device-Management': 'Google Chrome Device Management',
|
||||
'Google-Coordinate': 'Google Coordinate',
|
||||
'Google-Drive-storage': 'Google Drive Storage',
|
||||
'Google-Vault': 'Google Vault',
|
||||
}
|
||||
@@ -220,7 +240,6 @@ PRODUCTID_NAME_MAPPINGS = {
|
||||
# Legacy APIs that use v1 discovery. Newer APIs should all use v2.
|
||||
V1_DISCOVERY_APIS = {
|
||||
'admin',
|
||||
'appsactivity',
|
||||
'calendar',
|
||||
'drive',
|
||||
'oauth2',
|
||||
@@ -239,13 +258,15 @@ API_NAME_MAPPING = {
|
||||
|
||||
API_VER_MAPPING = {
|
||||
'alertcenter': 'v1beta1',
|
||||
'appsactivity': 'v1',
|
||||
'driveactivity': 'v2',
|
||||
'calendar': 'v3',
|
||||
'cbcm': 'v1.1beta1',
|
||||
'classroom': 'v1',
|
||||
'cloudidentity': 'v1',
|
||||
'cloudidentity_beta': 'v1beta1',
|
||||
'cloudresourcemanager': 'v2',
|
||||
'cloudresourcemanagerv1': 'v1',
|
||||
'contactdelegation': 'v1',
|
||||
'datatransfer': 'datatransfer_v1',
|
||||
'directory': 'directory_v1',
|
||||
'drive': 'v2',
|
||||
@@ -271,12 +292,12 @@ USERINFO_EMAIL_SCOPE = 'https://www.googleapis.com/auth/userinfo.email'
|
||||
|
||||
API_SCOPE_MAPPING = {
|
||||
'alertcenter': ['https://www.googleapis.com/auth/apps.alerts',],
|
||||
'appsactivity': [
|
||||
'https://www.googleapis.com/auth/activity',
|
||||
'driveactivity': [
|
||||
'https://www.googleapis.com/auth/drive.activity',
|
||||
'https://www.googleapis.com/auth/drive',
|
||||
],
|
||||
'calendar': ['https://www.googleapis.com/auth/calendar',],
|
||||
'cloudidentity': ['https://www.googleapis.com/auth/cloud-identity',],
|
||||
'cloudidentity': ['https://www.googleapis.com/auth/cloud-identity'],
|
||||
'drive': ['https://www.googleapis.com/auth/drive',],
|
||||
'drive3': ['https://www.googleapis.com/auth/drive',],
|
||||
'gmail': [
|
||||
@@ -380,6 +401,7 @@ DRIVEFILE_FIELDS_CHOICES_MAP = {
|
||||
'appdatacontents': 'appDataContents',
|
||||
'cancomment': 'canComment',
|
||||
'canreadrevisions': 'canReadRevisions',
|
||||
'contentrestrictions': 'contentRestrictions',
|
||||
'copyable': 'copyable',
|
||||
'copyrequireswriterpermission': 'copyRequiresWriterPermission',
|
||||
'createddate': 'createdDate',
|
||||
@@ -499,6 +521,7 @@ MIMETYPE_GA_SCRIPT = f'{APPLICATION_VND_GOOGLE_APPS}script'
|
||||
MIMETYPE_GA_SITES = f'{APPLICATION_VND_GOOGLE_APPS}sites'
|
||||
MIMETYPE_GA_SPREADSHEET = f'{APPLICATION_VND_GOOGLE_APPS}spreadsheet'
|
||||
MIMETYPE_GA_SHORTCUT = f'{APPLICATION_VND_GOOGLE_APPS}shortcut'
|
||||
MIMETYPE_GA_3P_SHORTCUT = f'{APPLICATION_VND_GOOGLE_APPS}drive-sdk'
|
||||
|
||||
MIMETYPE_CHOICES_MAP = {
|
||||
'gdoc': MIMETYPE_GA_DOCUMENT,
|
||||
@@ -510,6 +533,8 @@ MIMETYPE_CHOICES_MAP = {
|
||||
'gfusion': MIMETYPE_GA_FUSIONTABLE,
|
||||
'gpresentation': MIMETYPE_GA_PRESENTATION,
|
||||
'gscript': MIMETYPE_GA_SCRIPT,
|
||||
'gshortcut': MIMETYPE_GA_SHORTCUT,
|
||||
'g3pshortcut': MIMETYPE_GA_3P_SHORTCUT,
|
||||
'gsite': MIMETYPE_GA_SITES,
|
||||
'gsheet': MIMETYPE_GA_SPREADSHEET,
|
||||
'gspreadsheet': MIMETYPE_GA_SPREADSHEET,
|
||||
@@ -881,6 +906,7 @@ CROS_ARGUMENT_TO_PROPERTY_MAP = {
|
||||
'ethernetmacaddress0': ['ethernetMacAddress0',],
|
||||
'firmwareversion': ['firmwareVersion',],
|
||||
'lastenrollmenttime': ['lastEnrollmentTime',],
|
||||
'lastknownnetwork': ['lastKnownNetwork'],
|
||||
'lastsync': ['lastSync',],
|
||||
'location': ['annotatedLocation',],
|
||||
'macaddress': ['macAddress',],
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
admin.googleapis.com
|
||||
alertcenter.googleapis.com
|
||||
appsactivity.googleapis.com
|
||||
calendar-json.googleapis.com
|
||||
chat.googleapis.com
|
||||
classroom.googleapis.com
|
||||
cloudidentity.googleapis.com
|
||||
contacts.googleapis.com
|
||||
drive.googleapis.com
|
||||
driveactivity.googleapis.com
|
||||
iap.googleapis.com
|
||||
gmail.googleapis.com
|
||||
groupssettings.googleapis.com
|
||||
|
||||
Binary file not shown.
@@ -1,38 +0,0 @@
|
||||
cd src
|
||||
if [[ "$TRAVIS_JOB_NAME" == *"Testing" ]]; then
|
||||
export gam="$python -m gam"
|
||||
export gampath=$(readlink -e .)
|
||||
else
|
||||
export gampath="dist/gam"
|
||||
rm -rf $gampath
|
||||
mkdir -p $gampath
|
||||
export gampath=$(readlink -e $gampath)
|
||||
$python -OO -m PyInstaller --clean --noupx --strip -F --distpath $gampath gam.spec
|
||||
export gam="${gampath}/gam"
|
||||
export GAMVERSION=`$gam version simple`
|
||||
cp LICENSE $gampath
|
||||
cp GamCommands.txt $gampath
|
||||
this_glibc_ver=$(ldd --version | awk '/ldd/{print $NF}')
|
||||
GAM_ARCHIVE=gam-$GAMVERSION-$GAMOS-$PLATFORM-glibc$this_glibc_ver.tar.xz
|
||||
rm $gampath/lastupdatecheck.txt
|
||||
# tar will cd to dist and tar up gam/
|
||||
tar -C dist/ --create --file $GAM_ARCHIVE --xz gam
|
||||
echo "PyInstaller GAM info:"
|
||||
du -h $gam
|
||||
time $gam version extended
|
||||
if [ "${TRAVIS_DIST}" == "xenial" ] && [ "${PLATFORM}" == "x86_64" ]; then
|
||||
GAM_LEGACY_ARCHIVE=gam-${GAMVERSION}-${GAMOS}-${PLATFORM}-legacy.tar.xz
|
||||
$python -OO -m staticx -l /lib/x86_64-linux-gnu/libresolv.so.2 -l /lib/x86_64-linux-gnu/libnss_dns.so.2 $gam $gam-staticx
|
||||
strip $gam-staticx
|
||||
rm $gampath/gam
|
||||
mv $gam-staticx $gam
|
||||
chmod 755 $gam
|
||||
rm $gampath/lastupdatecheck.txt
|
||||
tar -C dist/ --create --file $GAM_LEGACY_ARCHIVE --xz gam
|
||||
echo "Legacy StaticX GAM info:"
|
||||
du -h $gam
|
||||
time $gam version extended
|
||||
fi
|
||||
echo "GAM packages:"
|
||||
ls -l gam-*.tar.xz
|
||||
fi
|
||||
@@ -1,111 +0,0 @@
|
||||
mypath=$HOME
|
||||
whereibelong=$(pwd)
|
||||
cpucount=$(sysctl -n hw.ncpu)
|
||||
echo "This device has $cpucount CPUs for compiling..."
|
||||
|
||||
#echo "Brew installing xz..."
|
||||
#brew install xz > /dev/null
|
||||
|
||||
#brew upgrade
|
||||
|
||||
# prefer standard GNU tools like date over MacOS defaults
|
||||
export PATH="/usr/local/opt/coreutils/libexec/gnubin:$(brew --prefix)/opt/gnu-tar/libexec/gnubin:$PATH"
|
||||
|
||||
cd ~
|
||||
|
||||
#if [ ! -f python-$MIN_PYTHON_VERSION-macosx10.9.pkg ]; then
|
||||
# wget --quiet https://www.python.org/ftp/python/$MIN_PYTHON_VERSION/python-$MIN_PYTHON_VERSION-macosx10.9.pkg
|
||||
#fi
|
||||
#sudo installer -pkg python-$MIN_PYTHON_VERSION-macosx10.9.pkg -target /
|
||||
|
||||
#brew install openssl@1.1
|
||||
#brew upgrade python
|
||||
|
||||
#export python=python3
|
||||
#export pip=pip3
|
||||
|
||||
#echo "Python location:"
|
||||
#which $python
|
||||
|
||||
cd ~
|
||||
|
||||
export LD_LIBRARY_PATH=~/ssl/lib:~/python/lib
|
||||
export openssl=~/ssl/bin/openssl
|
||||
export python=~/python/bin/python3
|
||||
export pip=~/python/bin/pip3
|
||||
SSLVER=$($openssl version)
|
||||
SSLRESULT=$?
|
||||
PYVER=$($python -V)
|
||||
PYRESULT=$?
|
||||
if [ $SSLRESULT -ne 0 ] || [[ "$SSLVER" != "OpenSSL $BUILD_OPENSSL_VERSION "* ]] || [ $PYRESULT -ne 0 ] || [[ "$PYVER" != "Python $BUILD_PYTHON_VERSION"* ]]; then
|
||||
echo "SSL Result: $SSLRESULT - SSL Ver: $SSLVER - Py Result: $PYRESULT - Py Ver: $PYVER"
|
||||
if [ $SSLRESULT -ne 0 ]; then
|
||||
echo "sslresult -ne 0"
|
||||
fi
|
||||
if [[ "$SSLVER" != "OpenSSL $BUILD_OPENSSL_VERSION "* ]]; then
|
||||
echo "sslver not equal to..."
|
||||
fi
|
||||
if [ $PYRESULT -ne 0 ]; then
|
||||
echo "pyresult -ne 0"
|
||||
fi
|
||||
if [[ "$PYVER" != "Python $BUILD_PYTHON_VERSION" ]]; then
|
||||
echo "pyver not equal to..."
|
||||
fi
|
||||
|
||||
# Start clean
|
||||
rm -rf python
|
||||
rm -rf ssl
|
||||
mkdir python
|
||||
mkdir ssl
|
||||
|
||||
# Compile latest OpenSSL
|
||||
wget --quiet https://www.openssl.org/source/openssl-$BUILD_OPENSSL_VERSION.tar.gz
|
||||
echo "Extracting OpenSSL..."
|
||||
tar xf openssl-$BUILD_OPENSSL_VERSION.tar.gz
|
||||
cd openssl-$BUILD_OPENSSL_VERSION
|
||||
echo "Compiling OpenSSL $BUILD_OPENSSL_VERSION..."
|
||||
./config shared --prefix=$HOME/ssl
|
||||
echo "Running make for OpenSSL..."
|
||||
make -j$cpucount -s
|
||||
echo "Running make install for OpenSSL..."
|
||||
make install > /dev/null
|
||||
cd ~
|
||||
|
||||
# Compile latest Python
|
||||
echo "Downloading Python $BUILD_PYTHON_VERSION..."
|
||||
curl -O https://www.python.org/ftp/python/$BUILD_PYTHON_VERSION/Python-$BUILD_PYTHON_VERSION.tar.xz
|
||||
echo "Extracting Python..."
|
||||
tar xf Python-$BUILD_PYTHON_VERSION.tar.xz
|
||||
cd Python-$BUILD_PYTHON_VERSION
|
||||
echo "Compiling Python $BUILD_PYTHON_VERSION..."
|
||||
safe_flags="--with-openssl=$HOME/ssl --enable-shared --prefix=$HOME/python --with-ensurepip=upgrade"
|
||||
unsafe_flags="--enable-optimizations --with-lto"
|
||||
if [ ! -e Makefile ]; then
|
||||
echo "running configure with safe and unsafe"
|
||||
./configure $safe_flags $unsafe_flags > /dev/null
|
||||
fi
|
||||
make -j$cpucount PROFILE_TASK="-m test.regrtest --pgo -j$(( $cpucount * 2 ))" -s
|
||||
RESULT=$?
|
||||
echo "First make exited with $RESULT"
|
||||
if [ $RESULT != 0 ]; then
|
||||
echo "Trying Python compile again without unsafe flags..."
|
||||
make clean
|
||||
./configure $safe_flags > /dev/null
|
||||
make -j$cpucount -s
|
||||
echo "Sticking with safe Python for now..."
|
||||
fi
|
||||
echo "Installing Python..."
|
||||
make install > /dev/null
|
||||
cd ~
|
||||
fi
|
||||
|
||||
$python -V
|
||||
|
||||
cd $whereibelong
|
||||
|
||||
#export PATH=/usr/local/opt/python/libexec/bin:$PATH
|
||||
$pip install --upgrade pip
|
||||
$pip list --outdated --format=freeze | grep -v '^\-e' | cut -d = -f 1 | xargs -n1 $pip install -U
|
||||
$pip install --upgrade -r src/requirements.txt
|
||||
$pip install --upgrade git+git://github.com/pyinstaller/pyinstaller.git@$PYINSTALLER_COMMIT
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
if [[ "$PLATFORM" == "x86_64" ]]; then
|
||||
export BITS="64"
|
||||
export PYTHONFILE_BITS="-amd64"
|
||||
export OPENSSL_BITS="-x64"
|
||||
export WIX_BITS="x64"
|
||||
elif [[ "$PLATFORM" == "x86" ]]; then
|
||||
export BITS="32"
|
||||
export PYTHONFILE_BITS=""
|
||||
export OPENSSL_BITS=""
|
||||
export WIX_BITS="x86"
|
||||
fi
|
||||
echo "This is a ${BITS}-bit build for ${PLATFORM}"
|
||||
|
||||
export mypath=$(pwd)
|
||||
cd ~
|
||||
|
||||
# .NET Core
|
||||
echo "Installing Net-Framework-Core..."
|
||||
until powershell Install-WindowsFeature Net-Framework-Core; do echo "trying .net again..."; done
|
||||
|
||||
# VS 2015
|
||||
echo "Installing Visual Studio 2015.."
|
||||
until choco install vcbuildtools; do echo "Trying Visual Studio again..."; done
|
||||
|
||||
# Python
|
||||
echo "Installing Python..."
|
||||
export python_file=python-${BUILD_PYTHON_VERSION}${PYTHONFILE_BITS}.exe
|
||||
if [ ! -e $python_file ]; then
|
||||
echo "Downloading $python_file..."
|
||||
wget --quiet https://www.python.org/ftp/python/$BUILD_PYTHON_VERSION/$python_file
|
||||
fi
|
||||
until powershell ".\\${python_file} /quiet InstallAllUsers=1 TargetDir=c:\\python"; do echo "trying python again..."; done
|
||||
export python=/c/python/python.exe
|
||||
export pip=/c/python/scripts/pip.exe
|
||||
until [ -f $python ]; do sleep 1; done
|
||||
export PATH=$PATH:/c/python/scripts
|
||||
|
||||
# OpenSSL
|
||||
echo "Installing OpenSSL..."
|
||||
export exefile=Win${BITS}OpenSSL_Light-${BUILD_OPENSSL_VERSION//./_}.exe
|
||||
if [ ! -e $exefile ]; then
|
||||
echo "Downloading $exefile..."
|
||||
wget --quiet https://slproweb.com/download/$exefile
|
||||
fi
|
||||
until powershell ".\\${exefile} /silent /sp- /suppressmsgboxes /DIR=C:\\ssl"; do echo "trying openssl again..."; done
|
||||
until cp -v /c/ssl/libcrypto-1_1${OPENSSL_BITS}.dll /c/python/DLLs/; do echo "trying libcrypto copy again..."; sleep 3; done
|
||||
until cp -v /c/ssl/libssl-1_1${OPENSSL_BITS}.dll /c/python/DLLs/; do echo "trying libssl copy again..."; done
|
||||
if [[ "$PLATFORM" == "x86_64" ]]; then
|
||||
cp -v /c/python/DLLs/libssl-1_1-x64.dll /c/python/DLLs/libssl-1_1.dll
|
||||
cp -v /c/python/DLLs/libcrypto-1_1-x64.dll /c/python/DLLs/libcrypto-1_1.dll
|
||||
fi
|
||||
|
||||
# WIX Toolset
|
||||
until cinst -y wixtoolset; do echo "trying wix install again..."; done
|
||||
|
||||
cd $mypath
|
||||
|
||||
$pip install --upgrade pip
|
||||
$pip list --outdated --format=freeze | grep -v '^\-e' | cut -d = -f 1 | xargs -n1 $pip install -U
|
||||
$pip install --upgrade -r src/requirements.txt
|
||||
#$pip install --upgrade pyinstaller
|
||||
# Install PyInstaller from source and build bootloader
|
||||
# to try and avoid getting flagged as malware since
|
||||
# lots of malware uses PyInstaller default bootloader
|
||||
# https://stackoverflow.com/questions/53584395/how-to-recompile-the-bootloader-of-pyinstaller
|
||||
echo "Downloading PyInstaller..."
|
||||
wget --quiet https://github.com/pyinstaller/pyinstaller/archive/$PYINSTALLER_COMMIT.tar.gz
|
||||
tar xf $PYINSTALLER_COMMIT.tar.gz
|
||||
mv pyinstaller-$PYINSTALLER_COMMIT pyinstaller
|
||||
cd pyinstaller/bootloader
|
||||
echo "bootloader before:"
|
||||
md5sum ../PyInstaller/bootloader/Windows-${BITS}bit/*
|
||||
|
||||
$python ./waf all --target-arch=${BITS}bit --msvc_version "msvc 14.0"
|
||||
|
||||
echo "bootloader after:"
|
||||
md5sum ../PyInstaller/bootloader/Windows-${BITS}bit/*
|
||||
echo "PATH: $PATH"
|
||||
cd ..
|
||||
$python setup.py install
|
||||
echo "cd to $mypath"
|
||||
cd $mypath
|
||||
Reference in New Issue
Block a user