mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-29 10:21:35 +00:00
Compare commits
127 Commits
v6.5
...
20230415.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4eb89b187f | ||
|
|
c5734beef6 | ||
|
|
f4735ebd80 | ||
|
|
43ae6a4a37 | ||
|
|
f362f58f95 | ||
|
|
6d211264fc | ||
|
|
3d919f5df6 | ||
|
|
f9d5f9852a | ||
|
|
0e79035765 | ||
|
|
d5cf38eaca | ||
|
|
1cfa14d8d2 | ||
|
|
bf5a50eb2a | ||
|
|
f296579aad | ||
|
|
16bb53d0e4 | ||
|
|
b6e2549436 | ||
|
|
0814173210 | ||
|
|
375ffada5c | ||
|
|
ae37de0dd2 | ||
|
|
ce4b4771db | ||
|
|
56c61ac723 | ||
|
|
9900dd64b8 | ||
|
|
53400b6322 | ||
|
|
47537ab30a | ||
|
|
6a3692d7f4 | ||
|
|
eef2b95948 | ||
|
|
7012bef28d | ||
|
|
b3b44d144e | ||
|
|
841eba79a3 | ||
|
|
77234f9e3d | ||
|
|
14478d7831 | ||
|
|
50aa7d937e | ||
|
|
2c7e01e003 | ||
|
|
a6ce5f04aa | ||
|
|
8bc6814b42 | ||
|
|
024177b0c7 | ||
|
|
b7faa0acae | ||
|
|
0dbdbc7a13 | ||
|
|
08271e60bf | ||
|
|
ec74698001 | ||
|
|
6cecacd334 | ||
|
|
c3d27900e1 | ||
|
|
f10df3607f | ||
|
|
416be24722 | ||
|
|
e53b4a2285 | ||
|
|
a88320b1b2 | ||
|
|
76f9a144ac | ||
|
|
a673772cc1 | ||
|
|
9e6d8195eb | ||
|
|
91d97c4a2c | ||
|
|
5e1df9263b | ||
|
|
e54921ad71 | ||
|
|
1b8d0877f3 | ||
|
|
a4e962560c | ||
|
|
be7d3ceb15 | ||
|
|
1e652d5725 | ||
|
|
1e7e5422be | ||
|
|
723e9e2bb1 | ||
|
|
1f572cc95b | ||
|
|
fb63eea4a0 | ||
|
|
7efb37010d | ||
|
|
6372af8d8a | ||
|
|
0b823ea43e | ||
|
|
cebb92199f | ||
|
|
6deabf8a66 | ||
|
|
5de74a51e0 | ||
|
|
85d6305874 | ||
|
|
30d685a6f7 | ||
|
|
fcc8a58839 | ||
|
|
5a608a9b62 | ||
|
|
eb9c127a10 | ||
|
|
ed55690ff3 | ||
|
|
502afa5213 | ||
|
|
24185d66ce | ||
|
|
181ba65c63 | ||
|
|
702f36a529 | ||
|
|
e2f73bf858 | ||
|
|
7265e8c6f4 | ||
|
|
b8b9808e94 | ||
|
|
7639773c40 | ||
|
|
6ab7370149 | ||
|
|
73994fe603 | ||
|
|
3fa646723d | ||
|
|
eb08b1fbdc | ||
|
|
93ac820005 | ||
|
|
c100e25ab9 | ||
|
|
716489ceed | ||
|
|
07d5f5e52c | ||
|
|
b889debd5e | ||
|
|
b273fe1f68 | ||
|
|
376cd6e83f | ||
|
|
e8cb1a7b9f | ||
|
|
9f0c5beae7 | ||
|
|
0ea2f16322 | ||
|
|
13ca2e8d93 | ||
|
|
3833256c8c | ||
|
|
30521612b2 | ||
|
|
d069cfc309 | ||
|
|
27461b067a | ||
|
|
017712742b | ||
|
|
afce21a1bd | ||
|
|
030e2e270f | ||
|
|
c69a86b535 | ||
|
|
b64e4cf3dc | ||
|
|
a2e06adbbe | ||
|
|
43b3397541 | ||
|
|
bd0bb1542c | ||
|
|
a92a07f9c0 | ||
|
|
42ed5509ee | ||
|
|
a6582503f2 | ||
|
|
7aecb889d2 | ||
|
|
c273f87cc7 | ||
|
|
76d00c993a | ||
|
|
013b47e6e7 | ||
|
|
9f1e9934ff | ||
|
|
48b218bd9c | ||
|
|
af5baa4f3a | ||
|
|
a2cf38d904 | ||
|
|
185522d943 | ||
|
|
a42e4dd080 | ||
|
|
3a5486889f | ||
|
|
1a1f100902 | ||
|
|
c67b214298 | ||
|
|
3ad1d5c661 | ||
|
|
13400d9bde | ||
|
|
048e8dfef5 | ||
|
|
aaf7a89192 | ||
|
|
e3ee9135ff |
218
.github/workflows/build.yml
vendored
218
.github/workflows/build.yml
vendored
@@ -6,13 +6,17 @@ on:
|
|||||||
schedule:
|
schedule:
|
||||||
- cron: '37 22 * * *'
|
- cron: '37 22 * * *'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
id-token: write
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: src
|
working-directory: src
|
||||||
|
|
||||||
env:
|
env:
|
||||||
OPENSSL_CONFIG_OPTS: no-fips
|
OPENSSL_CONFIG_OPTS: no-fips --api=3.0.0
|
||||||
OPENSSL_INSTALL_PATH: ${{ github.workspace }}/bin/ssl
|
OPENSSL_INSTALL_PATH: ${{ github.workspace }}/bin/ssl
|
||||||
OPENSSL_SOURCE_PATH: ${{ github.workspace }}/src/openssl
|
OPENSSL_SOURCE_PATH: ${{ github.workspace }}/src/openssl
|
||||||
PYTHON_INSTALL_PATH: ${{ github.workspace }}/bin/python
|
PYTHON_INSTALL_PATH: ${{ github.workspace }}/bin/python
|
||||||
@@ -25,16 +29,16 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
#- os: ubuntu-20.04
|
- os: ubuntu-20.04
|
||||||
# jid: 1
|
jid: 1
|
||||||
# goal: build
|
goal: build
|
||||||
# arch: x86_64
|
arch: x86_64
|
||||||
# openssl_archs: linux-x86_64
|
openssl_archs: linux-x86_64
|
||||||
#- os: [self-hosted, linux, arm64, gcp]
|
- os: [self-hosted, linux, arm64, gcp]
|
||||||
# jid: 2
|
jid: 2
|
||||||
# goal: build
|
goal: build
|
||||||
# arch: aarch64
|
arch: aarch64
|
||||||
# openssl_archs: linux-aarch64
|
openssl_archs: linux-aarch64
|
||||||
- os: ubuntu-20.04
|
- os: ubuntu-20.04
|
||||||
jid: 3
|
jid: 3
|
||||||
goal: build
|
goal: build
|
||||||
@@ -62,11 +66,6 @@ jobs:
|
|||||||
goal: build
|
goal: build
|
||||||
arch: Win32
|
arch: Win32
|
||||||
openssl_archs: VC-WIN32
|
openssl_archs: VC-WIN32
|
||||||
- os: ubuntu-22.04
|
|
||||||
goal: test
|
|
||||||
python: "3.7"
|
|
||||||
jid: 8
|
|
||||||
arch: x86_64
|
|
||||||
- os: ubuntu-22.04
|
- os: ubuntu-22.04
|
||||||
goal: test
|
goal: test
|
||||||
python: "3.8"
|
python: "3.8"
|
||||||
@@ -95,6 +94,13 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- id: auth
|
||||||
|
name: Authenticate to Google Cloud
|
||||||
|
uses: google-github-actions/auth@v1
|
||||||
|
with:
|
||||||
|
workload_identity_provider: projects/297925809119/locations/global/workloadIdentityPools/gha-pool/providers/gha-provider
|
||||||
|
service_account: github-actions-testing-for-gam@gam-project-wyo-lub-ivl.iam.gserviceaccount.com
|
||||||
|
|
||||||
- name: Cache multiple paths
|
- name: Cache multiple paths
|
||||||
if: matrix.goal == 'build'
|
if: matrix.goal == 'build'
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
@@ -103,7 +109,7 @@ jobs:
|
|||||||
path: |
|
path: |
|
||||||
bin.tar.xz
|
bin.tar.xz
|
||||||
src/cpython
|
src/cpython
|
||||||
key: gam-${{ matrix.jid }}-20230208
|
key: gam-${{ matrix.jid }}-20230405
|
||||||
|
|
||||||
- name: Untar Cache archive
|
- name: Untar Cache archive
|
||||||
if: matrix.goal == 'build' && steps.cache-python-ssl.outputs.cache-hit == 'true'
|
if: matrix.goal == 'build' && steps.cache-python-ssl.outputs.cache-hit == 'true'
|
||||||
@@ -117,6 +123,17 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python }}
|
python-version: ${{ matrix.python }}
|
||||||
|
|
||||||
|
- name: Set cURL retry flag
|
||||||
|
run: |
|
||||||
|
curl_version=$(curl --version | head -n 1 | awk '{ print $2 }')
|
||||||
|
echo "cURL is ${curl_version}"
|
||||||
|
if [ "$curl_version" == "7.68.0" ]; then
|
||||||
|
export curl_retry="--retry 5 --retry-connrefused"
|
||||||
|
else
|
||||||
|
export curl_retry="--retry 5 --retry-all-errors"
|
||||||
|
fi
|
||||||
|
echo "curl_retry=${curl_retry}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Set env variables for test
|
- name: Set env variables for test
|
||||||
if: matrix.goal == 'test'
|
if: matrix.goal == 'test'
|
||||||
env:
|
env:
|
||||||
@@ -144,17 +161,11 @@ jobs:
|
|||||||
sudo apt-get -qq --yes update
|
sudo apt-get -qq --yes update
|
||||||
sudo apt-get -qq --yes install swig libpcsclite-dev
|
sudo apt-get -qq --yes install swig libpcsclite-dev
|
||||||
|
|
||||||
#- name: MacOS remove Homebrew
|
|
||||||
# if: runner.os == 'macOS'
|
|
||||||
# run: |
|
|
||||||
# # remove everything except the libraries needed by yubikey-manager
|
|
||||||
# brew uninstall $(brew list | grep -v 'pcre\|swig\|pcsc-lite')
|
|
||||||
|
|
||||||
- name: MacOS install tools
|
- name: MacOS install tools
|
||||||
if: runner.os == 'macOS'
|
if: runner.os == 'macOS'
|
||||||
run: |
|
run: |
|
||||||
# Install latest Rust
|
# Install latest Rust
|
||||||
curl --retry 5 --retry-connrefused -fsS -o rust.sh https://sh.rustup.rs
|
curl $curl_retry -fsS -o rust.sh https://sh.rustup.rs
|
||||||
bash ./rust.sh -y
|
bash ./rust.sh -y
|
||||||
source $HOME/.cargo/env
|
source $HOME/.cargo/env
|
||||||
# needed for Rust to compile cryptography Python package for universal2
|
# needed for Rust to compile cryptography Python package for universal2
|
||||||
@@ -175,7 +186,7 @@ jobs:
|
|||||||
staticx: ${{ matrix.staticx }}
|
staticx: ${{ matrix.staticx }}
|
||||||
run: |
|
run: |
|
||||||
echo "We are running on ${RUNNER_OS}"
|
echo "We are running on ${RUNNER_OS}"
|
||||||
LD_LIBRARY_PATH="${OPENSSL_INSTALL_PATH}/lib:${PYTHON_INSTALL_PATH}/lib"
|
LD_LIBRARY_PATH="${OPENSSL_INSTALL_PATH}/lib:${PYTHON_INSTALL_PATH}/lib:/usr/local/lib"
|
||||||
if [[ "${arch}" == "Win64" ]]; then
|
if [[ "${arch}" == "Win64" ]]; then
|
||||||
PYEXTERNALS_PATH="amd64"
|
PYEXTERNALS_PATH="amd64"
|
||||||
PYBUILDRELEASE_ARCH="x64"
|
PYBUILDRELEASE_ARCH="x64"
|
||||||
@@ -197,7 +208,7 @@ jobs:
|
|||||||
PERL=perl
|
PERL=perl
|
||||||
echo "MACOSX_DEPLOYMENT_TARGET=10.15" >> $GITHUB_ENV
|
echo "MACOSX_DEPLOYMENT_TARGET=10.15" >> $GITHUB_ENV
|
||||||
echo "PYTHON=${PYTHON_INSTALL_PATH}/bin/python3" >> $GITHUB_ENV
|
echo "PYTHON=${PYTHON_INSTALL_PATH}/bin/python3" >> $GITHUB_ENV
|
||||||
echo "PIP_ARGS=--no-binary=:all:" >> $GITHUB_ENV
|
#echo "PIP_ARGS=--no-binary=:all:" >> $GITHUB_ENV
|
||||||
elif [[ "${RUNNER_OS}" == "Linux" ]]; then
|
elif [[ "${RUNNER_OS}" == "Linux" ]]; then
|
||||||
MAKE=make
|
MAKE=make
|
||||||
MAKEOPT="-j$(nproc)"
|
MAKEOPT="-j$(nproc)"
|
||||||
@@ -298,7 +309,7 @@ jobs:
|
|||||||
rm -rf ${GITHUB_WORKSPACE}/bin/ssl-darwin64-arm64
|
rm -rf ${GITHUB_WORKSPACE}/bin/ssl-darwin64-arm64
|
||||||
echo "LDFLAGS=-L${OPENSSL_INSTALL_PATH}/lib" >> $GITHUB_ENV
|
echo "LDFLAGS=-L${OPENSSL_INSTALL_PATH}/lib" >> $GITHUB_ENV
|
||||||
echo "CRYPTOGRAPHY_SUPPRESS_LINK_FLAGS=1" >> $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 "CFLAGS=-I${OPENSSL_INSTALL_PATH}/include -arch arm64 -arch x86_64 ${CFLAGS}" >> $GITHUB_ENV
|
||||||
echo "ARCHFLAGS=-arch x86_64 -arch arm64" >> $GITHUB_ENV
|
echo "ARCHFLAGS=-arch x86_64 -arch arm64" >> $GITHUB_ENV
|
||||||
else
|
else
|
||||||
cd "${GITHUB_WORKSPACE}/src/openssl-${openssl_archs}"
|
cd "${GITHUB_WORKSPACE}/src/openssl-${openssl_archs}"
|
||||||
@@ -310,6 +321,7 @@ jobs:
|
|||||||
if: matrix.goal == 'build'
|
if: matrix.goal == 'build'
|
||||||
run: |
|
run: |
|
||||||
"${OPENSSL_INSTALL_PATH}/bin/openssl" version
|
"${OPENSSL_INSTALL_PATH}/bin/openssl" version
|
||||||
|
"${OPENSSL_INSTALL_PATH}/bin/openssl" version -f
|
||||||
file "${OPENSSL_INSTALL_PATH}/bin/openssl"
|
file "${OPENSSL_INSTALL_PATH}/bin/openssl"
|
||||||
|
|
||||||
- name: Get latest stable Python source
|
- name: Get latest stable Python source
|
||||||
@@ -407,7 +419,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upgrade pip, wheel, etc
|
- name: Upgrade pip, wheel, etc
|
||||||
run: |
|
run: |
|
||||||
curl --retry 5 --retry-connrefused -O https://bootstrap.pypa.io/get-pip.py
|
curl $curl_retry -O https://bootstrap.pypa.io/get-pip.py
|
||||||
"${PYTHON}" get-pip.py
|
"${PYTHON}" get-pip.py
|
||||||
"${PYTHON}" -m pip install --upgrade pip
|
"${PYTHON}" -m pip install --upgrade pip
|
||||||
"${PYTHON}" -m pip install --upgrade wheel
|
"${PYTHON}" -m pip install --upgrade wheel
|
||||||
@@ -415,8 +427,21 @@ jobs:
|
|||||||
|
|
||||||
- name: Install pip requirements
|
- name: Install pip requirements
|
||||||
run: |
|
run: |
|
||||||
|
echo "before anything..."
|
||||||
|
"${PYTHON}" -m pip list
|
||||||
if [[ "${RUNNER_OS}" == "macOS" ]]; then
|
if [[ "${RUNNER_OS}" == "macOS" ]]; then
|
||||||
"${PYTHON}" -m pip install --upgrade cffi ${PIP_ARGS}
|
# cffi is a dep of cryptography and doesn't ship
|
||||||
|
# a universal2 wheel so we must build one ourself :-/
|
||||||
|
export CFLAGS="-arch x86_64 -arch arm64"
|
||||||
|
export ARCHFLAGS="-arch x86_64 -arch arm64"
|
||||||
|
"${PYTHON}" -m pip install --upgrade --force-reinstall --no-binary :all: \
|
||||||
|
--no-cache-dir --no-deps --use-pep517 \
|
||||||
|
--use-feature=no-binary-enable-wheel-cache \
|
||||||
|
cffi
|
||||||
|
echo "before cryptography..."
|
||||||
|
"${PYTHON}" -m pip list
|
||||||
|
# cryptography has a universal2 wheel but getting it installed
|
||||||
|
# on x86-64 MacOS is a royal pain in the keester.
|
||||||
"${PYTHON}" -m pip download --only-binary :all: \
|
"${PYTHON}" -m pip download --only-binary :all: \
|
||||||
--dest . \
|
--dest . \
|
||||||
--no-cache \
|
--no-cache \
|
||||||
@@ -424,8 +449,16 @@ jobs:
|
|||||||
--platform macosx_10_15_universal2 \
|
--platform macosx_10_15_universal2 \
|
||||||
cryptography
|
cryptography
|
||||||
"${PYTHON}" -m pip install --force-reinstall --no-deps cryptography*.whl
|
"${PYTHON}" -m pip install --force-reinstall --no-deps cryptography*.whl
|
||||||
|
echo "after cryptography..."
|
||||||
|
"${PYTHON}" -m pip list
|
||||||
|
"${PYTHON}" -m pip install --upgrade --no-binary :all: -r requirements.txt
|
||||||
|
else
|
||||||
|
"${PYTHON}" -m pip install --upgrade -r requirements.txt
|
||||||
|
echo "after requirements..."
|
||||||
|
"${PYTHON}" -m pip list
|
||||||
|
"${PYTHON}" -m pip install --force-reinstall --no-deps --upgrade cryptography
|
||||||
fi
|
fi
|
||||||
"${PYTHON}" -m pip install --upgrade -r requirements.txt ${PIP_ARGS}
|
echo "after everything..."
|
||||||
"${PYTHON}" -m pip list
|
"${PYTHON}" -m pip list
|
||||||
|
|
||||||
- name: Install PyInstaller
|
- name: Install PyInstaller
|
||||||
@@ -434,6 +467,8 @@ jobs:
|
|||||||
git clone https://github.com/pyinstaller/pyinstaller.git
|
git clone https://github.com/pyinstaller/pyinstaller.git
|
||||||
cd pyinstaller
|
cd pyinstaller
|
||||||
export latest_release=$(git tag --list | grep -v dev | grep -v rc | sort -Vr | head -n1)
|
export latest_release=$(git tag --list | grep -v dev | grep -v rc | sort -Vr | head -n1)
|
||||||
|
# temp pin to 5.9
|
||||||
|
export latest_release="v5.9.0"
|
||||||
git checkout "${latest_release}"
|
git checkout "${latest_release}"
|
||||||
# remove pre-compiled bootloaders so we fail if bootloader compile fails
|
# remove pre-compiled bootloaders so we fail if bootloader compile fails
|
||||||
rm -rvf PyInstaller/bootloader/*-*/*
|
rm -rvf PyInstaller/bootloader/*-*/*
|
||||||
@@ -492,25 +527,6 @@ jobs:
|
|||||||
cp -v gam-setup.bat $gampath
|
cp -v gam-setup.bat $gampath
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Basic Tests all jobs
|
|
||||||
run: |
|
|
||||||
$PYTHON -m unittest discover --start-directory ./ --pattern "*_test.py" --buffer
|
|
||||||
$gam version extended
|
|
||||||
export GAMVERSION=$($gam version simple)
|
|
||||||
echo "GAM Version ${GAMVERSION}"
|
|
||||||
echo "GAMVERSION=${GAMVERSION}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Linux/MacOS package
|
|
||||||
if: runner.os != 'Windows' && matrix.goal == 'build' && matrix.staticx != 'yes'
|
|
||||||
run: |
|
|
||||||
if [[ "${RUNNER_OS}" == "macOS" ]]; then
|
|
||||||
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"
|
|
||||||
fi
|
|
||||||
tar -C dist/ --create --verbose --exclude-from "${GITHUB_WORKSPACE}/.github/actions/package_exclusions.txt" --file $GAM_ARCHIVE --xz gam
|
|
||||||
|
|
||||||
- name: Install StaticX
|
- name: Install StaticX
|
||||||
if: matrix.staticx == 'yes'
|
if: matrix.staticx == 'yes'
|
||||||
run: |
|
run: |
|
||||||
@@ -530,17 +546,31 @@ jobs:
|
|||||||
esac
|
esac
|
||||||
echo "ldlib=${ldlib}"
|
echo "ldlib=${ldlib}"
|
||||||
$PYTHON -m staticx -l "${ldlib}" "${gam}" "${gam}-staticx"
|
$PYTHON -m staticx -l "${ldlib}" "${gam}" "${gam}-staticx"
|
||||||
|
rm -v "${gam}"
|
||||||
- name: Run StaticX
|
|
||||||
if: matrix.staticx == 'yes'
|
|
||||||
run: |
|
|
||||||
"${gam}-staticx" version extended
|
|
||||||
mv -v "${gam}-staticx" "${gam}"
|
mv -v "${gam}-staticx" "${gam}"
|
||||||
|
|
||||||
- name: Linux package staticx
|
- name: Basic Tests all jobs
|
||||||
if: matrix.staticx == 'yes'
|
id: basictests
|
||||||
run: |
|
run: |
|
||||||
GAM_ARCHIVE="gam-${GAMVERSION}-linux-$(uname -m)-legacy.tar.xz"
|
$PYTHON -m unittest discover --start-directory ./ --pattern "*_test.py" --buffer
|
||||||
|
$gam version extended
|
||||||
|
export GAMVERSION=$($gam version simple)
|
||||||
|
echo "GAM Version ${GAMVERSION}"
|
||||||
|
echo "GAMVERSION=${GAMVERSION}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Linux/MacOS package
|
||||||
|
if: runner.os != 'Windows' && matrix.goal == 'build'
|
||||||
|
run: |
|
||||||
|
if [[ "${RUNNER_OS}" == "macOS" ]]; then
|
||||||
|
GAM_ARCHIVE="gam-${GAMVERSION}-macos-universal2.tar.xz"
|
||||||
|
elif [[ "${RUNNER_OS}" == "Linux" ]]; then
|
||||||
|
if [[ "${staticx}" == "yes" ]]; then
|
||||||
|
libver="legacy"
|
||||||
|
else
|
||||||
|
libver="glibc$(ldd --version | awk '/ldd/{print $NF}')"
|
||||||
|
fi
|
||||||
|
GAM_ARCHIVE="gam-${GAMVERSION}-linux-$(arch)-$libver}.tar.xz"
|
||||||
|
fi
|
||||||
tar -C dist/ --create --verbose --exclude-from "${GITHUB_WORKSPACE}/.github/actions/package_exclusions.txt" --file $GAM_ARCHIVE --xz gam
|
tar -C dist/ --create --verbose --exclude-from "${GITHUB_WORKSPACE}/.github/actions/package_exclusions.txt" --file $GAM_ARCHIVE --xz gam
|
||||||
|
|
||||||
- name: Windows package
|
- name: Windows package
|
||||||
@@ -583,14 +613,20 @@ jobs:
|
|||||||
brew install gnupg
|
brew install gnupg
|
||||||
fi
|
fi
|
||||||
source ../.github/actions/decrypt.sh ../.github/actions/creds.tar.xz.gpg creds.tar.xz
|
source ../.github/actions/decrypt.sh ../.github/actions/creds.tar.xz.gpg creds.tar.xz
|
||||||
|
rm $gampath/oauth2service.json
|
||||||
export OAUTHFILE="oauth2.txt-gam-gha-${JID}"
|
export OAUTHFILE="oauth2.txt-gam-gha-${JID}"
|
||||||
echo "OAUTHFILE=${OAUTHFILE}" >> $GITHUB_ENV
|
echo "OAUTHFILE=${OAUTHFILE}" >> $GITHUB_ENV
|
||||||
export gam_user="gam-gha-${JID}@pdl.jaylee.us"
|
export gam_user="gam-gha-${JID}@pdl.jaylee.us"
|
||||||
echo "gam_user=${gam_user}" >> $GITHUB_ENV
|
echo "gam_user=${gam_user}" >> $GITHUB_ENV
|
||||||
$gam checkconn
|
$gam checkconn
|
||||||
$gam oauth info
|
$gam create signjwtserviceaccount
|
||||||
|
export CUSTOMER_ID="C03uzfv2s"
|
||||||
|
export GA_DOMAIN="pdl.jaylee.us"
|
||||||
|
export GA_ADMIN_EMAIL="$gam_user"
|
||||||
|
touch "${gampath}/enabledasa.txt"
|
||||||
|
#$gam oauth info
|
||||||
$gam info domain
|
$gam info domain
|
||||||
$gam oauth refresh
|
#$gam oauth refresh
|
||||||
$gam info user
|
$gam info user
|
||||||
export tstamp=$($PYTHON -c "import time; print(time.time_ns())")
|
export tstamp=$($PYTHON -c "import time; print(time.time_ns())")
|
||||||
export newbase="gha_test_${JID}_${tstamp}"
|
export newbase="gha_test_${JID}_${tstamp}"
|
||||||
@@ -602,7 +638,9 @@ jobs:
|
|||||||
export newou="aaaGithub Actions/${newbase}"
|
export newou="aaaGithub Actions/${newbase}"
|
||||||
|
|
||||||
# cleanup old runs
|
# cleanup old runs
|
||||||
|
rm "${gampath}/enabledasa.txt"
|
||||||
GAM_CSV_ROW_FILTER="name:regex:gha_test_${JID}_" $gam print vaultholds | $gam csv - gam delete vaulthold "id:~~holdId~~" matter "id:~~matterId~~"
|
GAM_CSV_ROW_FILTER="name:regex:gha_test_${JID}_" $gam print vaultholds | $gam csv - gam delete vaulthold "id:~~holdId~~" matter "id:~~matterId~~"
|
||||||
|
touch "${gampath}/enabledasa.txt"
|
||||||
GAM_CSV_ROW_FILTER="name:regex:gha_test_${JID}_" $gam print features | $gam csv - gam delete feature ~name
|
GAM_CSV_ROW_FILTER="name:regex:gha_test_${JID}_" $gam print features | $gam csv - gam delete feature ~name
|
||||||
GAM_CSV_ROW_FILTER="name:regex:^gha_test_${JID}_" $gam user $gam_user print shareddrives asadmin | $gam csv - gam user $gam_user delete shareddrive ~id nukefromorbit
|
GAM_CSV_ROW_FILTER="name:regex:^gha_test_${JID}_" $gam user $gam_user print shareddrives asadmin | $gam csv - gam user $gam_user delete shareddrive ~id nukefromorbit
|
||||||
$gam print users query "gha.jid=$JID" | $gam csv - gam delete user ~primaryEmail
|
$gam print users query "gha.jid=$JID" | $gam csv - gam delete user ~primaryEmail
|
||||||
@@ -622,20 +660,30 @@ jobs:
|
|||||||
$gam user $newuser update photo https://dummyimage.com/400x600/000/fff
|
$gam user $newuser update photo https://dummyimage.com/400x600/000/fff
|
||||||
$gam user $newuser get photo
|
$gam user $newuser get photo
|
||||||
$gam user $newuser delete photo
|
$gam user $newuser delete photo
|
||||||
|
$gam create alias $newalias user $newuser
|
||||||
$gam create group $newgroup name "GHA $JID group" description "This is a description" isarchived true
|
$gam 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 $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 user $gam_user sendemail recipient exchange@pdl.jaylee.us subject "test ${tstamp}" message "test message"
|
||||||
|
rm "${gampath}/enabledasa.txt"
|
||||||
$gam user $newuser add license workspaceenterpriseplus
|
$gam user $newuser add license workspaceenterpriseplus
|
||||||
$gam print privileges
|
$gam print privileges
|
||||||
$gam update cigroup $newgroup memberrestriction 'member.type == 1 || member.customer_id == groupCustomerId()'
|
touch "${gampath}/enabledasa.txt"
|
||||||
|
$gam update cigroup $newgroup security memberrestriction 'member.type == 1 || member.customer_id == groupCustomerId()'
|
||||||
$gam info cigroup $newgroup
|
$gam info cigroup $newgroup
|
||||||
$gam update group $newgroup add owner $gam_user
|
$gam update group $newgroup add owner $gam_user
|
||||||
$gam update group $newgroup add member $newuser
|
$gam update group $newgroup add member $newuser
|
||||||
|
rm "${gampath}/enabledasa.txt"
|
||||||
$gam create admin $newuser _GROUPS_EDITOR_ROLE CUSTOMER # condition nonsecuritygroup
|
$gam create admin $newuser _GROUPS_EDITOR_ROLE CUSTOMER # condition nonsecuritygroup
|
||||||
|
$gam create admin $newgroup _HELP_DESK_ADMIN_ROLE org_unit "${newou}"
|
||||||
|
GAM_CSV_ROW_FILTER="assignedToUser:regex:${newuser}" $gam print admins | $gam csv - gam delete admin "~roleAssignmentId"
|
||||||
|
GAM_CSV_ROW_FILTER="assignedToGroup:regex:${newgroup}" $gam print admins | $gam csv - gam delete admin "~roleAssignmentId"
|
||||||
|
touch "${gampath}/enabledasa.txt"
|
||||||
$gam csv sample.csv gam create user ~~email~~ firstname "GHA Bulk" lastname ~~email~~ gha.jid $JID ou "${newou}"
|
$gam csv sample.csv gam create user ~~email~~ firstname "GHA Bulk" lastname ~~email~~ gha.jid $JID ou "${newou}"
|
||||||
$gam csv sample.csv gam update user ~~email~~ recoveryphone 12125121110 recoveryemail jay0lee@gmail.com password random displayname "GitHub Actions Bulk ${JID}"
|
$gam csv sample.csv gam update user ~~email~~ recoveryphone 12125121110 recoveryemail jay0lee@gmail.com password random displayname "GitHub Actions Bulk ${JID}"
|
||||||
$gam csv sample.csv gam update user ~~email~~ recoveryphone "" recoveryemail ""
|
$gam csv sample.csv gam update user ~~email~~ recoveryphone "" recoveryemail ""
|
||||||
|
rm "${gampath}/enabledasa.txt"
|
||||||
$gam csv sample.csv gam user ~email add license workspaceenterpriseplus
|
$gam csv sample.csv gam user ~email add license workspaceenterpriseplus
|
||||||
|
touch "${gampath}/enabledasa.txt"
|
||||||
$gam csv sample.csv gam user $gam_user sendemail recipient ~~email~~@pdl.jaylee.us subject "test message $newbase" message "GHA test message"
|
$gam csv sample.csv gam user $gam_user sendemail recipient ~~email~~@pdl.jaylee.us subject "test message $newbase" message "GHA test message"
|
||||||
$gam csv sample.csv gam update group $newgroup add member ~email
|
$gam csv sample.csv gam update group $newgroup add member ~email
|
||||||
$gam info group $newgroup
|
$gam info group $newgroup
|
||||||
@@ -679,23 +727,29 @@ jobs:
|
|||||||
endtime=$($PYTHON -c "import datetime; print((datetime.datetime.now() + datetime.timedelta(hours=2)).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 addevent summary "GHA test event" start "${starttime}" end "${endtime}" attendee $newgroup hangoutsmeet guestscanmodify true sendupdates all
|
||||||
$gam calendar $gam_user printevents after -0d
|
$gam calendar $gam_user printevents after -0d
|
||||||
|
rm "${gampath}/enabledasa.txt"
|
||||||
matterid=uid:$($gam create vaultmatter name "GHA matter $newbase" description "test matter" collaborators $newuser | head -1 | cut -d ' ' -f 3)
|
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 create vaulthold matter $matterid name "GHA hold $newbase" corpus mail accounts $newuser
|
||||||
$gam print vaultmatters matterstate open
|
$gam print vaultmatters matterstate open
|
||||||
$gam print vaultholds matter $matterid
|
$gam print vaultholds matter $matterid
|
||||||
$gam print vaultcount matter $matterid corpus mail everyone todrive
|
$gam print vaultcount matter $matterid corpus mail everyone todrive
|
||||||
$gam create vaultexport matter $matterid name "GHA export $newbase" corpus mail accounts $newuser use_new_export false
|
$gam create vaultexport matter $matterid name "GHA export $newbase" corpus mail accounts $newuser
|
||||||
$gam print exports matter $matterid | $gam csv - gam info export $matterid id:~~id~~
|
$gam print exports matter $matterid | $gam csv - gam info export $matterid id:~~id~~
|
||||||
|
touch "${gampath}/enabledasa.txt"
|
||||||
$gam csv sample.csv gam user ~email add calendar id:$newresource
|
$gam csv sample.csv gam user ~email add calendar id:$newresource
|
||||||
$gam delete resource $newresource
|
$gam delete resource $newresource
|
||||||
$gam delete feature Whiteboard-$newbase
|
$gam delete feature Whiteboard-$newbase
|
||||||
$gam delete feature VC-$newbase
|
$gam delete feature VC-$newbase
|
||||||
$gam delete building $newbuilding
|
$gam delete building $newbuilding
|
||||||
$gam delete group $newgroup
|
$gam delete group $newgroup
|
||||||
$gam create alias $newalias user $newuser
|
rm "${gampath}/enabledasa.txt"
|
||||||
|
echo start
|
||||||
$gam user $newuser delete license workspaceenterpriseplus
|
$gam user $newuser delete license workspaceenterpriseplus
|
||||||
|
echo finish
|
||||||
|
touch "${gampath}/enabledasa.txt"
|
||||||
$gam whatis $newuser
|
$gam whatis $newuser
|
||||||
$gam user $gam_user show tokens
|
$gam user $gam_user show tokens
|
||||||
|
rm "${gampath}/enabledasa.txt"
|
||||||
$gam print exports matter $matterid | $gam csv - gam download export $matterid id:~~id~~
|
$gam print exports matter $matterid | $gam csv - gam download export $matterid id:~~id~~
|
||||||
$gam delete hold "GHA hold $newbase" matter $matterid
|
$gam delete hold "GHA hold $newbase" matter $matterid
|
||||||
$gam update matter $matterid action close
|
$gam update matter $matterid action close
|
||||||
@@ -703,13 +757,14 @@ jobs:
|
|||||||
# shakes off vault hold on user so we can delete
|
# shakes off vault hold on user so we can delete
|
||||||
$gam print users query "email:${newuser}" orgunitpath | $gam csv - gam update user ~primaryEmail ou ~orgUnitPath
|
$gam print users query "email:${newuser}" orgunitpath | $gam csv - gam update user ~primaryEmail ou ~orgUnitPath
|
||||||
$gam user $newuser show holds
|
$gam user $newuser show holds
|
||||||
|
export sn="$JID$JID$JID$JID-$(openssl rand -base64 32 | sed 's/[^a-zA-Z0-9]//g')"
|
||||||
|
$gam create device serialnumber $sn devicetype android
|
||||||
|
touch "${gampath}/enabledasa.txt"
|
||||||
$gam delete user $newuser
|
$gam delete user $newuser
|
||||||
$gam print users query "gha.jid=$JID" | $gam csv - gam delete user ~primaryEmail
|
$gam print users query "gha.jid=$JID" | $gam csv - gam delete user ~primaryEmail
|
||||||
$gam print mobile
|
$gam print mobile
|
||||||
$gam print devices
|
$gam print devices
|
||||||
$gam print browsers
|
$gam print browsers
|
||||||
export sn="$JID$JID$JID$JID-$(openssl rand -base64 32 | sed 's/[^a-zA-Z0-9]//g')"
|
|
||||||
$gam create device serialnumber $sn devicetype android
|
|
||||||
$gam print cros allfields orderby serialnumber
|
$gam print cros allfields orderby serialnumber
|
||||||
$gam show crostelemetry storagepercentonly
|
$gam show crostelemetry storagepercentonly
|
||||||
$gam report usageparameters customer
|
$gam report usageparameters customer
|
||||||
@@ -718,8 +773,10 @@ jobs:
|
|||||||
$gam report users fields accounts:is_less_secure_apps_access_allowed,gmail:last_imap_time,gmail:last_pop_time filters "accounts:last_login_time>2019-01-01T00:00:00.000Z" todrive
|
$gam report users fields accounts:is_less_secure_apps_access_allowed,gmail:last_imap_time,gmail:last_pop_time filters "accounts:last_login_time>2019-01-01T00:00:00.000Z" todrive
|
||||||
$gam report admin start -3d todrive
|
$gam report admin start -3d todrive
|
||||||
$gam print devices nopersonaldevices nodeviceusers filter "serial:$JID$JID$JID$JID-" | $gam csv - gam delete device id ~name
|
$gam print devices nopersonaldevices nodeviceusers filter "serial:$JID$JID$JID$JID-" | $gam csv - gam delete device id ~name
|
||||||
|
rm "${gampath}/enabledasa.txt"
|
||||||
$gam print userinvitations
|
$gam print userinvitations
|
||||||
$gam print userinvitations | $gam csv - gam send userinvitation ~name
|
$gam print userinvitations | $gam csv - gam send userinvitation ~name
|
||||||
|
touch "${gampath}/enabledasa.txt"
|
||||||
$gam create caalevel "zzz_${newbase}" basic condition ipsubnetworks 1.1.1.1/32,2.2.2.2/32 endcondition
|
$gam create caalevel "zzz_${newbase}" basic condition ipsubnetworks 1.1.1.1/32,2.2.2.2/32 endcondition
|
||||||
$gam print caalevels
|
$gam print caalevels
|
||||||
$gam delete caalevel "zzz_${newbase}"
|
$gam delete caalevel "zzz_${newbase}"
|
||||||
@@ -731,19 +788,17 @@ jobs:
|
|||||||
$gam user $gam_user update shareddrive "${driveid}" ou "aaaGithub Actions" # so we can delete our OU...
|
$gam user $gam_user update shareddrive "${driveid}" ou "aaaGithub Actions" # so we can delete our OU...
|
||||||
$gam user $gam_user delete shareddrive "${driveid}" nukefromorbit
|
$gam user $gam_user delete shareddrive "${driveid}" nukefromorbit
|
||||||
echo "printer model count:"
|
echo "printer model count:"
|
||||||
|
ssoprofile=$($gam create inboundssoprofile name "El Goog ${newbase}" loginurl https://www.google.com logouturl https://www.google.com changepasswordurl https://www.google.com entityid ElGoog return_name_only)
|
||||||
|
$gam create inboundssocredential profile "id:${ssoprofile}" generate_key
|
||||||
|
$gam create inboundssoassignment profile "id:${ssoprofile}" orgunit "${newou}" mode SAML_SSO
|
||||||
|
$gam delete inboundssoassignment "orgunit:${newou}"
|
||||||
|
$gam delete inboundssoprofile "id:${ssoprofile}"
|
||||||
$gam print printermodels | wc -l
|
$gam print printermodels | wc -l
|
||||||
#ssoprofile=$($gam create inboundssoprofile name "El Goog ${newbase}" loginurl https://www.google.com logouturl https://www.google.com changepasswordurl https://www.google.com entityid ElGoog return_name_only)
|
$gam print printers
|
||||||
#$gam create inboundssocredential profile "id:${ssoprofile}" generate_key
|
printerid=$($gam create printer displayname "${newbase}" uri ipp://localhost:631 driverless description "made by $(gam_user)" ou "${newou}" | grep 'id: [a-z,0-9]*$' | cut -d' ' -f3)
|
||||||
#$gam create inboundssoassignment profile "id:${ssoprofile}" orgunit "${newou}" mode SAML_SSO
|
$gam info printer "$printerid"
|
||||||
|
$gam delete printer "$printerid"
|
||||||
$gam delete ou "${newou}"
|
$gam delete ou "${newou}"
|
||||||
#$gam delete inboundssoprofile "id:${ssoprofile}"
|
|
||||||
#$gam print printers
|
|
||||||
#$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: Archive production artifacts
|
- name: Archive production artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
@@ -765,6 +820,10 @@ jobs:
|
|||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build
|
needs: build
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
packages: write
|
||||||
|
pull-requests: read
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@@ -782,11 +841,18 @@ jobs:
|
|||||||
files: |
|
files: |
|
||||||
gam-binaries/*
|
gam-binaries/*
|
||||||
|
|
||||||
|
- name: Set datetime version string
|
||||||
|
id: dateversion
|
||||||
|
run: |
|
||||||
|
export dateversion="$(date +'%Y%m%d.%H%M%S')"
|
||||||
|
echo "Date version: ${dateversion}"
|
||||||
|
echo "dateversion=${dateversion}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- uses: "marvinpinto/action-automatic-releases@latest"
|
- uses: "marvinpinto/action-automatic-releases@latest"
|
||||||
name: Publish draft release
|
name: Publish draft release
|
||||||
with:
|
with:
|
||||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
automatic_release_tag: latest
|
automatic_release_tag: "${{ steps.dateversion.outputs.dateversion }}"
|
||||||
prerelease: false
|
prerelease: false
|
||||||
draft: true
|
draft: true
|
||||||
files: |
|
files: |
|
||||||
|
|||||||
@@ -911,8 +911,8 @@ gam oauth|oauth2 refresh
|
|||||||
|
|
||||||
gam <UserTypeEntity> check serviceaccount [scope|scopes <APIScopeURLList>]
|
gam <UserTypeEntity> check serviceaccount [scope|scopes <APIScopeURLList>]
|
||||||
|
|
||||||
gam yubikey [resetpiv]
|
gam yubikey resetpiv [yubikeyserialnumber <Number>]
|
||||||
gam rotate sakey yubikey yubikey_pin yubikey_slot AUTHENTICATION yubikeypin <String> yubikeyserialnumber <String>
|
gam rotate sakey yubikey yubikey_pin yubikey_slot AUTHENTICATION yubikeyserialnumber <Number>
|
||||||
|
|
||||||
gam create [gcpserviceaccount|signjwtserviceaccount]
|
gam create [gcpserviceaccount|signjwtserviceaccount]
|
||||||
gam enable apis [auto|manual]
|
gam enable apis [auto|manual]
|
||||||
@@ -1595,6 +1595,7 @@ gam create inboundssoassignment (group <GroupItem> rank <Number>)|(ou|org|orguni
|
|||||||
(mode sso_off)|(mode saml_sso profile <SSOProfileItem>)(mode domain_wide_saml_if_enabled) [neverredirect]
|
(mode sso_off)|(mode saml_sso profile <SSOProfileItem>)(mode domain_wide_saml_if_enabled) [neverredirect]
|
||||||
gam update inboundssoassignment [(group <GroupItem> rank <Number>)|(ou|org|orgunit <OrgUnitItem>)]
|
gam update inboundssoassignment [(group <GroupItem> rank <Number>)|(ou|org|orgunit <OrgUnitItem>)]
|
||||||
[(mode sso_off)|(mode saml_sso profile <SSOProfileItem>)(mode domain_wide_saml_if_enabled)] [neverredirect]
|
[(mode sso_off)|(mode saml_sso profile <SSOProfileItem>)(mode domain_wide_saml_if_enabled)] [neverredirect]
|
||||||
|
gam delete inboundssoassignment <SSOAssignmentSelector>
|
||||||
gam info inboundssoassignment <SSOAssignmentSelector>
|
gam info inboundssoassignment <SSOAssignmentSelector>
|
||||||
gam show inboundssoassignments
|
gam show inboundssoassignments
|
||||||
gam print inboundssoassignments [todrive]
|
gam print inboundssoassignments [todrive]
|
||||||
|
|||||||
@@ -412,7 +412,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"revision": "20201203",
|
"revision": "20201203",
|
||||||
"rootUrl": "https://www.googleapis.com/admin/directory/v1.1beta1/customer/",
|
"rootUrl": "https://admin.googleapis.com/admin/directory/v1.1beta1/customer/",
|
||||||
"schemas": {
|
"schemas": {
|
||||||
"ChromeBrowser": {
|
"ChromeBrowser": {
|
||||||
"id": "ChromeBrowser",
|
"id": "ChromeBrowser",
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
# -*- mode: python ; coding: utf-8 -*-
|
# -*- mode: python ; coding: utf-8 -*-
|
||||||
from os import getenv
|
from os import getenv
|
||||||
|
from re import search
|
||||||
from sys import platform
|
from sys import platform
|
||||||
|
|
||||||
from PyInstaller.utils.hooks import copy_metadata
|
from PyInstaller.utils.hooks import copy_metadata
|
||||||
|
|
||||||
|
from gam.var import GAM_VER_LIBS
|
||||||
|
|
||||||
extra_files = []
|
extra_files = []
|
||||||
extra_files += copy_metadata('google-api-python-client')
|
for pkg in GAM_VER_LIBS:
|
||||||
|
extra_files += copy_metadata(pkg, recursive=True)
|
||||||
extra_files += [('cbcm-v1.1beta1.json', '.')]
|
extra_files += [('cbcm-v1.1beta1.json', '.')]
|
||||||
extra_files += [('contactdelegation-v1.json', '.')]
|
extra_files += [('contactdelegation-v1.json', '.')]
|
||||||
extra_files += [('admin-directory_v1.1beta1.json', '.')]
|
extra_files += [('admin-directory_v1.1beta1.json', '.')]
|
||||||
|
|||||||
@@ -6,10 +6,7 @@ import configparser
|
|||||||
import csv
|
import csv
|
||||||
import datetime
|
import datetime
|
||||||
from email import message_from_string
|
from email import message_from_string
|
||||||
try:
|
from importlib.metadata import version as lib_version
|
||||||
from importlib.metadata import version as lib_version
|
|
||||||
except ImportError:
|
|
||||||
from importlib_metadata import version as lib_version
|
|
||||||
import io
|
import io
|
||||||
import json
|
import json
|
||||||
import mimetypes
|
import mimetypes
|
||||||
@@ -589,7 +586,7 @@ def SetGlobalVariables():
|
|||||||
GM_Globals[GM_ENABLEDASA_TXT] = os.path.join(
|
GM_Globals[GM_ENABLEDASA_TXT] = os.path.join(
|
||||||
GC_Values[GC_CONFIG_DIR], FN_ENABLEDASA_TXT)
|
GC_Values[GC_CONFIG_DIR], FN_ENABLEDASA_TXT)
|
||||||
if not GC_Values[GC_NO_UPDATE_CHECK]:
|
if not GC_Values[GC_NO_UPDATE_CHECK]:
|
||||||
doGAMCheckForUpdates()
|
doGAMCheckForUpdates(forceCheck=0)
|
||||||
|
|
||||||
# domain must be set and customer_id must be set and != my_customer when enable_dasa = true
|
# domain must be set and customer_id must be set and != my_customer when enable_dasa = true
|
||||||
if GC_Values[GC_ENABLE_DASA]:
|
if GC_Values[GC_ENABLE_DASA]:
|
||||||
@@ -636,8 +633,10 @@ TIME_OFFSET_UNITS = [('day', 86400), ('hour', 3600), ('minute', 60),
|
|||||||
|
|
||||||
|
|
||||||
def getLocalGoogleTimeOffset(testLocation='admin.googleapis.com'):
|
def getLocalGoogleTimeOffset(testLocation='admin.googleapis.com'):
|
||||||
|
# If local time is well off, it breaks https because the server certificate
|
||||||
|
# will be seen as too old or new and thus invalid; http doesn't have that issue.
|
||||||
# Try with http first, if time is close (<MAX_LOCAL_GOOGLE_TIME_OFFSET seconds),
|
# Try with http first, if time is close (<MAX_LOCAL_GOOGLE_TIME_OFFSET seconds),
|
||||||
# retry with https
|
# retry with https as it should be OK
|
||||||
badhttp = transport.create_http()
|
badhttp = transport.create_http()
|
||||||
for prot in ['http', 'https']:
|
for prot in ['http', 'https']:
|
||||||
localUTC = datetime.datetime.now(datetime.timezone.utc)
|
localUTC = datetime.datetime.now(datetime.timezone.utc)
|
||||||
@@ -646,6 +645,12 @@ def getLocalGoogleTimeOffset(testLocation='admin.googleapis.com'):
|
|||||||
badhttp.request(f'{prot}://' + testLocation, 'HEAD')[0]['date'])
|
badhttp.request(f'{prot}://' + testLocation, 'HEAD')[0]['date'])
|
||||||
except (httplib2.ServerNotFoundError, RuntimeError, ValueError) as e:
|
except (httplib2.ServerNotFoundError, RuntimeError, ValueError) as e:
|
||||||
controlflow.system_error_exit(4, str(e))
|
controlflow.system_error_exit(4, str(e))
|
||||||
|
except httplib2.socks.HTTPError as e:
|
||||||
|
# If user has specified an HTTPS proxy, the http request will probably fail as httplib2
|
||||||
|
# turns a GET into a CONNECT which is not valid for an http address
|
||||||
|
if prot == 'http':
|
||||||
|
continue
|
||||||
|
handleServerError(e)
|
||||||
offset = remainder = int(abs((localUTC - googleUTC).total_seconds()))
|
offset = remainder = int(abs((localUTC - googleUTC).total_seconds()))
|
||||||
if offset < MAX_LOCAL_GOOGLE_TIME_OFFSET and prot == 'http':
|
if offset < MAX_LOCAL_GOOGLE_TIME_OFFSET and prot == 'http':
|
||||||
continue
|
continue
|
||||||
@@ -660,7 +665,7 @@ def getLocalGoogleTimeOffset(testLocation='admin.googleapis.com'):
|
|||||||
return (offset, nicetime)
|
return (offset, nicetime)
|
||||||
|
|
||||||
|
|
||||||
def doGAMCheckForUpdates(forceCheck=False):
|
def doGAMCheckForUpdates(forceCheck=0):
|
||||||
|
|
||||||
def _gamLatestVersionNotAvailable():
|
def _gamLatestVersionNotAvailable():
|
||||||
if forceCheck:
|
if forceCheck:
|
||||||
@@ -703,6 +708,8 @@ def doGAMCheckForUpdates(forceCheck=False):
|
|||||||
print(
|
print(
|
||||||
f'Version Check:\n Current: {current_version}\n Latest: {latest_version}'
|
f'Version Check:\n Current: {current_version}\n Latest: {latest_version}'
|
||||||
)
|
)
|
||||||
|
if forceCheck < 0:
|
||||||
|
sys.exit(1 if latest_version > current_version else 0)
|
||||||
if latest_version <= current_version:
|
if latest_version <= current_version:
|
||||||
fileutils.write_file(GM_Globals[GM_LAST_UPDATE_CHECK_TXT],
|
fileutils.write_file(GM_Globals[GM_LAST_UPDATE_CHECK_TXT],
|
||||||
str(now_time),
|
str(now_time),
|
||||||
@@ -822,14 +829,18 @@ def checkConnection():
|
|||||||
controlflow.system_error_exit(3, createYellowText('Some hosts failed to connect! Please follow the recommendations for those hosts to correct any issues and try again.'))
|
controlflow.system_error_exit(3, createYellowText('Some hosts failed to connect! Please follow the recommendations for those hosts to correct any issues and try again.'))
|
||||||
|
|
||||||
def doGAMVersion(checkForArgs=True):
|
def doGAMVersion(checkForArgs=True):
|
||||||
force_check = extended = simple = timeOffset = False
|
forceCheck = 0
|
||||||
|
extended = simple = timeOffset = False
|
||||||
testLocation = 'admin.googleapis.com'
|
testLocation = 'admin.googleapis.com'
|
||||||
if checkForArgs:
|
if checkForArgs:
|
||||||
i = 2
|
i = 2
|
||||||
while i < len(sys.argv):
|
while i < len(sys.argv):
|
||||||
myarg = sys.argv[i].lower().replace('_', '')
|
myarg = sys.argv[i].lower().replace('_', '')
|
||||||
if myarg == 'check':
|
if myarg == 'check':
|
||||||
force_check = True
|
forceCheck = 1
|
||||||
|
i += 1
|
||||||
|
elif myarg == 'checkrc':
|
||||||
|
forceCheck = -1
|
||||||
i += 1
|
i += 1
|
||||||
elif myarg == 'simple':
|
elif myarg == 'simple':
|
||||||
simple = True
|
simple = True
|
||||||
@@ -869,21 +880,11 @@ def doGAMVersion(checkForArgs=True):
|
|||||||
(testLocation, nicetime))
|
(testLocation, nicetime))
|
||||||
if offset > MAX_LOCAL_GOOGLE_TIME_OFFSET:
|
if offset > MAX_LOCAL_GOOGLE_TIME_OFFSET:
|
||||||
controlflow.system_error_exit(4, 'Please fix your system time.')
|
controlflow.system_error_exit(4, 'Please fix your system time.')
|
||||||
if force_check:
|
if forceCheck:
|
||||||
doGAMCheckForUpdates(forceCheck=True)
|
doGAMCheckForUpdates(forceCheck)
|
||||||
if extended:
|
if extended:
|
||||||
print(ssl.OPENSSL_VERSION)
|
print(ssl.OPENSSL_VERSION)
|
||||||
libs = ['cryptography',
|
for lib in GAM_VER_LIBS:
|
||||||
'filelock',
|
|
||||||
'google-auth-httplib2',
|
|
||||||
'google-auth-oauthlib',
|
|
||||||
'google-auth',
|
|
||||||
'httplib2',
|
|
||||||
'passlib',
|
|
||||||
'python-dateutil',
|
|
||||||
'yubikey-manager',
|
|
||||||
]
|
|
||||||
for lib in libs:
|
|
||||||
try:
|
try:
|
||||||
print(f'{lib} {lib_version(lib)}')
|
print(f'{lib} {lib_version(lib)}')
|
||||||
except:
|
except:
|
||||||
@@ -1131,39 +1132,66 @@ def buildGAPIObjectNoAuthentication(api):
|
|||||||
service = getService(api, httpObj)
|
service = getService(api, httpObj)
|
||||||
return service
|
return service
|
||||||
|
|
||||||
# Convert UID to email address
|
def get_user_email_from_id(uid, cd):
|
||||||
|
try:
|
||||||
|
result = gapi.call(
|
||||||
|
cd.users(),
|
||||||
|
'get',
|
||||||
|
throw_reasons=[gapi_errors.ErrorReason.USER_NOT_FOUND],
|
||||||
|
userKey=uid,
|
||||||
|
fields='primaryEmail')
|
||||||
|
return result.get('primaryEmail')
|
||||||
|
except gapi_errors.GapiUserNotFoundError:
|
||||||
|
return
|
||||||
|
|
||||||
|
def get_group_email_from_id(uid, cd):
|
||||||
|
try:
|
||||||
|
result = gapi.call(
|
||||||
|
cd.groups(),
|
||||||
|
'get',
|
||||||
|
throw_reasons=[gapi_errors.ErrorReason.GROUP_NOT_FOUND],
|
||||||
|
groupKey=uid,
|
||||||
|
fields='email')
|
||||||
|
return result.get('email')
|
||||||
|
except gapi_errors.GapiGroupNotFoundError:
|
||||||
|
return
|
||||||
|
|
||||||
def convertUIDtoEmailAddress(emailAddressOrUID, cd=None, email_types=['user']):
|
def convertUIDtoEmailAddress(emailAddressOrUID, cd=None, email_types=['user']):
|
||||||
|
'''convert UID to email address
|
||||||
|
returns email address and object type'''
|
||||||
if isinstance(email_types, str):
|
if isinstance(email_types, str):
|
||||||
email_types = email_types.split(',')
|
email_types = email_types.split(',')
|
||||||
normalizedEmailAddressOrUID = normalizeEmailAddressOrUID(emailAddressOrUID)
|
normalizedEmailAddressOrUID = normalizeEmailAddressOrUID(emailAddressOrUID)
|
||||||
if normalizedEmailAddressOrUID.find('@') > 0:
|
if normalizedEmailAddressOrUID.find('@') > 0:
|
||||||
return normalizedEmailAddressOrUID
|
return normalizedEmailAddressOrUID, 'email'
|
||||||
if not cd:
|
if not cd:
|
||||||
cd = buildGAPIObject('directory')
|
cd = buildGAPIObject('directory')
|
||||||
if 'user' in email_types:
|
if 'user' in email_types and 'group' in email_types:
|
||||||
try:
|
# Google User IDs *TEND* to be integers while groups tend to have letters
|
||||||
result = gapi.call(
|
# thus we can optimize which check we try first. We'll still check
|
||||||
cd.users(),
|
# both since there is no guarantee this will always be true.
|
||||||
'get',
|
if normalizedEmailAddressOrUID.isdigit():
|
||||||
throw_reasons=[gapi_errors.ErrorReason.USER_NOT_FOUND],
|
uid = get_user_email_from_id(normalizedEmailAddressOrUID, cd)
|
||||||
userKey=normalizedEmailAddressOrUID,
|
if uid:
|
||||||
fields='primaryEmail')
|
return uid, 'user'
|
||||||
if 'primaryEmail' in result:
|
uid = get_group_email_from_id(normalizedEmailAddressOrUID, cd)
|
||||||
return result['primaryEmail'].lower()
|
if uid:
|
||||||
except gapi_errors.GapiUserNotFoundError:
|
return uid, 'group'
|
||||||
pass
|
else:
|
||||||
if 'group' in email_types:
|
uid = get_group_email_from_id(normalizedEmailAddressOrUID, cd)
|
||||||
try:
|
if uid:
|
||||||
result = gapi.call(
|
return uid, 'group'
|
||||||
cd.groups(),
|
uid = get_user_email_from_id(normalizedEmailAddressOrUID, cd)
|
||||||
'get',
|
if uid:
|
||||||
throw_reasons=[gapi_errors.ErrorReason.GROUP_NOT_FOUND],
|
return uid, 'user'
|
||||||
groupKey=normalizedEmailAddressOrUID,
|
elif 'user' in email_types:
|
||||||
fields='email')
|
uid = get_user_email_from_id(normalizedEmailAddressOrUID, cd)
|
||||||
if 'email' in result:
|
if uid:
|
||||||
return result['email'].lower()
|
return uid, 'user'
|
||||||
except gapi_errors.GapiGroupNotFoundError:
|
elif 'group' in email_types:
|
||||||
pass
|
uid = get_group_email_from_id(normalizedEmailAddressOrUID, cd)
|
||||||
|
if uid:
|
||||||
|
return uid, 'group'
|
||||||
if 'resource' in email_types:
|
if 'resource' in email_types:
|
||||||
try:
|
try:
|
||||||
result = gapi.call(
|
result = gapi.call(
|
||||||
@@ -1174,10 +1202,10 @@ def convertUIDtoEmailAddress(emailAddressOrUID, cd=None, email_types=['user']):
|
|||||||
customer=GC_Values[GC_CUSTOMER_ID],
|
customer=GC_Values[GC_CUSTOMER_ID],
|
||||||
fields='resourceEmail')
|
fields='resourceEmail')
|
||||||
if 'resourceEmail' in result:
|
if 'resourceEmail' in result:
|
||||||
return result['resourceEmail'].lower()
|
return result['resourceEmail'].lower(), 'resource'
|
||||||
except gapi_errors.GapiResourceNotFoundError:
|
except gapi_errors.GapiResourceNotFoundError:
|
||||||
pass
|
pass
|
||||||
return normalizedEmailAddressOrUID
|
return normalizedEmailAddressOrUID, 'unknown'
|
||||||
|
|
||||||
|
|
||||||
# Convert email address to UID
|
# Convert email address to UID
|
||||||
@@ -1191,12 +1219,13 @@ def convertEmailAddressToUID(emailAddressOrUID, cd=None, email_type='user'):
|
|||||||
result = gapi.call(
|
result = gapi.call(
|
||||||
cd.users(),
|
cd.users(),
|
||||||
'get',
|
'get',
|
||||||
throw_reasons=[gapi_errors.ErrorReason.USER_NOT_FOUND],
|
throw_reasons=[gapi_errors.ErrorReason.USER_NOT_FOUND,
|
||||||
|
gapi_errors.ErrorReason.BAD_REQUEST],
|
||||||
userKey=normalizedEmailAddressOrUID,
|
userKey=normalizedEmailAddressOrUID,
|
||||||
fields='id')
|
fields='id')
|
||||||
if 'id' in result:
|
if 'id' in result:
|
||||||
return result['id']
|
return result['id']
|
||||||
except gapi_errors.GapiUserNotFoundError:
|
except (gapi_errors.GapiUserNotFoundError, gam.gapi.errors.GapiBadRequestError):
|
||||||
pass
|
pass
|
||||||
try:
|
try:
|
||||||
result = gapi.call(
|
result = gapi.call(
|
||||||
@@ -1248,27 +1277,27 @@ def buildGAPIServiceObject(api, act_as, showAuthError=True, scopes=None):
|
|||||||
|
|
||||||
|
|
||||||
def buildAlertCenterGAPIObject(user):
|
def buildAlertCenterGAPIObject(user):
|
||||||
userEmail = convertUIDtoEmailAddress(user)
|
userEmail, _ = convertUIDtoEmailAddress(user)
|
||||||
return (userEmail, buildGAPIServiceObject('alertcenter', userEmail))
|
return (userEmail, buildGAPIServiceObject('alertcenter', userEmail))
|
||||||
|
|
||||||
|
|
||||||
def buildActivityGAPIObject(user):
|
def buildActivityGAPIObject(user):
|
||||||
userEmail = convertUIDtoEmailAddress(user)
|
userEmail, _ = convertUIDtoEmailAddress(user)
|
||||||
return (userEmail, buildGAPIServiceObject('driveactivity', userEmail))
|
return (userEmail, buildGAPIServiceObject('driveactivity', userEmail))
|
||||||
|
|
||||||
|
|
||||||
def buildDriveGAPIObject(user):
|
def buildDriveGAPIObject(user):
|
||||||
userEmail = convertUIDtoEmailAddress(user)
|
userEmail, _ = convertUIDtoEmailAddress(user)
|
||||||
return (userEmail, buildGAPIServiceObject('drive', userEmail))
|
return (userEmail, buildGAPIServiceObject('drive', userEmail))
|
||||||
|
|
||||||
|
|
||||||
def buildDrive3GAPIObject(user):
|
def buildDrive3GAPIObject(user):
|
||||||
userEmail = convertUIDtoEmailAddress(user)
|
userEmail, _ = convertUIDtoEmailAddress(user)
|
||||||
return (userEmail, buildGAPIServiceObject('drive3', userEmail))
|
return (userEmail, buildGAPIServiceObject('drive3', userEmail))
|
||||||
|
|
||||||
|
|
||||||
def buildGmailGAPIObject(user):
|
def buildGmailGAPIObject(user):
|
||||||
userEmail = convertUIDtoEmailAddress(user)
|
userEmail, _ = convertUIDtoEmailAddress(user)
|
||||||
return (userEmail, buildGAPIServiceObject('gmail', userEmail))
|
return (userEmail, buildGAPIServiceObject('gmail', userEmail))
|
||||||
|
|
||||||
|
|
||||||
@@ -2292,7 +2321,7 @@ def doGetCourseInfo():
|
|||||||
croom = buildGAPIObject('classroom')
|
croom = buildGAPIObject('classroom')
|
||||||
courseId = addCourseIdScope(sys.argv[3])
|
courseId = addCourseIdScope(sys.argv[3])
|
||||||
info = gapi.call(croom.courses(), 'get', id=courseId)
|
info = gapi.call(croom.courses(), 'get', id=courseId)
|
||||||
info['ownerEmail'] = convertUIDtoEmailAddress(f'uid:{info["ownerId"]}')
|
info['ownerEmail'], _ = convertUIDtoEmailAddress(f'uid:{info["ownerId"]}')
|
||||||
display.print_json(info)
|
display.print_json(info)
|
||||||
teachers = gapi.get_all_pages(croom.courses().teachers(),
|
teachers = gapi.get_all_pages(croom.courses().teachers(),
|
||||||
'list',
|
'list',
|
||||||
@@ -2477,7 +2506,7 @@ def doPrintCourses():
|
|||||||
if ownerEmails is not None:
|
if ownerEmails is not None:
|
||||||
ownerId = course['ownerId']
|
ownerId = course['ownerId']
|
||||||
if ownerId not in ownerEmails:
|
if ownerId not in ownerEmails:
|
||||||
ownerEmails[ownerId] = convertUIDtoEmailAddress(f'uid:{ownerId}',
|
ownerEmails[ownerId], _ = convertUIDtoEmailAddress(f'uid:{ownerId}',
|
||||||
cd=cd)
|
cd=cd)
|
||||||
course['ownerEmail'] = ownerEmails[ownerId]
|
course['ownerEmail'] = ownerEmails[ownerId]
|
||||||
for field in skipFieldsList:
|
for field in skipFieldsList:
|
||||||
@@ -7181,9 +7210,12 @@ def enable_apis():
|
|||||||
controlflow.invalid_argument_exit(sys.argv[i],
|
controlflow.invalid_argument_exit(sys.argv[i],
|
||||||
'gam enable apis')
|
'gam enable apis')
|
||||||
GAMProjectAPIs = getGAMProjectFile('project-apis.txt').splitlines()
|
GAMProjectAPIs = getGAMProjectFile('project-apis.txt').splitlines()
|
||||||
|
request = signjwt.get_request()
|
||||||
try:
|
try:
|
||||||
_, projectId = google.auth.default()
|
_, projectId = google.auth.default(scopes=signjwt._IAM_SCOPES,
|
||||||
except google.auth.exceptions.DefaultCredentialsError as e:
|
request=request)
|
||||||
|
except (google.auth.exceptions.DefaultCredentialsError,
|
||||||
|
google.auth.exceptions.RefreshError) as e:
|
||||||
projectId = input('Please enter your project ID: ')
|
projectId = input('Please enter your project ID: ')
|
||||||
while a_or_m not in ['a', 'm']:
|
while a_or_m not in ['a', 'm']:
|
||||||
a_or_m = input('Do you want to enable projects [a]utomatically or [m]anually? (a/m): ').strip().lower()
|
a_or_m = input('Do you want to enable projects [a]utomatically or [m]anually? (a/m): ').strip().lower()
|
||||||
@@ -7412,7 +7444,7 @@ def _createClientSecretsOauth2service(httpObj, projectId, login_hint):
|
|||||||
while True:
|
while True:
|
||||||
print(f'''Please go to:
|
print(f'''Please go to:
|
||||||
|
|
||||||
{console_url}
|
{console_url}
|
||||||
|
|
||||||
1. Choose "Desktop App" or "Other" for "Application type".
|
1. Choose "Desktop App" or "Other" for "Application type".
|
||||||
2. Enter a desired value for "Name" or leave as is.
|
2. Enter a desired value for "Name" or leave as is.
|
||||||
@@ -7451,6 +7483,24 @@ def _createClientSecretsOauth2service(httpObj, projectId, login_hint):
|
|||||||
fileutils.write_file(GC_Values[GC_CLIENT_SECRETS_JSON],
|
fileutils.write_file(GC_Values[GC_CLIENT_SECRETS_JSON],
|
||||||
cs_data,
|
cs_data,
|
||||||
continue_on_error=False)
|
continue_on_error=False)
|
||||||
|
print(f'''
|
||||||
|
Now it's important to mark the GAM Client ID as trusted by your Workspace instance.
|
||||||
|
|
||||||
|
1. Please go to:
|
||||||
|
|
||||||
|
https://admin.google.com/ac/owl/list?tab=configuredApps
|
||||||
|
|
||||||
|
2. Click on: Add app > OAuth App Name Or Client ID.
|
||||||
|
3. Enter the following Client ID value:
|
||||||
|
|
||||||
|
{client_id}
|
||||||
|
|
||||||
|
4. Search for the ID, select the GAM app, check the box and press Select.
|
||||||
|
5. Keep the default scope or select a preferred scope that includes your GAM admin.
|
||||||
|
6. Press Continue
|
||||||
|
7. Select Trusted radio button, Continue and Finish.
|
||||||
|
''')
|
||||||
|
input('Press Enter when complete.')
|
||||||
print('That\'s it! Your GAM Project is created and ready to use.')
|
print('That\'s it! Your GAM Project is created and ready to use.')
|
||||||
|
|
||||||
|
|
||||||
@@ -7791,7 +7841,7 @@ def doUpdateProjects():
|
|||||||
_grantRotateRights(iam, sa_email, sa_email)
|
_grantRotateRights(iam, sa_email, sa_email)
|
||||||
|
|
||||||
|
|
||||||
def _generatePrivateKeyAndPublicCert(client_id, key_size, b64enc_pub=True):
|
def _generatePrivateKeyAndPublicCert(client_id, key_size, b64enc_pub=True, validity_hours=0):
|
||||||
print(' Generating new private key...')
|
print(' Generating new private key...')
|
||||||
private_key = rsa.generate_private_key(public_exponent=65537,
|
private_key = rsa.generate_private_key(public_exponent=65537,
|
||||||
key_size=key_size,
|
key_size=key_size,
|
||||||
@@ -7808,10 +7858,16 @@ def _generatePrivateKeyAndPublicCert(client_id, key_size, b64enc_pub=True):
|
|||||||
builder = builder.issuer_name(
|
builder = builder.issuer_name(
|
||||||
x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, client_id)]))
|
x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, client_id)]))
|
||||||
# Gooogle seems to enforce the not before date strictly. Set the not before
|
# Gooogle seems to enforce the not before date strictly. Set the not before
|
||||||
# date to be UTC one hour ago should cover any clock skew.
|
# date to be UTC two minutes ago which should cover any clock skew.
|
||||||
builder = builder.not_valid_before(datetime.datetime.utcnow() - datetime.timedelta(hours=1))
|
now = datetime.datetime.utcnow()
|
||||||
# Google uses 12/31/9999 date for end time
|
builder = builder.not_valid_before(now - datetime.timedelta(minutes=2))
|
||||||
builder = builder.not_valid_after(datetime.datetime(9999, 12, 31, 23, 59))
|
# Google defaults to 12/31/9999 date for end time if there's no
|
||||||
|
# policy to restrict key age
|
||||||
|
if validity_hours:
|
||||||
|
expires = now + datetime.timedelta(hours=validity_hours) - datetime.timedelta(minutes=2)
|
||||||
|
builder = builder.not_valid_after(expires)
|
||||||
|
else:
|
||||||
|
builder = builder.not_valid_after(datetime.datetime(9999, 12, 31, 23, 59))
|
||||||
builder = builder.serial_number(x509.random_serial_number())
|
builder = builder.serial_number(x509.random_serial_number())
|
||||||
builder = builder.public_key(public_key)
|
builder = builder.public_key(public_key)
|
||||||
builder = builder.add_extension(x509.BasicConstraints(ca=False,
|
builder = builder.add_extension(x509.BasicConstraints(ca=False,
|
||||||
@@ -7877,6 +7933,7 @@ def doShowServiceAccountKeys():
|
|||||||
'list',
|
'list',
|
||||||
'keys',
|
'keys',
|
||||||
name=name,
|
name=name,
|
||||||
|
fields='*',
|
||||||
keyTypes=keyTypes)
|
keyTypes=keyTypes)
|
||||||
if not keys:
|
if not keys:
|
||||||
print('No keys')
|
print('No keys')
|
||||||
@@ -7891,6 +7948,28 @@ def doShowServiceAccountKeys():
|
|||||||
display.print_json(keys)
|
display.print_json(keys)
|
||||||
|
|
||||||
|
|
||||||
|
def getYubiKeySerialNumber(new_data, serial_number):
|
||||||
|
try:
|
||||||
|
new_data['yubikey_serial_number'] = int(serial_number)
|
||||||
|
except ValueError:
|
||||||
|
controlflow.system_error_exit(
|
||||||
|
3,
|
||||||
|
'yubikey_serial_number must be a number')
|
||||||
|
|
||||||
|
def doResetYubiKeyPIV():
|
||||||
|
new_data = {}
|
||||||
|
i = 3
|
||||||
|
while i < len(sys.argv):
|
||||||
|
myarg = sys.argv[i].lower().replace('_', '')
|
||||||
|
if myarg == 'yubikeyserialnumber':
|
||||||
|
getYubiKeySerialNumber(new_data, sys.argv[i+1])
|
||||||
|
i += 2
|
||||||
|
else:
|
||||||
|
controlflow.invalid_argument_exit(myarg, 'gam yubikey resetpiv')
|
||||||
|
yk = yubikey.YubiKey(new_data)
|
||||||
|
yk.serial_number = yk.get_serial_number()
|
||||||
|
yk.reset_piv()
|
||||||
|
|
||||||
def create_signjwt_serviceaccount():
|
def create_signjwt_serviceaccount():
|
||||||
i = 3
|
i = 3
|
||||||
if i < len(sys.argv):
|
if i < len(sys.argv):
|
||||||
@@ -7901,11 +7980,13 @@ def create_signjwt_serviceaccount():
|
|||||||
'key_type': 'signjwt',
|
'key_type': 'signjwt',
|
||||||
'token_uri': 'https://oauth2.googleapis.com/token'
|
'token_uri': 'https://oauth2.googleapis.com/token'
|
||||||
}
|
}
|
||||||
|
request = signjwt.get_request()
|
||||||
try:
|
try:
|
||||||
creds, sa_info['project_id'] = google.auth.default()
|
creds, sa_info['project_id'] = google.auth.default(scopes=signjwt._IAM_SCOPES,
|
||||||
except google.auth.exceptions.DefaultCredentialsError as e:
|
request=request)
|
||||||
|
except (google.auth.exceptions.DefaultCredentialsError,
|
||||||
|
google.auth.exceptions.RefreshError) as e:
|
||||||
controlflow.system_error_exit(2, e)
|
controlflow.system_error_exit(2, e)
|
||||||
request = transport.create_request()
|
|
||||||
creds.refresh(request)
|
creds.refresh(request)
|
||||||
sa_info['client_email'] = creds.service_account_email
|
sa_info['client_email'] = creds.service_account_email
|
||||||
oa2 = buildGAPIObjectNoAuthentication('oauth2')
|
oa2 = buildGAPIObjectNoAuthentication('oauth2')
|
||||||
@@ -7915,13 +7996,13 @@ def create_signjwt_serviceaccount():
|
|||||||
fileutils.write_file(GC_Values[GC_OAUTH2SERVICE_JSON],
|
fileutils.write_file(GC_Values[GC_OAUTH2SERVICE_JSON],
|
||||||
sa_output,
|
sa_output,
|
||||||
continue_on_error=False)
|
continue_on_error=False)
|
||||||
|
|
||||||
|
|
||||||
def doCreateOrRotateServiceAccountKeys(iam=None,
|
def doCreateOrRotateServiceAccountKeys(iam=None,
|
||||||
project_id=None,
|
project_id=None,
|
||||||
client_email=None,
|
client_email=None,
|
||||||
client_id=None):
|
client_id=None):
|
||||||
local_key_size = 2048
|
local_key_size = 2048
|
||||||
|
validity_hours = 0
|
||||||
mode = 'retainexisting'
|
mode = 'retainexisting'
|
||||||
body = {}
|
body = {}
|
||||||
if iam:
|
if iam:
|
||||||
@@ -7972,12 +8053,10 @@ def doCreateOrRotateServiceAccountKeys(iam=None,
|
|||||||
new_data['yubikey_pin'] = input('Enter your YubiKey PIN: ')
|
new_data['yubikey_pin'] = input('Enter your YubiKey PIN: ')
|
||||||
i += 1
|
i += 1
|
||||||
elif myarg == 'yubikeyserialnumber':
|
elif myarg == 'yubikeyserialnumber':
|
||||||
try:
|
getYubiKeySerialNumber(new_data, sys.argv[i+1])
|
||||||
new_data['yubikey_serial_number'] = int(sys.argv[i+1])
|
i += 2
|
||||||
except ValueError:
|
elif myarg == 'validityhours':
|
||||||
controlflow.system_error_exit(
|
validity_hours = int(sys.argv[i + 1])
|
||||||
3,
|
|
||||||
'yubikey_serial_number must be a number')
|
|
||||||
i += 2
|
i += 2
|
||||||
elif myarg in ['retainnone', 'retainexisting', 'replacecurrent']:
|
elif myarg in ['retainnone', 'retainexisting', 'replacecurrent']:
|
||||||
mode = myarg
|
mode = myarg
|
||||||
@@ -7999,7 +8078,7 @@ def doCreateOrRotateServiceAccountKeys(iam=None,
|
|||||||
elif local_key_size:
|
elif local_key_size:
|
||||||
# Generate private key locally, store in file
|
# Generate private key locally, store in file
|
||||||
new_data['private_key'], publicKeyData = _generatePrivateKeyAndPublicCert(
|
new_data['private_key'], publicKeyData = _generatePrivateKeyAndPublicCert(
|
||||||
sa_name, local_key_size)
|
sa_name, local_key_size, validity_hours=validity_hours)
|
||||||
new_data['key_type'] = 'default'
|
new_data['key_type'] = 'default'
|
||||||
for key in list(new_data):
|
for key in list(new_data):
|
||||||
if key.startswith('yubikey_'):
|
if key.startswith('yubikey_'):
|
||||||
@@ -11813,6 +11892,8 @@ def ProcessGAMCommand(args):
|
|||||||
gapi_cloudidentity_inboundsso.delete_profile()
|
gapi_cloudidentity_inboundsso.delete_profile()
|
||||||
elif argument in ['inboundssocredential', 'inboundssocredentials']:
|
elif argument in ['inboundssocredential', 'inboundssocredentials']:
|
||||||
gapi_cloudidentity_inboundsso.delete_credentials()
|
gapi_cloudidentity_inboundsso.delete_credentials()
|
||||||
|
elif argument in ['inboundssoassignment', 'inboundssoassignments']:
|
||||||
|
gapi_cloudidentity_inboundsso.delete_assignment()
|
||||||
elif argument == 'resource':
|
elif argument == 'resource':
|
||||||
gapi_directory_resource.deleteResourceCalendar()
|
gapi_directory_resource.deleteResourceCalendar()
|
||||||
elif argument == 'mobile':
|
elif argument == 'mobile':
|
||||||
@@ -11877,6 +11958,8 @@ def ProcessGAMCommand(args):
|
|||||||
argument = sys.argv[2].lower()
|
argument = sys.argv[2].lower()
|
||||||
if argument in ['browsertoken', 'browserokens']:
|
if argument in ['browsertoken', 'browserokens']:
|
||||||
gapi_cbcm.revoketoken()
|
gapi_cbcm.revoketoken()
|
||||||
|
else:
|
||||||
|
controlflow.invalid_argument_exit(argument, 'gam revoke')
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
elif command in ['close', 'reopen']:
|
elif command in ['close', 'reopen']:
|
||||||
# close and reopen will have to be split apart if either takes a new argument
|
# close and reopen will have to be split apart if either takes a new argument
|
||||||
@@ -12041,6 +12124,8 @@ def ProcessGAMCommand(args):
|
|||||||
argument = sys.argv[2].lower()
|
argument = sys.argv[2].lower()
|
||||||
if argument in ['browser', 'browsers']:
|
if argument in ['browser', 'browsers']:
|
||||||
gapi_cbcm.move()
|
gapi_cbcm.move()
|
||||||
|
else:
|
||||||
|
controlflow.invalid_argument_exit(argument, 'gam move')
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
elif command in ['oauth', 'oauth2']:
|
elif command in ['oauth', 'oauth2']:
|
||||||
argument = sys.argv[2].lower()
|
argument = sys.argv[2].lower()
|
||||||
@@ -12138,6 +12223,8 @@ def ProcessGAMCommand(args):
|
|||||||
argument = sys.argv[2].lower()
|
argument = sys.argv[2].lower()
|
||||||
if argument in ['isinvitable', 'userinvitation', 'userinvitations']:
|
if argument in ['isinvitable', 'userinvitation', 'userinvitations']:
|
||||||
gapi_cloudidentity_userinvitations.check()
|
gapi_cloudidentity_userinvitations.check()
|
||||||
|
else:
|
||||||
|
controlflow.invalid_argument_exit(argument, 'gam check')
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
elif command in ['cancelwipe', 'wipe', 'approve', 'block', 'sync']:
|
elif command in ['cancelwipe', 'wipe', 'approve', 'block', 'sync']:
|
||||||
target = sys.argv[2].lower().replace('_', '')
|
target = sys.argv[2].lower().replace('_', '')
|
||||||
@@ -12157,6 +12244,8 @@ def ProcessGAMCommand(args):
|
|||||||
gapi_cloudidentity_devices.approve_user()
|
gapi_cloudidentity_devices.approve_user()
|
||||||
elif command == 'block':
|
elif command == 'block':
|
||||||
gapi_cloudidentity_devices.block_user()
|
gapi_cloudidentity_devices.block_user()
|
||||||
|
else:
|
||||||
|
controlflow.invalid_argument_exit(target, f'gam {command}')
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
elif command in ['issuecommand', 'getcommand']:
|
elif command in ['issuecommand', 'getcommand']:
|
||||||
target = sys.argv[2].lower().replace('_', '')
|
target = sys.argv[2].lower().replace('_', '')
|
||||||
@@ -12165,18 +12254,22 @@ def ProcessGAMCommand(args):
|
|||||||
gapi_directory_cros.issue_command()
|
gapi_directory_cros.issue_command()
|
||||||
elif command == 'getcommand':
|
elif command == 'getcommand':
|
||||||
gapi_directory_cros.get_command()
|
gapi_directory_cros.get_command()
|
||||||
|
else:
|
||||||
|
controlflow.invalid_argument_exit(target, f'gam {command}')
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
elif command in ['yubikey']:
|
elif command in ['yubikey']:
|
||||||
action = sys.argv[2].lower().replace('_', '')
|
action = sys.argv[2].lower().replace('_', '')
|
||||||
if action == 'resetpiv':
|
if action == 'resetpiv':
|
||||||
yk = yubikey.YubiKey()
|
doResetYubiKeyPIV()
|
||||||
yk.serial_number = yk.get_serial_number()
|
else:
|
||||||
yk.reset_piv()
|
controlflow.invalid_argument_exit(action, f'gam yubikey')
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
elif command == 'enable':
|
elif command == 'enable':
|
||||||
enable_what = sys.argv[2].lower().replace('_', '')
|
enable_what = sys.argv[2].lower().replace('_', '')
|
||||||
if enable_what in ['api', 'apis']:
|
if enable_what in ['api', 'apis']:
|
||||||
enable_apis()
|
enable_apis()
|
||||||
|
else:
|
||||||
|
controlflow.invalid_argument_exit(enable_what, 'gam enable')
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
users = getUsersToModify()
|
users = getUsersToModify()
|
||||||
command = sys.argv[3].lower()
|
command = sys.argv[3].lower()
|
||||||
|
|||||||
@@ -22,25 +22,33 @@ For more information, see https://jaylee.us/gam
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from multiprocessing import freeze_support
|
|
||||||
from multiprocessing import set_start_method
|
|
||||||
|
|
||||||
from gam import controlflow
|
|
||||||
import gam
|
|
||||||
|
|
||||||
|
|
||||||
|
# Note that this file (and only this file) should remain compatible
|
||||||
|
# with older Python versions so we can return a meaningful error
|
||||||
|
# instead of a syntax error.
|
||||||
def main():
|
def main():
|
||||||
|
required_ver = (3, 8, 0)
|
||||||
|
if sys.version_info[:3] < required_ver:
|
||||||
|
err_result = ('ERROR: GAM requires Python %s.%s.%s or newer. You are '
|
||||||
|
'running %s.%s.%s. Please upgrade your Python version '
|
||||||
|
'or use one of the binary GAM downloads.\n' %
|
||||||
|
(required_ver[0],
|
||||||
|
required_ver[1],
|
||||||
|
required_ver[2],
|
||||||
|
sys.version_info[0],
|
||||||
|
sys.version_info[1],
|
||||||
|
sys.version_info[2]))
|
||||||
|
sys.stderr.write(err_result)
|
||||||
|
sys.exit(5)
|
||||||
|
from multiprocessing import freeze_support
|
||||||
freeze_support()
|
freeze_support()
|
||||||
if sys.platform == 'darwin':
|
if sys.platform == 'darwin':
|
||||||
# https://bugs.python.org/issue33725 in Python 3.8.0 seems
|
# https://bugs.python.org/issue33725 in Python 3.8.0 seems
|
||||||
# to break parallel operations with errors about extra -b
|
# to break parallel operations with errors about extra -b
|
||||||
# command line arguments
|
# command line arguments
|
||||||
|
from multiprocessing import set_start_method
|
||||||
set_start_method('fork')
|
set_start_method('fork')
|
||||||
if sys.version_info[0] < 3 or sys.version_info[1] < 7:
|
import gam
|
||||||
controlflow.system_error_exit(
|
|
||||||
5,
|
|
||||||
f'GAM requires Python 3.7 or newer. You are running %s.%s.%s. Please upgrade your Python version or use one of the binary GAM downloads.'
|
|
||||||
% sys.version_info[:3])
|
|
||||||
sys.exit(gam.ProcessGAMCommand(sys.argv))
|
sys.exit(gam.ProcessGAMCommand(sys.argv))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -29,9 +29,11 @@ def get_admin_credentials_filename():
|
|||||||
# some custom name in it. Otherwise, just use the default name.
|
# some custom name in it. Otherwise, just use the default name.
|
||||||
if GC_Values[GC_ENABLE_DASA]:
|
if GC_Values[GC_ENABLE_DASA]:
|
||||||
return GC_Values[GC_OAUTH2SERVICE_JSON] if GC_Values[GC_OAUTH2SERVICE_JSON] else _FN_OAUTH2SERVICE_JSON
|
return GC_Values[GC_OAUTH2SERVICE_JSON] if GC_Values[GC_OAUTH2SERVICE_JSON] else _FN_OAUTH2SERVICE_JSON
|
||||||
else:
|
return GC_Values[GC_OAUTH2_TXT] if GC_Values[GC_OAUTH2_TXT] else _FN_OAUTH2_TXT
|
||||||
return GC_Values[GC_OAUTH2_TXT] if GC_Values[GC_OAUTH2_TXT] else _FN_OAUTH2_TXT
|
|
||||||
|
|
||||||
|
APIS_NEEDING_ACCESS_TOKEN = {
|
||||||
|
'cbcm': ['https://www.googleapis.com/auth/admin.directory.device.chromebrowsers']
|
||||||
|
}
|
||||||
|
|
||||||
def get_admin_credentials(api=None):
|
def get_admin_credentials(api=None):
|
||||||
"""Gets oauth.Credentials that are authenticated as the domain's admin user."""
|
"""Gets oauth.Credentials that are authenticated as the domain's admin user."""
|
||||||
@@ -41,18 +43,23 @@ def get_admin_credentials(api=None):
|
|||||||
with open(credential_file) as f:
|
with open(credential_file) as f:
|
||||||
creds_data = json.load(f)
|
creds_data = json.load(f)
|
||||||
# Validate that enable DASA matches content of authorization file
|
# Validate that enable DASA matches content of authorization file
|
||||||
if GC_Values[GC_ENABLE_DASA] and 'key_type' in creds_data:
|
if GC_Values[GC_ENABLE_DASA] and creds_data.get('type') == 'service_account':
|
||||||
|
if api in APIS_NEEDING_ACCESS_TOKEN:
|
||||||
|
return gam.getSvcAcctCredentials(scopes=APIS_NEEDING_ACCESS_TOKEN[api],
|
||||||
|
act_as=None,
|
||||||
|
api=None,
|
||||||
|
force_oauth=True)
|
||||||
audience = f'https://{api}.googleapis.com/'
|
audience = f'https://{api}.googleapis.com/'
|
||||||
key_type = creds_data.get('key_type', 'default')
|
key_type = creds_data.get('key_type', 'default')
|
||||||
if key_type == 'default':
|
if key_type == 'default':
|
||||||
return JWTCredentials.from_service_account_info(creds_data,
|
return JWTCredentials.from_service_account_info(creds_data,
|
||||||
audience=audience)
|
audience=audience)
|
||||||
elif key_type == 'yubikey':
|
if key_type == 'yubikey':
|
||||||
yksigner = yubikey.YubiKey(creds_data)
|
yksigner = yubikey.YubiKey(creds_data)
|
||||||
return JWTCredentials._from_signer_and_info(yksigner,
|
return JWTCredentials._from_signer_and_info(yksigner,
|
||||||
creds_data,
|
creds_data,
|
||||||
audience=audience)
|
audience=audience)
|
||||||
elif key_type == 'signjwt':
|
if key_type == 'signjwt':
|
||||||
sjsigner = signjwt.SignJwt(creds_data)
|
sjsigner = signjwt.SignJwt(creds_data)
|
||||||
return signjwt.JWTCredentials._from_signer_and_info(sjsigner,
|
return signjwt.JWTCredentials._from_signer_and_info(sjsigner,
|
||||||
creds_data,
|
creds_data,
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ MESSAGE_LOCAL_SERVER_SUCCESS = ('The authentication flow has completed. You may'
|
|||||||
' close this browser window and return to GAM.')
|
' close this browser window and return to GAM.')
|
||||||
|
|
||||||
MESSAGE_AUTHENTICATION_COMPLETE = ('\nThe authentication flow has completed.\n')
|
MESSAGE_AUTHENTICATION_COMPLETE = ('\nThe authentication flow has completed.\n')
|
||||||
|
MESSAGE_AUTHENTICATION_FAILED = ('\nThe authentication flow failed, reissue command')
|
||||||
|
|
||||||
|
|
||||||
class CredentialsError(Exception):
|
class CredentialsError(Exception):
|
||||||
@@ -629,15 +630,22 @@ class _ShortURLFlow(google_auth_oauthlib.flow.InstalledAppFlow):
|
|||||||
print(MESSAGE_CONSOLE_AUTHORIZATION_PROMPT.format(url=d['auth_url']))
|
print(MESSAGE_CONSOLE_AUTHORIZATION_PROMPT.format(url=d['auth_url']))
|
||||||
user_input.start()
|
user_input.start()
|
||||||
userInput = False
|
userInput = False
|
||||||
while True:
|
alive = 2
|
||||||
|
while alive > 0:
|
||||||
sleep(0.1)
|
sleep(0.1)
|
||||||
if not http_client.is_alive():
|
if not http_client.is_alive():
|
||||||
user_input.terminate()
|
if 'code' in d:
|
||||||
break
|
user_input.terminate()
|
||||||
elif not user_input.is_alive():
|
break
|
||||||
|
alive -= 1
|
||||||
|
if not user_input.is_alive():
|
||||||
userInput = True
|
userInput = True
|
||||||
http_client.terminate()
|
if 'code' in d:
|
||||||
break
|
http_client.terminate()
|
||||||
|
break
|
||||||
|
alive -= 1
|
||||||
|
if 'code' not in d:
|
||||||
|
controlflow.system_error_exit(8, MESSAGE_AUTHENTICATION_FAILED)
|
||||||
while True:
|
while True:
|
||||||
code = d['code']
|
code = d['code']
|
||||||
if code.startswith('http'):
|
if code.startswith('http'):
|
||||||
|
|||||||
@@ -14,6 +14,13 @@ from gam.var import GM_Globals, GM_CACHE_DIR
|
|||||||
|
|
||||||
_DEFAULT_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds
|
_DEFAULT_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds
|
||||||
_GOOGLE_OAUTH2_TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token"
|
_GOOGLE_OAUTH2_TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token"
|
||||||
|
_IAM_SCOPES = ['https://www.googleapis.com/auth/iam']
|
||||||
|
|
||||||
|
# Some Workforce Identity Federation endpoints such as GitHub Actions
|
||||||
|
# only allow TLS 1.2 as of April 2023.
|
||||||
|
def get_request():
|
||||||
|
httpc = transport.create_http(override_min_tls='TLSv1_2')
|
||||||
|
return transport.create_request(httpc)
|
||||||
|
|
||||||
|
|
||||||
class JWTCredentials(google.auth.jwt.Credentials):
|
class JWTCredentials(google.auth.jwt.Credentials):
|
||||||
@@ -73,9 +80,12 @@ class SignJwt(google.auth.crypt.Signer):
|
|||||||
|
|
||||||
def sign(self, message):
|
def sign(self, message):
|
||||||
''' Call IAM Credentials SignJWT API to get our signed JWT '''
|
''' Call IAM Credentials SignJWT API to get our signed JWT '''
|
||||||
|
request = get_request()
|
||||||
try:
|
try:
|
||||||
credentials, _ = google.auth.default()
|
credentials, _ = google.auth.default(scopes=_IAM_SCOPES,
|
||||||
except google.auth.exceptions.DefaultCredentialsError as e:
|
request=request)
|
||||||
|
except (google.auth.exceptions.DefaultCredentialsError,
|
||||||
|
google.auth.exceptions.RefreshError) as e:
|
||||||
controlflow.system_error_exit(2, e)
|
controlflow.system_error_exit(2, e)
|
||||||
httpObj = transport.AuthorizedHttp(
|
httpObj = transport.AuthorizedHttp(
|
||||||
credentials,
|
credentials,
|
||||||
|
|||||||
@@ -87,6 +87,9 @@ class YubiKey():
|
|||||||
def get_serial_number(self):
|
def get_serial_number(self):
|
||||||
try:
|
try:
|
||||||
devices = list_all_devices()
|
devices = list_all_devices()
|
||||||
|
if not devices:
|
||||||
|
msg = f'Could not find any YubiKey'
|
||||||
|
controlflow.system_error_exit(3, msg)
|
||||||
if self.serial_number:
|
if self.serial_number:
|
||||||
for (device, info) in devices:
|
for (device, info) in devices:
|
||||||
if info.serial == self.serial_number:
|
if info.serial == self.serial_number:
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ def normalizeCalendarId(calname, checkPrimary=False):
|
|||||||
return calname
|
return calname
|
||||||
if not GC_Values[GC_DOMAIN]:
|
if not GC_Values[GC_DOMAIN]:
|
||||||
GC_Values[GC_DOMAIN] = gam._getValueFromOAuth('hd')
|
GC_Values[GC_DOMAIN] = gam._getValueFromOAuth('hd')
|
||||||
return gam.convertUIDtoEmailAddress(calname,
|
email, _ = gam.convertUIDtoEmailAddress(calname,
|
||||||
email_types=['user', 'resource'])
|
email_types=['user', 'resource'])
|
||||||
|
return email
|
||||||
|
|
||||||
def buildCalendarGAPIObject(calname):
|
def buildCalendarGAPIObject(calname):
|
||||||
calendarId = normalizeCalendarId(calname)
|
calendarId = normalizeCalendarId(calname)
|
||||||
|
|||||||
@@ -408,7 +408,7 @@ def update_policy():
|
|||||||
f'{expected_enums}, got {value}'
|
f'{expected_enums}, got {value}'
|
||||||
controlflow.system_error_exit(8, msg)
|
controlflow.system_error_exit(8, msg)
|
||||||
elif vtype in ['TYPE_LIST']:
|
elif vtype in ['TYPE_LIST']:
|
||||||
value = value.split(',')
|
value = value.split(',') if value else []
|
||||||
if myarg == 'chrome.users.chromebrowserupdates' and \
|
if myarg == 'chrome.users.chromebrowserupdates' and \
|
||||||
cased_field == 'targetVersionPrefixSetting':
|
cased_field == 'targetVersionPrefixSetting':
|
||||||
mg = re.compile(r'^([a-z]+)-(\d+)$').match(value)
|
mg = re.compile(r'^([a-z]+)-(\d+)$').match(value)
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ def print_():
|
|||||||
todrive = True
|
todrive = True
|
||||||
i += 1
|
i += 1
|
||||||
elif myarg == 'enterprisemember':
|
elif myarg == 'enterprisemember':
|
||||||
member = gam.convertUIDtoEmailAddress(sys.argv[i + 1], email_types=['user', 'group'])
|
member, _ = gam.convertUIDtoEmailAddress(sys.argv[i + 1], email_types=['user', 'group'])
|
||||||
usemember = f"member_key_id == '{member}' && 'cloudidentity.googleapis.com/groups.discussion_forum' in labels"
|
usemember = f"member_key_id == '{member}' && 'cloudidentity.googleapis.com/groups.discussion_forum' in labels"
|
||||||
i += 2
|
i += 2
|
||||||
elif myarg == 'delimiter':
|
elif myarg == 'delimiter':
|
||||||
@@ -501,7 +501,7 @@ def print_members():
|
|||||||
)
|
)
|
||||||
i += 2
|
i += 2
|
||||||
elif myarg == 'enterprisemember':
|
elif myarg == 'enterprisemember':
|
||||||
member = gam.convertUIDtoEmailAddress(sys.argv[i + 1], email_types=['user', 'group'])
|
member, _ = gam.convertUIDtoEmailAddress(sys.argv[i + 1], email_types=['user', 'group'])
|
||||||
usemember = f"member_key_id == '{member}' && 'cloudidentity.googleapis.com/groups.discussion_forum' in labels"
|
usemember = f"member_key_id == '{member}' && 'cloudidentity.googleapis.com/groups.discussion_forum' in labels"
|
||||||
i += 2
|
i += 2
|
||||||
elif myarg in ['cigroup', 'cigroups']:
|
elif myarg in ['cigroup', 'cigroups']:
|
||||||
@@ -876,6 +876,13 @@ def update():
|
|||||||
'cloudidentity.googleapis.com/groups.discussion_forum': ''
|
'cloudidentity.googleapis.com/groups.discussion_forum': ''
|
||||||
}
|
}
|
||||||
i += 1
|
i += 1
|
||||||
|
elif myarg == 'locked':
|
||||||
|
body['labels'] = {
|
||||||
|
'cloudidentity.googleapis.com/groups.locked': '',
|
||||||
|
'cloudidentity.googleapis.com/groups.security': '',
|
||||||
|
'cloudidentity.googleapis.com/groups.discussion_forum': ''
|
||||||
|
}
|
||||||
|
i += 1
|
||||||
elif myarg == 'dynamicsecurity':
|
elif myarg == 'dynamicsecurity':
|
||||||
body['labels'] = {
|
body['labels'] = {
|
||||||
'cloudidentity.googleapis.com/groups.dynamic': '',
|
'cloudidentity.googleapis.com/groups.dynamic': '',
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ def get_orgunit_id(orgunit):
|
|||||||
|
|
||||||
'''build Cloud Identity API'''
|
'''build Cloud Identity API'''
|
||||||
def build():
|
def build():
|
||||||
return gapi_cloudidentity.build('cloudidentity_beta')
|
return gapi_cloudidentity.build('cloudidentity')
|
||||||
|
|
||||||
|
|
||||||
'''parse cmd for profile create/update'''
|
'''parse cmd for profile create/update'''
|
||||||
@@ -484,6 +484,16 @@ def update_assignment():
|
|||||||
controlflow.system_error_exit(3, 'Update did not finish {result}')
|
controlflow.system_error_exit(3, 'Update did not finish {result}')
|
||||||
|
|
||||||
|
|
||||||
|
'''gam delete inboundssoassignment'''
|
||||||
|
def delete_assignment():
|
||||||
|
ci = build()
|
||||||
|
assignment = assignment_by_target(sys.argv[3], ci).get('name')
|
||||||
|
print(f'Deleting Inbound SSO Assignmnet {assignment}...')
|
||||||
|
gapi.call(ci.inboundSsoAssignments(),
|
||||||
|
'delete',
|
||||||
|
name=assignment)
|
||||||
|
|
||||||
|
|
||||||
'''gam info inboundssoassignment'''
|
'''gam info inboundssoassignment'''
|
||||||
def info_assignment():
|
def info_assignment():
|
||||||
ci = build()
|
ci = build()
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ NONSECURITY_GROUP_CONDITION = f'!{SECURITY_GROUP_CONDITION}'
|
|||||||
def create():
|
def create():
|
||||||
cd = gapi_directory.build()
|
cd = gapi_directory.build()
|
||||||
user = gam.normalizeEmailAddressOrUID(sys.argv[3])
|
user = gam.normalizeEmailAddressOrUID(sys.argv[3])
|
||||||
body = {'assignedTo': gam.convertEmailAddressToUID(user, cd)}
|
body = {'assignedTo': gam.convertEmailAddressToUID(sys.argv[3],
|
||||||
|
cd=cd,
|
||||||
|
email_type='any')}
|
||||||
role = sys.argv[4]
|
role = sys.argv[4]
|
||||||
body['roleId'] = gapi_directory_roles.getRoleId(role)
|
body['roleId'] = gapi_directory_roles.getRoleId(role)
|
||||||
body['scopeType'] = sys.argv[5].upper()
|
body['scopeType'] = sys.argv[5].upper()
|
||||||
@@ -70,7 +72,7 @@ def print_():
|
|||||||
item_fields = ['roleAssignmentId', 'roleId', 'assignedTo', 'scopeType', 'orgUnitId']
|
item_fields = ['roleAssignmentId', 'roleId', 'assignedTo', 'scopeType', 'orgUnitId']
|
||||||
titles = [
|
titles = [
|
||||||
'roleAssignmentId', 'roleId', 'role', 'assignedTo', 'assignedToUser',
|
'roleAssignmentId', 'roleId', 'role', 'assignedTo', 'assignedToUser',
|
||||||
'scopeType', 'orgUnitId', 'orgUnit'
|
'assignedToGroup', 'scopeType', 'orgUnitId', 'orgUnit'
|
||||||
]
|
]
|
||||||
csvRows = []
|
csvRows = []
|
||||||
i = 3
|
i = 3
|
||||||
@@ -107,7 +109,21 @@ def print_():
|
|||||||
admin_attrib = {}
|
admin_attrib = {}
|
||||||
for key, value in list(admin.items()):
|
for key, value in list(admin.items()):
|
||||||
if key == 'assignedTo':
|
if key == 'assignedTo':
|
||||||
admin_attrib['assignedToUser'] = gam.user_from_userid(value)
|
email_types = admin_attrib.get('assigneeType')
|
||||||
|
if email_types == 'user':
|
||||||
|
email_field = 'assignedToUser'
|
||||||
|
elif email_types == 'group':
|
||||||
|
email_field = 'assignedToGroup'
|
||||||
|
else:
|
||||||
|
email_field = None
|
||||||
|
assignment_email, assignment_type = gam.convertUIDtoEmailAddress(f'uid:{value}', cd, email_types=['user', 'group'])
|
||||||
|
if not email_field and assignment_type in ['user', 'group']:
|
||||||
|
if assignment_type == 'user':
|
||||||
|
email_field = 'assignedToUser'
|
||||||
|
else:
|
||||||
|
email_field = 'assignedToGroup'
|
||||||
|
if email_field:
|
||||||
|
admin_attrib[email_field] = assignment_email
|
||||||
elif key == 'roleId':
|
elif key == 'roleId':
|
||||||
admin_attrib['role'] = gapi_directory_roles.role_from_roleid(value)
|
admin_attrib['role'] = gapi_directory_roles.role_from_roleid(value)
|
||||||
elif key == 'orgUnitId':
|
elif key == 'orgUnitId':
|
||||||
|
|||||||
@@ -4,5 +4,5 @@ import gam
|
|||||||
def build(user=None):
|
def build(user=None):
|
||||||
if not user:
|
if not user:
|
||||||
user = gam._get_admin_email()
|
user = gam._get_admin_email()
|
||||||
userEmail = gam.convertUIDtoEmailAddress(user)
|
userEmail, _ = gam.convertUIDtoEmailAddress(user)
|
||||||
return (userEmail, gam.buildGAPIServiceObject('drive3', userEmail))
|
return (userEmail, gam.buildGAPIServiceObject('drive3', userEmail))
|
||||||
|
|||||||
@@ -511,7 +511,7 @@ def getHoldInfo():
|
|||||||
account_type = 'group' if results['corpus'] == 'GROUPS' else 'user'
|
account_type = 'group' if results['corpus'] == 'GROUPS' else 'user'
|
||||||
for i in range(0, len(results['accounts'])):
|
for i in range(0, len(results['accounts'])):
|
||||||
uid = f'uid:{results["accounts"][i]["accountId"]}'
|
uid = f'uid:{results["accounts"][i]["accountId"]}'
|
||||||
acct_email = gam.convertUIDtoEmailAddress(uid, cd, [account_type])
|
acct_email, _ = gam.convertUIDtoEmailAddress(uid, cd, [account_type])
|
||||||
results['accounts'][i]['email'] = acct_email
|
results['accounts'][i]['email'] = acct_email
|
||||||
if 'orgUnit' in results:
|
if 'orgUnit' in results:
|
||||||
results['orgUnit']['orgUnitPath'] = gapi_directory_orgunits.info(
|
results['orgUnit']['orgUnitPath'] = gapi_directory_orgunits.info(
|
||||||
@@ -792,7 +792,7 @@ def getMatterInfo():
|
|||||||
cd = gam.buildGAPIObject('directory')
|
cd = gam.buildGAPIObject('directory')
|
||||||
for i in range(0, len(result['matterPermissions'])):
|
for i in range(0, len(result['matterPermissions'])):
|
||||||
uid = f'uid:{result["matterPermissions"][i]["accountId"]}'
|
uid = f'uid:{result["matterPermissions"][i]["accountId"]}'
|
||||||
user_email = gam.convertUIDtoEmailAddress(uid, cd)
|
user_email, _ = gam.convertUIDtoEmailAddress(uid, cd)
|
||||||
result['matterPermissions'][i]['email'] = user_email
|
result['matterPermissions'][i]['email'] = user_email
|
||||||
display.print_json(result)
|
display.print_json(result)
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ from gam.var import GC_TLS_MAX_VERSION
|
|||||||
from gam.var import GC_TLS_MIN_VERSION
|
from gam.var import GC_TLS_MIN_VERSION
|
||||||
from gam.var import GC_Values
|
from gam.var import GC_Values
|
||||||
|
|
||||||
|
# Bump default retries
|
||||||
|
httplib2.RETRIES = 5
|
||||||
|
|
||||||
def create_http(cache=None,
|
def create_http(cache=None,
|
||||||
timeout=None,
|
timeout=None,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import platform
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
GAM_AUTHOR = 'Jay Lee <jay0lee@gmail.com>'
|
GAM_AUTHOR = 'Jay Lee <jay0lee@gmail.com>'
|
||||||
GAM_VERSION = '6.50'
|
GAM_VERSION = '6.57'
|
||||||
GAM_LICENSE = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
GAM_LICENSE = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
||||||
|
|
||||||
GAM_URL = 'https://jaylee.us/gam'
|
GAM_URL = 'https://jaylee.us/gam'
|
||||||
@@ -17,6 +17,18 @@ GAM_INFO = (
|
|||||||
f'Python {platform.python_version()} {sys.version_info.releaselevel} / '
|
f'Python {platform.python_version()} {sys.version_info.releaselevel} / '
|
||||||
f'{platform.platform()} {platform.machine()}')
|
f'{platform.platform()} {platform.machine()}')
|
||||||
|
|
||||||
|
# Packages we want version info available
|
||||||
|
# for "gam version extended"
|
||||||
|
GAM_VER_LIBS = ['cryptography',
|
||||||
|
'filelock',
|
||||||
|
'google-auth-httplib2',
|
||||||
|
'google-auth-oauthlib',
|
||||||
|
'google-auth',
|
||||||
|
'httplib2',
|
||||||
|
'passlib',
|
||||||
|
'python-dateutil',
|
||||||
|
'yubikey-manager',
|
||||||
|
]
|
||||||
GAM_RELEASES = 'https://github.com/GAM-team/GAM/releases'
|
GAM_RELEASES = 'https://github.com/GAM-team/GAM/releases'
|
||||||
GAM_WIKI = 'https://github.com/GAM-team/GAM/wiki'
|
GAM_WIKI = 'https://github.com/GAM-team/GAM/wiki'
|
||||||
GAM_ALL_RELEASES = 'https://api.github.com/repos/GAM-team/GAM/releases'
|
GAM_ALL_RELEASES = 'https://api.github.com/repos/GAM-team/GAM/releases'
|
||||||
@@ -62,6 +74,21 @@ SKUS = {
|
|||||||
'aliases': ['cloudsearch'],
|
'aliases': ['cloudsearch'],
|
||||||
'displayName': 'Google Cloud Search',
|
'displayName': 'Google Cloud Search',
|
||||||
},
|
},
|
||||||
|
'1010380001': {
|
||||||
|
'product': '101038',
|
||||||
|
'aliases': ['appsheetcore'],
|
||||||
|
'displayName': 'AppSheet Core',
|
||||||
|
},
|
||||||
|
'1010380002': {
|
||||||
|
'product': '101038',
|
||||||
|
'aliases': ['appsheetstandard', 'appsheetenterprisestandard'],
|
||||||
|
'displayName': 'AppSheet Enterprise Standard',
|
||||||
|
},
|
||||||
|
'1010380003': {
|
||||||
|
'product': '101038',
|
||||||
|
'aliases': ['appsheetplus', 'appsheetenterpriseplus'],
|
||||||
|
'displayName': 'AppSheet Enterprise Plus',
|
||||||
|
},
|
||||||
'1010310002': {
|
'1010310002': {
|
||||||
'product': '101031',
|
'product': '101031',
|
||||||
'aliases': ['gsefe', 'e4e', 'gsuiteenterpriseeducation'],
|
'aliases': ['gsefe', 'e4e', 'gsuiteenterpriseeducation'],
|
||||||
@@ -300,6 +327,7 @@ PRODUCTID_NAME_MAPPINGS = {
|
|||||||
'101035': 'Cloud Search',
|
'101035': 'Cloud Search',
|
||||||
'101036': 'Google Meet Global Dialing',
|
'101036': 'Google Meet Global Dialing',
|
||||||
'101037': 'G Suite Workspace for Education',
|
'101037': 'G Suite Workspace for Education',
|
||||||
|
'101038': 'AppSheet',
|
||||||
'101039': 'Assured Controls',
|
'101039': 'Assured Controls',
|
||||||
'101040': 'Beyond Corp',
|
'101040': 'Beyond Corp',
|
||||||
'Google-Apps': 'Google Workspace',
|
'Google-Apps': 'Google Workspace',
|
||||||
@@ -641,7 +669,7 @@ GOOGLEDOC_VALID_EXTENSIONS_MAP = {
|
|||||||
'.docx', '.html', '.odt', '.pdf', '.rtf', '.txt', '.zip'
|
'.docx', '.html', '.odt', '.pdf', '.rtf', '.txt', '.zip'
|
||||||
],
|
],
|
||||||
MIMETYPE_GA_PRESENTATION: ['.pdf', '.pptx', '.odp', '.txt'],
|
MIMETYPE_GA_PRESENTATION: ['.pdf', '.pptx', '.odp', '.txt'],
|
||||||
MIMETYPE_GA_SPREADSHEET: ['.csv', '.ods', '.pdf', '.xlsx', '.zip'],
|
MIMETYPE_GA_SPREADSHEET: ['.csv', '.ods', '.pdf', '.tsv', '.xlsx', '.zip'],
|
||||||
}
|
}
|
||||||
|
|
||||||
MACOS_CODENAMES = {
|
MACOS_CODENAMES = {
|
||||||
@@ -1331,7 +1359,7 @@ GC_TLS_MAX_VERSION = 'tls_max_ver'
|
|||||||
# Path to certificate authority file for validating TLS hosts
|
# Path to certificate authority file for validating TLS hosts
|
||||||
GC_CA_FILE = 'ca_file'
|
GC_CA_FILE = 'ca_file'
|
||||||
|
|
||||||
TLS_MIN = 'TLSv1_3' if hasattr(ssl.SSLContext(), 'minimum_version') else None
|
TLS_MIN = 'TLSv1_3'
|
||||||
GC_Defaults = {
|
GC_Defaults = {
|
||||||
GC_ADMIN_EMAIL: '',
|
GC_ADMIN_EMAIL: '',
|
||||||
GC_AUTO_BATCH_MIN: 0,
|
GC_AUTO_BATCH_MIN: 0,
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ google-auth-httplib2
|
|||||||
google-auth-oauthlib>=0.4.1
|
google-auth-oauthlib>=0.4.1
|
||||||
google-auth>=2.3.2
|
google-auth>=2.3.2
|
||||||
httplib2>=0.17.0
|
httplib2>=0.17.0
|
||||||
importlib.metadata; python_version < '3.8'
|
|
||||||
passlib>=1.7.2
|
passlib>=1.7.2
|
||||||
pathvalidate
|
pathvalidate
|
||||||
python-dateutil
|
python-dateutil
|
||||||
|
|||||||
@@ -13,15 +13,15 @@ keywords = google, oauth2, gsuite, google-apps, google-admin-sdk, google-drive,
|
|||||||
classifiers =
|
classifiers =
|
||||||
Programming Language :: Python :: 3
|
Programming Language :: Python :: 3
|
||||||
Programming Language :: Python :: 3 :: Only
|
Programming Language :: Python :: 3 :: Only
|
||||||
Programming Language :: Python :: 3.7
|
|
||||||
Programming Language :: Python :: 3.8
|
Programming Language :: Python :: 3.8
|
||||||
Programming Language :: Python :: 3.9
|
Programming Language :: Python :: 3.9
|
||||||
Programming Language :: Python :: 3.10
|
Programming Language :: Python :: 3.10
|
||||||
|
Programming Language :: Python :: 3.11
|
||||||
License :: OSI Approved :: Apache License
|
License :: OSI Approved :: Apache License
|
||||||
|
|
||||||
[options]
|
[options]
|
||||||
packages = find:
|
packages = find:
|
||||||
python_requires = >= 3.7
|
python_requires = >= 3.8
|
||||||
install_requires =
|
install_requires =
|
||||||
cryptography
|
cryptography
|
||||||
distro; sys_platform == 'linux'
|
distro; sys_platform == 'linux'
|
||||||
@@ -31,11 +31,10 @@ install_requires =
|
|||||||
google-auth-oauthlib >= 0.4.6
|
google-auth-oauthlib >= 0.4.6
|
||||||
google-auth >= 2.3.3
|
google-auth >= 2.3.3
|
||||||
httplib2 >= 0.20.2
|
httplib2 >= 0.20.2
|
||||||
importlib.metadata; python_version < '3.8'
|
|
||||||
passlib >= 1.7.4
|
passlib >= 1.7.4
|
||||||
python-dateutil
|
|
||||||
yubikey-manager >= 4.0.0
|
|
||||||
pathvalidate
|
pathvalidate
|
||||||
|
python-dateutil
|
||||||
|
yubikey-manager >= 5.0
|
||||||
|
|
||||||
[options.package_data]
|
[options.package_data]
|
||||||
* = *.pem
|
* = *.pem
|
||||||
|
|||||||
Reference in New Issue
Block a user