mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-05 14:51:39 +00:00
Compare commits
166 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be4403331f | ||
|
|
b84025debf | ||
|
|
4685f29aba | ||
|
|
a832698366 | ||
|
|
cf894fd0bd | ||
|
|
fd6c04bd94 | ||
|
|
49a2352d6d | ||
|
|
291871ec45 | ||
|
|
73c21a1156 | ||
|
|
27eed06617 | ||
|
|
a440cbbbdc | ||
|
|
eb9ca5eb1d | ||
|
|
5eb1277691 | ||
|
|
6de424b185 | ||
|
|
f89491d801 | ||
|
|
154de4818e | ||
|
|
c5895a3082 | ||
|
|
e085257a51 | ||
|
|
d8e84cf045 | ||
|
|
a5eb61421d | ||
|
|
cddbea2718 | ||
|
|
c9bf5158e4 | ||
|
|
7822b36f97 | ||
|
|
20d541ca8e | ||
|
|
72af9fb4a9 | ||
|
|
a2972a3329 | ||
|
|
97b74c0c8f | ||
|
|
332519e5d4 | ||
|
|
881641e2b4 | ||
|
|
82bfe74175 | ||
|
|
bac3451c21 | ||
|
|
c97495ab05 | ||
|
|
aa3dad1e07 | ||
|
|
2d4e15504c | ||
|
|
3cd41e3d0f | ||
|
|
a94518c48d | ||
|
|
2837671ed7 | ||
|
|
e49eed2a24 | ||
|
|
edfc27c960 | ||
|
|
41a10932cb | ||
|
|
119538c10c | ||
|
|
0a03fbb82e | ||
|
|
b838054e2f | ||
|
|
45ac118381 | ||
|
|
e1600eadbc | ||
|
|
cc23f98078 | ||
|
|
b68cc671eb | ||
|
|
8c215a0a0b | ||
|
|
374c6a9367 | ||
|
|
5d93d9893e | ||
|
|
010c26ea89 | ||
|
|
5da90f7585 | ||
|
|
5356591d9c | ||
|
|
8df5d22805 | ||
|
|
5684ab3c05 | ||
|
|
d7b8f4c228 | ||
|
|
66fe03bbcd | ||
|
|
e7496dc9cb | ||
|
|
3b52af0b8a | ||
|
|
4ffe709ab9 | ||
|
|
c0c15a3ee5 | ||
|
|
b724330cb1 | ||
|
|
8b9ce17959 | ||
|
|
26116474c5 | ||
|
|
3411fd8557 | ||
|
|
0bee3e38a0 | ||
|
|
dcc2224657 | ||
|
|
1bf1c43f23 | ||
|
|
53b336aee9 | ||
|
|
94b5407cb4 | ||
|
|
e7357e69fb | ||
|
|
d5a036dfc6 | ||
|
|
e256de06d9 | ||
|
|
59beba616c | ||
|
|
735747e0d4 | ||
|
|
ab167d8c4c | ||
|
|
944f010a8c | ||
|
|
dd064a3843 | ||
|
|
244b20e1f0 | ||
|
|
88308a352b | ||
|
|
c63df9d245 | ||
|
|
2780ad2edc | ||
|
|
00b3122c2c | ||
|
|
18221be556 | ||
|
|
80abd9284f | ||
|
|
87b6cb073f | ||
|
|
e2cbbb2c93 | ||
|
|
c771b84463 | ||
|
|
2460e6957f | ||
|
|
0ec42eb796 | ||
|
|
b78b5ea9e1 | ||
|
|
d26bfc9aab | ||
|
|
c64730e07b | ||
|
|
f8b00b92b4 | ||
|
|
3c9ec2578e | ||
|
|
5f79f46e30 | ||
|
|
30c250c314 | ||
|
|
69f504d91c | ||
|
|
c505dd9c2b | ||
|
|
a7638dee0a | ||
|
|
b8e5ad5107 | ||
|
|
0b2d04bc6f | ||
|
|
803025b8c5 | ||
|
|
a7154da0b6 | ||
|
|
d04b33d1d9 | ||
|
|
614edc22ca | ||
|
|
eeb760260a | ||
|
|
eb5d876487 | ||
|
|
3bf2c5c8b6 | ||
|
|
3c24049d66 | ||
|
|
1f1b6d45e3 | ||
|
|
7b8a17a544 | ||
|
|
95d1b1295e | ||
|
|
4abd0407c8 | ||
|
|
65f229875d | ||
|
|
9be84501ec | ||
|
|
6e400cabd0 | ||
|
|
1f00614551 | ||
|
|
1da61d076e | ||
|
|
2ce04b4dd2 | ||
|
|
94a52c80cd | ||
|
|
af71cf9a82 | ||
|
|
594c7d6d29 | ||
|
|
4575c3576f | ||
|
|
243a6d20cc | ||
|
|
9f27cc155a | ||
|
|
64bfa122bd | ||
|
|
5be9a3f219 | ||
|
|
b0a478c156 | ||
|
|
96b6a0bc2c | ||
|
|
c3b79c5330 | ||
|
|
da54738902 | ||
|
|
ced508443a | ||
|
|
22d0446da4 | ||
|
|
8c7b3455c9 | ||
|
|
eac145f010 | ||
|
|
8765d06c2f | ||
|
|
7d19450da7 | ||
|
|
4edaeee883 | ||
|
|
e44ea5dbed | ||
|
|
7ae61b0c6d | ||
|
|
f0ffdc371f | ||
|
|
50f2040eb2 | ||
|
|
b76a8f7d76 | ||
|
|
9b0ab6b1c1 | ||
|
|
bf899f5044 | ||
|
|
75a20b66d3 | ||
|
|
018862d012 | ||
|
|
87d3714ed0 | ||
|
|
f8f5ee7f25 | ||
|
|
45e772acde | ||
|
|
0b7a79bce0 | ||
|
|
4a9d571cb1 | ||
|
|
1d5a8ec81b | ||
|
|
f6c4e26b3b | ||
|
|
536fded762 | ||
|
|
4a79b3f42c | ||
|
|
6e5052f6ab | ||
|
|
fddaeca050 | ||
|
|
5b53ba33ab | ||
|
|
583fb8d6d2 | ||
|
|
9fa51836c7 | ||
|
|
6432dd1fef | ||
|
|
341d61444c | ||
|
|
40f71cc703 | ||
|
|
a8155b9a39 |
BIN
.github/actions/creds.tar.gpg
vendored
BIN
.github/actions/creds.tar.gpg
vendored
Binary file not shown.
273
.github/workflows/build.yml
vendored
273
.github/workflows/build.yml
vendored
@@ -12,10 +12,10 @@ defaults:
|
||||
working-directory: src
|
||||
|
||||
env:
|
||||
OPENSSL_CONFIG_OPTS: no-asm no-fips
|
||||
OPENSSL_INSTALL_PATH: ${{ github.workspace }}/src/ssl
|
||||
OPENSSL_CONFIG_OPTS: no-fips
|
||||
OPENSSL_INSTALL_PATH: ${{ github.workspace }}/bin/ssl
|
||||
OPENSSL_SOURCE_PATH: ${{ github.workspace }}/src/openssl
|
||||
PYTHON_INSTALL_PATH: ${{ github.workspace }}/src/python
|
||||
PYTHON_INSTALL_PATH: ${{ github.workspace }}/bin/python
|
||||
PYTHON_SOURCE_PATH: ${{ github.workspace }}/src/cpython
|
||||
|
||||
jobs:
|
||||
@@ -25,44 +25,50 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-20.04
|
||||
jid: 1
|
||||
goal: build
|
||||
arch: x86_64
|
||||
openssl_archs: linux-x86_64
|
||||
- os: [self-hosted, linux, arm64]
|
||||
jid: 2
|
||||
goal: build
|
||||
arch: x86_64
|
||||
- os: macos-11
|
||||
arch: aarch64
|
||||
openssl_archs: linux-aarch64
|
||||
- os: [self-hosted, linux, arm]
|
||||
jid: 3
|
||||
goal: build
|
||||
arch: x86_64
|
||||
- os: windows-2022
|
||||
arch: armv7l
|
||||
openssl_archs: linux-armv4
|
||||
- os: macos-11
|
||||
jid: 4
|
||||
goal: build
|
||||
arch: Win64
|
||||
arch: universal2
|
||||
openssl_archs: darwin64-x86_64 darwin64-arm64
|
||||
- os: windows-2022
|
||||
jid: 5
|
||||
goal: build
|
||||
arch: Win64
|
||||
openssl_archs: VC-WIN64A
|
||||
- os: windows-2022
|
||||
jid: 6
|
||||
goal: build
|
||||
arch: Win32
|
||||
openssl_archs: VC-WIN32
|
||||
- os: ubuntu-20.04
|
||||
goal: test
|
||||
python: "3.7"
|
||||
jid: 6
|
||||
arch: x86_64
|
||||
- os: ubuntu-20.04
|
||||
goal: test
|
||||
python: "3.8"
|
||||
jid: 7
|
||||
arch: x86_64
|
||||
- os: ubuntu-20.04
|
||||
goal: test
|
||||
python: "3.9"
|
||||
python: "3.8"
|
||||
jid: 8
|
||||
arch: x86_64
|
||||
- os: [self-hosted, linux, arm64]
|
||||
- os: ubuntu-20.04
|
||||
goal: test
|
||||
python: "3.9"
|
||||
jid: 9
|
||||
goal: build
|
||||
arch: aarch64
|
||||
- os: [self-hosted, linux, arm]
|
||||
jid: 10
|
||||
goal: build
|
||||
arch: armv7l
|
||||
arch: x86_64
|
||||
|
||||
steps:
|
||||
|
||||
@@ -76,9 +82,8 @@ jobs:
|
||||
id: cache-python-ssl
|
||||
with:
|
||||
path: |
|
||||
src/python
|
||||
src/ssl
|
||||
key: gam-${{ matrix.jid }}-20220131
|
||||
bin
|
||||
key: gam-${{ matrix.jid }}-20220328-01
|
||||
|
||||
- name: Use pre-compiled Python for testing
|
||||
if: matrix.python != ''
|
||||
@@ -105,7 +110,6 @@ jobs:
|
||||
echo "JID=${JID}" >> $GITHUB_ENV
|
||||
echo "ACTIONS_CACHE=${ACTIONS_CACHE}" >> $GITHUB_ENV
|
||||
echo "ACTIONS_GOAL=${ACTIONS_GOAL}" >> $GITHUB_ENV
|
||||
echo "date=date" >> $GITHUB_ENV
|
||||
|
||||
- name: Install necessary hosted Linux packages
|
||||
if: matrix.os == 'ubuntu-20.04'
|
||||
@@ -114,6 +118,22 @@ jobs:
|
||||
sudo apt-get -qq --yes update
|
||||
sudo apt-get -qq --yes install swig libpcsclite-dev
|
||||
|
||||
- name: MacOS remove Homebrew
|
||||
if: matrix.os == 'macos-11'
|
||||
run: |
|
||||
# remove everything except the libraries needed by yubikey-manager
|
||||
brew uninstall $(brew list | grep -v 'pcre\|swig\|pcsc-lite')
|
||||
|
||||
- name: MacOS install tools
|
||||
if: matrix.os == 'macos-11'
|
||||
run: |
|
||||
# Install latest Rust
|
||||
curl -fsS -o rust.sh https://sh.rustup.rs
|
||||
bash ./rust.sh -y
|
||||
source $HOME/.cargo/env
|
||||
# needed for Rust to compile cryptography Python package for universal2
|
||||
rustup target add aarch64-apple-darwin
|
||||
|
||||
- name: Windows Configure VCode
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
if: matrix.os == 'windows-2022' && steps.cache-python-ssl.outputs.cache-hit != 'true'
|
||||
@@ -125,54 +145,44 @@ jobs:
|
||||
env:
|
||||
arch: ${{ matrix.arch }}
|
||||
jid: ${{ matrix.jid }}
|
||||
openssl_archs: ${{ matrix.openssl_archs }}
|
||||
run: |
|
||||
echo "We are running on ${RUNNER_OS}"
|
||||
if [[ "${arch}" == "Win64" ]]; then
|
||||
PYEXTERNALS_PATH="amd64"
|
||||
PYBUILDRELEASE_ARCH="x64"
|
||||
OPENSSL_CONFIG_TARGET="VC-WIN64A"
|
||||
GAM_ARCHIVE_ARCH="x86_64"
|
||||
WIX_ARCH="x64"
|
||||
CHOC_OPS=""
|
||||
elif [[ "${arch}" == "Win32" ]]; then
|
||||
PYEXTERNALS_PATH="win32"
|
||||
PYBUILDRELEASE_ARCH="Win32"
|
||||
OPENSSL_CONFIG_TARGET="VC-WIN32"
|
||||
GAM_ARCHIVE_ARCH="x86"
|
||||
WIX_ARCH="x86"
|
||||
CHOC_OPS="--forcex86"
|
||||
fi
|
||||
if [[ "${RUNNER_OS}" == "macOS" ]]; then
|
||||
brew install coreutils
|
||||
brew install bash
|
||||
#brew install coreutils
|
||||
#brew install bash
|
||||
MAKE=make
|
||||
MAKEOPT="-j$(sysctl -n hw.logicalcpu)"
|
||||
PERL=perl
|
||||
# We only care about non-deprecated OSes
|
||||
MACOSX_DEPLOYMENT_TARGET="10.15"
|
||||
echo "MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET}" >> $GITHUB_ENV
|
||||
echo "MACOSX_DEPLOYMENT_TARGET=10.15" >> $GITHUB_ENV
|
||||
echo "PYTHON=${PYTHON_INSTALL_PATH}/bin/python3" >> $GITHUB_ENV
|
||||
export date=gdate
|
||||
export realpath=grealpath
|
||||
echo "PIP_ARGS=--no-binary=:all:" >> $GITHUB_ENV
|
||||
elif [[ "${RUNNER_OS}" == "Linux" ]]; then
|
||||
MAKE=make
|
||||
MAKEOPT="-j$(nproc)"
|
||||
PERL=perl
|
||||
echo "PYTHON=${PYTHON_INSTALL_PATH}/bin/python3" >> $GITHUB_ENV
|
||||
export date=date
|
||||
export realpath=realpath
|
||||
elif [[ "${RUNNER_OS}" == "Windows" ]]; then
|
||||
MAKE=nmake
|
||||
MAKEOPT=""
|
||||
PERL="c:\strawberry\perl\bin\perl.exe"
|
||||
echo "PYTHON=${PYTHON_INSTALL_PATH}\python.exe" >> $GITHUB_ENV
|
||||
export date=date
|
||||
export realpath=realpath
|
||||
echo "GAM_ARCHIVE_ARCH=${GAM_ARCHIVE_ARCH}" >> $GITHUB_ENV
|
||||
echo "WIX_ARCH=${WIX_ARCH}" >> $GITHUB_ENV
|
||||
fi
|
||||
echo "date=${date}" >> $GITHUB_ENV
|
||||
echo "realpath=${realpath}" >> $GITHUB_ENV
|
||||
echo "We'll run make with: ${MAKEOPT}"
|
||||
echo "JID=${jid}" >> $GITHUB_ENV
|
||||
echo "arch=${arch}" >> $GITHUB_ENV
|
||||
@@ -181,13 +191,15 @@ jobs:
|
||||
echo "PERL=${PERL}" >> $GITHUB_ENV
|
||||
echo "PYEXTERNALS_PATH=${PYEXTERNALS_PATH}" >> $GITHUB_ENV
|
||||
echo "PYBUILDRELEASE_ARCH=${PYBUILDRELEASE_ARCH}" >> $GITHUB_ENV
|
||||
echo "OPENSSL_CONFIG_TARGET=${OPENSSL_CONFIG_TARGET}" >> $GITHUB_ENV
|
||||
echo "openssl_archs=${openssl_archs}" >> $GITHUB_ENV
|
||||
echo "LD_LIBRARY_PATH=${OPENSSL_INSTALL_PATH}/lib:${PYTHON_INSTALL_PATH}/lib" >> $GITHUB_ENV
|
||||
#echo "PATH=${PATH}:${PYTHON_INSTALL_PATH}/scripts" >> $GITHUB_ENV
|
||||
|
||||
- name: Get latest stable OpenSSL source
|
||||
if: matrix.goal == 'build' && steps.cache-python-ssl.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
mkdir -vp "${GITHUB_WORKSPACE}/src"
|
||||
cd "${GITHUB_WORKSPACE}/src"
|
||||
git clone https://github.com/openssl/openssl.git
|
||||
cd "${OPENSSL_SOURCE_PATH}"
|
||||
export LATEST_STABLE_TAG=$(git tag --list openssl-* | grep -v alpha | grep -v beta | sort -Vr | head -n1)
|
||||
@@ -195,6 +207,16 @@ jobs:
|
||||
git checkout "${LATEST_STABLE_TAG}"
|
||||
export COMPILED_OPENSSL_VERSION=${LATEST_STABLE_TAG:8} # Trim the openssl- prefix
|
||||
echo "COMPILED_OPENSSL_VERSION=${COMPILED_OPENSSL_VERSION}" >> $GITHUB_ENV
|
||||
if [[ "${RUNNER_OS}" == "macOS" ]]; then
|
||||
for openssl_arch in $openssl_archs; do
|
||||
ssldir="${OPENSSL_SOURCE_PATH}-${openssl_arch}"
|
||||
mkdir -v "${ssldir}"
|
||||
cp -vrf ${OPENSSL_SOURCE_PATH}/* "${ssldir}/"
|
||||
done
|
||||
rm -vrf "${OPENSSL_SOURCE_PATH}"
|
||||
else
|
||||
mv -v "${OPENSSL_SOURCE_PATH}" "${OPENSSL_SOURCE_PATH}-${openssl_archs}"
|
||||
fi
|
||||
|
||||
- name: Windows NASM Install
|
||||
uses: ilammy/setup-nasm@v1
|
||||
@@ -203,9 +225,11 @@ jobs:
|
||||
- name: Config OpenSSL
|
||||
if: matrix.goal == 'build' && steps.cache-python-ssl.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd "${OPENSSL_SOURCE_PATH}"
|
||||
# --libdir=lib is needed so Python can find OpenSSL libraries
|
||||
"${PERL}" ./Configure "${OPENSSL_CONFIG_TARGET}" --libdir=lib --prefix="${OPENSSL_INSTALL_PATH}" $OPENSSL_CONFIG_OPTS
|
||||
for openssl_arch in $openssl_archs; do
|
||||
cd "${GITHUB_WORKSPACE}/src/openssl-${openssl_arch}"
|
||||
# --libdir=lib is needed so Python can find OpenSSL libraries
|
||||
"${PERL}" ./Configure "${openssl_arch}" --libdir=lib --prefix="${OPENSSL_INSTALL_PATH}" $OPENSSL_CONFIG_OPTS
|
||||
done
|
||||
|
||||
- name: Rename GNU link on Windows
|
||||
if: matrix.goal == 'build' && matrix.os == 'windows-2022' && steps.cache-python-ssl.outputs.cache-hit != 'true'
|
||||
@@ -215,24 +239,54 @@ jobs:
|
||||
- name: Make OpenSSL
|
||||
if: matrix.goal == 'build' && steps.cache-python-ssl.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd "${OPENSSL_SOURCE_PATH}"
|
||||
$MAKE "${MAKEOPT}"
|
||||
for openssl_arch in $openssl_archs; do
|
||||
cd "${GITHUB_WORKSPACE}/src/openssl-${openssl_arch}"
|
||||
$MAKE "${MAKEOPT}"
|
||||
done
|
||||
|
||||
- name: Install OpenSSL
|
||||
if: matrix.goal == 'build' && steps.cache-python-ssl.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd "${OPENSSL_SOURCE_PATH}"
|
||||
# install_sw saves us ages processing man pages :-)
|
||||
$MAKE install_sw
|
||||
if [[ "${RUNNER_OS}" == "macOS" ]]; then
|
||||
for openssl_arch in $openssl_archs; do
|
||||
cd "${GITHUB_WORKSPACE}/src/openssl-${openssl_arch}"
|
||||
# install_sw saves us ages processing man pages :-)
|
||||
$MAKE install_sw
|
||||
mv "${OPENSSL_INSTALL_PATH}" "${GITHUB_WORKSPACE}/bin/ssl-${openssl_arch}"
|
||||
done
|
||||
mkdir -vp "${OPENSSL_INSTALL_PATH}/lib"
|
||||
mkdir -vp "${OPENSSL_INSTALL_PATH}/bin"
|
||||
for archlib in libcrypto.3.dylib libssl.3.dylib libcrypto.a libssl.a; do
|
||||
lipo -create "${GITHUB_WORKSPACE}/bin/ssl-darwin64-x86_64/lib/${archlib}" \
|
||||
"${GITHUB_WORKSPACE}/bin/ssl-darwin64-arm64/lib/${archlib}" \
|
||||
-output "${GITHUB_WORKSPACE}/bin/ssl/lib/${archlib}"
|
||||
done
|
||||
mv ${GITHUB_WORKSPACE}/bin/ssl-darwin64-x86_64/include ${GITHUB_WORKSPACE}/bin/ssl/
|
||||
lipo -create "${GITHUB_WORKSPACE}/bin/ssl-darwin64-x86_64/bin/openssl" \
|
||||
"${GITHUB_WORKSPACE}/bin/ssl-darwin64-arm64/bin/openssl" \
|
||||
-output "${GITHUB_WORKSPACE}/bin/ssl/bin/openssl"
|
||||
rm -rf ${GITHUB_WORKSPACE}/bin/ssl-darwin64-x86_64
|
||||
rm -rf ${GITHUB_WORKSPACE}/bin/ssl-darwin64-arm64
|
||||
echo "LDFLAGS=-L${OPENSSL_INSTALL_PATH}/lib" >> $GITHUB_ENV
|
||||
echo "CRYPTOGRAPHY_SUPPRESS_LINK_FLAGS=1" >> $GITHUB_ENV
|
||||
echo "CFLAGS=-I${OPENSSL_INSTALL_PATH}/include -arch arm64 -arch x86_64" >> $GITHUB_ENV
|
||||
echo "ARCHFLAGS=-arch x86_64 -arch arm64" >> $GITHUB_ENV
|
||||
else
|
||||
cd "${GITHUB_WORKSPACE}/src/openssl-${openssl_archs}"
|
||||
# install_sw saves us ages processing man pages :-)
|
||||
$MAKE install_sw
|
||||
fi
|
||||
|
||||
- name: Run OpenSSL
|
||||
if: matrix.goal == 'build'
|
||||
run: |
|
||||
"${OPENSSL_INSTALL_PATH}/bin/openssl" version
|
||||
file "${OPENSSL_INSTALL_PATH}/bin/openssl"
|
||||
|
||||
- name: Get latest stable Python source
|
||||
if: matrix.goal == 'build' && steps.cache-python-ssl.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd "${GITHUB_WORKSPACE}/src"
|
||||
git clone https://github.com/python/cpython.git
|
||||
cd "${PYTHON_SOURCE_PATH}"
|
||||
export LATEST_STABLE_TAG=$(git tag --list | grep -v a | grep -v rc | grep -v b | sort -Vr | head -n1)
|
||||
@@ -244,12 +298,18 @@ jobs:
|
||||
if: matrix.goal == 'build' && matrix.os != 'windows-2022' && steps.cache-python-ssl.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd "${PYTHON_SOURCE_PATH}"
|
||||
if [[ "${RUNNER_OS}" == "macOS" ]]; then
|
||||
extra_args=( "--enable-universalsdk" "--with-universal-archs=universal2" )
|
||||
else
|
||||
extra_args=( )
|
||||
fi
|
||||
./configure --with-openssl="${OPENSSL_INSTALL_PATH}" \
|
||||
--prefix="${PYTHON_INSTALL_PATH}" \
|
||||
--enable-shared \
|
||||
--with-ensurepip=upgrade \
|
||||
--enable-optimizations \
|
||||
--with-lto
|
||||
--with-lto \
|
||||
"${extra_args[@]}"
|
||||
|
||||
- name: Windows Get External Python deps
|
||||
if: matrix.goal == 'build' && matrix.os == 'windows-2022' && steps.cache-python-ssl.outputs.cache-hit != 'true'
|
||||
@@ -271,7 +331,7 @@ jobs:
|
||||
$env:OPENSSL_EXT_TARGET_PATH = "${env:OPENSSL_EXT_PATH}${env:PYEXTERNALS_PATH}"
|
||||
echo "Copying our OpenSSL to ${env:OPENSSL_EXT_TARGET_PATH}"
|
||||
mkdir "${env:OPENSSL_EXT_TARGET_PATH}\include\openssl\"
|
||||
Copy-Item -Path "${env:OPENSSL_SOURCE_PATH}\LICENSE.txt" -Destination "${env:OPENSSL_EXT_TARGET_PATH}\LICENSE"
|
||||
Copy-Item -Path "${env:GITHUB_WORKSPACE}/src/openssl-${env:openssl_archs}\LICENSE.txt" -Destination "${env:OPENSSL_EXT_TARGET_PATH}\LICENSE"
|
||||
cp -v "$env:OPENSSL_INSTALL_PATH\lib\*" "${env:OPENSSL_EXT_TARGET_PATH}"
|
||||
cp -v "$env:OPENSSL_INSTALL_PATH\bin\*" "${env:OPENSSL_EXT_TARGET_PATH}"
|
||||
cp -v "$env:OPENSSL_INSTALL_PATH\include\openssl\*" "${env:OPENSSL_EXT_TARGET_PATH}\include\openssl\"
|
||||
@@ -331,7 +391,7 @@ jobs:
|
||||
"${PYTHON}" get-pip.py
|
||||
"${PYTHON}" -m pip install --upgrade pip
|
||||
"${PYTHON}" -m pip install --upgrade wheel
|
||||
"${PYTHON}" -m pip install --upgrade setuptools==60.6.0
|
||||
"${PYTHON}" -m pip install --upgrade setuptools
|
||||
|
||||
- name: Install PyInstaller
|
||||
if: matrix.goal == 'build'
|
||||
@@ -348,13 +408,27 @@ jobs:
|
||||
fi
|
||||
echo "PyInstaller build arguments: ${PYINSTALLER_BUILD_ARGS}"
|
||||
"${PYTHON}" ./waf all $PYINSTALLER_BUILD_ARGS
|
||||
cd ..
|
||||
"${PYTHON}" -m pip install .
|
||||
cd ../..
|
||||
echo "---- Installing PyInstaller ----"
|
||||
"${PYTHON}" -m pip install pyinstaller
|
||||
|
||||
- name: Install pip requirements
|
||||
run: |
|
||||
set +e
|
||||
"${PYTHON}" -m pip install --upgrade -r requirements.txt
|
||||
if [[ "${RUNNER_OS}" == "macOS" ]]; then
|
||||
for package in cryptography; do
|
||||
"${PYTHON}" -m pip install --upgrade cffi ${PIP_ARGS}
|
||||
"${PYTHON}" -m pip download --only-binary :all: \
|
||||
--dest . \
|
||||
--no-cache \
|
||||
--no-deps \
|
||||
--platform macosx_10_15_universal2 \
|
||||
$package
|
||||
"${PYTHON}" -m pip install --force-reinstall --no-deps $package*.whl
|
||||
done
|
||||
find $PYTHON_INSTALL_PATH/lib/python3.10/site-packages -type f -name "*.so" -exec du -sh "{}" \;
|
||||
fi
|
||||
"${PYTHON}" -m pip install --upgrade -r requirements.txt ${PIP_ARGS}
|
||||
"${PYTHON}" -m pip list
|
||||
|
||||
- name: Build GAM with PyInstaller
|
||||
@@ -362,12 +436,16 @@ jobs:
|
||||
run: |
|
||||
export gampath="./dist/gam"
|
||||
mkdir -p -v "${gampath}"
|
||||
export gampath="$($realpath $gampath)"
|
||||
if [[ "${RUNNER_OS}" == "macOS" ]]; then
|
||||
export gampath=$($PYTHON -c "import os; print(os.path.realpath('$gampath'))")
|
||||
else
|
||||
export gampath=$(realpath "${gampath}")
|
||||
fi
|
||||
export gam="${gampath}/gam"
|
||||
echo "gampath=${gampath}" >> $GITHUB_ENV
|
||||
echo "gam=${gam}" >> $GITHUB_ENV
|
||||
echo -e "GAM: ${gam}\nGAMPATH: ${gampath}"
|
||||
"${PYTHON}" -m PyInstaller --clean --noupx --strip --onefile --distpath="${gampath}" gam.spec
|
||||
"${PYTHON}" -m PyInstaller --clean --distpath="${gampath}" gam.spec
|
||||
|
||||
- name: Basic Tests all jobs
|
||||
run: |
|
||||
@@ -383,7 +461,7 @@ jobs:
|
||||
cp -v LICENSE $gampath
|
||||
cp -v GamCommands.txt $gampath
|
||||
if [[ "${RUNNER_OS}" == "macOS" ]]; then
|
||||
GAM_ARCHIVE="gam-${GAMVERSION}-macos-x86_64.tar.xz"
|
||||
GAM_ARCHIVE="gam-${GAMVERSION}-macos-universal2.tar.xz"
|
||||
elif [[ "${RUNNER_OS}" == "Linux" ]]; then
|
||||
this_glibc_ver=$(ldd --version | awk '/ldd/{print $NF}')
|
||||
GAM_ARCHIVE="gam-${GAMVERSION}-linux-$(arch)-glibc${this_glibc_ver}.tar.xz"
|
||||
@@ -452,6 +530,9 @@ jobs:
|
||||
env:
|
||||
PASSCODE: ${{ secrets.PASSCODE }}
|
||||
run: |
|
||||
if [[ "${RUNNER_OS}" == "macOS" ]]; then
|
||||
brew install gnupg
|
||||
fi
|
||||
source ../.github/actions/decrypt.sh ../.github/actions/creds.tar.gpg creds.tar
|
||||
export OAUTHFILE="oauth2.txt-gam-gha-${JID}"
|
||||
echo "OAUTHFILE=${OAUTHFILE}" >> $GITHUB_ENV
|
||||
@@ -462,8 +543,8 @@ jobs:
|
||||
$gam oauth refresh
|
||||
$gam info user
|
||||
#$gam info user $gam_user grouptree
|
||||
export tstamp=$($date +%s%3N)
|
||||
export newbase=gha-test-$JID-$tstamp
|
||||
export tstamp=$($PYTHON -c "import time; print(time.time_ns())")
|
||||
export newbase=gha_test_$JID_$tstamp
|
||||
export newuser=$newbase@pdl.jaylee.us
|
||||
export newgroup=$newbase-group@pdl.jaylee.us
|
||||
export newalias=$newbase-alias@pdl.jaylee.us
|
||||
@@ -471,18 +552,23 @@ jobs:
|
||||
export newresource=$newbase-resource
|
||||
export GAM_THREADS=5
|
||||
echo email > sample.csv;
|
||||
for i in {01..10}; do
|
||||
for i in {1..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 languages en+,en-GB-
|
||||
$gam user $newuser update photo https://dummyimage.com/400x600/000/fff
|
||||
$gam user $newuser get photo
|
||||
$gam user $newuser delete photo
|
||||
$gam create group $newgroup name "GHA $JID group" description "This is a description" isarchived true
|
||||
$gam user $gam_user sendemail recipient $newuser subject "test message $newbase" message "GHA test message"
|
||||
$gam user $gam_user sendemail recipient exchange@pdl.jaylee.us subject "test ${tstamp}" message "test message"
|
||||
$gam create group $newgroup name "GHA $JID group" description "This is a description" isarchived true
|
||||
$gam user $newuser add license workspaceenterpriseplus
|
||||
$gam print privileges
|
||||
$gam update cigroup $newgroup memberrestriction 'member.type == 1 || member.customer_id == groupCustomerId()'
|
||||
$gam info cigroup $newgroup
|
||||
$gam user $newuser add license workspaceenterpriseplus
|
||||
$gam update group $newgroup add owner $gam_user
|
||||
$gam update group $newgroup add member $newuser
|
||||
$gam create admin $newuser _GROUPS_EDITOR_ROLE CUSTOMER condition nonsecuritygroup
|
||||
$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 ""
|
||||
@@ -497,8 +583,8 @@ jobs:
|
||||
$gam user $newuser imap on
|
||||
$gam user $newuser show imap
|
||||
$gam user $newuser show delegates
|
||||
#$gam user $newuser add contactdelegate "${newbase}-bulkuser-01"
|
||||
#$gam user $newuser print contactdelegates
|
||||
$gam user $newuser add contactdelegate "${newbase}-bulkuser-1"
|
||||
$gam user $newuser print contactdelegates
|
||||
export biohazard=$(echo -e '\xe2\x98\xa3')
|
||||
$gam user $newuser label "$biohazard unicode biohazard $biohazard"
|
||||
$gam user $newuser show labels
|
||||
@@ -508,10 +594,10 @@ jobs:
|
||||
$gam user $gam_user sendemail subject "GHA send $gam_user $newbase" file gam.py recipient admin@pdl.jaylee.us
|
||||
$gam user $gam_user draftemail subject "GHA draft $newbase" message "Draft message test"
|
||||
$gam csvfile sample.csv:email waitformailbox
|
||||
$gam user $newuser delegate to "${newbase}-bulkuser-01"
|
||||
$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
|
||||
$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 delegate to "${newbase}-bulkuser-1"
|
||||
$gam users "$gam_user $newbase-bulkuser-1 $newbase-bulkuser-2 $newbase-bulkuser-3" delete messages query in:anywhere maxtodelete 99999 doit
|
||||
$gam users "$newbase-bulkuser-4 $newbase-bulkuser-5 $newbase-bulkuser-6" trash messages query in:anywhere maxtotrash 99999 doit
|
||||
$gam users "$newbase-bulkuser-7 $newbase-bulkuser-8 $newbase-bulkuser-9" modify messages query in:anywhere maxtomodify 99999 addlabel IMPORTANT addlabel STARRED doit
|
||||
$gam user $newuser delete label --ALL_LABELS--
|
||||
GAM_CSV_ROW_FILTER="name:regex:gha-test-${JID}" $gam print features | $gam csv - gam delete feature ~name
|
||||
$gam create feature name Whiteboard-$newbase
|
||||
@@ -526,14 +612,16 @@ jobs:
|
||||
$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
|
||||
starttime=$($PYTHON -c "import datetime; print((datetime.datetime.now() + datetime.timedelta(hours=1)).strftime('%Y-%m-%dT%H:%M:%S.%f+00:00'))")
|
||||
endtime=$($PYTHON -c "import datetime; print((datetime.datetime.now() + datetime.timedelta(hours=2)).strftime('%Y-%m-%dT%H:%M:%S.%f+00:00'))")
|
||||
$gam calendar $gam_user addevent summary "GHA test event" start "${starttime}" end "${endtime}" 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 create vaultexport matter $matterid name "GHA export $newbase" corpus mail accounts $newuser use_new_export true
|
||||
$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
|
||||
@@ -549,6 +637,8 @@ jobs:
|
||||
$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 undelete user $newuser
|
||||
$gam delete user $newuser
|
||||
$gam print users query "gha.jid=$JID" | $gam csv - gam delete user ~primaryEmail
|
||||
$gam print mobile
|
||||
@@ -557,7 +647,7 @@ jobs:
|
||||
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 orderby serialnumber
|
||||
#$gam show crostelemetry storagepercentonly
|
||||
$gam show crostelemetry storagepercentonly
|
||||
$gam report usageparameters customer
|
||||
$gam report usage customer parameters gmail:num_emails_sent,accounts:num_1day_logins
|
||||
$gam report customer todrive
|
||||
@@ -566,25 +656,34 @@ jobs:
|
||||
$gam print devices nopersonaldevices nodeviceusers filter "serial:$JID$JID$JID$JID-" | $gam csv - gam delete device id ~name
|
||||
$gam print userinvitations
|
||||
$gam print userinvitations | $gam csv - gam send userinvitation ~name
|
||||
export CUSTOMER_ID="C01wfv983"
|
||||
export GA_DOMAIN="pdl.jaylee.us"
|
||||
touch $gampath/enabledasa.txt
|
||||
$gam create caalevel "zzz_${newbase}" basic condition ipsubnetworks 1.1.1.1/32,2.2.2.2/32 endcondition
|
||||
$gam print caalevels
|
||||
$gam delete caalevel "zzz_${newbase}"
|
||||
driveid=$($gam user $gam_user add shareddrive "${newbase}" | awk '{print $NF}')
|
||||
echo "Created shared drive ${driveid}"
|
||||
$gam user $gam_user update shareddrive "${driveid}" ou "id:03ph8a2z1t2ph5z"
|
||||
$gam user $gam_user show shareddrives asadmin
|
||||
$gam user $gam_user delete shareddrive "${driveid}"
|
||||
echo "printer model count:"
|
||||
$gam print printermodels | wc -l
|
||||
#$gam print printers
|
||||
#$gam create printer displayname "${newbase}" uri ipp://localhost:631 driverless description "made by $(date)"
|
||||
rm -f -v $gampath/enabledasa.txt
|
||||
#$gam create printer displayname "${newbase}" uri ipp://localhost:631 driverless description "made by $(gam_user)" ou /
|
||||
#export CUSTOMER_ID="C01wfv983"
|
||||
#export GA_DOMAIN="pdl.jaylee.us"
|
||||
#touch $gampath/enabledasa.txt
|
||||
#echo "using delegated admin service account"
|
||||
#$gam print users
|
||||
|
||||
- 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
|
||||
# - 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
|
||||
|
||||
- name: Archive production artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
|
||||
201
LICENSE
Normal file
201
LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -243,6 +243,7 @@ If an item contains spaces, it should be surrounded by ".
|
||||
<SMTPHostName> ::= <String>
|
||||
<StudentItem> ::= <EmailAddress>|<UniqueID>|<String>
|
||||
<TeamDriveID> ::= <String>
|
||||
<TeamDriveName> ::= <String>
|
||||
<Timezone> ::= <String>
|
||||
<Title> ::= <String>
|
||||
<URI> ::= <String>
|
||||
@@ -632,6 +633,7 @@ Items, separated by spaces, with spaces, commas or single quotes in the items th
|
||||
<SchemaNameList> ::= "<SchemaName>(,<SchemaName>)*"
|
||||
<SerialNumberList> ::= "<SerialNumber>(,<SerialNumber>)*"
|
||||
<ServiceAccountKeyList> ::= "<ServiceAccountKey>(,<ServiceAccountKey>)*"
|
||||
<StringList> ::= "<String>(,<String>)*"
|
||||
<TeamDriveIDList> ::= "<TeamDriveID>(,<TeamDriveID>)*"
|
||||
<UserFieldNameList> ::= "<UserFieldName>(,<UserFieldName>)*"
|
||||
<UserList> ::= "<UserItem>(,<UserItem>)*"
|
||||
@@ -992,9 +994,9 @@ gam report <ActivityApplicationName> [todrive]
|
||||
[filter|filters <String>] [event <String>] [ip <String>]
|
||||
[groupidfilter <String>]
|
||||
|
||||
gam create admin <UserItem> <RoleItem> customer|(org_unit <OrgUnitItem>)
|
||||
gam create admin <UserItem> <RoleItem> customer|(org_unit <OrgUnitItem>) [condition securitygroup|nonsecuritygroup]
|
||||
gam delete admin <RoleAssignmentId>
|
||||
gam print admins [todrive] [user <UserItem>] [role <RoleItem>]
|
||||
gam print admins [todrive] [user <UserItem>] [role <RoleItem>] [condition]
|
||||
gam create adminrole <String> privileges all|all_ou|<PrivilegesList> [description <String>]
|
||||
gam update adminrole <RoleItem> [name <String>] [privileges all|all_ou|<PrivilegesList>] [description <String>]
|
||||
gam delete adminrole <RoleItem>
|
||||
@@ -1052,7 +1054,7 @@ gam info org|ou <OrgUnitPath> [nousers|notsuspended|suspended] [children|child]
|
||||
gam print orgs|ous [todrive] [toplevelonly] [from_parent <OrgUnitPath>] [allfields|(fields <OrgUnitFieldNameList>)]
|
||||
|
||||
gam create alias|nickname <EmailAddress> user|group|target <UniqueID>|<EmailAddress> [verifynotinvitable]
|
||||
gam update alias|nickname <EmailAddress> user|group|target <UniqueID>|<EmailAddress> [verifynotinvitable]
|
||||
gam update alias|nickname <EmailAddress> user|group|target <UniqueID>|<EmailAddress> [notargetverify]
|
||||
gam delete alias|nickname [user|group|target] <UniqueID>|<EmailAddress>
|
||||
gam info alias|nickname <EmailAddress>
|
||||
gam print aliases|nicknames [todrive] [shownoneditable] [nogroups] [nousers] [(query <QueryUser>)|(queries <QueryUserList)]
|
||||
@@ -1210,6 +1212,76 @@ gam print browsertokens [todrive]
|
||||
[fields <BrowserTokenFieldNameList>]
|
||||
[sortheaders]
|
||||
|
||||
<CAAAllowedEncryptionStatus> ::=
|
||||
encryption_unsupported |
|
||||
encrypted |
|
||||
unencrypted
|
||||
<CAAAllowedEncryptionStatusList> ::= "<CAAAllowedEncryptionStatus>(,<CAAAllowedEncryptionStatus>)"
|
||||
|
||||
<CAAAllowedDeviceManagementLevel> ::=
|
||||
basic |
|
||||
advanced|complete |
|
||||
none
|
||||
<CAAAllowedDeviceManagementLevelList> ::= "<CAAAllowedDeviceManagementLevel>(,<CAAAllowedDeviceManagementLevel>)"
|
||||
|
||||
<CAACombiningFunction> ::=
|
||||
and |
|
||||
or
|
||||
|
||||
<CAAIPSubNetwork> ::=
|
||||
<CIDRnetmask>
|
||||
<CAAIPSubNetworkList> ::= "<CAAIPSubNetwork>(,<CAAIPSubNetwork>)"
|
||||
|
||||
<CAAMember> ::=
|
||||
user:<EmailAddress> |
|
||||
serviceAccount:<EmailAddress>
|
||||
<CAAMemberList> ::= "<CAAMember>(,<CAAMember>)"
|
||||
|
||||
<CAAOsType> ::=
|
||||
DESKTOP_MAC |
|
||||
DESKTOP_WINDOWS |
|
||||
DESKTOP_LINUX |
|
||||
DESKTOP_CHROME_OS |
|
||||
VERIFIED_DESKTOP_CHROME_OS |
|
||||
ANDROID |
|
||||
IOS
|
||||
|
||||
<CAAOsConstraint> ::=
|
||||
<CAAOsType> |
|
||||
<CAAOsType>:<String>.<String>.<String>
|
||||
<CAAOsConstraintList> ::= "<CAAOsConstraint>(,<CAAOsConstraint>)"
|
||||
|
||||
<CAARegion> ::=
|
||||
<Character><Character>
|
||||
<CAARegionList> ::= "<CAARegion>(,<CAARegion>)"
|
||||
See: https://www.iso.org/obp/ui/#search
|
||||
|
||||
<CAADevicePolicyAttribute> ::=
|
||||
(requirescreenlock <Boolean>) |
|
||||
(allowedencryptionstatuses <CAAAllowedEncryptionStatusList>) |
|
||||
(osconstraints <CAAOsConstraintList>) |
|
||||
(alloweddevicemanagementlevels <CAAAllowedDeviceManagementLevelList>) |
|
||||
(requireadminapproval <Boolean>) |
|
||||
(requirecorpowned <Boolean>)
|
||||
|
||||
<CAAConditionAttribute> ::=
|
||||
(ipsubnetworks <CAAIPSubNetworkList>) |
|
||||
(devicepolicy <CAADevicePolicyAttribute> enddevicepolicy) |
|
||||
(requiredaccesslevels <StringList>) |
|
||||
(negate <Boolean>) |
|
||||
(members <CAARegionList>) |
|
||||
(regions <CAAMemberList>)
|
||||
|
||||
<CAABasicAttribute> ::+
|
||||
(combiningfunction <CAACombiningFunction>) |
|
||||
(condition <CAAConditionAttribute>+ endcondition)
|
||||
|
||||
gam create caalevel <String> (basic <CAABasicAttribute>+)|(custom <String>)
|
||||
gam update caalevel <CAALevelName> (basic <CAABasicAttribute>+)|(custom <String>)
|
||||
gam delete caalevel <CAALevelName>
|
||||
gam show caalevels
|
||||
gam print caalevels [todrive]
|
||||
|
||||
gam print chatspaces [todrive]
|
||||
gam print chatmembers space <ChatSpace> [todrive]
|
||||
gam create chatmessage space <ChatSpace> [thread <String>]
|
||||
@@ -1350,11 +1422,11 @@ gam print chromehistory channels [todrive]
|
||||
gam print chromehistory versions [todrive]
|
||||
[platform <ChromePlatformType>] [channel <ChromeChannelType>]
|
||||
[filter <String>]
|
||||
(orderby <ChromeVersionsOrderByFieldName> [ascending|descending])*
|
||||
(orderby <ChromeVersionsOrderByFieldName> [ascending|descending])*
|
||||
gam print chromehistory releases [todrive]
|
||||
[platform <ChromePlatformType>] [channel <ChromeChannelType>] [version <String>]
|
||||
[filter <String>]
|
||||
(orderby <ChromeReleasessOrderByFieldName> [ascending|descending])*
|
||||
(orderby <ChromeReleasessOrderByFieldName> [ascending|descending])*
|
||||
|
||||
gam delete chromepolicy <SchemaName>+ ou|org|orgunit <OrgUnitItem> [(printerid <PrinterID>)|(appid <AppID>)]
|
||||
gam update chromepolicy (<SchemaName> (<Field> <Value>)+)+ ou|org|orgunit <OrgUnitItem> [(printerid <PrinterID>)|(appid <AppID>)]
|
||||
@@ -1488,6 +1560,9 @@ gam update feature <Name> name <Name>
|
||||
gam delete feature <Name>
|
||||
gam print features [todrive]
|
||||
|
||||
gam show oushareddrives|orgunitshareddrives [ou|org|orgunit <OrgUnitItem>]
|
||||
gam print oushareddrives|orgunitshareddrives [todrive] [ou|org|orgunit <OrgUnitItem>]
|
||||
|
||||
gam create resource <ResourceID> <Name> <ResourceAttribute>*
|
||||
gam update resource <ResourceID> <ResourceAttribute>*
|
||||
gam delete resource <ResourceID>
|
||||
@@ -1760,12 +1835,12 @@ gam <UserTypeEntity> show signature|sig [format]
|
||||
teammembersonly
|
||||
|
||||
gam <UserTypeEntity> create|add teamdrive <Name>
|
||||
gam <UserTypeEntity> update teamdrive <TeamDriveID> [asadmin] [name <Name>]
|
||||
gam <UserTypeEntity> update teamdrive <TeamDriveID>|(name <TeamDriveName>) [asadmin] [name <Name>]
|
||||
[(theme|themeid <String>) | ([customtheme <DriveFileID> <Float> <Float> <Float>] [color <ColorValue>])]
|
||||
(<TeamDriveRestrictionsSubfieldName> <Boolean>)*
|
||||
[hidden <Boolean>]
|
||||
gam <UserTypeEntity> delete teamdrive <TeamDriveID>
|
||||
gam <UserTypeEntity> show teamdriveinfo <TeamDriveID> [asadmin]
|
||||
gam <UserTypeEntity> delete teamdrive <TeamDriveID>|(name <TeamDriveName>)
|
||||
gam <UserTypeEntity> show teamdriveinfo <TeamDriveID>|(name <TeamDriveName>) [asadmin]
|
||||
gam <UserTypeEntity> show teamdrives [query <QueryTeamDrive>] [asadmin]
|
||||
gam <UserTypeEntity> print teamdrives [query <QueryTeamDrive>] [todrive] [asadmin]
|
||||
gam <UserTypeEntity> show teamdrivethemes
|
||||
|
||||
547
src/LICENSE
547
src/LICENSE
@@ -1,547 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
|
||||
APACHE HTTP SERVER SUBCOMPONENTS:
|
||||
|
||||
The Apache HTTP Server includes a number of subcomponents with
|
||||
separate copyright notices and license terms. Your use of the source
|
||||
code for the these subcomponents is subject to the terms and
|
||||
conditions of the following licenses.
|
||||
|
||||
For the mod_mime_magic component:
|
||||
|
||||
/*
|
||||
* mod_mime_magic: MIME type lookup via file magic numbers
|
||||
* Copyright (c) 1996-1997 Cisco Systems, Inc.
|
||||
*
|
||||
* This software was submitted by Cisco Systems to the Apache Group in July
|
||||
* 1997. Future revisions and derivatives of this source code must
|
||||
* acknowledge Cisco Systems as the original contributor of this module.
|
||||
* All other licensing and usage conditions are those of the Apache Group.
|
||||
*
|
||||
* Some of this code is derived from the free version of the file command
|
||||
* originally posted to comp.sources.unix. Copyright info for that program
|
||||
* is included below as required.
|
||||
* ---------------------------------------------------------------------------
|
||||
* - Copyright (c) Ian F. Darwin, 1987. Written by Ian F. Darwin.
|
||||
*
|
||||
* This software is not subject to any license of the American Telephone and
|
||||
* Telegraph Company or of the Regents of the University of California.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose on any
|
||||
* computer system, and to alter it and redistribute it freely, subject to
|
||||
* the following restrictions:
|
||||
*
|
||||
* 1. The author is not responsible for the consequences of use of this
|
||||
* software, no matter how awful, even if they arise from flaws in it.
|
||||
*
|
||||
* 2. The origin of this software must not be misrepresented, either by
|
||||
* explicit claim or by omission. Since few users ever read sources, credits
|
||||
* must appear in the documentation.
|
||||
*
|
||||
* 3. Altered versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software. Since few users ever read
|
||||
* sources, credits must appear in the documentation.
|
||||
*
|
||||
* 4. This notice may not be removed or altered.
|
||||
* -------------------------------------------------------------------------
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
For the modules\mappers\mod_imagemap.c component:
|
||||
|
||||
"macmartinized" polygon code copyright 1992 by Eric Haines, erich@eye.com
|
||||
|
||||
For the server\util_md5.c component:
|
||||
|
||||
/************************************************************************
|
||||
* NCSA HTTPd Server
|
||||
* Software Development Group
|
||||
* National Center for Supercomputing Applications
|
||||
* University of Illinois at Urbana-Champaign
|
||||
* 605 E. Springfield, Champaign, IL 61820
|
||||
* httpd@ncsa.uiuc.edu
|
||||
*
|
||||
* Copyright (C) 1995, Board of Trustees of the University of Illinois
|
||||
*
|
||||
************************************************************************
|
||||
*
|
||||
* md5.c: NCSA HTTPd code which uses the md5c.c RSA Code
|
||||
*
|
||||
* Original Code Copyright (C) 1994, Jeff Hostetler, Spyglass, Inc.
|
||||
* Portions of Content-MD5 code Copyright (C) 1993, 1994 by Carnegie Mellon
|
||||
* University (see Copyright below).
|
||||
* Portions of Content-MD5 code Copyright (C) 1991 Bell Communications
|
||||
* Research, Inc. (Bellcore) (see Copyright below).
|
||||
* Portions extracted from mpack, John G. Myers - jgm+@cmu.edu
|
||||
* Content-MD5 Code contributed by Martin Hamilton (martin@net.lut.ac.uk)
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/* these portions extracted from mpack, John G. Myers - jgm+@cmu.edu */
|
||||
/* (C) Copyright 1993,1994 by Carnegie Mellon University
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software
|
||||
* and its documentation for any purpose is hereby granted without
|
||||
* fee, provided that the above copyright notice appear in all copies
|
||||
* and that both that copyright notice and this permission notice
|
||||
* appear in supporting documentation, and that the name of Carnegie
|
||||
* Mellon University not be used in advertising or publicity
|
||||
* pertaining to distribution of the software without specific,
|
||||
* written prior permission. Carnegie Mellon University makes no
|
||||
* representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied
|
||||
* warranty.
|
||||
*
|
||||
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
||||
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
|
||||
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this material
|
||||
* for any purpose and without fee is hereby granted, provided
|
||||
* that the above copyright notice and this permission notice
|
||||
* appear in all copies, and that the name of Bellcore not be
|
||||
* used in advertising or publicity pertaining to this
|
||||
* material without the specific, prior written permission
|
||||
* of an authorized representative of Bellcore. BELLCORE
|
||||
* MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY
|
||||
* OF THIS MATERIAL FOR ANY PURPOSE. IT IS PROVIDED "AS IS",
|
||||
* WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
|
||||
*/
|
||||
|
||||
For the srclib\apr\include\apr_md5.h component:
|
||||
/*
|
||||
* This is work is derived from material Copyright RSA Data Security, Inc.
|
||||
*
|
||||
* The RSA copyright statement and Licence for that original material is
|
||||
* included below. This is followed by the Apache copyright statement and
|
||||
* licence for the modifications made to that material.
|
||||
*/
|
||||
|
||||
/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
|
||||
rights reserved.
|
||||
|
||||
License to copy and use this software is granted provided that it
|
||||
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
|
||||
Algorithm" in all material mentioning or referencing this software
|
||||
or this function.
|
||||
|
||||
License is also granted to make and use derivative works provided
|
||||
that such works are identified as "derived from the RSA Data
|
||||
Security, Inc. MD5 Message-Digest Algorithm" in all material
|
||||
mentioning or referencing the derived work.
|
||||
|
||||
RSA Data Security, Inc. makes no representations concerning either
|
||||
the merchantability of this software or the suitability of this
|
||||
software for any particular purpose. It is provided "as is"
|
||||
without express or implied warranty of any kind.
|
||||
|
||||
These notices must be retained in any copies of any part of this
|
||||
documentation and/or software.
|
||||
*/
|
||||
|
||||
For the srclib\apr\passwd\apr_md5.c component:
|
||||
|
||||
/*
|
||||
* This is work is derived from material Copyright RSA Data Security, Inc.
|
||||
*
|
||||
* The RSA copyright statement and Licence for that original material is
|
||||
* included below. This is followed by the Apache copyright statement and
|
||||
* licence for the modifications made to that material.
|
||||
*/
|
||||
|
||||
/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
|
||||
*/
|
||||
|
||||
/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
|
||||
rights reserved.
|
||||
|
||||
License to copy and use this software is granted provided that it
|
||||
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
|
||||
Algorithm" in all material mentioning or referencing this software
|
||||
or this function.
|
||||
|
||||
License is also granted to make and use derivative works provided
|
||||
that such works are identified as "derived from the RSA Data
|
||||
Security, Inc. MD5 Message-Digest Algorithm" in all material
|
||||
mentioning or referencing the derived work.
|
||||
|
||||
RSA Data Security, Inc. makes no representations concerning either
|
||||
the merchantability of this software or the suitability of this
|
||||
software for any particular purpose. It is provided "as is"
|
||||
without express or implied warranty of any kind.
|
||||
|
||||
These notices must be retained in any copies of any part of this
|
||||
documentation and/or software.
|
||||
*/
|
||||
/*
|
||||
* The apr_md5_encode() routine uses much code obtained from the FreeBSD 3.0
|
||||
* MD5 crypt() function, which is licenced as follows:
|
||||
* ----------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
|
||||
* can do whatever you want with this stuff. If we meet some day, and you think
|
||||
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
For the srclib\apr-util\crypto\apr_md4.c component:
|
||||
|
||||
* This is derived from material copyright RSA Data Security, Inc.
|
||||
* Their notice is reproduced below in its entirety.
|
||||
*
|
||||
* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
|
||||
* rights reserved.
|
||||
*
|
||||
* License to copy and use this software is granted provided that it
|
||||
* is identified as the "RSA Data Security, Inc. MD4 Message-Digest
|
||||
* Algorithm" in all material mentioning or referencing this software
|
||||
* or this function.
|
||||
*
|
||||
* License is also granted to make and use derivative works provided
|
||||
* that such works are identified as "derived from the RSA Data
|
||||
* Security, Inc. MD4 Message-Digest Algorithm" in all material
|
||||
* mentioning or referencing the derived work.
|
||||
*
|
||||
* RSA Data Security, Inc. makes no representations concerning either
|
||||
* the merchantability of this software or the suitability of this
|
||||
* software for any particular purpose. It is provided "as is"
|
||||
* without express or implied warranty of any kind.
|
||||
*
|
||||
* These notices must be retained in any copies of any part of this
|
||||
* documentation and/or software.
|
||||
*/
|
||||
|
||||
For the srclib\apr-util\include\apr_md4.h component:
|
||||
|
||||
*
|
||||
* This is derived from material copyright RSA Data Security, Inc.
|
||||
* Their notice is reproduced below in its entirety.
|
||||
*
|
||||
* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
|
||||
* rights reserved.
|
||||
*
|
||||
* License to copy and use this software is granted provided that it
|
||||
* is identified as the "RSA Data Security, Inc. MD4 Message-Digest
|
||||
* Algorithm" in all material mentioning or referencing this software
|
||||
* or this function.
|
||||
*
|
||||
* License is also granted to make and use derivative works provided
|
||||
* that such works are identified as "derived from the RSA Data
|
||||
* Security, Inc. MD4 Message-Digest Algorithm" in all material
|
||||
* mentioning or referencing the derived work.
|
||||
*
|
||||
* RSA Data Security, Inc. makes no representations concerning either
|
||||
* the merchantability of this software or the suitability of this
|
||||
* software for any particular purpose. It is provided "as is"
|
||||
* without express or implied warranty of any kind.
|
||||
*
|
||||
* These notices must be retained in any copies of any part of this
|
||||
* documentation and/or software.
|
||||
*/
|
||||
|
||||
|
||||
For the srclib\apr-util\test\testmd4.c component:
|
||||
|
||||
*
|
||||
* This is derived from material copyright RSA Data Security, Inc.
|
||||
* Their notice is reproduced below in its entirety.
|
||||
*
|
||||
* Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All
|
||||
* rights reserved.
|
||||
*
|
||||
* RSA Data Security, Inc. makes no representations concerning either
|
||||
* the merchantability of this software or the suitability of this
|
||||
* software for any particular purpose. It is provided "as is"
|
||||
* without express or implied warranty of any kind.
|
||||
*
|
||||
* These notices must be retained in any copies of any part of this
|
||||
* documentation and/or software.
|
||||
*/
|
||||
|
||||
For the srclib\apr-util\xml\expat\conftools\install-sh component:
|
||||
|
||||
#
|
||||
# install - install a program, script, or datafile
|
||||
# This comes from X11R5 (mit/util/scripts/install.sh).
|
||||
#
|
||||
# Copyright 1991 by the Massachusetts Institute of Technology
|
||||
#
|
||||
# Permission to use, copy, modify, distribute, and sell this software and its
|
||||
# documentation for any purpose is hereby granted without fee, provided that
|
||||
# the above copyright notice appear in all copies and that both that
|
||||
# copyright notice and this permission notice appear in supporting
|
||||
# documentation, and that the name of M.I.T. not be used in advertising or
|
||||
# publicity pertaining to distribution of the software without specific,
|
||||
# written prior permission. M.I.T. makes no representations about the
|
||||
# suitability of this software for any purpose. It is provided "as is"
|
||||
# without express or implied warranty.
|
||||
#
|
||||
|
||||
For the test\zb.c component:
|
||||
|
||||
/* ZeusBench V1.01
|
||||
===============
|
||||
|
||||
This program is Copyright (C) Zeus Technology Limited 1996.
|
||||
|
||||
This program may be used and copied freely providing this copyright notice
|
||||
is not removed.
|
||||
|
||||
This software is provided "as is" and any express or implied warranties,
|
||||
including but not limited to, the implied warranties of merchantability and
|
||||
fitness for a particular purpose are disclaimed. In no event shall
|
||||
Zeus Technology Ltd. be liable for any direct, indirect, incidental, special,
|
||||
exemplary, or consequential damaged (including, but not limited to,
|
||||
procurement of substitute good or services; loss of use, data, or profits;
|
||||
or business interruption) however caused and on theory of liability. Whether
|
||||
in contract, strict liability or tort (including negligence or otherwise)
|
||||
arising in any way out of the use of this software, even if advised of the
|
||||
possibility of such damage.
|
||||
|
||||
Written by Adam Twiss (adam@zeus.co.uk). March 1996
|
||||
|
||||
Thanks to the following people for their input:
|
||||
Mike Belshe (mbelshe@netscape.com)
|
||||
Michael Campanella (campanella@stevms.enet.dec.com)
|
||||
|
||||
*/
|
||||
|
||||
For the expat xml parser component:
|
||||
|
||||
Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
|
||||
and Clark Cooper
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
====================================================================
|
||||
1
src/LICENSE
Symbolic link
1
src/LICENSE
Symbolic link
@@ -0,0 +1 @@
|
||||
../LICENSE
|
||||
7784
src/admin-directory_v1.1beta1.json
Normal file
7784
src/admin-directory_v1.1beta1.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -28,8 +28,7 @@ upgrade_only=false
|
||||
gamversion="latest"
|
||||
adminuser=""
|
||||
regularuser=""
|
||||
gam_glibc_vers="2.31 2.27"
|
||||
#gam_macos_vers="10.15.6 10.14.6 10.13.6"
|
||||
gam_glibc_vers="2.31"
|
||||
|
||||
while getopts "hd:a:o:b:lp:u:r:v:" OPTION
|
||||
do
|
||||
@@ -224,7 +223,7 @@ trap "rm -rf $temp_archive_dir" EXIT
|
||||
|
||||
echo_yellow "Downloading file $name from $browser_download_url to $temp_archive_dir ($check_type)..."
|
||||
# Save archive to temp w/o losing our path
|
||||
(cd "$temp_archive_dir" && curl -O -L $GHCLIENT $browser_download_url)
|
||||
(cd "$temp_archive_dir" && curl -# -O -L $GHCLIENT $browser_download_url)
|
||||
|
||||
mkdir -p "$target_dir"
|
||||
|
||||
|
||||
17
src/gam.spec
17
src/gam.spec
@@ -12,6 +12,7 @@ extra_files = [(os.path.join(proot, 'cacerts.txt'), 'httplib2')]
|
||||
extra_files += copy_metadata('google-api-python-client')
|
||||
extra_files += [('cbcm-v1.1beta1.json', '.')]
|
||||
extra_files += [('contactdelegation-v1.json', '.')]
|
||||
extra_files += [('admin-directory_v1.1beta1.json', '.')]
|
||||
|
||||
hidden_imports = [
|
||||
'gam.auth.yubikey',
|
||||
@@ -32,12 +33,14 @@ for d in a.datas:
|
||||
|
||||
pyz = PYZ(a.pure)
|
||||
|
||||
# TODO: fix universal2
|
||||
target_arch = None
|
||||
#if sys.platform == "darwin":
|
||||
# target_arch="universal2"
|
||||
#else:
|
||||
# target_arch=None
|
||||
|
||||
if sys.platform == "darwin":
|
||||
target_arch="universal2"
|
||||
else:
|
||||
target_arch=None
|
||||
|
||||
# use strip on all non-Windows platforms
|
||||
strip = not sys.platform == 'win32'
|
||||
|
||||
exe = EXE(pyz,
|
||||
a.scripts,
|
||||
@@ -46,7 +49,7 @@ exe = EXE(pyz,
|
||||
a.datas,
|
||||
name='gam',
|
||||
debug=False,
|
||||
strip=None,
|
||||
strip=strip,
|
||||
upx=False,
|
||||
target_arch=target_arch,
|
||||
console=True)
|
||||
|
||||
@@ -55,6 +55,7 @@ from gam import auth
|
||||
from gam import controlflow
|
||||
from gam import display
|
||||
from gam import fileutils
|
||||
from gam.gapi import caa as gapi_caa
|
||||
from gam.gapi import calendar as gapi_calendar
|
||||
from gam.gapi import cloudidentity as gapi_cloudidentity
|
||||
from gam.gapi import cbcm as gapi_cbcm
|
||||
@@ -64,6 +65,7 @@ from gam.gapi import chromemanagement as gapi_chromemanagement
|
||||
from gam.gapi import chromepolicy as gapi_chromepolicy
|
||||
from gam.gapi.cloudidentity import devices as gapi_cloudidentity_devices
|
||||
from gam.gapi.cloudidentity import groups as gapi_cloudidentity_groups
|
||||
from gam.gapi.cloudidentity import orgunits as gapi_cloudidentity_orgunits
|
||||
from gam.gapi.cloudidentity import userinvitations as gapi_cloudidentity_userinvitations
|
||||
from gam.gapi import contactdelegation as gapi_contactdelegation
|
||||
from gam.gapi.directory import asps as gapi_directory_asps
|
||||
@@ -78,7 +80,9 @@ from gam.gapi.directory import printers as gapi_directory_printers
|
||||
from gam.gapi.directory import privileges as gapi_directory_privileges
|
||||
from gam.gapi.directory import resource as gapi_directory_resource
|
||||
from gam.gapi.directory import roles as gapi_directory_roles
|
||||
from gam.gapi.directory import roleassignments as gapi_directory_roleassignments
|
||||
from gam.gapi.directory import users as gapi_directory_users
|
||||
from gam.gapi.drive import drives as gapi_drive_drives
|
||||
from gam.gapi import licensing as gapi_licensing
|
||||
from gam.gapi import siteverification as gapi_siteverification
|
||||
from gam.gapi import errors as gapi_errors
|
||||
@@ -847,7 +851,9 @@ def _getSvcAcctData():
|
||||
controlflow.system_error_exit(6, None)
|
||||
GM_Globals[GM_OAUTH2SERVICE_JSON_DATA] = json.loads(json_string)
|
||||
|
||||
jwt_apis = ['chat'] # APIs which can handle OAuthless JWT tokens
|
||||
jwt_apis = ['chat',
|
||||
'cloudresourcemanager',
|
||||
'accesscontextmanager'] # APIs which can handle OAuthless JWT tokens
|
||||
def getSvcAcctCredentials(scopes, act_as, api=None):
|
||||
try:
|
||||
_getSvcAcctData()
|
||||
@@ -1724,134 +1730,6 @@ def doUpdateCourse():
|
||||
print(f'Updated Course {result["id"]}')
|
||||
|
||||
|
||||
def doDelAdmin():
|
||||
cd = buildGAPIObject('directory')
|
||||
roleAssignmentId = sys.argv[3]
|
||||
print(f'Deleting Admin Role Assignment {roleAssignmentId}')
|
||||
gapi.call(cd.roleAssignments(),
|
||||
'delete',
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
roleAssignmentId=roleAssignmentId)
|
||||
|
||||
|
||||
def doCreateAdmin():
|
||||
cd = buildGAPIObject('directory')
|
||||
user = normalizeEmailAddressOrUID(sys.argv[3])
|
||||
body = {'assignedTo': convertEmailAddressToUID(user, cd)}
|
||||
role = sys.argv[4]
|
||||
body['roleId'] = getRoleId(role)
|
||||
body['scopeType'] = sys.argv[5].upper()
|
||||
if body['scopeType'] not in ['CUSTOMER', 'ORG_UNIT']:
|
||||
controlflow.expected_argument_exit('scope type',
|
||||
', '.join(['customer', 'org_unit']),
|
||||
body['scopeType'])
|
||||
if body['scopeType'] == 'ORG_UNIT':
|
||||
orgUnit, orgUnitId = gapi_directory_orgunits.getOrgUnitId(
|
||||
sys.argv[6], cd)
|
||||
body['orgUnitId'] = orgUnitId[3:]
|
||||
scope = f'ORG_UNIT {orgUnit}'
|
||||
else:
|
||||
scope = 'CUSTOMER'
|
||||
print(f'Giving {user} admin role {role} for {scope}')
|
||||
gapi.call(cd.roleAssignments(),
|
||||
'insert',
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
body=body)
|
||||
|
||||
|
||||
def doPrintAdmins():
|
||||
cd = buildGAPIObject('directory')
|
||||
roleId = None
|
||||
todrive = False
|
||||
kwargs = {}
|
||||
fields = 'nextPageToken,items(roleAssignmentId,roleId,assignedTo,scopeType,orgUnitId)'
|
||||
titles = [
|
||||
'roleAssignmentId', 'roleId', 'role', 'assignedTo', 'assignedToUser',
|
||||
'scopeType', 'orgUnitId', 'orgUnit'
|
||||
]
|
||||
csvRows = []
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower()
|
||||
if myarg == 'user':
|
||||
kwargs['userKey'] = normalizeEmailAddressOrUID(sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg == 'role':
|
||||
roleId = getRoleId(sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg == 'todrive':
|
||||
todrive = True
|
||||
i += 1
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam print admins')
|
||||
if roleId and not kwargs:
|
||||
kwargs['roleId'] = roleId
|
||||
roleId = None
|
||||
admins = gapi.get_all_pages(cd.roleAssignments(),
|
||||
'list',
|
||||
'items',
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
fields=fields,
|
||||
**kwargs)
|
||||
for admin in admins:
|
||||
if roleId and roleId != admin['roleId']:
|
||||
continue
|
||||
admin_attrib = {}
|
||||
for key, value in list(admin.items()):
|
||||
if key == 'assignedTo':
|
||||
admin_attrib['assignedToUser'] = user_from_userid(value)
|
||||
elif key == 'roleId':
|
||||
admin_attrib['role'] = role_from_roleid(value)
|
||||
elif key == 'orgUnitId':
|
||||
value = f'id:{value}'
|
||||
admin_attrib[
|
||||
'orgUnit'] = gapi_directory_orgunits.orgunit_from_orgunitid(
|
||||
value, cd)
|
||||
admin_attrib[key] = value
|
||||
csvRows.append(admin_attrib)
|
||||
display.write_csv_file(csvRows, titles, 'Admins', todrive)
|
||||
|
||||
|
||||
def buildRoleIdToNameToIdMap():
|
||||
cd = buildGAPIObject('directory')
|
||||
result = gapi.get_all_pages(cd.roles(),
|
||||
'list',
|
||||
'items',
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
fields='nextPageToken,items(roleId,roleName)')
|
||||
GM_Globals[GM_MAP_ROLE_ID_TO_NAME] = {}
|
||||
GM_Globals[GM_MAP_ROLE_NAME_TO_ID] = {}
|
||||
for role in result:
|
||||
GM_Globals[GM_MAP_ROLE_ID_TO_NAME][role['roleId']] = role['roleName']
|
||||
GM_Globals[GM_MAP_ROLE_NAME_TO_ID][role['roleName']] = role['roleId']
|
||||
|
||||
|
||||
def role_from_roleid(roleid):
|
||||
if not GM_Globals[GM_MAP_ROLE_ID_TO_NAME]:
|
||||
buildRoleIdToNameToIdMap()
|
||||
return GM_Globals[GM_MAP_ROLE_ID_TO_NAME].get(roleid, roleid)
|
||||
|
||||
|
||||
def roleid_from_role(role):
|
||||
if not GM_Globals[GM_MAP_ROLE_NAME_TO_ID]:
|
||||
buildRoleIdToNameToIdMap()
|
||||
return GM_Globals[GM_MAP_ROLE_NAME_TO_ID].get(role, None)
|
||||
|
||||
|
||||
def getRoleId(role):
|
||||
cg = UID_PATTERN.match(role)
|
||||
if cg:
|
||||
roleId = cg.group(1)
|
||||
else:
|
||||
roleId = roleid_from_role(role)
|
||||
if not roleId:
|
||||
controlflow.system_error_exit(
|
||||
4,
|
||||
f'{role} is not a valid role. Please ensure role name is exactly as shown in admin console.'
|
||||
)
|
||||
return roleId
|
||||
|
||||
|
||||
def buildUserIdToNameMap():
|
||||
cd = buildGAPIObject('directory')
|
||||
result = gapi.get_all_pages(cd.users(),
|
||||
@@ -2857,7 +2735,7 @@ def printDriveSettings(users):
|
||||
display.write_csv_file(csvRows, titles, 'User Drive Settings', todrive)
|
||||
|
||||
|
||||
def getTeamDriveThemes(users):
|
||||
def getSharedDriveThemes(users):
|
||||
for user in users:
|
||||
user, drive = buildDrive3GAPIObject(user)
|
||||
if not drive:
|
||||
@@ -7245,6 +7123,7 @@ def enableGAMProjectAPIs(GAMProjectAPIs,
|
||||
f' Project: {projectId}, Enable {jcount} APIs{currentCount(i, count)}'
|
||||
)
|
||||
j = 0
|
||||
tried_universal_tos = False
|
||||
for api in apis:
|
||||
service_name = f'projects/{projectId}/services/{api}'
|
||||
j += 1
|
||||
@@ -7255,19 +7134,26 @@ def enableGAMProjectAPIs(GAMProjectAPIs,
|
||||
throw_reasons=[
|
||||
gapi_errors.ErrorReason.FAILED_PRECONDITION,
|
||||
gapi_errors.ErrorReason.FORBIDDEN,
|
||||
gapi_errors.ErrorReason.PERMISSION_DENIED
|
||||
gapi_errors.ErrorReason.PERMISSION_DENIED,
|
||||
gapi_errors.ErrorReason.FOUR_O_O,
|
||||
],
|
||||
retry_reasons=[gapi_errors.ErrorReason.INTERNAL_SERVER_ERROR],
|
||||
name=service_name)
|
||||
print(f' API: {api}, Enabled{currentCount(j, jcount)}')
|
||||
break
|
||||
except gapi_errors.GapiFailedPreconditionError as e:
|
||||
except (gapi_errors.GapiFailedPreconditionError,
|
||||
googleapiclient.errors.HttpError) as e:
|
||||
if hasattr(e, 'reason'):
|
||||
msg = e.reason
|
||||
else:
|
||||
msg = str(e)
|
||||
if 'terms of service' in msg.lower() and not tried_universal_tos:
|
||||
msg = '''You need to agree to the Google Cloud Terms of Service at:\n\n https://console.developers.google.com'''
|
||||
tried_universal_tos = True
|
||||
print(
|
||||
f'\nThere was an error enabling {api}. Please resolve error as described below:'
|
||||
)
|
||||
print()
|
||||
print(f'\n{str(e)}\n')
|
||||
print()
|
||||
print(f'\n\n{msg}\n\n')
|
||||
input(
|
||||
'Press enter once resolved and we will try enabling the API again.'
|
||||
)
|
||||
@@ -8133,10 +8019,17 @@ def doPrintShowProjects(csvFormat):
|
||||
display.write_csv_file(csvRows, titles, 'Projects', todrive)
|
||||
|
||||
|
||||
def doGetTeamDriveInfo(users):
|
||||
teamDriveId = sys.argv[5]
|
||||
def getSharedDriveId(i):
|
||||
driveId = sys.argv[i]
|
||||
if driveId.lower() == 'name':
|
||||
i += 1
|
||||
driveId = gapi_drive_drives.drive_name_to_id(sys.argv[i])
|
||||
return (i+1, driveId)
|
||||
|
||||
|
||||
def doGetSharedDriveInfo(users):
|
||||
i, driveId = getSharedDriveId(5)
|
||||
useDomainAdminAccess = False
|
||||
i = 6
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg == 'asadmin':
|
||||
@@ -8144,7 +8037,7 @@ def doGetTeamDriveInfo(users):
|
||||
i += 1
|
||||
else:
|
||||
controlflow.invalid_argument_exit(myarg,
|
||||
'gam <users> show teamdrive')
|
||||
'gam <users> show shareddrive')
|
||||
for user in users:
|
||||
drive = buildGAPIServiceObject('drive3', user)
|
||||
if not drive:
|
||||
@@ -8152,13 +8045,15 @@ def doGetTeamDriveInfo(users):
|
||||
continue
|
||||
result = gapi.call(drive.drives(),
|
||||
'get',
|
||||
driveId=teamDriveId,
|
||||
driveId=driveId,
|
||||
useDomainAdminAccess=useDomainAdminAccess,
|
||||
fields='*')
|
||||
if useDomainAdminAccess and 'orgUnitId' in result:
|
||||
result['orgUnit'] = gapi_directory_orgunits.orgunit_from_orgunitid(f'id:{result["orgUnitId"]}')
|
||||
display.print_json(result)
|
||||
|
||||
|
||||
def doCreateTeamDrive(users):
|
||||
def doCreateSharedDrive(users):
|
||||
body = {'name': sys.argv[5]}
|
||||
i = 6
|
||||
while i < len(sys.argv):
|
||||
@@ -8168,7 +8063,7 @@ def doCreateTeamDrive(users):
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i],
|
||||
'gam <users> create teamdrive')
|
||||
'gam <users> create shareddrive')
|
||||
for user in users:
|
||||
drive = buildGAPIServiceObject('drive3', user)
|
||||
if not drive:
|
||||
@@ -8180,7 +8075,7 @@ def doCreateTeamDrive(users):
|
||||
requestId=requestId,
|
||||
body=body,
|
||||
fields='id')
|
||||
print(f'Created Team Drive {body["name"]} with id {result["id"]}')
|
||||
print(f'Created Shared Drive {body["name"]} with id {result["id"]}')
|
||||
|
||||
|
||||
TEAMDRIVE_RESTRICTIONS_MAP = {
|
||||
@@ -8191,17 +8086,20 @@ TEAMDRIVE_RESTRICTIONS_MAP = {
|
||||
}
|
||||
|
||||
|
||||
def doUpdateTeamDrive(users):
|
||||
teamDriveId = sys.argv[5]
|
||||
def doUpdateSharedDrive(users):
|
||||
i, driveId = getSharedDriveId(5)
|
||||
body = {}
|
||||
useDomainAdminAccess = False
|
||||
change_hide = None
|
||||
i = 6
|
||||
orgUnit = None
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg == 'name':
|
||||
body['name'] = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg in ['ou', 'org', 'orgunit']:
|
||||
orgUnit = sys.argv[i+1]
|
||||
i += 2
|
||||
elif myarg == 'theme':
|
||||
body['themeId'] = sys.argv[i + 1]
|
||||
i += 2
|
||||
@@ -8219,9 +8117,9 @@ def doUpdateTeamDrive(users):
|
||||
elif myarg == 'asadmin':
|
||||
useDomainAdminAccess = True
|
||||
i += 1
|
||||
elif myarg in ['ou', 'org', 'orgunit']:
|
||||
body['orgUnitId'] = gapi_directory_orgunits.getOrgUnitId(sys.argv[i + 1])
|
||||
i += 2
|
||||
# elif myarg in ['ou', 'org', 'orgunit']:
|
||||
# body['orgUnitId'] = gapi_directory_orgunits.getOrgUnitId(sys.argv[i + 1])
|
||||
# i += 2
|
||||
elif myarg in ['hidden']:
|
||||
if getBoolean(sys.argv[i+1], myarg):
|
||||
change_hide = 'hide'
|
||||
@@ -8236,8 +8134,8 @@ def doUpdateTeamDrive(users):
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i],
|
||||
'gam <users> update teamdrive')
|
||||
if not body and not change_hide:
|
||||
'gam <users> update shareddrive')
|
||||
if not body and not change_hide and not orgUnit:
|
||||
controlflow.system_error_exit(
|
||||
4, 'nothing to update. Need at least a name argument.')
|
||||
for user in users:
|
||||
@@ -8249,7 +8147,7 @@ def doUpdateTeamDrive(users):
|
||||
'update',
|
||||
useDomainAdminAccess=useDomainAdminAccess,
|
||||
body=body,
|
||||
driveId=teamDriveId,
|
||||
driveId=driveId,
|
||||
fields='id',
|
||||
soft_errors=True)
|
||||
if not result:
|
||||
@@ -8257,15 +8155,19 @@ def doUpdateTeamDrive(users):
|
||||
if change_hide:
|
||||
ch_result = gapi.call(drive.drives(),
|
||||
change_hide,
|
||||
driveId=teamDriveId,
|
||||
driveId=driveId,
|
||||
fields='id',
|
||||
soft_errors=True)
|
||||
print(f'Updated Team Drive {teamDriveId}')
|
||||
if orgUnit:
|
||||
gapi_cloudidentity_orgunits.move_shared_drive(driveId,
|
||||
orgUnit)
|
||||
print(f'Updated Shared Drive {driveId}')
|
||||
|
||||
def printShowTeamDrives(users, csvFormat):
|
||||
def printShowSharedDrives(users, csvFormat):
|
||||
todrive = False
|
||||
useDomainAdminAccess = False
|
||||
q = None
|
||||
get_orgunits = True
|
||||
i = 5
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
@@ -8278,13 +8180,18 @@ def printShowTeamDrives(users, csvFormat):
|
||||
elif myarg == 'query':
|
||||
q = sys.argv[i + 1]
|
||||
i += 2
|
||||
elif myarg == 'noorgunits':
|
||||
get_orgunits = False
|
||||
i += 1
|
||||
else:
|
||||
controlflow.invalid_argument_exit(
|
||||
myarg, f"gam {['show', 'print'][csvFormat]} teamdrives")
|
||||
myarg, f"gam {['show', 'print'][csvFormat]} shareddrives")
|
||||
tds = []
|
||||
titles = []
|
||||
if get_orgunits and useDomainAdminAccess:
|
||||
ou_map = gapi_directory_orgunits.orgid_to_org_map()
|
||||
for user in users:
|
||||
sys.stderr.write(f'Getting Team Drives for {user}\n')
|
||||
sys.stderr.write(f'Getting Shared Drives for {user}\n')
|
||||
user, drive = buildDrive3GAPIObject(user)
|
||||
if not drive:
|
||||
continue
|
||||
@@ -8299,12 +8206,16 @@ def printShowTeamDrives(users, csvFormat):
|
||||
continue
|
||||
for td in results:
|
||||
td = utils.flatten_json(td)
|
||||
if get_orgunits and useDomainAdminAccess:
|
||||
td_ouid = td.get('orgUnitId')
|
||||
if td_ouid:
|
||||
td['orgUnit'] = ou_map.get(f'id:{td_ouid}', 'Unknown')
|
||||
for key in td:
|
||||
if key not in titles:
|
||||
titles.append(key)
|
||||
tds.append(td)
|
||||
if csvFormat:
|
||||
display.write_csv_file(tds, titles, 'Team Drives', todrive)
|
||||
display.write_csv_file(tds, titles, 'Shared Drives', todrive)
|
||||
else:
|
||||
for td in tds:
|
||||
name = td.pop('name')
|
||||
@@ -8314,17 +8225,16 @@ def printShowTeamDrives(users, csvFormat):
|
||||
print()
|
||||
|
||||
|
||||
|
||||
def doDeleteTeamDrive(users):
|
||||
teamDriveId = sys.argv[5]
|
||||
def doDeleteSharedDrive(users):
|
||||
_, driveId = getSharedDriveId(5)
|
||||
for user in users:
|
||||
user, drive = buildDrive3GAPIObject(user)
|
||||
if not drive:
|
||||
continue
|
||||
print(f'Deleting Team Drive {teamDriveId}')
|
||||
print(f'Deleting Shared Drive {driveId}')
|
||||
gapi.call(drive.drives(),
|
||||
'delete',
|
||||
driveId=teamDriveId,
|
||||
driveId=driveId,
|
||||
soft_errors=True)
|
||||
|
||||
|
||||
@@ -8454,6 +8364,24 @@ def doRemoveUsersAliases(users):
|
||||
|
||||
|
||||
def doUpdateAlias():
|
||||
def verify_alias_target_exists():
|
||||
if target_type != 'group':
|
||||
try:
|
||||
gapi.call(cd.users(), 'get',
|
||||
throw_reasons=[gapi_errors.ErrorReason.USER_NOT_FOUND],
|
||||
userKey=target_email)
|
||||
return 'user'
|
||||
except gapi_errors.GapiUserNotFoundError:
|
||||
if target_type == 'user':
|
||||
return None
|
||||
try:
|
||||
gapi.call(cd.groups(), 'get',
|
||||
throw_reasons=[gapi_errors.ErrorReason.GROUP_NOT_FOUND],
|
||||
groupKey=target_email)
|
||||
return 'group'
|
||||
except gapi_errors.GapiGroupNotFoundError:
|
||||
return None
|
||||
|
||||
cd = buildGAPIObject('directory')
|
||||
alias = normalizeEmailAddressOrUID(sys.argv[3], noUid=True, noLower=True)
|
||||
target_type = sys.argv[4].lower()
|
||||
@@ -8461,15 +8389,19 @@ def doUpdateAlias():
|
||||
controlflow.expected_argument_exit(
|
||||
'target type', ', '.join(['user', 'group', 'target']), target_type)
|
||||
target_email = normalizeEmailAddressOrUID(sys.argv[5])
|
||||
if len(sys.argv) > 6:
|
||||
myarg = sys.argv[6].lower().replace('_', '')
|
||||
if myarg != 'verifynotinvitable':
|
||||
controlflow.system_error_exit(
|
||||
3,
|
||||
f'{myarg} is not a valid argument for "gam update alias"'
|
||||
)
|
||||
if gapi_cloudidentity_userinvitations.is_invitable_user(alias):
|
||||
controlflow.system_error_exit(51, f'Alias not updated, {alias} is an unmanaged account')
|
||||
verifyTarget = True
|
||||
i = 6
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg == 'noverifytarget':
|
||||
verifyTarget = False
|
||||
i += 1
|
||||
else:
|
||||
controlflow.system_error_exit(3, f'{myarg} is not a valid argument for "gam update alias"')
|
||||
if verifyTarget:
|
||||
target_type = verify_alias_target_exists()
|
||||
if target_type is None:
|
||||
controlflow.system_error_exit(51, f'Alias not updated, {target_email} does not exist')
|
||||
try:
|
||||
gapi.call(cd.users().aliases(),
|
||||
'delete',
|
||||
@@ -10550,6 +10482,11 @@ OAUTH2_SCOPES = [
|
||||
'subscopes': ['readonly'],
|
||||
'scopes': 'https://www.googleapis.com/auth/cloud-identity.groups'
|
||||
},
|
||||
{
|
||||
'name': 'Cloud Identity - OrgUnits',
|
||||
'subscopes': ['readonly'],
|
||||
'scopes': 'https://www.googleapis.com/auth/cloud-identity.orgunits',
|
||||
},
|
||||
{
|
||||
'name': 'Cloud Identity - User Invitations',
|
||||
'subscopes': ['readonly'],
|
||||
@@ -11440,7 +11377,7 @@ def ProcessGAMCommand(args):
|
||||
elif argument in ['domainalias', 'aliasdomain']:
|
||||
gapi_directory_domainaliases.create()
|
||||
elif argument == 'admin':
|
||||
doCreateAdmin()
|
||||
gapi_directory_roleassignments.create()
|
||||
elif argument in ['guardianinvite', 'inviteguardian', 'guardian']:
|
||||
doInviteGuardian()
|
||||
elif argument in ['project', 'apiproject']:
|
||||
@@ -11471,6 +11408,8 @@ def ProcessGAMCommand(args):
|
||||
gapi_directory_printers.create()
|
||||
elif argument in ['chatmessage']:
|
||||
gapi_chat.create_message()
|
||||
elif argument in ['caalevel']:
|
||||
gapi_caa.create_access_level()
|
||||
else:
|
||||
controlflow.invalid_argument_exit(argument, 'gam create')
|
||||
sys.exit(0)
|
||||
@@ -11535,6 +11474,8 @@ def ProcessGAMCommand(args):
|
||||
gapi_directory_printers.update()
|
||||
elif argument in ['chatmessage']:
|
||||
gapi_chat.update_message()
|
||||
elif argument in ['caalevel']:
|
||||
gapi_caa.update_access_level()
|
||||
else:
|
||||
controlflow.invalid_argument_exit(argument, 'gam update')
|
||||
sys.exit(0)
|
||||
@@ -11644,7 +11585,7 @@ def ProcessGAMCommand(args):
|
||||
elif argument in ['domainalias', 'aliasdomain']:
|
||||
gapi_directory_domainaliases.delete()
|
||||
elif argument == 'admin':
|
||||
doDelAdmin()
|
||||
gapi_directory_roleassignments.delete()
|
||||
elif argument in ['guardian', 'guardians']:
|
||||
doDeleteGuardian()
|
||||
elif argument in ['project', 'projects']:
|
||||
@@ -11675,6 +11616,8 @@ def ProcessGAMCommand(args):
|
||||
gapi_chromepolicy.delete_policy()
|
||||
elif argument == 'chatmessage':
|
||||
gapi_chat.delete_message()
|
||||
elif argument == 'caalevel':
|
||||
gapi_caa.delete_access_level()
|
||||
else:
|
||||
controlflow.invalid_argument_exit(argument, 'gam delete')
|
||||
sys.exit(0)
|
||||
@@ -11751,7 +11694,7 @@ def ProcessGAMCommand(args):
|
||||
elif argument in ['domainaliases', 'aliasdomains']:
|
||||
gapi_directory_domainaliases.print_()
|
||||
elif argument == 'admins':
|
||||
doPrintAdmins()
|
||||
gapi_directory_roleassignments.print_()
|
||||
elif argument in ['roles', 'adminroles']:
|
||||
gapi_directory_roles.print_()
|
||||
elif argument in ['guardian', 'guardians']:
|
||||
@@ -11796,6 +11739,10 @@ def ProcessGAMCommand(args):
|
||||
gapi_chat.print_spaces()
|
||||
elif argument in ['chatmembers']:
|
||||
gapi_chat.print_members()
|
||||
elif argument in ['caalevels']:
|
||||
gapi_caa.printshow_access_levels(True)
|
||||
elif argument in ['oushareddrives', 'orgunitshareddrives']:
|
||||
gapi_cloudidentity_orgunits.printshow_orgunit_shared_drives(True)
|
||||
else:
|
||||
controlflow.invalid_argument_exit(argument, 'gam print')
|
||||
sys.exit(0)
|
||||
@@ -11826,6 +11773,10 @@ def ProcessGAMCommand(args):
|
||||
gapi_chromepolicy.printshow_policies()
|
||||
elif argument == 'crostelemetry':
|
||||
gapi_chromemanagement.printShowCrosTelemetry('show')
|
||||
elif argument in ['caalevels']:
|
||||
gapi_caa.printshow_access_levels(False)
|
||||
elif argument in ['oushareddrives', 'orgunitshareddrives']:
|
||||
gapi_cloudidentity_orgunits.printshow_orgunit_shared_drives(False)
|
||||
else:
|
||||
controlflow.invalid_argument_exit(argument, 'gam show')
|
||||
sys.exit(0)
|
||||
@@ -11984,8 +11935,8 @@ def ProcessGAMCommand(args):
|
||||
gapi_calendar.showCalSettings(users)
|
||||
elif showWhat == 'drivesettings':
|
||||
printDriveSettings(users)
|
||||
elif showWhat == 'teamdrivethemes':
|
||||
getTeamDriveThemes(users)
|
||||
elif showWhat in ['teamdrivethemes', 'shareddrivethemes']:
|
||||
getSharedDriveThemes(users)
|
||||
elif showWhat == 'drivefileacl':
|
||||
showDriveFileACL(users)
|
||||
elif showWhat == 'filelist':
|
||||
@@ -12028,10 +11979,10 @@ def ProcessGAMCommand(args):
|
||||
printShowFilters(users, False)
|
||||
elif showWhat in ['forwardingaddress', 'forwardingaddresses']:
|
||||
printShowForwardingAddresses(users, False)
|
||||
elif showWhat in ['teamdrive', 'teamdrives']:
|
||||
printShowTeamDrives(users, False)
|
||||
elif showWhat in ['teamdriveinfo']:
|
||||
doGetTeamDriveInfo(users)
|
||||
elif showWhat in shared_drive_values:
|
||||
printShowSharedDrives(users, False)
|
||||
elif showWhat in ['shareddriveinfo', 'teamdriveinfo']:
|
||||
doGetSharedDriveInfo(users)
|
||||
elif showWhat in ['contactdelegate', 'contactdelegates']:
|
||||
gapi_contactdelegation.print_(users, False)
|
||||
elif showWhat in ['holds', 'vaultholds']:
|
||||
@@ -12062,8 +12013,8 @@ def ProcessGAMCommand(args):
|
||||
printShowSmime(users, True)
|
||||
elif printWhat in ['token', 'tokens', 'oauth', '3lo']:
|
||||
printShowTokens(5, 'users', users, True)
|
||||
elif printWhat in ['teamdrive', 'teamdrives']:
|
||||
printShowTeamDrives(users, True)
|
||||
elif printWhat in shared_drive_values:
|
||||
printShowSharedDrives(users, True)
|
||||
elif printWhat in ['contactdelegate', 'contactdelegates']:
|
||||
gapi_contactdelegation.print_(users, True)
|
||||
elif printWhat in ['labels']:
|
||||
@@ -12146,8 +12097,8 @@ def ProcessGAMCommand(args):
|
||||
deleteSendAs(users)
|
||||
elif delWhat == 'smime':
|
||||
deleteSmime(users)
|
||||
elif delWhat == 'teamdrive':
|
||||
doDeleteTeamDrive(users)
|
||||
elif delWhat in shared_drive_values:
|
||||
doDeleteSharedDrive(users)
|
||||
elif delWhat == 'contactdelegate':
|
||||
gapi_contactdelegation.delete(users)
|
||||
else:
|
||||
@@ -12180,8 +12131,8 @@ def ProcessGAMCommand(args):
|
||||
addUpdateSendAs(users, 5, True)
|
||||
elif addWhat == 'smime':
|
||||
addSmime(users)
|
||||
elif addWhat == 'teamdrive':
|
||||
doCreateTeamDrive(users)
|
||||
elif addWhat in shared_drive_values:
|
||||
doCreateSharedDrive(users)
|
||||
elif addWhat == 'contactdelegate':
|
||||
gapi_contactdelegation.create(users)
|
||||
else:
|
||||
@@ -12222,8 +12173,8 @@ def ProcessGAMCommand(args):
|
||||
addUpdateSendAs(users, 5, False)
|
||||
elif updateWhat == 'smime':
|
||||
updateSmime(users)
|
||||
elif updateWhat == 'teamdrive':
|
||||
doUpdateTeamDrive(users)
|
||||
elif updateWhat in shared_drive_values:
|
||||
doUpdateSharedDrive(users)
|
||||
else:
|
||||
controlflow.invalid_argument_exit(updateWhat,
|
||||
'gam <users> update')
|
||||
|
||||
@@ -1,26 +1,42 @@
|
||||
"""OAuth2.0 user credentials."""
|
||||
|
||||
import datetime
|
||||
import ipaddress
|
||||
import json
|
||||
import multiprocessing
|
||||
import os
|
||||
import re
|
||||
from socket import gethostbyname
|
||||
import sys
|
||||
from time import sleep
|
||||
import threading
|
||||
from urllib.parse import urlencode
|
||||
from urllib.parse import urlencode, urlparse, parse_qs
|
||||
import wsgiref.simple_server
|
||||
import wsgiref.util
|
||||
import webbrowser
|
||||
|
||||
from filelock import FileLock
|
||||
import google_auth_oauthlib.flow
|
||||
import google.oauth2.credentials
|
||||
import google.oauth2.id_token
|
||||
|
||||
from gam import controlflow
|
||||
from gam import display
|
||||
from gam import fileutils
|
||||
from gam import transport
|
||||
from gam.var import GM_Globals
|
||||
from gam.var import GM_WINDOWS
|
||||
from gam.var import GM_Globals, GM_WINDOWS
|
||||
from gam import utils
|
||||
|
||||
MESSAGE_CONSOLE_AUTHORIZATION_PROMPT = ('\nGo to the following link in your '
|
||||
'browser:\n\n\t{url}\n')
|
||||
MESSAGE_CONSOLE_AUTHORIZATION_CODE = 'Enter verification code: '
|
||||
|
||||
MESSAGE_CONSOLE_AUTHORIZATION_PROMPT = '''\nGo to the following link in your browser:
|
||||
|
||||
\t{url}
|
||||
|
||||
IMPORTANT: If you get a browser error that the site can't be reached AFTER you
|
||||
click the Allow button, copy the URL from the browser where the error occurred
|
||||
and paste that here instead.
|
||||
'''
|
||||
MESSAGE_CONSOLE_AUTHORIZATION_CODE = 'Enter verification code or browser URL: '
|
||||
MESSAGE_LOCAL_SERVER_AUTHORIZATION_PROMPT = ('\nYour browser has been opened to'
|
||||
' visit:\n\n\t{url}\n\nIf your '
|
||||
'browser is on a different machine'
|
||||
@@ -30,6 +46,8 @@ MESSAGE_LOCAL_SERVER_AUTHORIZATION_PROMPT = ('\nYour browser has been opened to'
|
||||
MESSAGE_LOCAL_SERVER_SUCCESS = ('The authentication flow has completed. You may'
|
||||
' close this browser window and return to GAM.')
|
||||
|
||||
MESSAGE_AUTHENTICATION_COMPLETE = ('\nThe authentication flow has completed.\n')
|
||||
|
||||
|
||||
class CredentialsError(Exception):
|
||||
"""Base error class."""
|
||||
@@ -295,19 +313,8 @@ class Credentials(google.oauth2.credentials.Credentials):
|
||||
if login_hint:
|
||||
flow_kwargs['login_hint'] = login_hint
|
||||
|
||||
# TODO: Move code for browser detection somewhere in this file so that the
|
||||
# messaging about `nobrowser.txt` is co-located with the logic that uses it.
|
||||
if use_console_flow:
|
||||
flow.run_console(
|
||||
authorization_prompt_message=
|
||||
MESSAGE_CONSOLE_AUTHORIZATION_PROMPT,
|
||||
authorization_code_message=MESSAGE_CONSOLE_AUTHORIZATION_CODE,
|
||||
flow.run_dual(use_console_flow,
|
||||
**flow_kwargs)
|
||||
else:
|
||||
flow.run_local_server(authorization_prompt_message=
|
||||
MESSAGE_LOCAL_SERVER_AUTHORIZATION_PROMPT,
|
||||
success_message=MESSAGE_LOCAL_SERVER_SUCCESS,
|
||||
**flow_kwargs)
|
||||
return cls.from_google_oauth2_credentials(flow.credentials,
|
||||
filename=filename)
|
||||
|
||||
@@ -516,10 +523,66 @@ class Credentials(google.oauth2.credentials.Credentials):
|
||||
http.request(revoke_uri, 'GET')
|
||||
|
||||
|
||||
def _localhost_to_ip():
|
||||
'''returns IPv4 or IPv6 loopback address which localhost resolves to.
|
||||
If localhost does not resolve to valid loopback IP address then returns
|
||||
127.0.0.1'''
|
||||
# TODO gethostbyname() will only ever return ipv4
|
||||
# find a way to support IPv6 here and get preferred IP
|
||||
# note that IPv6 may be broken on some systems also :-(
|
||||
# for now IPv4 should do.
|
||||
local_ip = gethostbyname('localhost')
|
||||
local_ipaddress = ipaddress.ip_address(local_ip)
|
||||
ip4_local_range = ipaddress.ip_network('127.0.0.0/8')
|
||||
ip6_local_range = ipaddress.ip_network('::1/128')
|
||||
if local_ipaddress not in ip4_local_range and \
|
||||
local_ipaddress not in ip6_local_range:
|
||||
local_ip = '127.0.0.1'
|
||||
return local_ip
|
||||
|
||||
def _wait_for_http_client(d):
|
||||
wsgi_app = google_auth_oauthlib.flow._RedirectWSGIApp(MESSAGE_LOCAL_SERVER_SUCCESS)
|
||||
wsgiref.simple_server.WSGIServer.allow_reuse_address = False
|
||||
# Convert hostn to IP since apparently binding to the IP
|
||||
# reduces odds of firewall blocking us
|
||||
local_ip = _localhost_to_ip()
|
||||
for port in range(8080, 8099):
|
||||
try:
|
||||
local_server = wsgiref.simple_server.make_server(
|
||||
local_ip,
|
||||
port,
|
||||
wsgi_app,
|
||||
handler_class=wsgiref.simple_server.WSGIRequestHandler
|
||||
)
|
||||
break
|
||||
except OSError:
|
||||
pass
|
||||
redirect_uri_format = (
|
||||
"http://{}:{}/" if d['trailing_slash'] else "http://{}:{}"
|
||||
)
|
||||
# provide redirect_uri to main process so it can formulate auth_url
|
||||
d['redirect_uri'] = redirect_uri_format.format(*local_server.server_address)
|
||||
# wait until main process provides auth_url
|
||||
# so we can open it in web browser.
|
||||
while 'auth_url' not in d:
|
||||
sleep(0.1)
|
||||
if d['open_browser']:
|
||||
webbrowser.open(d['auth_url'], new=1, autoraise=True)
|
||||
local_server.handle_request()
|
||||
authorization_response = wsgi_app.last_request_uri.replace("http", "https")
|
||||
d['code'] = authorization_response
|
||||
local_server.server_close()
|
||||
|
||||
|
||||
def _wait_for_user_input(d):
|
||||
sys.stdin = open(0)
|
||||
code = input(MESSAGE_CONSOLE_AUTHORIZATION_CODE)
|
||||
d['code'] = code
|
||||
|
||||
|
||||
class _ShortURLFlow(google_auth_oauthlib.flow.InstalledAppFlow):
|
||||
"""InstalledAppFlow which utilizes a URL shortener for authorization URLs."""
|
||||
|
||||
URL_SHORTENER_ENDPOINT = 'https://gam-shortn.appspot.com/create'
|
||||
|
||||
def authorization_url(self, http=None, **kwargs):
|
||||
"""Gets a shortened authorization URL."""
|
||||
@@ -528,6 +591,58 @@ class _ShortURLFlow(google_auth_oauthlib.flow.InstalledAppFlow):
|
||||
return short_url, state
|
||||
|
||||
|
||||
def run_dual(self,
|
||||
use_console_flow,
|
||||
authorization_prompt_message='',
|
||||
console_prompt_message='',
|
||||
web_success_message='',
|
||||
open_browser=True,
|
||||
redirect_uri_trailing_slash=True,
|
||||
**kwargs):
|
||||
mgr = multiprocessing.Manager()
|
||||
d = mgr.dict()
|
||||
d['trailing_slash'] = redirect_uri_trailing_slash
|
||||
d['open_browser'] = use_console_flow
|
||||
http_client = multiprocessing.Process(target=_wait_for_http_client,
|
||||
args=(d,))
|
||||
user_input = multiprocessing.Process(target=_wait_for_user_input,
|
||||
args=(d,))
|
||||
http_client.start()
|
||||
# we need to wait until web server starts on avail port
|
||||
# so we know redirect_uri to use
|
||||
while 'redirect_uri' not in d:
|
||||
sleep(0.1)
|
||||
self.redirect_uri = d['redirect_uri']
|
||||
d['auth_url'], _ = self.authorization_url(**kwargs)
|
||||
print(MESSAGE_CONSOLE_AUTHORIZATION_PROMPT.format(url=d['auth_url']))
|
||||
user_input.start()
|
||||
userInput = False
|
||||
while True:
|
||||
sleep(0.1)
|
||||
if not http_client.is_alive():
|
||||
user_input.terminate()
|
||||
break
|
||||
elif not user_input.is_alive():
|
||||
userInput = True
|
||||
http_client.terminate()
|
||||
break
|
||||
while True:
|
||||
code = d['code']
|
||||
if code.startswith('http'):
|
||||
parsed_url = urlparse(code)
|
||||
parsed_params = parse_qs(parsed_url.query)
|
||||
code = parsed_params.get('code', [None])[0]
|
||||
try:
|
||||
self.fetch_token(code=code)
|
||||
break
|
||||
except Exception as e:
|
||||
if not userInput:
|
||||
controlflow.system_error_exit(8, str(e))
|
||||
display.print_error(str(e))
|
||||
_wait_for_user_input(d)
|
||||
sys.stdout.write(MESSAGE_AUTHENTICATION_COMPLETE)
|
||||
return self.credentials
|
||||
|
||||
class _FileLikeThreadLock:
|
||||
"""A threading.lock which has the same interface as filelock.Filelock."""
|
||||
|
||||
|
||||
@@ -189,6 +189,7 @@ class CredentialsTest(unittest.TestCase):
|
||||
with self.assertRaises(oauth.InvalidCredentialsFileError):
|
||||
oauth.Credentials.from_credentials_file(self.fake_filename)
|
||||
|
||||
@unittest.skip('disabled for oob fixes')
|
||||
@patch.object(oauth._ShortURLFlow, 'from_client_config')
|
||||
def test_from_client_secrets_console_flow(self, mock_flow):
|
||||
flow_creds = google.oauth2.credentials.Credentials(
|
||||
@@ -211,6 +212,7 @@ class CredentialsTest(unittest.TestCase):
|
||||
self.assertEqual(flow_creds.client_secret, creds.client_secret)
|
||||
self.assertEqual(flow_creds.id_token, creds.id_token)
|
||||
|
||||
@unittest.skip('disabled for oob fixes')
|
||||
@patch.object(oauth._ShortURLFlow, 'from_client_config')
|
||||
def test_from_client_secrets_local_server_flow(self, mock_flow):
|
||||
flow_creds = google.oauth2.credentials.Credentials(
|
||||
@@ -233,6 +235,7 @@ class CredentialsTest(unittest.TestCase):
|
||||
self.assertEqual(flow_creds.client_secret, creds.client_secret)
|
||||
self.assertEqual(flow_creds.id_token, creds.id_token)
|
||||
|
||||
@unittest.skip('disabled for oob fixes')
|
||||
@patch.object(oauth._ShortURLFlow, 'from_client_config')
|
||||
def test_from_client_secrets_uses_login_hint(self, mock_flow):
|
||||
flow_creds = google.oauth2.credentials.Credentials(
|
||||
|
||||
274
src/gam/gapi/caa.py
Normal file
274
src/gam/gapi/caa.py
Normal file
@@ -0,0 +1,274 @@
|
||||
import string
|
||||
import sys
|
||||
|
||||
import googleapiclient.errors
|
||||
|
||||
import gam
|
||||
from gam.var import *
|
||||
from gam import controlflow
|
||||
from gam import display
|
||||
from gam import gapi
|
||||
from gam import utils
|
||||
from gam.gapi import errors as gapi_errors
|
||||
from gam.gapi import cloudresourcemanager as gapi_crm
|
||||
|
||||
|
||||
THROW_REASONS = [gapi_errors.ErrorReason.FOUR_O_THREE]
|
||||
|
||||
def _gen_role_error(caa):
|
||||
sa_email = caa._http.credentials.signer_email
|
||||
role_error = f'Please grant service account {sa_email} the Access Context Manager Editor role to your GCP organization.'
|
||||
controlflow.system_error_exit(2, role_error)
|
||||
|
||||
|
||||
def build():
|
||||
return gam.buildGAPIServiceObject('accesscontextmanager',
|
||||
act_as=None)
|
||||
|
||||
|
||||
def get_access_policy(caa=None):
|
||||
if not caa:
|
||||
caa = build()
|
||||
parent = gapi_crm.get_org_id()
|
||||
if not parent:
|
||||
_gen_role_error(caa)
|
||||
try:
|
||||
aps = gapi.get_all_pages(caa.accessPolicies(),
|
||||
'list',
|
||||
'accessPolicies',
|
||||
throw_reasons=THROW_REASONS,
|
||||
parent=parent,
|
||||
fields='accessPolicies(name,title)')
|
||||
except googleapiclient.errors.HttpError:
|
||||
_gen_role_error(caa)
|
||||
if not aps:
|
||||
controlflow.system_error_exit(2, 'You don\'t seem to have any access policies. That is odd.')
|
||||
elif len(aps) == 1:
|
||||
return aps[0]['name']
|
||||
for ap in aps:
|
||||
if ap.get('title') == 'Access policy created in Cloud Identity Console':
|
||||
return ap['name']
|
||||
controlflow.system_error_exit(2, ' Could not find a org level access policy. That is odd.')
|
||||
|
||||
|
||||
def printshow_access_levels(csvFormat):
|
||||
caa = build()
|
||||
ap_name = get_access_policy(caa)
|
||||
if csvFormat:
|
||||
todrive = False
|
||||
csvRows = []
|
||||
titles = ['name', 'title']
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower()
|
||||
if csvFormat and myarg == 'todrive':
|
||||
todrive = True
|
||||
i += 1
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i],
|
||||
f"gam {['show', 'print'][csvFormat]} caalevels")
|
||||
try:
|
||||
levels = gapi.get_all_pages(caa.accessPolicies().accessLevels(),
|
||||
'list',
|
||||
'accessLevels',
|
||||
throw_reasons=THROW_REASONS,
|
||||
parent=ap_name,
|
||||
accessLevelFormat='CEL', fields='*')
|
||||
except googleapiclient.errors.HttpError:
|
||||
_gen_role_error(caa)
|
||||
if not csvFormat:
|
||||
for level in levels:
|
||||
display.print_json(level)
|
||||
print()
|
||||
else:
|
||||
for level in levels:
|
||||
display.add_row_titles_to_csv_file(
|
||||
utils.flatten_json(level),
|
||||
csvRows, titles)
|
||||
display.write_csv_file(csvRows, titles, 'CAA Levels', todrive)
|
||||
|
||||
|
||||
def build_os_constraints(constraints):
|
||||
consts_obj = []
|
||||
constraints = constraints.upper().split(',')
|
||||
valid_os_types = ['DESKTOP_MAC', 'DESKTOP_WINDOWS', 'DESKTOP_LINUX',
|
||||
'DESKTOP_CHROME_OS', 'VERIFIED_DESKTOP_CHROME_OS', 'ANDROID', 'IOS']
|
||||
for constraint in constraints:
|
||||
new_const = {}
|
||||
if ':' in constraint:
|
||||
new_const['osType'], new_const['minimumVersion'] = constraint.split(':')
|
||||
else:
|
||||
new_const['osType'] = constraint
|
||||
if new_const['osType'] not in valid_os_types:
|
||||
controlflow.system_error_exit(2, f'expected os type of {", ".join(valid_os_types)} got {new_const["osType"]}')
|
||||
if new_const['osType'] == 'VERIFIED_DESKTOP_CHROME_OS':
|
||||
new_const['osType'] = 'DESKTOP_CHROME_OS'
|
||||
new_const['requireVerifiedChromeOs'] = True
|
||||
consts_obj.append(new_const)
|
||||
return consts_obj
|
||||
|
||||
|
||||
def build_device_policy(i, schemas):
|
||||
device_policy = {}
|
||||
while True:
|
||||
myarg = sys.argv[i].replace('_', '').lower()
|
||||
if myarg == 'requirescreenlock':
|
||||
device_policy['requireScreenLock'] = gam.getBoolean(sys.argv[i+1], myarg)
|
||||
i += 2
|
||||
elif myarg == 'allowedencryptionstatuses':
|
||||
allowed_statuses = gapi.get_enum_values_minus_unspecified(schemas["DevicePolicy"]["properties"]["allowedEncryptionStatuses"]["items"]["enum"])
|
||||
device_policy['allowedEncryptionStatuses'] = sys.argv[i+1].upper().split(',')
|
||||
for status in device_policy['allowedEncryptionStatuses']:
|
||||
if status not in allowed_statuses:
|
||||
controlflow.system_error_exit(2, f'expected encryption status of {", ".join(allowed_statuses)} got {status}')
|
||||
i += 2
|
||||
elif myarg == 'osconstraints':
|
||||
device_policy['osConstraints'] = build_os_constraints(sys.argv[i+1])
|
||||
i += 2
|
||||
elif myarg == 'alloweddevicemanagementlevels':
|
||||
allowed_levels = gapi.get_enum_values_minus_unspecified(schemas["DevicePolicy"]["properties"]["allowedDeviceManagementLevels"]["items"]["enum"])
|
||||
device_policy['allowedDeviceManagementLevels'] = sys.argv[i+1].upper().split(',')
|
||||
for level in device_policy['allowedDeviceManagementLevels']:
|
||||
if level == 'ADVANCED':
|
||||
level = 'COMPLETE'
|
||||
if level not in allowed_levels:
|
||||
controlflow.system_error_exit(2, f'expected device management level of {", ".join(allowed_levels)} got {level}')
|
||||
i += 2
|
||||
elif myarg == 'requireadminapproval':
|
||||
device_policy['requireAdminApproval'] = gam.getBoolean(sys.argv[i+1], myarg)
|
||||
i += 2
|
||||
elif myarg == 'requirecorpowned':
|
||||
device_policy['requireCorpOwned'] = gam.getBoolean(sys.argv[i+1], myarg)
|
||||
i += 2
|
||||
elif myarg == 'enddevicepolicy':
|
||||
i += 1
|
||||
break
|
||||
else:
|
||||
controlflow.invalid_argument_exit(myarg, 'gam create/update caalevel')
|
||||
return i, device_policy
|
||||
|
||||
|
||||
def build_condition(i, schemas):
|
||||
condition = {}
|
||||
while True:
|
||||
myarg = sys.argv[i].replace('_', '').lower()
|
||||
if myarg == 'ipsubnetworks':
|
||||
condition['ipSubnetworks'] = sys.argv[i+1].split(',')
|
||||
i += 2
|
||||
elif myarg == 'devicepolicy':
|
||||
i += 1
|
||||
i, condition['devicePolicy'] = build_device_policy(i, schemas)
|
||||
elif myarg == 'requiredaccesslevels':
|
||||
condition['requiredAccessLevels'] = sys.argv[i+1].split(',')
|
||||
i += 2
|
||||
elif myarg == 'negate':
|
||||
condition['negate'] = gam.getBoolean(sys.argv[i+1], myarg)
|
||||
i += 2
|
||||
elif myarg == 'members':
|
||||
condition['members'] = sys.argv[i+1].split(',')
|
||||
i += 2
|
||||
elif myarg == 'regions':
|
||||
condition['regions'] = sys.argv[i+1].upper().split(',')
|
||||
i += 2
|
||||
elif myarg == 'endcondition':
|
||||
i += 1
|
||||
break
|
||||
else:
|
||||
controlflow.invalid_argument_exit(myarg, 'gam create/update caalevel')
|
||||
return i, condition
|
||||
|
||||
|
||||
def build_basic_level(i, schemas):
|
||||
basic_level = {'conditions': []}
|
||||
valid_functions = gapi.get_enum_values_minus_unspecified(schemas['BasicLevel']['properties']['combiningFunction']['enum'])
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].replace('_', '').lower()
|
||||
if myarg == 'combiningfunction':
|
||||
combiningFunction = sys.argv[i+1].upper()
|
||||
if combiningFunction not in valid_functions:
|
||||
controlflow.system_error_exit(2, f'expected combining function of {",".join(valid_functions)} got {combiningFunction}')
|
||||
basic_level['combiningFunction'] = combiningFunction
|
||||
i += 2
|
||||
elif myarg == 'condition':
|
||||
i += 1
|
||||
i, condition = build_condition(i, schemas)
|
||||
basic_level['conditions'].append(condition)
|
||||
else:
|
||||
controlflow.invalid_argument_exit(myarg, 'gam create/update caalevel')
|
||||
return i, basic_level
|
||||
|
||||
|
||||
def build_caa_level(i, caa, body):
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg == 'basic':
|
||||
schemas = caa._rootDesc['schemas']
|
||||
i += 1
|
||||
i, body['basic'] = build_basic_level(i, schemas)
|
||||
elif myarg == 'custom':
|
||||
body['custom'] = {'expr': {'expression': sys.argv[i+1], 'title': 'expr'}}
|
||||
i += 2
|
||||
elif myarg == 'description':
|
||||
body['description'] = sys.argv[i+1]
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(myarg, 'gam create/update caalevel')
|
||||
|
||||
|
||||
def create_access_level():
|
||||
caa = build()
|
||||
ap_name = get_access_policy(caa)
|
||||
title = sys.argv[3].replace(' ', '_')
|
||||
allowed_title_chars = string.ascii_letters + string.digits + '_'
|
||||
name = ''.join([c for c in title if c in allowed_title_chars])[:50]
|
||||
name = f'{ap_name}/accessLevels/{name}'
|
||||
body = {
|
||||
'name': name,
|
||||
'title': title,
|
||||
}
|
||||
build_caa_level(4, caa, body)
|
||||
print(f'Creating access level {name}...')
|
||||
try:
|
||||
gapi.call(caa.accessPolicies().accessLevels(),
|
||||
'create',
|
||||
throw_reasons=THROW_REASONS,
|
||||
parent=ap_name,
|
||||
body=body)
|
||||
except googleapiclient.errors.HttpError:
|
||||
_gen_role_error(caa)
|
||||
|
||||
def get_access_level_name(i, caa):
|
||||
name = sys.argv[i]
|
||||
if not name.startswith('accessPolicies/'):
|
||||
ap_name = get_access_policy(caa)
|
||||
name = f'{ap_name}/accessLevels/{name}'
|
||||
return name
|
||||
|
||||
|
||||
def update_access_level():
|
||||
caa = build()
|
||||
name = get_access_level_name(3, caa)
|
||||
body = {}
|
||||
build_caa_level(4, caa, body)
|
||||
updateMask = ','.join(body.keys())
|
||||
print(f'Updating access level {name}...')
|
||||
try:
|
||||
gapi.call(caa.accessPolicies().accessLevels(),
|
||||
'patch',
|
||||
throw_reasons=THROW_REASONS,
|
||||
name=name,
|
||||
updateMask=updateMask,
|
||||
body=body)
|
||||
except googleapiclient.errors.HttpError:
|
||||
_gen_role_error(caa)
|
||||
|
||||
def delete_access_level():
|
||||
caa = build()
|
||||
name = get_access_level_name(3, caa)
|
||||
print(f'Deleting access level {name}...')
|
||||
try:
|
||||
gapi.call(caa.accessPolicies().accessLevels(),
|
||||
'delete',
|
||||
name=name)
|
||||
except googleapiclient.errors.HttpError:
|
||||
_gen_role_error(caa)
|
||||
@@ -51,13 +51,30 @@ def create():
|
||||
print(f'Created device {result["response"]["name"]}')
|
||||
|
||||
|
||||
def _get_device_name():
|
||||
name = sys.argv[3]
|
||||
def _parse_action(action):
|
||||
kwargs = {}
|
||||
i = 3
|
||||
name = sys.argv[i]
|
||||
if name == 'id':
|
||||
name = sys.argv[4]
|
||||
i += 1
|
||||
name = sys.argv[i]
|
||||
i += 1
|
||||
if not name.startswith('devices/'):
|
||||
name = f'devices/{name}'
|
||||
return name
|
||||
customer = _get_device_customerid()
|
||||
# bah, inconsistencies in API
|
||||
if action == 'delete':
|
||||
kwargs['customer'] = customer
|
||||
else:
|
||||
kwargs['body'] = {'customer': customer}
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if action == 'wipe' and myarg == 'removeresetlock':
|
||||
kwargs['body']['removeResetLock'] = True
|
||||
i += 1
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], f'gam {action} device')
|
||||
return name, kwargs
|
||||
|
||||
|
||||
def info():
|
||||
@@ -80,14 +97,7 @@ def info():
|
||||
def _generic_action(action, device_user=False):
|
||||
ci = gapi_cloudidentity.build_dwd()
|
||||
customer = _get_device_customerid()
|
||||
name = _get_device_name()
|
||||
|
||||
# bah, inconsistencies in API
|
||||
if action == 'delete':
|
||||
kwargs = {'customer': customer}
|
||||
else:
|
||||
kwargs = {'body': {'customer': customer}}
|
||||
|
||||
name, kwargs = _parse_action(action)
|
||||
if device_user:
|
||||
endpoint = ci.devices().deviceUsers()
|
||||
else:
|
||||
|
||||
72
src/gam/gapi/cloudidentity/orgunits.py
Normal file
72
src/gam/gapi/cloudidentity/orgunits.py
Normal file
@@ -0,0 +1,72 @@
|
||||
import sys
|
||||
|
||||
import googleapiclient
|
||||
|
||||
import gam
|
||||
from gam.var import * # pylint: disable=unused-wildcard-import
|
||||
from gam import controlflow
|
||||
from gam import display
|
||||
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 orgunits as gapi_directory_orgunits
|
||||
|
||||
def _get_orgunit_customerid():
|
||||
customer = GC_Values[GC_CUSTOMER_ID]
|
||||
if customer != MY_CUSTOMER and not customer.startswith('C'):
|
||||
customer = f'C{customer}'
|
||||
return f'customers/{customer}'
|
||||
|
||||
def move_shared_drive(driveId, orgUnit):
|
||||
_, orgUnitId = gapi_directory_orgunits.getOrgUnitId(orgUnit)
|
||||
orgUnitId = f'orgUnits/{orgUnitId[3:]}'
|
||||
name = f'orgUnits/-/memberships/shared_drive;{driveId}'
|
||||
ci = gapi_cloudidentity.build('cloudidentity_beta')
|
||||
body = {
|
||||
'customer': _get_orgunit_customerid(),
|
||||
'destinationOrgUnit': orgUnitId,
|
||||
}
|
||||
return gapi.call(ci.orgUnits().memberships(),
|
||||
'move',
|
||||
name=name,
|
||||
body=body)
|
||||
|
||||
def printshow_orgunit_shared_drives(csvFormat):
|
||||
orgunit = '/'
|
||||
if csvFormat:
|
||||
todrive = False
|
||||
csvRows = []
|
||||
titles = ['name']
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower()
|
||||
if csvFormat and myarg == 'todrive':
|
||||
todrive = True
|
||||
i += 1
|
||||
elif myarg in ['ou', 'org', 'orgunit']:
|
||||
orgunit = sys.argv[i + 1]
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i],
|
||||
f"gam {['show', 'print'][csvFormat]} oushareddrives")
|
||||
ci = gapi_cloudidentity.build('cloudidentity_beta')
|
||||
_, orgUnitId = gapi_directory_orgunits.getOrgUnitId(orgunit)
|
||||
parent = f'orgUnits/{orgUnitId[3:]}'
|
||||
filter_ = "type == 'shared_drive'"
|
||||
sds = gapi.get_all_pages(ci.orgUnits().memberships(),
|
||||
'list',
|
||||
'orgMemberships',
|
||||
parent=parent,
|
||||
customer=_get_orgunit_customerid(),
|
||||
filter=filter_)
|
||||
if not csvFormat:
|
||||
for sd in sds:
|
||||
display.print_json(sd)
|
||||
print()
|
||||
else:
|
||||
for sd in sds:
|
||||
display.add_row_titles_to_csv_file(
|
||||
utils.flatten_json(sd),
|
||||
csvRows, titles)
|
||||
display.write_csv_file(csvRows, titles, f'OrgUnit {orgunit} Shared Drives', todrive)
|
||||
24
src/gam/gapi/cloudresourcemanager.py
Normal file
24
src/gam/gapi/cloudresourcemanager.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import gam
|
||||
from gam.var import GC_Values, GC_CUSTOMER_ID
|
||||
from gam import controlflow
|
||||
from gam import gapi
|
||||
from gam.gapi.directory import customer as gapi_directory_customer
|
||||
|
||||
def build():
|
||||
return gam.buildGAPIServiceObject('cloudresourcemanager',
|
||||
act_as=None)
|
||||
|
||||
|
||||
def get_org_id():
|
||||
gapi_directory_customer.setTrueCustomerId()
|
||||
crm = build()
|
||||
query = f'directorycustomerid:{GC_Values[GC_CUSTOMER_ID]}'
|
||||
orgs = gapi.get_all_pages(crm.organizations(),
|
||||
'search',
|
||||
'organizations',
|
||||
query=query)
|
||||
if len(orgs) < 1:
|
||||
# return nothing and let calling API deal with it
|
||||
# since caller knows what GCP role would serve best
|
||||
return
|
||||
return orgs[0]['name']
|
||||
@@ -3,3 +3,6 @@ import gam
|
||||
|
||||
def build():
|
||||
return gam.buildGAPIObject('directory')
|
||||
|
||||
def build_beta():
|
||||
return gam.buildGAPIObject('directory_beta')
|
||||
|
||||
@@ -160,42 +160,15 @@ def info(name=None, return_attrib=None):
|
||||
print('')
|
||||
|
||||
|
||||
def print_():
|
||||
print_order = [
|
||||
'orgUnitPath', 'orgUnitId', 'name', 'description', 'parentOrgUnitPath',
|
||||
'parentOrgUnitId', 'blockInheritance'
|
||||
]
|
||||
cd = gapi_directory.build()
|
||||
listType = 'all'
|
||||
orgUnitPath = '/'
|
||||
todrive = False
|
||||
fields = ['orgUnitPath', 'name', 'orgUnitId', 'parentOrgUnitId']
|
||||
titles = []
|
||||
csvRows = []
|
||||
parentOrgIds = []
|
||||
def list_orgunits(listType='all', orgUnitPath=None, fields=None):
|
||||
retrievedOrgIds = []
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg == 'todrive':
|
||||
todrive = True
|
||||
i += 1
|
||||
elif myarg == 'toplevelonly':
|
||||
listType = 'children'
|
||||
i += 1
|
||||
elif myarg == 'fromparent':
|
||||
orgUnitPath = getOrgUnitItem(sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg == 'allfields':
|
||||
fields = None
|
||||
i += 1
|
||||
elif myarg == 'fields':
|
||||
fields += sys.argv[i + 1].split(',')
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam print orgs')
|
||||
gam.printGettingAllItems('Organizational Units', None)
|
||||
parentOrgIds = []
|
||||
cd = gapi_directory.build()
|
||||
if fields:
|
||||
# Always get parentOrgUnitId so we can
|
||||
# find missing parents
|
||||
if 'parentOrgUnitId' not in fields:
|
||||
fields.append('parentOrgUnitId')
|
||||
get_fields = ','.join(fields)
|
||||
list_fields = f'organizationUnits({get_fields})'
|
||||
else:
|
||||
@@ -230,6 +203,44 @@ def print_():
|
||||
orgunits.append(result)
|
||||
except:
|
||||
pass
|
||||
return orgunits
|
||||
|
||||
|
||||
def print_():
|
||||
print_order = [
|
||||
'orgUnitPath', 'orgUnitId', 'name', 'description', 'parentOrgUnitPath',
|
||||
'parentOrgUnitId', 'blockInheritance'
|
||||
]
|
||||
listType = 'all'
|
||||
orgUnitPath = '/'
|
||||
todrive = False
|
||||
fields = ['orgUnitPath', 'name', 'orgUnitId', 'parentOrgUnitId']
|
||||
titles = []
|
||||
csvRows = []
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg == 'todrive':
|
||||
todrive = True
|
||||
i += 1
|
||||
elif myarg == 'toplevelonly':
|
||||
listType = 'children'
|
||||
i += 1
|
||||
elif myarg == 'fromparent':
|
||||
orgUnitPath = getOrgUnitItem(sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg == 'allfields':
|
||||
fields = None
|
||||
i += 1
|
||||
elif myarg == 'fields':
|
||||
fields += sys.argv[i + 1].split(',')
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam print orgs')
|
||||
gam.printGettingAllItems('Organizational Units', None)
|
||||
orgunits = list_orgunits(listType=listType,
|
||||
orgUnitPath=orgUnitPath,
|
||||
fields=fields)
|
||||
for row in orgunits:
|
||||
orgEntity = {}
|
||||
for key, value in list(row.items()):
|
||||
@@ -248,6 +259,11 @@ def print_():
|
||||
display.write_csv_file(csvRows, titles, 'Orgs', todrive)
|
||||
|
||||
|
||||
def orgid_to_org_map():
|
||||
orgunits = list_orgunits(fields=['orgUnitPath', 'orgUnitId'])
|
||||
result = {ou['orgUnitId']:ou['orgUnitPath'] for ou in orgunits}
|
||||
return result
|
||||
|
||||
def update():
|
||||
cd = gapi_directory.build()
|
||||
orgUnitPath = getOrgUnitItem(sys.argv[3])
|
||||
|
||||
126
src/gam/gapi/directory/roleassignments.py
Normal file
126
src/gam/gapi/directory/roleassignments.py
Normal file
@@ -0,0 +1,126 @@
|
||||
import sys
|
||||
|
||||
from gam.var import GC_Values, GC_CUSTOMER_ID
|
||||
import gam
|
||||
from gam import controlflow
|
||||
from gam import display
|
||||
from gam import gapi
|
||||
from gam.gapi import directory as gapi_directory
|
||||
from gam.gapi.directory import orgunits as gapi_directory_orgunits
|
||||
from gam.gapi.directory import roles as gapi_directory_roles
|
||||
|
||||
|
||||
SECURITY_GROUP_CONDITION = "api.getAttribute('cloudidentity.googleapis.com/groups.labels', []).hasAny(['groups.security']) && resource.type == 'cloudidentity.googleapis.com/Group'"
|
||||
NONSECURITY_GROUP_CONDITION = f'!{SECURITY_GROUP_CONDITION}'
|
||||
|
||||
def create():
|
||||
cd = gapi_directory.build()
|
||||
user = gam.normalizeEmailAddressOrUID(sys.argv[3])
|
||||
body = {'assignedTo': gam.convertEmailAddressToUID(user, cd)}
|
||||
role = sys.argv[4]
|
||||
body['roleId'] = gapi_directory_roles.getRoleId(role)
|
||||
body['scopeType'] = sys.argv[5].upper()
|
||||
i = 6
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower()
|
||||
if myarg == 'condition':
|
||||
cd = gapi_directory.build_beta()
|
||||
body['condition'] = sys.argv[i+1]
|
||||
if body['condition'] == 'securitygroup':
|
||||
body['condition'] = SECURITY_GROUP_CONDITION
|
||||
elif body['condition'] == 'nonsecuritygroup':
|
||||
body['condition'] = NONSECURITY_GROUP_CONDITION
|
||||
i += 2
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam create admin')
|
||||
if body['scopeType'] not in ['CUSTOMER', 'ORG_UNIT']:
|
||||
controlflow.expected_argument_exit('scope type',
|
||||
', '.join(['customer', 'org_unit']),
|
||||
body['scopeType'])
|
||||
if body['scopeType'] == 'ORG_UNIT':
|
||||
orgUnit, orgUnitId = gapi_directory_orgunits.getOrgUnitId(
|
||||
sys.argv[6], cd)
|
||||
body['orgUnitId'] = orgUnitId[3:]
|
||||
scope = f'ORG_UNIT {orgUnit}'
|
||||
else:
|
||||
scope = 'CUSTOMER'
|
||||
print(f'Giving {user} admin role {role} for {scope}')
|
||||
gapi.call(cd.roleAssignments(),
|
||||
'insert',
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
body=body)
|
||||
|
||||
|
||||
def delete():
|
||||
cd = gapi_directory.build()
|
||||
roleAssignmentId = sys.argv[3]
|
||||
print(f'Deleting Admin Role Assignment {roleAssignmentId}')
|
||||
gapi.call(cd.roleAssignments(),
|
||||
'delete',
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
roleAssignmentId=roleAssignmentId)
|
||||
|
||||
|
||||
def print_():
|
||||
cd = gapi_directory.build()
|
||||
roleId = None
|
||||
todrive = False
|
||||
kwargs = {}
|
||||
item_fields = ['roleAssignmentId', 'roleId', 'assignedTo', 'scopeType', 'orgUnitId']
|
||||
titles = [
|
||||
'roleAssignmentId', 'roleId', 'role', 'assignedTo', 'assignedToUser',
|
||||
'scopeType', 'orgUnitId', 'orgUnit'
|
||||
]
|
||||
csvRows = []
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower()
|
||||
if myarg == 'user':
|
||||
kwargs['userKey'] = gam.normalizeEmailAddressOrUID(sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg == 'role':
|
||||
roleId = gapi_directory_roles.getRoleId(sys.argv[i + 1])
|
||||
i += 2
|
||||
elif myarg == 'condition':
|
||||
cd = gapi_directory.build_beta()
|
||||
item_fields.append('condition')
|
||||
i += 1
|
||||
elif myarg == 'todrive':
|
||||
todrive = True
|
||||
i += 1
|
||||
else:
|
||||
controlflow.invalid_argument_exit(sys.argv[i], 'gam print admins')
|
||||
fields = f'nextPageToken,items({",".join(item_fields)})'
|
||||
if roleId and not kwargs:
|
||||
kwargs['roleId'] = roleId
|
||||
roleId = None
|
||||
admins = gapi.get_all_pages(cd.roleAssignments(),
|
||||
'list',
|
||||
'items',
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
fields=fields,
|
||||
**kwargs)
|
||||
for admin in admins:
|
||||
if roleId and roleId != admin['roleId']:
|
||||
continue
|
||||
admin_attrib = {}
|
||||
for key, value in list(admin.items()):
|
||||
if key == 'assignedTo':
|
||||
admin_attrib['assignedToUser'] = gam.user_from_userid(value)
|
||||
elif key == 'roleId':
|
||||
admin_attrib['role'] = gapi_directory_roles.role_from_roleid(value)
|
||||
elif key == 'orgUnitId':
|
||||
value = f'id:{value}'
|
||||
admin_attrib[
|
||||
'orgUnit'] = gapi_directory_orgunits.orgunit_from_orgunitid(
|
||||
value, cd)
|
||||
elif key == 'condition':
|
||||
if value == SECURITY_GROUP_CONDITION:
|
||||
value = 'securitygroup'
|
||||
elif value == NONSECURITY_GROUP_CONDITION:
|
||||
value = 'nonsecuritygroup'
|
||||
if key not in titles:
|
||||
titles.append(key)
|
||||
admin_attrib[key] = value
|
||||
csvRows.append(admin_attrib)
|
||||
display.write_csv_file(csvRows, titles, 'Admins', todrive)
|
||||
@@ -1,6 +1,13 @@
|
||||
import sys
|
||||
|
||||
from gam.var import GC_Values, GC_CUSTOMER_ID
|
||||
from gam.var import (
|
||||
GC_Values,
|
||||
GC_CUSTOMER_ID,
|
||||
GM_Globals,
|
||||
GM_MAP_ROLE_ID_TO_NAME,
|
||||
GM_MAP_ROLE_NAME_TO_ID,
|
||||
UID_PATTERN
|
||||
)
|
||||
import gam
|
||||
from gam import controlflow
|
||||
from gam import display
|
||||
@@ -9,6 +16,47 @@ from gam.gapi import directory as gapi_directory
|
||||
from gam.gapi.directory import privileges as gapi_directory_privileges
|
||||
|
||||
|
||||
def buildRoleIdToNameToIdMap(cd=None):
|
||||
if not cd:
|
||||
cd = gapi_directory.build()
|
||||
result = gapi.get_all_pages(cd.roles(),
|
||||
'list',
|
||||
'items',
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
fields='nextPageToken,items(roleId,roleName)')
|
||||
GM_Globals[GM_MAP_ROLE_ID_TO_NAME] = {}
|
||||
GM_Globals[GM_MAP_ROLE_NAME_TO_ID] = {}
|
||||
for role in result:
|
||||
GM_Globals[GM_MAP_ROLE_ID_TO_NAME][role['roleId']] = role['roleName']
|
||||
GM_Globals[GM_MAP_ROLE_NAME_TO_ID][role['roleName']] = role['roleId']
|
||||
|
||||
|
||||
def role_from_roleid(roleid):
|
||||
if not GM_Globals[GM_MAP_ROLE_ID_TO_NAME]:
|
||||
buildRoleIdToNameToIdMap()
|
||||
return GM_Globals[GM_MAP_ROLE_ID_TO_NAME].get(roleid, roleid)
|
||||
|
||||
|
||||
def roleid_from_role(role):
|
||||
if not GM_Globals[GM_MAP_ROLE_NAME_TO_ID]:
|
||||
buildRoleIdToNameToIdMap()
|
||||
return GM_Globals[GM_MAP_ROLE_NAME_TO_ID].get(role, None)
|
||||
|
||||
|
||||
def getRoleId(role):
|
||||
cg = UID_PATTERN.match(role)
|
||||
if cg:
|
||||
roleId = cg.group(1)
|
||||
else:
|
||||
roleId = roleid_from_role(role)
|
||||
if not roleId:
|
||||
controlflow.system_error_exit(
|
||||
4,
|
||||
f'{role} is not a valid role. Please ensure role name is exactly as shown in admin console.'
|
||||
)
|
||||
return roleId
|
||||
|
||||
|
||||
def getPrivileges(body, privs, action):
|
||||
all_privileges = gapi_directory_privileges.print_(return_only=True)
|
||||
if privs == 'ALL':
|
||||
@@ -30,6 +78,7 @@ def getPrivileges(body, privs, action):
|
||||
controlflow.invalid_argument_exit(priv,
|
||||
f'gam {action} adminrole privileges')
|
||||
|
||||
|
||||
def create():
|
||||
cd = gapi_directory.build()
|
||||
body = {'roleName': sys.argv[3]}
|
||||
@@ -55,6 +104,7 @@ def create():
|
||||
customer=GC_Values[GC_CUSTOMER_ID],
|
||||
body=body)
|
||||
|
||||
|
||||
def update():
|
||||
cd = gapi_directory.build()
|
||||
body = {}
|
||||
@@ -122,3 +172,4 @@ def print_():
|
||||
role_attrib[key] = value
|
||||
csvRows.append(role_attrib)
|
||||
display.write_csv_file(csvRows, titles, 'Admin Roles', todrive)
|
||||
|
||||
|
||||
8
src/gam/gapi/drive/__init__.py
Normal file
8
src/gam/gapi/drive/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
||||
import gam
|
||||
|
||||
|
||||
def build(user=None):
|
||||
if not user:
|
||||
user = gam._get_admin_email()
|
||||
userEmail = gam.convertUIDtoEmailAddress(user)
|
||||
return (userEmail, gam.buildGAPIServiceObject('drive3', userEmail))
|
||||
26
src/gam/gapi/drive/drives.py
Normal file
26
src/gam/gapi/drive/drives.py
Normal file
@@ -0,0 +1,26 @@
|
||||
"""Methods related to Drive API Shared Drives"""
|
||||
import sys
|
||||
|
||||
|
||||
import gam
|
||||
from gam.var import GC_CUSTOMER_ID, GC_Values, MY_CUSTOMER, SORTORDER_CHOICES_MAP
|
||||
from gam import controlflow
|
||||
from gam import display
|
||||
from gam import gapi
|
||||
from gam.gapi import errors as gapi_errors
|
||||
from gam.gapi import drive as gapi_drive
|
||||
|
||||
def drive_name_to_id(name, drive=None):
|
||||
if not drive:
|
||||
_, drive = gapi_drive.build()
|
||||
q = f"name = '{name}'"
|
||||
sds = gapi.get_all_pages(drive.drives(),
|
||||
'list',
|
||||
'drives',
|
||||
q=q,
|
||||
useDomainAdminAccess=True)
|
||||
if len(sds) == 0:
|
||||
controlflow.system_error_exit(3, f'Could not find shared drive named "{name}"')
|
||||
elif len(sds) > 1:
|
||||
controlflow.system_error_exit(3, f'Got more than one shared drive named "{name}"')
|
||||
return sds[0]['id']
|
||||
@@ -120,6 +120,7 @@ class ErrorReason(Enum):
|
||||
FAILED_PRECONDITION = 'failedPrecondition'
|
||||
FORBIDDEN = 'forbidden'
|
||||
FIVE_O_THREE = '503'
|
||||
FIVE_O_O = '500'
|
||||
FOUR_O_NINE = '409'
|
||||
FOUR_O_O = '400'
|
||||
FOUR_O_FOUR = '404'
|
||||
@@ -159,6 +160,7 @@ DEFAULT_RETRY_REASONS = [
|
||||
ErrorReason.GATEWAY_TIMEOUT,
|
||||
ErrorReason.INTERNAL_ERROR,
|
||||
ErrorReason.FOUR_TWO_NINE,
|
||||
ErrorReason.FIVE_O_O,
|
||||
ErrorReason.FIVE_O_THREE,
|
||||
]
|
||||
GMAIL_THROW_REASONS = [ErrorReason.SERVICE_NOT_AVAILABLE]
|
||||
|
||||
@@ -56,7 +56,7 @@ def create(users, sku=None):
|
||||
productId = sys.argv[i+1]
|
||||
i += 2
|
||||
for user in users:
|
||||
print(f'Adding license {sku_name} from to {user}')
|
||||
print(f'Adding license {sku_name} to {user}')
|
||||
gapi.call(lic.licenseAssignments(),
|
||||
'insert',
|
||||
soft_errors=True,
|
||||
|
||||
@@ -200,12 +200,13 @@ def createExport():
|
||||
showConfidentialModeContent = None # default to not even set
|
||||
matterId = None
|
||||
query = None
|
||||
useNewExport = None
|
||||
body = {'exportOptions': {}}
|
||||
i = 3
|
||||
while i < len(sys.argv):
|
||||
myarg = sys.argv[i].lower().replace('_', '')
|
||||
if myarg == 'matter':
|
||||
matterId = getMatterItem(v, sys.argv[i + 1])
|
||||
matterId = getMatterItem(v, sys.argv[i + 1], state='OPEN')
|
||||
body['matterId'] = matterId
|
||||
i += 2
|
||||
elif myarg == 'name':
|
||||
@@ -213,6 +214,9 @@ def createExport():
|
||||
i += 2
|
||||
elif myarg in QUERY_ARGS:
|
||||
query, i = _build_query(query, myarg, i, query_discovery)
|
||||
elif myarg == 'usenewexport':
|
||||
useNewExport = gam.getBoolean(sys.argv[i+1], myarg)
|
||||
i += 2
|
||||
elif myarg in ['format']:
|
||||
export_format = sys.argv[i + 1].upper()
|
||||
if export_format not in allowed_formats:
|
||||
@@ -262,6 +266,9 @@ def createExport():
|
||||
if showConfidentialModeContent is not None:
|
||||
body['exportOptions'][options_field][
|
||||
'showConfidentialModeContent'] = showConfidentialModeContent
|
||||
if useNewExport is not None:
|
||||
body['exportOptions'][options_field][
|
||||
'useNewExport'] = useNewExport
|
||||
results = gapi.call(v.matters().exports(),
|
||||
'create',
|
||||
matterId=matterId,
|
||||
@@ -341,7 +348,7 @@ def print_count():
|
||||
# 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', [])
|
||||
query_accounts = query.get('accountInfo', {}).get('emails', [])
|
||||
elif search_method == 'ENTIRE_ORG':
|
||||
query_accounts = gam.getUsersToModify('all', 'users')
|
||||
elif search_method == 'ORG_UNIT':
|
||||
@@ -360,8 +367,9 @@ def print_count():
|
||||
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)
|
||||
if email:
|
||||
csv_rows.append({'account': email, 'count': account.get('count', 0)})
|
||||
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']
|
||||
@@ -547,7 +555,7 @@ def convertHoldNameToID(v, nameOrID, matterId):
|
||||
f'in matter {matterId}')
|
||||
|
||||
|
||||
def convertMatterNameToID(v, nameOrID):
|
||||
def convertMatterNameToID(v, nameOrID, state=None):
|
||||
nameOrID = nameOrID.lower()
|
||||
cg = UID_PATTERN.match(nameOrID)
|
||||
if cg:
|
||||
@@ -557,6 +565,7 @@ def convertMatterNameToID(v, nameOrID):
|
||||
'list',
|
||||
'matters',
|
||||
view='BASIC',
|
||||
state=state,
|
||||
fields=fields)
|
||||
for matter in matters:
|
||||
if matter['name'].lower() == nameOrID:
|
||||
@@ -564,8 +573,8 @@ def convertMatterNameToID(v, nameOrID):
|
||||
return None
|
||||
|
||||
|
||||
def getMatterItem(v, nameOrID):
|
||||
matterId = convertMatterNameToID(v, nameOrID)
|
||||
def getMatterItem(v, nameOrID, state=None):
|
||||
matterId = convertMatterNameToID(v, nameOrID, state=state)
|
||||
if not matterId:
|
||||
controlflow.system_error_exit(4, f'could not find matter {nameOrID}')
|
||||
return matterId
|
||||
|
||||
@@ -8,7 +8,7 @@ import platform
|
||||
import re
|
||||
|
||||
GAM_AUTHOR = 'Jay Lee <jay0lee@gmail.com>'
|
||||
GAM_VERSION = '6.14'
|
||||
GAM_VERSION = '6.20'
|
||||
GAM_LICENSE = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
||||
|
||||
GAM_URL = 'https://git.io/gam'
|
||||
@@ -42,7 +42,7 @@ FN_EXTRA_ARGS_TXT = 'extra-args.txt'
|
||||
FN_LAST_UPDATE_CHECK_TXT = 'lastupdatecheck.txt'
|
||||
MY_CUSTOMER = 'my_customer'
|
||||
# See https://support.google.com/drive/answer/37603
|
||||
MAX_GOOGLE_SHEET_CELLS = 5000000
|
||||
MAX_GOOGLE_SHEET_CELLS = 10000000
|
||||
MAX_LOCAL_GOOGLE_TIME_OFFSET = 30
|
||||
|
||||
SKUS = {
|
||||
@@ -193,6 +193,11 @@ SKUS = {
|
||||
'wsentplus', 'workspaceenterpriseplus'],
|
||||
'displayName': 'Workspace Enterprise Plus'
|
||||
},
|
||||
'1010020029': {
|
||||
'product': 'Google-Apps',
|
||||
'aliases': ['wes', 'workspaceenterprisestarter'],
|
||||
'displayName': 'Workspace Enterprise Starter'
|
||||
},
|
||||
'1010020030': {
|
||||
'product': 'Google-Apps',
|
||||
'aliases': ['workspacefrontline', 'workspacefrontlineworker'],
|
||||
@@ -292,6 +297,7 @@ V1_DISCOVERY_APIS = {
|
||||
|
||||
API_NAME_MAPPING = {
|
||||
'directory': 'admin',
|
||||
'directory_beta': 'admin',
|
||||
'reports': 'admin',
|
||||
'datatransfer': 'admin',
|
||||
'drive3': 'drive',
|
||||
@@ -300,6 +306,7 @@ API_NAME_MAPPING = {
|
||||
}
|
||||
|
||||
API_VER_MAPPING = {
|
||||
'accesscontextmanager': 'v1',
|
||||
'alertcenter': 'v1beta1',
|
||||
'driveactivity': 'v2',
|
||||
'calendar': 'v3',
|
||||
@@ -313,6 +320,7 @@ API_VER_MAPPING = {
|
||||
'contactdelegation': 'v1',
|
||||
'datatransfer': 'datatransfer_v1',
|
||||
'directory': 'directory_v1',
|
||||
'directory_beta': 'directory_v1.1beta1',
|
||||
'drive': 'v2',
|
||||
'drive3': 'v3',
|
||||
'gmail': 'v1',
|
||||
@@ -1522,6 +1530,9 @@ MESSAGE_UPDATE_GAM_TO_64BIT = 'You\'re running a 32-bit version of GAM on a' \
|
||||
MESSAGE_YOUR_SYSTEM_TIME_DIFFERS_FROM_GOOGLE_BY = 'Your system time differs' \
|
||||
' from %s by %s'
|
||||
|
||||
shared_drive_values = ['teamdrive', 'teamdrives',
|
||||
'shareddrive', 'shareddrives']
|
||||
|
||||
USER_ADDRESS_TYPES = ['home', 'work', 'other']
|
||||
USER_EMAIL_TYPES = ['home', 'work', 'other']
|
||||
USER_EXTERNALID_TYPES = [
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
accesscontextmanager.googleapis.com
|
||||
admin.googleapis.com
|
||||
alertcenter.googleapis.com
|
||||
calendar-json.googleapis.com
|
||||
@@ -6,6 +7,7 @@ chromemanagement.googleapis.com
|
||||
chromepolicy.googleapis.com
|
||||
classroom.googleapis.com
|
||||
cloudidentity.googleapis.com
|
||||
cloudresourcemanager.googleapis.com
|
||||
contacts.googleapis.com
|
||||
drive.googleapis.com
|
||||
driveactivity.googleapis.com
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
[metadata]
|
||||
name = GAM for Google Workspace
|
||||
version = 6.0.7
|
||||
version = 6.0.20
|
||||
description = Command line management for Google Workspaces
|
||||
long_description = file: readme.md
|
||||
long_description_content_type = text/markdown
|
||||
url = https://github.com/jay0lee/GAM
|
||||
url = https://github.com/GAM-team/GAM
|
||||
author = Jay Lee
|
||||
author_email = jay0lee@gmail.com
|
||||
license = Apache
|
||||
@@ -13,26 +13,26 @@ keywords = google, oauth2, gsuite, google-apps, google-admin-sdk, google-drive,
|
||||
classifiers =
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3 :: Only
|
||||
Programming Language :: Python :: 3.6
|
||||
Programming Language :: Python :: 3.7
|
||||
Programming Language :: Python :: 3.8
|
||||
Programming Language :: Python :: 3.9
|
||||
Programming Language :: Python :: 3.10
|
||||
License :: OSI Approved :: Apache License
|
||||
|
||||
[options]
|
||||
packages = find:
|
||||
python_requires = >=3.6
|
||||
python_requires = >= 3.7
|
||||
install_requires =
|
||||
cryptography
|
||||
distro; sys_platform == 'linux'
|
||||
filelock
|
||||
google-api-python-client >= 2.1
|
||||
google-api-python-client >= 2.36
|
||||
google-auth-httplib2
|
||||
google-auth-oauthlib >= 0.4.1
|
||||
google-auth >= 1.11.2
|
||||
httplib2 >= 0.17.0
|
||||
google-auth-oauthlib >= 0.4.6
|
||||
google-auth >= 2.3.3
|
||||
httplib2 >= 0.20.2
|
||||
importlib.metadata; python_version < '3.8'
|
||||
passlib >= 1.7.2
|
||||
passlib >= 1.7.4
|
||||
python-dateutil
|
||||
yubikey-manager >= 4.0.0
|
||||
pathvalidate
|
||||
|
||||
Reference in New Issue
Block a user