Compare commits

...

132 Commits
v6.5 ... v6.58

Author SHA1 Message Date
Jay Lee
4d79e9de4f 6.58, workaround for #1625 2023-04-26 12:42:13 +00:00
Jay Lee
1ae54db7de fix pageSize 2023-04-19 14:42:00 +00:00
Jay Lee
6d63df24a3 revert 1hr workaround. Fixes #1534 2023-04-19 14:39:59 +00:00
Jay Lee
85dd32e0ce Update build.yml 2023-04-17 08:17:43 -04:00
Jay Lee
28e418ff23 Update build.yml 2023-04-16 09:05:31 -04:00
Jay Lee
4eb89b187f Update build.yml 2023-04-14 19:43:41 -04:00
Jay Lee
c5734beef6 Update var.py 2023-04-14 18:53:52 -04:00
Jay Lee
f4735ebd80 Update build.yml 2023-04-14 16:07:45 -04:00
Jay Lee
43ae6a4a37 Update build.yml 2023-04-14 15:55:36 -04:00
Jay Lee
f362f58f95 fi 2023-04-14 18:42:59 +00:00
Jay Lee
6d211264fc Support delegated admin role assignments to groups 2023-04-14 18:35:44 +00:00
Jay Lee
3d919f5df6 fix legacy linux package name 2023-04-14 18:09:04 +00:00
Jay Lee
f9d5f9852a Update gam.spec 2023-04-12 16:49:14 -04:00
Jay Lee
0e79035765 Update build.yml 2023-04-12 16:38:59 -04:00
Jay Lee
d5cf38eaca single static list of libs 2023-04-12 14:56:31 +00:00
Jay Lee
1cfa14d8d2 only copy metadata for imports that exist 2023-04-12 14:38:24 +00:00
Jay Lee
bf5a50eb2a copy_metadata for all reqs. Fixes "gam version extended" dep outputs 2023-04-12 14:23:31 +00:00
Jay Lee
f296579aad Update transport.py 2023-04-11 10:43:21 -04:00
Jay Lee
16bb53d0e4 Update var.py 2023-04-11 09:54:43 -04:00
Jay Lee
b6e2549436 Update build.yml 2023-04-11 09:11:53 -04:00
Jay Lee
0814173210 [no ci] use default Vault export format 2023-04-11 08:18:33 -04:00
Jay Lee
375ffada5c Update build.yml 2023-04-11 08:02:52 -04:00
Jay Lee
ae37de0dd2 Update build.yml 2023-04-10 16:22:59 -04:00
Jay Lee
ce4b4771db Update build.yml 2023-04-10 15:10:20 -04:00
Jay Lee
56c61ac723 Update build.yml 2023-04-10 13:32:21 -04:00
Jay Lee
9900dd64b8 prepare 6.55 2023-04-10 17:14:36 +00:00
Jay Lee
53400b6322 Update __init__.py 2023-04-10 12:20:22 -04:00
Jay Lee
47537ab30a Update signjwt.py 2023-04-10 12:18:56 -04:00
Jay Lee
6a3692d7f4 catch signjwt refresh error on create 2023-04-10 10:21:59 -04:00
Jay Lee
eef2b95948 Only run publish on new tag starting with v 2023-04-07 09:49:02 -04:00
Jay Lee
7012bef28d package and test staticx builds as part of normal flow 2023-04-06 13:27:51 -04:00
Jay Lee
b3b44d144e GAM 6.54 2023-04-06 12:44:30 -04:00
Jay Lee
841eba79a3 Update build.yml 2023-04-06 11:02:55 -04:00
Jay Lee
77234f9e3d Update build.yml 2023-04-06 09:59:53 -04:00
Jay Lee
14478d7831 Update build.yml 2023-04-06 09:07:32 -04:00
Jay Lee
50aa7d937e Update build.yml 2023-04-06 08:07:05 -04:00
Jay Lee
2c7e01e003 Update build.yml 2023-04-06 07:01:35 -04:00
Jay Lee
a6ce5f04aa Update build.yml 2023-04-06 05:37:24 -04:00
Jay Lee
8bc6814b42 Use TLS 1.2 for SignJWT 2023-04-06 09:36:23 +00:00
Jay Lee
024177b0c7 Update __main__.py 2023-04-06 05:18:05 -04:00
Jay Lee
b7faa0acae Update build.yml 2023-04-06 05:15:35 -04:00
Ross Scroggs
0dbdbc7a13 Two updates (#1618)
* Update documentation: gam delete inboundssoassignment <SSOAssignmentSelector>

* tsv is a valid Google Doc extension
2023-04-06 05:13:50 -04:00
Jay Lee
08271e60bf Update __init__.py 2023-04-06 05:05:08 -04:00
Jay Lee
ec74698001 Update build.yml 2023-04-06 04:23:28 -04:00
Jay Lee
6cecacd334 Update __init__.py 2023-04-05 20:27:35 -04:00
Jay Lee
c3d27900e1 Update __main__.py 2023-04-05 20:16:10 -04:00
Jay Lee
f10df3607f Update __init__.py 2023-04-05 20:01:49 -04:00
Jay Lee
416be24722 Update build.yml 2023-04-05 14:55:21 -04:00
Jay Lee
e53b4a2285 backout retries 2023-04-05 18:51:03 +00:00
Jay Lee
a88320b1b2 backout sign change 2023-04-05 18:48:47 +00:00
Jay Lee
76f9a144ac debug network issues 2023-04-05 18:41:20 +00:00
Jay Lee
a673772cc1 add request to createsignjwtserviceaccount 2023-04-05 18:36:17 +00:00
Jay Lee
9e6d8195eb scope is a list, not str 2023-04-05 18:26:04 +00:00
Jay Lee
91d97c4a2c Merge branch 'main' of https://github.com/GAM-team/GAM 2023-04-05 17:18:22 +00:00
Jay Lee
5e1df9263b use transport and bump retries 2023-04-05 17:18:10 +00:00
Jay Lee
e54921ad71 rebuild for Python 3.11.3 2023-04-05 10:49:09 -04:00
Jay Lee
1b8d0877f3 retire soon-to-be EoL Python 3.7. Hello walrus operator... 2023-04-05 14:33:17 +00:00
Jay Lee
a4e962560c Update build.yml 2023-04-05 08:48:29 -04:00
Jay Lee
be7d3ceb15 [no ci] make cigroup a security group 2023-04-05 08:36:55 -04:00
Jay Lee
1e652d5725 Update build.yml 2023-04-05 08:28:50 -04:00
Jay Lee
1e7e5422be sso v1 and delete assignments 2023-04-05 12:27:28 +00:00
Jay Lee
723e9e2bb1 Update build.yml 2023-04-04 14:20:05 -04:00
Jay Lee
1f572cc95b Update build.yml 2023-04-04 14:13:19 -04:00
Jay Lee
fb63eea4a0 Update build.yml 2023-04-04 12:50:18 -04:00
Jay Lee
7efb37010d Update build.yml 2023-04-04 12:47:46 -04:00
Jay Lee
6372af8d8a Update build.yml 2023-04-04 12:18:35 -04:00
Jay Lee
0b823ea43e Update build.yml 2023-04-04 12:15:24 -04:00
Jay Lee
cebb92199f Update build.yml 2023-04-04 12:12:42 -04:00
Jay Lee
6deabf8a66 Update build.yml 2023-04-04 12:10:55 -04:00
Jay Lee
5de74a51e0 Update build.yml 2023-04-04 12:02:11 -04:00
Jay Lee
85d6305874 Update build.yml 2023-04-04 11:57:30 -04:00
Jay Lee
30d685a6f7 Update build.yml 2023-04-04 11:54:42 -04:00
Jay Lee
fcc8a58839 Update build.yml 2023-04-04 11:26:26 -04:00
Jay Lee
5a608a9b62 Update build.yml 2023-04-04 11:19:04 -04:00
Jay Lee
eb9c127a10 Update build.yml 2023-04-03 12:46:49 -04:00
Jay Lee
ed55690ff3 Update build.yml 2023-04-03 12:30:45 -04:00
Jay Lee
502afa5213 Update __init__.py 2023-04-03 12:13:34 -04:00
Jay Lee
24185d66ce Update cbcm-v1.1beta1.json 2023-04-03 11:55:41 -04:00
Jay Lee
181ba65c63 Update build.yml 2023-04-03 11:13:01 -04:00
Jay Lee
702f36a529 Update build.yml 2023-04-03 11:06:52 -04:00
Jay Lee
e2f73bf858 Update build.yml 2023-04-03 09:37:47 -04:00
Jay Lee
7265e8c6f4 Update build.yml 2023-04-03 09:26:12 -04:00
Jay Lee
b8b9808e94 Update build.yml 2023-04-03 09:02:34 -04:00
Jay Lee
7639773c40 Update build.yml 2023-04-03 08:52:54 -04:00
Jay Lee
6ab7370149 Update build.yml 2023-04-03 08:47:58 -04:00
Jay Lee
73994fe603 Update build.yml 2023-04-03 08:39:56 -04:00
Jay Lee
3fa646723d Update build.yml 2023-04-03 08:31:36 -04:00
Jay Lee
eb08b1fbdc Update build.yml 2023-04-03 08:11:44 -04:00
Jay Lee
93ac820005 Update build.yml 2023-04-03 08:03:49 -04:00
Jay Lee
c100e25ab9 Update build.yml 2023-04-03 08:01:09 -04:00
Jay Lee
716489ceed key_type is a GAMism, use type to identify SA file. 2023-04-03 08:00:12 -04:00
Jay Lee
07d5f5e52c Update build.yml 2023-04-02 17:00:18 -04:00
Jay Lee
b889debd5e Update build.yml 2023-04-02 16:57:04 -04:00
Jay Lee
b273fe1f68 Update build.yml 2023-04-02 16:54:56 -04:00
Jay Lee
376cd6e83f Update signjwt.py 2023-04-02 15:58:04 -04:00
Jay Lee
e8cb1a7b9f Update __init__.py 2023-04-02 15:53:17 -04:00
Jay Lee
9f0c5beae7 Update __init__.py 2023-04-02 15:49:02 -04:00
Jay Lee
0ea2f16322 Update build.yml 2023-04-02 15:08:49 -04:00
Jay Lee
13ca2e8d93 Update build.yml 2023-04-02 15:03:27 -04:00
Jay Lee
3833256c8c Update build.yml 2023-04-02 14:40:50 -04:00
Jay Lee
30521612b2 fix permissions 2023-04-02 14:34:56 -04:00
Jay Lee
d069cfc309 Use WIF for service account credentials 2023-04-02 14:33:15 -04:00
Jay Lee
27461b067a Update var.py 2023-03-31 09:17:10 -04:00
Jay Lee
017712742b Merge branch 'main' of https://github.com/GAM-team/GAM 2023-03-30 17:37:23 +00:00
Jay Lee
afce21a1bd Add steps to trust GAM client_ID 2023-03-30 17:34:59 +00:00
Jay Lee
030e2e270f Another attempt at fixing cryptography 2023-03-30 10:14:24 -04:00
Jay Lee
c69a86b535 use constraints.txt to prevent any downgrades during pip install 2023-03-28 08:49:03 -04:00
Jay Lee
b64e4cf3dc [no ci] that didn't work, we'll try rolling forward... 2023-03-28 08:43:57 -04:00
Jay Lee
a2e06adbbe pin cryptography to 39.0.2 for time being 2023-03-28 08:21:46 -04:00
Jay Lee
43b3397541 Rebuild for cryptography 2023-03-25 10:00:29 -04:00
Jay Lee
bd0bb1542c AppSheet licenses 2023-03-22 12:54:27 +00:00
Jay Lee
a92a07f9c0 Update var.py 2023-03-16 12:35:11 -04:00
Ross Scroggs
42ed5509ee Updates/cleanup (#1614)
* Handle socks error when checking local time offset

* Keep pylint happy

* Avoid trap when authentication flow blows up

* Fix bug that causes trap on create project
2023-03-16 12:31:20 -04:00
Jay Lee
a6582503f2 Update var.py 2023-03-15 18:31:35 -04:00
Jay Lee
7aecb889d2 Allow setting sakey validity_hours 2023-03-15 20:41:25 +00:00
Jay Lee
c273f87cc7 clear cache to pickup OpenSSL 3.1.0 2023-03-14 10:08:02 -04:00
Jay Lee
76d00c993a Update build.yml 2023-03-06 10:13:17 -05:00
Jay Lee
013b47e6e7 Update build.yml 2023-03-06 09:43:50 -05:00
Jay Lee
9f1e9934ff Update build.yml 2023-03-06 09:29:08 -05:00
Jay Lee
48b218bd9c Update build.yml 2023-03-06 08:40:59 -05:00
Jay Lee
af5baa4f3a Update build.yml 2023-03-06 08:37:37 -05:00
Jay Lee
a2cf38d904 Update build.yml 2023-03-06 07:40:01 -05:00
Jay Lee
185522d943 Update build.yml 2023-03-05 15:29:27 -05:00
Jay Lee
a42e4dd080 openssl security level 2 2023-03-05 15:28:02 -05:00
Ross Scroggs
3a5486889f Update chromepolicy.py (#1610)
split of an empty string returns [''], the API wants []
2023-03-04 11:33:22 -05:00
Jay Lee
1a1f100902 completely disalbe TLS 1.0/1.1 2023-03-03 07:09:46 -05:00
Ross Scroggs
c67b214298 Allow user to optionally specify serial number on resetpiv (#1607) 2023-02-27 11:17:20 -05:00
Ross Scroggs
3ad1d5c661 Give error on invalid subargument; handle no YubiKeys in resetpiv (#1606) 2023-02-27 10:37:22 -05:00
Jay Lee
13400d9bde Update build.yml 2023-02-24 14:20:37 -05:00
Ross Scroggs
048e8dfef5 Add gam version checkrc (#1605)
* Add gam version checkrc

* Fix code

* gam version checkrc cleanup
2023-02-24 10:58:23 -05:00
Ross Scroggs
aaf7a89192 Fix documenataion error (#1604) 2023-02-24 09:45:22 -05:00
Jay Lee
e3ee9135ff Update build.yml 2023-02-23 16:30:23 -05:00
23 changed files with 486 additions and 304 deletions

View File

@@ -6,13 +6,17 @@ on:
schedule:
- cron: '37 22 * * *'
permissions:
contents: read
id-token: write
defaults:
run:
shell: bash
working-directory: src
env:
OPENSSL_CONFIG_OPTS: no-fips
OPENSSL_CONFIG_OPTS: no-fips --api=3.0.0
OPENSSL_INSTALL_PATH: ${{ github.workspace }}/bin/ssl
OPENSSL_SOURCE_PATH: ${{ github.workspace }}/src/openssl
PYTHON_INSTALL_PATH: ${{ github.workspace }}/bin/python
@@ -25,16 +29,16 @@ jobs:
fail-fast: false
matrix:
include:
#- os: ubuntu-20.04
# jid: 1
# goal: build
# arch: x86_64
# openssl_archs: linux-x86_64
#- os: [self-hosted, linux, arm64, gcp]
# jid: 2
# goal: build
# arch: aarch64
# openssl_archs: linux-aarch64
- os: ubuntu-20.04
jid: 1
goal: build
arch: x86_64
openssl_archs: linux-x86_64
- os: [self-hosted, linux, arm64, gcp]
jid: 2
goal: build
arch: aarch64
openssl_archs: linux-aarch64
- os: ubuntu-20.04
jid: 3
goal: build
@@ -62,11 +66,6 @@ jobs:
goal: build
arch: Win32
openssl_archs: VC-WIN32
- os: ubuntu-22.04
goal: test
python: "3.7"
jid: 8
arch: x86_64
- os: ubuntu-22.04
goal: test
python: "3.8"
@@ -95,6 +94,13 @@ jobs:
persist-credentials: false
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
if: matrix.goal == 'build'
uses: actions/cache@v3
@@ -103,7 +109,7 @@ jobs:
path: |
bin.tar.xz
src/cpython
key: gam-${{ matrix.jid }}-20230208
key: gam-${{ matrix.jid }}-20230417
- name: Untar Cache archive
if: matrix.goal == 'build' && steps.cache-python-ssl.outputs.cache-hit == 'true'
@@ -117,6 +123,17 @@ jobs:
with:
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
if: matrix.goal == 'test'
env:
@@ -144,17 +161,11 @@ jobs:
sudo apt-get -qq --yes update
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
if: runner.os == 'macOS'
run: |
# 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
source $HOME/.cargo/env
# needed for Rust to compile cryptography Python package for universal2
@@ -175,7 +186,7 @@ jobs:
staticx: ${{ matrix.staticx }}
run: |
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
PYEXTERNALS_PATH="amd64"
PYBUILDRELEASE_ARCH="x64"
@@ -197,7 +208,7 @@ jobs:
PERL=perl
echo "MACOSX_DEPLOYMENT_TARGET=10.15" >> $GITHUB_ENV
echo "PYTHON=${PYTHON_INSTALL_PATH}/bin/python3" >> $GITHUB_ENV
echo "PIP_ARGS=--no-binary=:all:" >> $GITHUB_ENV
#echo "PIP_ARGS=--no-binary=:all:" >> $GITHUB_ENV
elif [[ "${RUNNER_OS}" == "Linux" ]]; then
MAKE=make
MAKEOPT="-j$(nproc)"
@@ -298,7 +309,7 @@ jobs:
rm -rf ${GITHUB_WORKSPACE}/bin/ssl-darwin64-arm64
echo "LDFLAGS=-L${OPENSSL_INSTALL_PATH}/lib" >> $GITHUB_ENV
echo "CRYPTOGRAPHY_SUPPRESS_LINK_FLAGS=1" >> $GITHUB_ENV
echo "CFLAGS=-I${OPENSSL_INSTALL_PATH}/include -arch arm64 -arch x86_64" >> $GITHUB_ENV
echo "CFLAGS=-I${OPENSSL_INSTALL_PATH}/include -arch arm64 -arch x86_64 ${CFLAGS}" >> $GITHUB_ENV
echo "ARCHFLAGS=-arch x86_64 -arch arm64" >> $GITHUB_ENV
else
cd "${GITHUB_WORKSPACE}/src/openssl-${openssl_archs}"
@@ -310,6 +321,7 @@ jobs:
if: matrix.goal == 'build'
run: |
"${OPENSSL_INSTALL_PATH}/bin/openssl" version
"${OPENSSL_INSTALL_PATH}/bin/openssl" version -f
file "${OPENSSL_INSTALL_PATH}/bin/openssl"
- name: Get latest stable Python source
@@ -407,7 +419,7 @@ jobs:
- name: Upgrade pip, wheel, etc
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}" -m pip install --upgrade pip
"${PYTHON}" -m pip install --upgrade wheel
@@ -415,8 +427,21 @@ jobs:
- name: Install pip requirements
run: |
echo "before anything..."
"${PYTHON}" -m pip list
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: \
--dest . \
--no-cache \
@@ -424,8 +449,16 @@ jobs:
--platform macosx_10_15_universal2 \
cryptography
"${PYTHON}" -m pip install --force-reinstall --no-deps cryptography*.whl
echo "after cryptography..."
"${PYTHON}" -m pip list
"${PYTHON}" -m pip install --upgrade --no-binary :all: -r requirements.txt
else
"${PYTHON}" -m pip install --upgrade -r requirements.txt
echo "after requirements..."
"${PYTHON}" -m pip list
"${PYTHON}" -m pip install --force-reinstall --no-deps --upgrade cryptography
fi
"${PYTHON}" -m pip install --upgrade -r requirements.txt ${PIP_ARGS}
echo "after everything..."
"${PYTHON}" -m pip list
- name: Install PyInstaller
@@ -434,6 +467,8 @@ jobs:
git clone https://github.com/pyinstaller/pyinstaller.git
cd pyinstaller
export latest_release=$(git tag --list | grep -v dev | grep -v rc | sort -Vr | head -n1)
git checkout "${latest_release}"
# remove pre-compiled bootloaders so we fail if bootloader compile fails
rm -rvf PyInstaller/bootloader/*-*/*
@@ -492,25 +527,6 @@ jobs:
cp -v gam-setup.bat $gampath
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
if: matrix.staticx == 'yes'
run: |
@@ -530,17 +546,31 @@ jobs:
esac
echo "ldlib=${ldlib}"
$PYTHON -m staticx -l "${ldlib}" "${gam}" "${gam}-staticx"
- name: Run StaticX
if: matrix.staticx == 'yes'
run: |
"${gam}-staticx" version extended
rm -v "${gam}"
mv -v "${gam}-staticx" "${gam}"
- name: Linux package staticx
if: matrix.staticx == 'yes'
- name: Basic Tests all jobs
id: basictests
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
- name: Windows package
@@ -583,14 +613,20 @@ jobs:
brew install gnupg
fi
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}"
echo "OAUTHFILE=${OAUTHFILE}" >> $GITHUB_ENV
export gam_user="gam-gha-${JID}@pdl.jaylee.us"
echo "gam_user=${gam_user}" >> $GITHUB_ENV
$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 oauth refresh
#$gam oauth refresh
$gam info user
export tstamp=$($PYTHON -c "import time; print(time.time_ns())")
export newbase="gha_test_${JID}_${tstamp}"
@@ -602,7 +638,9 @@ jobs:
export newou="aaaGithub Actions/${newbase}"
# 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~~"
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 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
@@ -622,20 +660,30 @@ jobs:
$gam user $newuser update photo https://dummyimage.com/400x600/000/fff
$gam user $newuser get photo
$gam user $newuser delete photo
$gam create alias $newalias user $newuser
$gam create group $newgroup name "GHA $JID group" description "This is a description" isarchived true
$gam user $gam_user sendemail recipient $newuser subject "test message $newbase" message "GHA test message"
$gam user $gam_user sendemail recipient exchange@pdl.jaylee.us subject "test ${tstamp}" message "test message"
rm "${gampath}/enabledasa.txt"
$gam user $newuser add license workspaceenterpriseplus
$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 update group $newgroup add owner $gam_user
$gam update group $newgroup add member $newuser
rm "${gampath}/enabledasa.txt"
$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 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 ""
rm "${gampath}/enabledasa.txt"
$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 update group $newgroup add member ~email
$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'))")
$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
rm "${gampath}/enabledasa.txt"
matterid=uid:$($gam create vaultmatter name "GHA matter $newbase" description "test matter" collaborators $newuser | head -1 | cut -d ' ' -f 3)
$gam create vaulthold matter $matterid name "GHA hold $newbase" corpus mail accounts $newuser
$gam print vaultmatters matterstate open
$gam print vaultholds matter $matterid
$gam print vaultcount matter $matterid corpus mail everyone todrive
$gam create vaultexport matter $matterid name "GHA export $newbase" corpus mail accounts $newuser 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~~
touch "${gampath}/enabledasa.txt"
$gam csv sample.csv gam user ~email add calendar id:$newresource
$gam delete resource $newresource
$gam delete feature Whiteboard-$newbase
$gam delete feature VC-$newbase
$gam delete building $newbuilding
$gam delete group $newgroup
$gam create alias $newalias user $newuser
rm "${gampath}/enabledasa.txt"
echo start
$gam user $newuser delete license workspaceenterpriseplus
echo finish
touch "${gampath}/enabledasa.txt"
$gam whatis $newuser
$gam user $gam_user show tokens
rm "${gampath}/enabledasa.txt"
$gam print exports matter $matterid | $gam csv - gam download export $matterid id:~~id~~
$gam delete hold "GHA hold $newbase" matter $matterid
$gam update matter $matterid action close
@@ -703,13 +757,14 @@ jobs:
# shakes off vault hold on user so we can delete
$gam print users query "email:${newuser}" orgunitpath | $gam csv - gam update user ~primaryEmail ou ~orgUnitPath
$gam user $newuser show holds
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 print users query "gha.jid=$JID" | $gam csv - gam delete user ~primaryEmail
$gam print mobile
$gam print devices
$gam print browsers
export sn="$JID$JID$JID$JID-$(openssl rand -base64 32 | sed 's/[^a-zA-Z0-9]//g')"
$gam create device serialnumber $sn devicetype android
$gam print cros allfields orderby serialnumber
$gam show crostelemetry storagepercentonly
$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 admin start -3d todrive
$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 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 print caalevels
$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 delete shareddrive "${driveid}" nukefromorbit
echo "printer model count:"
ssoprofile=$($gam create inboundssoprofile name "El Goog ${newbase}" loginurl https://www.google.com logouturl https://www.google.com changepasswordurl https://www.google.com entityid ElGoog return_name_only)
$gam create inboundssocredential profile "id:${ssoprofile}" generate_key
$gam create inboundssoassignment profile "id:${ssoprofile}" orgunit "${newou}" mode SAML_SSO
$gam delete inboundssoassignment "orgunit:${newou}"
$gam delete inboundssoprofile "id:${ssoprofile}"
$gam print printermodels | wc -l
#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 print printers
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 info printer "$printerid"
$gam delete printer "$printerid"
$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
uses: actions/upload-artifact@v3
@@ -765,6 +820,10 @@ jobs:
if: github.event_name == 'push'
runs-on: ubuntu-latest
needs: build
permissions:
contents: write
packages: write
pull-requests: read
steps:
- uses: actions/checkout@v3
@@ -782,11 +841,18 @@ jobs:
files: |
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"
name: Publish draft release
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
automatic_release_tag: latest
automatic_release_tag: "${{ steps.dateversion.outputs.dateversion }}"
prerelease: false
draft: true
files: |

View File

@@ -911,8 +911,8 @@ gam oauth|oauth2 refresh
gam <UserTypeEntity> check serviceaccount [scope|scopes <APIScopeURLList>]
gam yubikey [resetpiv]
gam rotate sakey yubikey yubikey_pin yubikey_slot AUTHENTICATION yubikeypin <String> yubikeyserialnumber <String>
gam yubikey resetpiv [yubikeyserialnumber <Number>]
gam rotate sakey yubikey yubikey_pin yubikey_slot AUTHENTICATION yubikeyserialnumber <Number>
gam create [gcpserviceaccount|signjwtserviceaccount]
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]
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]
gam delete inboundssoassignment <SSOAssignmentSelector>
gam info inboundssoassignment <SSOAssignmentSelector>
gam show inboundssoassignments
gam print inboundssoassignments [todrive]

View File

@@ -412,7 +412,7 @@
}
},
"revision": "20201203",
"rootUrl": "https://www.googleapis.com/admin/directory/v1.1beta1/customer/",
"rootUrl": "https://admin.googleapis.com/admin/directory/v1.1beta1/customer/",
"schemas": {
"ChromeBrowser": {
"id": "ChromeBrowser",

View File

@@ -1,12 +1,15 @@
# -*- mode: python ; coding: utf-8 -*-
from os import getenv
from re import search
from sys import platform
from PyInstaller.utils.hooks import copy_metadata
from gam.var import GAM_VER_LIBS
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 += [('contactdelegation-v1.json', '.')]
extra_files += [('admin-directory_v1.1beta1.json', '.')]

View File

@@ -6,10 +6,7 @@ import configparser
import csv
import datetime
from email import message_from_string
try:
from importlib.metadata import version as lib_version
except ImportError:
from importlib_metadata import version as lib_version
from importlib.metadata import version as lib_version
import io
import json
import mimetypes
@@ -589,7 +586,7 @@ def SetGlobalVariables():
GM_Globals[GM_ENABLEDASA_TXT] = os.path.join(
GC_Values[GC_CONFIG_DIR], FN_ENABLEDASA_TXT)
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
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'):
# 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),
# retry with https
# retry with https as it should be OK
badhttp = transport.create_http()
for prot in ['http', 'https']:
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'])
except (httplib2.ServerNotFoundError, RuntimeError, ValueError) as 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()))
if offset < MAX_LOCAL_GOOGLE_TIME_OFFSET and prot == 'http':
continue
@@ -660,7 +665,7 @@ def getLocalGoogleTimeOffset(testLocation='admin.googleapis.com'):
return (offset, nicetime)
def doGAMCheckForUpdates(forceCheck=False):
def doGAMCheckForUpdates(forceCheck=0):
def _gamLatestVersionNotAvailable():
if forceCheck:
@@ -703,6 +708,8 @@ def doGAMCheckForUpdates(forceCheck=False):
print(
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:
fileutils.write_file(GM_Globals[GM_LAST_UPDATE_CHECK_TXT],
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.'))
def doGAMVersion(checkForArgs=True):
force_check = extended = simple = timeOffset = False
forceCheck = 0
extended = simple = timeOffset = False
testLocation = 'admin.googleapis.com'
if checkForArgs:
i = 2
while i < len(sys.argv):
myarg = sys.argv[i].lower().replace('_', '')
if myarg == 'check':
force_check = True
forceCheck = 1
i += 1
elif myarg == 'checkrc':
forceCheck = -1
i += 1
elif myarg == 'simple':
simple = True
@@ -869,21 +880,11 @@ def doGAMVersion(checkForArgs=True):
(testLocation, nicetime))
if offset > MAX_LOCAL_GOOGLE_TIME_OFFSET:
controlflow.system_error_exit(4, 'Please fix your system time.')
if force_check:
doGAMCheckForUpdates(forceCheck=True)
if forceCheck:
doGAMCheckForUpdates(forceCheck)
if extended:
print(ssl.OPENSSL_VERSION)
libs = ['cryptography',
'filelock',
'google-auth-httplib2',
'google-auth-oauthlib',
'google-auth',
'httplib2',
'passlib',
'python-dateutil',
'yubikey-manager',
]
for lib in libs:
for lib in GAM_VER_LIBS:
try:
print(f'{lib} {lib_version(lib)}')
except:
@@ -1131,39 +1132,66 @@ def buildGAPIObjectNoAuthentication(api):
service = getService(api, httpObj)
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']):
'''convert UID to email address
returns email address and object type'''
if isinstance(email_types, str):
email_types = email_types.split(',')
normalizedEmailAddressOrUID = normalizeEmailAddressOrUID(emailAddressOrUID)
if normalizedEmailAddressOrUID.find('@') > 0:
return normalizedEmailAddressOrUID
return normalizedEmailAddressOrUID, 'email'
if not cd:
cd = buildGAPIObject('directory')
if 'user' in email_types:
try:
result = gapi.call(
cd.users(),
'get',
throw_reasons=[gapi_errors.ErrorReason.USER_NOT_FOUND],
userKey=normalizedEmailAddressOrUID,
fields='primaryEmail')
if 'primaryEmail' in result:
return result['primaryEmail'].lower()
except gapi_errors.GapiUserNotFoundError:
pass
if 'group' in email_types:
try:
result = gapi.call(
cd.groups(),
'get',
throw_reasons=[gapi_errors.ErrorReason.GROUP_NOT_FOUND],
groupKey=normalizedEmailAddressOrUID,
fields='email')
if 'email' in result:
return result['email'].lower()
except gapi_errors.GapiGroupNotFoundError:
pass
if 'user' in email_types and 'group' in email_types:
# Google User IDs *TEND* to be integers while groups tend to have letters
# thus we can optimize which check we try first. We'll still check
# both since there is no guarantee this will always be true.
if normalizedEmailAddressOrUID.isdigit():
uid = get_user_email_from_id(normalizedEmailAddressOrUID, cd)
if uid:
return uid, 'user'
uid = get_group_email_from_id(normalizedEmailAddressOrUID, cd)
if uid:
return uid, 'group'
else:
uid = get_group_email_from_id(normalizedEmailAddressOrUID, cd)
if uid:
return uid, 'group'
uid = get_user_email_from_id(normalizedEmailAddressOrUID, cd)
if uid:
return uid, 'user'
elif 'user' in email_types:
uid = get_user_email_from_id(normalizedEmailAddressOrUID, cd)
if uid:
return uid, 'user'
elif 'group' in email_types:
uid = get_group_email_from_id(normalizedEmailAddressOrUID, cd)
if uid:
return uid, 'group'
if 'resource' in email_types:
try:
result = gapi.call(
@@ -1174,10 +1202,10 @@ def convertUIDtoEmailAddress(emailAddressOrUID, cd=None, email_types=['user']):
customer=GC_Values[GC_CUSTOMER_ID],
fields='resourceEmail')
if 'resourceEmail' in result:
return result['resourceEmail'].lower()
return result['resourceEmail'].lower(), 'resource'
except gapi_errors.GapiResourceNotFoundError:
pass
return normalizedEmailAddressOrUID
return normalizedEmailAddressOrUID, 'unknown'
# Convert email address to UID
@@ -1191,12 +1219,13 @@ def convertEmailAddressToUID(emailAddressOrUID, cd=None, email_type='user'):
result = gapi.call(
cd.users(),
'get',
throw_reasons=[gapi_errors.ErrorReason.USER_NOT_FOUND],
throw_reasons=[gapi_errors.ErrorReason.USER_NOT_FOUND,
gapi_errors.ErrorReason.BAD_REQUEST],
userKey=normalizedEmailAddressOrUID,
fields='id')
if 'id' in result:
return result['id']
except gapi_errors.GapiUserNotFoundError:
except (gapi_errors.GapiUserNotFoundError, gam.gapi.errors.GapiBadRequestError):
pass
try:
result = gapi.call(
@@ -1248,27 +1277,27 @@ def buildGAPIServiceObject(api, act_as, showAuthError=True, scopes=None):
def buildAlertCenterGAPIObject(user):
userEmail = convertUIDtoEmailAddress(user)
userEmail, _ = convertUIDtoEmailAddress(user)
return (userEmail, buildGAPIServiceObject('alertcenter', userEmail))
def buildActivityGAPIObject(user):
userEmail = convertUIDtoEmailAddress(user)
userEmail, _ = convertUIDtoEmailAddress(user)
return (userEmail, buildGAPIServiceObject('driveactivity', userEmail))
def buildDriveGAPIObject(user):
userEmail = convertUIDtoEmailAddress(user)
userEmail, _ = convertUIDtoEmailAddress(user)
return (userEmail, buildGAPIServiceObject('drive', userEmail))
def buildDrive3GAPIObject(user):
userEmail = convertUIDtoEmailAddress(user)
userEmail, _ = convertUIDtoEmailAddress(user)
return (userEmail, buildGAPIServiceObject('drive3', userEmail))
def buildGmailGAPIObject(user):
userEmail = convertUIDtoEmailAddress(user)
userEmail, _ = convertUIDtoEmailAddress(user)
return (userEmail, buildGAPIServiceObject('gmail', userEmail))
@@ -2292,7 +2321,7 @@ def doGetCourseInfo():
croom = buildGAPIObject('classroom')
courseId = addCourseIdScope(sys.argv[3])
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)
teachers = gapi.get_all_pages(croom.courses().teachers(),
'list',
@@ -2477,7 +2506,7 @@ def doPrintCourses():
if ownerEmails is not None:
ownerId = course['ownerId']
if ownerId not in ownerEmails:
ownerEmails[ownerId] = convertUIDtoEmailAddress(f'uid:{ownerId}',
ownerEmails[ownerId], _ = convertUIDtoEmailAddress(f'uid:{ownerId}',
cd=cd)
course['ownerEmail'] = ownerEmails[ownerId]
for field in skipFieldsList:
@@ -7181,9 +7210,12 @@ def enable_apis():
controlflow.invalid_argument_exit(sys.argv[i],
'gam enable apis')
GAMProjectAPIs = getGAMProjectFile('project-apis.txt').splitlines()
request = signjwt.get_request()
try:
_, projectId = google.auth.default()
except google.auth.exceptions.DefaultCredentialsError as e:
_, projectId = google.auth.default(scopes=signjwt._IAM_SCOPES,
request=request)
except (google.auth.exceptions.DefaultCredentialsError,
google.auth.exceptions.RefreshError) as e:
projectId = input('Please enter your project ID: ')
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()
@@ -7412,7 +7444,7 @@ def _createClientSecretsOauth2service(httpObj, projectId, login_hint):
while True:
print(f'''Please go to:
{console_url}
{console_url}
1. Choose "Desktop App" or "Other" for "Application type".
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],
cs_data,
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.')
@@ -7791,7 +7841,7 @@ def doUpdateProjects():
_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...')
private_key = rsa.generate_private_key(public_exponent=65537,
key_size=key_size,
@@ -7808,10 +7858,16 @@ def _generatePrivateKeyAndPublicCert(client_id, key_size, b64enc_pub=True):
builder = builder.issuer_name(
x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, client_id)]))
# 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.
builder = builder.not_valid_before(datetime.datetime.utcnow() - datetime.timedelta(hours=1))
# Google uses 12/31/9999 date for end time
builder = builder.not_valid_after(datetime.datetime(9999, 12, 31, 23, 59))
# date to be UTC two minutes ago which should cover any clock skew.
now = datetime.datetime.utcnow()
builder = builder.not_valid_before(now - datetime.timedelta(minutes=2))
# 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.public_key(public_key)
builder = builder.add_extension(x509.BasicConstraints(ca=False,
@@ -7877,6 +7933,7 @@ def doShowServiceAccountKeys():
'list',
'keys',
name=name,
fields='*',
keyTypes=keyTypes)
if not keys:
print('No keys')
@@ -7891,6 +7948,28 @@ def doShowServiceAccountKeys():
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():
i = 3
if i < len(sys.argv):
@@ -7901,11 +7980,13 @@ def create_signjwt_serviceaccount():
'key_type': 'signjwt',
'token_uri': 'https://oauth2.googleapis.com/token'
}
request = signjwt.get_request()
try:
creds, sa_info['project_id'] = google.auth.default()
except google.auth.exceptions.DefaultCredentialsError as e:
creds, sa_info['project_id'] = google.auth.default(scopes=signjwt._IAM_SCOPES,
request=request)
except (google.auth.exceptions.DefaultCredentialsError,
google.auth.exceptions.RefreshError) as e:
controlflow.system_error_exit(2, e)
request = transport.create_request()
creds.refresh(request)
sa_info['client_email'] = creds.service_account_email
oa2 = buildGAPIObjectNoAuthentication('oauth2')
@@ -7915,13 +7996,13 @@ def create_signjwt_serviceaccount():
fileutils.write_file(GC_Values[GC_OAUTH2SERVICE_JSON],
sa_output,
continue_on_error=False)
def doCreateOrRotateServiceAccountKeys(iam=None,
project_id=None,
client_email=None,
client_id=None):
local_key_size = 2048
validity_hours = 0
mode = 'retainexisting'
body = {}
if iam:
@@ -7972,12 +8053,10 @@ def doCreateOrRotateServiceAccountKeys(iam=None,
new_data['yubikey_pin'] = input('Enter your YubiKey PIN: ')
i += 1
elif myarg == 'yubikeyserialnumber':
try:
new_data['yubikey_serial_number'] = int(sys.argv[i+1])
except ValueError:
controlflow.system_error_exit(
3,
'yubikey_serial_number must be a number')
getYubiKeySerialNumber(new_data, sys.argv[i+1])
i += 2
elif myarg == 'validityhours':
validity_hours = int(sys.argv[i + 1])
i += 2
elif myarg in ['retainnone', 'retainexisting', 'replacecurrent']:
mode = myarg
@@ -7999,7 +8078,7 @@ def doCreateOrRotateServiceAccountKeys(iam=None,
elif local_key_size:
# Generate private key locally, store in file
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'
for key in list(new_data):
if key.startswith('yubikey_'):
@@ -11813,6 +11892,8 @@ def ProcessGAMCommand(args):
gapi_cloudidentity_inboundsso.delete_profile()
elif argument in ['inboundssocredential', 'inboundssocredentials']:
gapi_cloudidentity_inboundsso.delete_credentials()
elif argument in ['inboundssoassignment', 'inboundssoassignments']:
gapi_cloudidentity_inboundsso.delete_assignment()
elif argument == 'resource':
gapi_directory_resource.deleteResourceCalendar()
elif argument == 'mobile':
@@ -11877,6 +11958,8 @@ def ProcessGAMCommand(args):
argument = sys.argv[2].lower()
if argument in ['browsertoken', 'browserokens']:
gapi_cbcm.revoketoken()
else:
controlflow.invalid_argument_exit(argument, 'gam revoke')
sys.exit(0)
elif command in ['close', 'reopen']:
# 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()
if argument in ['browser', 'browsers']:
gapi_cbcm.move()
else:
controlflow.invalid_argument_exit(argument, 'gam move')
sys.exit(0)
elif command in ['oauth', 'oauth2']:
argument = sys.argv[2].lower()
@@ -12138,6 +12223,8 @@ def ProcessGAMCommand(args):
argument = sys.argv[2].lower()
if argument in ['isinvitable', 'userinvitation', 'userinvitations']:
gapi_cloudidentity_userinvitations.check()
else:
controlflow.invalid_argument_exit(argument, 'gam check')
sys.exit(0)
elif command in ['cancelwipe', 'wipe', 'approve', 'block', 'sync']:
target = sys.argv[2].lower().replace('_', '')
@@ -12157,6 +12244,8 @@ def ProcessGAMCommand(args):
gapi_cloudidentity_devices.approve_user()
elif command == 'block':
gapi_cloudidentity_devices.block_user()
else:
controlflow.invalid_argument_exit(target, f'gam {command}')
sys.exit(0)
elif command in ['issuecommand', 'getcommand']:
target = sys.argv[2].lower().replace('_', '')
@@ -12165,18 +12254,22 @@ def ProcessGAMCommand(args):
gapi_directory_cros.issue_command()
elif command == 'getcommand':
gapi_directory_cros.get_command()
else:
controlflow.invalid_argument_exit(target, f'gam {command}')
sys.exit(0)
elif command in ['yubikey']:
action = sys.argv[2].lower().replace('_', '')
if action == 'resetpiv':
yk = yubikey.YubiKey()
yk.serial_number = yk.get_serial_number()
yk.reset_piv()
doResetYubiKeyPIV()
else:
controlflow.invalid_argument_exit(action, f'gam yubikey')
sys.exit(0)
elif command == 'enable':
enable_what = sys.argv[2].lower().replace('_', '')
if enable_what in ['api', 'apis']:
enable_apis()
else:
controlflow.invalid_argument_exit(enable_what, 'gam enable')
sys.exit(0)
users = getUsersToModify()
command = sys.argv[3].lower()

View File

@@ -22,25 +22,33 @@ For more information, see https://jaylee.us/gam
"""
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():
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()
if sys.platform == 'darwin':
# https://bugs.python.org/issue33725 in Python 3.8.0 seems
# to break parallel operations with errors about extra -b
# command line arguments
from multiprocessing import set_start_method
set_start_method('fork')
if sys.version_info[0] < 3 or sys.version_info[1] < 7:
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])
import gam
sys.exit(gam.ProcessGAMCommand(sys.argv))

View File

@@ -29,9 +29,11 @@ def get_admin_credentials_filename():
# some custom name in it. Otherwise, just use the default name.
if GC_Values[GC_ENABLE_DASA]:
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):
"""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:
creds_data = json.load(f)
# 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/'
key_type = creds_data.get('key_type', 'default')
if key_type == 'default':
return JWTCredentials.from_service_account_info(creds_data,
audience=audience)
elif key_type == 'yubikey':
if key_type == 'yubikey':
yksigner = yubikey.YubiKey(creds_data)
return JWTCredentials._from_signer_and_info(yksigner,
creds_data,
audience=audience)
elif key_type == 'signjwt':
if key_type == 'signjwt':
sjsigner = signjwt.SignJwt(creds_data)
return signjwt.JWTCredentials._from_signer_and_info(sjsigner,
creds_data,

View File

@@ -50,6 +50,7 @@ MESSAGE_LOCAL_SERVER_SUCCESS = ('The authentication flow has completed. You may'
' close this browser window and return to GAM.')
MESSAGE_AUTHENTICATION_COMPLETE = ('\nThe authentication flow has completed.\n')
MESSAGE_AUTHENTICATION_FAILED = ('\nThe authentication flow failed, reissue command')
class CredentialsError(Exception):
@@ -629,15 +630,22 @@ class _ShortURLFlow(google_auth_oauthlib.flow.InstalledAppFlow):
print(MESSAGE_CONSOLE_AUTHORIZATION_PROMPT.format(url=d['auth_url']))
user_input.start()
userInput = False
while True:
alive = 2
while alive > 0:
sleep(0.1)
if not http_client.is_alive():
user_input.terminate()
break
elif not user_input.is_alive():
if 'code' in d:
user_input.terminate()
break
alive -= 1
if not user_input.is_alive():
userInput = True
http_client.terminate()
break
if 'code' in d:
http_client.terminate()
break
alive -= 1
if 'code' not in d:
controlflow.system_error_exit(8, MESSAGE_AUTHENTICATION_FAILED)
while True:
code = d['code']
if code.startswith('http'):

View File

@@ -14,6 +14,13 @@ from gam.var import GM_Globals, GM_CACHE_DIR
_DEFAULT_TOKEN_LIFETIME_SECS = 3600 # 1 hour in seconds
_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):
@@ -73,9 +80,12 @@ class SignJwt(google.auth.crypt.Signer):
def sign(self, message):
''' Call IAM Credentials SignJWT API to get our signed JWT '''
request = get_request()
try:
credentials, _ = google.auth.default()
except google.auth.exceptions.DefaultCredentialsError as e:
credentials, _ = google.auth.default(scopes=_IAM_SCOPES,
request=request)
except (google.auth.exceptions.DefaultCredentialsError,
google.auth.exceptions.RefreshError) as e:
controlflow.system_error_exit(2, e)
httpObj = transport.AuthorizedHttp(
credentials,

View File

@@ -87,6 +87,9 @@ class YubiKey():
def get_serial_number(self):
try:
devices = list_all_devices()
if not devices:
msg = f'Could not find any YubiKey'
controlflow.system_error_exit(3, msg)
if self.serial_number:
for (device, info) in devices:
if info.serial == self.serial_number:

View File

@@ -18,9 +18,9 @@ def normalizeCalendarId(calname, checkPrimary=False):
return calname
if not GC_Values[GC_DOMAIN]:
GC_Values[GC_DOMAIN] = gam._getValueFromOAuth('hd')
return gam.convertUIDtoEmailAddress(calname,
email, _ = gam.convertUIDtoEmailAddress(calname,
email_types=['user', 'resource'])
return email
def buildCalendarGAPIObject(calname):
calendarId = normalizeCalendarId(calname)

View File

@@ -408,7 +408,7 @@ def update_policy():
f'{expected_enums}, got {value}'
controlflow.system_error_exit(8, msg)
elif vtype in ['TYPE_LIST']:
value = value.split(',')
value = value.split(',') if value else []
if myarg == 'chrome.users.chromebrowserupdates' and \
cased_field == 'targetVersionPrefixSetting':
mg = re.compile(r'^([a-z]+)-(\d+)$').match(value)

View File

@@ -253,13 +253,6 @@ def update_state():
def print_():
# This function is rather messy thanks to
# https://github.com/GAM-team/GAM/issues/1534
# I'd prefer to keep it all in this function for now but if:
# - we find other list() operations that also hit this bug OR
# - it looks like this issue is going to exist on Google's side
# for a long time.
# I'll enterain some cleanup here to "functionalize" (yuck) all of this.
ci = gapi_cloudidentity.build_dwd()
customer = _get_device_customerid()
parent = 'devices/-'
@@ -267,8 +260,6 @@ def print_():
get_device_users = True
view = None
orderByList = []
# default sort order needed by our 1 hour bug workaround
orderBy = 'create_time'
titles = []
csvRows = []
todrive = False
@@ -328,97 +319,26 @@ def print_():
}
if orderByList:
orderBy = ','.join(orderByList)
custom_device_filter = bool(device_filter)
# we store the devices in a dict keyed by name which is a unique ID.
# that way when we get duplicate devices we just overwrite the name
# with the latest copy we saw.
devices = {}
else:
orderBy = None
devices = []
page_message = gapi.got_total_items_msg(view_name_map[view], '...\n')
pageToken = None
newest_device_date = ''
total_items = 0
while True:
try:
a_page = gapi.call(ci.devices(),
'list',
customer=customer,
pageSize=100,
pageToken=pageToken,
filter=device_filter,
view=view,
orderBy=orderBy,
throw_reasons=[gapi_errors.ErrorReason.FOUR_O_O])
except googleapiclient.errors.HttpError:
sys.stderr.write('WARNING: GAM hit Google internal bug 237397223. Please file a Google Support ticket stating that you are encountering this bug.\n')
if orderBy != 'create_time' or custom_device_filter:
controlflow.system_error_exit(5, 'GAM workaround for this issue only works if filter and orderby arguments are not used.\n')
sys.stderr.write(f' attempting to work around the bug by filtering for devices created on or after the newest we\'ve seen ({newest_device_date})...')
device_filter = f'register:{newest_device_date}..'
pageToken = None
continue
for dev in a_page.get('devices', []):
total_items += 1
devices[dev['name']] = dev
dev_date = dev.get('createTime', '')
# remove the Z
dev_date = dev_date[:-1]
# remove microseconds
dev_date = dev_date.split('.')[0]
if dev_date > newest_device_date:
newest_device_date = dev_date
pageToken = a_page.get('nextPageToken')
if not pageToken:
break
sys.stderr.write(page_message.replace('%%total_items%%', str(total_items)))
devices += gapi.get_all_pages(ci.devices(), 'list', 'devices',
customer=customer, page_message=page_message,
pageSize=100, filter=device_filter, view=view, orderBy=orderBy)
if get_device_users:
page_message = gapi.got_total_items_msg('Device Users', '...\n')
pageToken = None
newest_deviceuser_date = ''
total_items = 0
device_users = {}
if not custom_device_filter:
device_filter = None
while True:
try:
a_page = gapi.call(ci.devices().deviceUsers(),
'list',
customer=customer,
parent=parent,
pageSize=20,
orderBy=orderBy,
filter=device_filter,
pageToken=pageToken,
throw_reasons=[gapi_errors.ErrorReason.FOUR_O_O])
except googleapiclient.errors.HttpError:
sys.stderr.write('WARNING: GAM hit Google internal bug 237397223. Please file a Google Support ticket stating that you are encountering this bug.\n')
if orderBy != 'create_time' or custom_device_filter:
controlflow.system_error_exit(5, 'GAM workaround for this issue only works if filter and orderby arguments are not used.\n')
sys.stderr.write(f' attempting to work around the bug by filtering for device users created on or after the newest we\'ve seen ({newest_deviceuser_date})...')
device_filter = f'register:{newest_deviceuser_date}..'
pageToken = None
continue
for device_user in a_page.get('deviceUsers', []):
total_items += 1
dev_date = device_user.get('createTime', '')
# remove the Z
dev_date = dev_date[:-1]
# remove microseconds
dev_date = dev_date.split('.')[0]
if dev_date > newest_deviceuser_date:
newest_deviceuser_date = dev_date
deviceuser_name = device_user['name']
device_users[deviceuser_name] = device_user
pageToken = a_page.get('nextPageToken')
if not pageToken:
break
sys.stderr.write(page_message.replace('%%total_items%%', str(total_items)))
for deviceuser_name, device_user in device_users.items():
device_id = deviceuser_name.split('/')[1]
device_name = f'devices/{device_id}'
if 'users' not in devices[device_name]:
devices[device_name]['users'] = []
devices[device_name]['users'].append(device_user)
for device in devices.values():
device_users = gapi.get_all_pages(ci.devices().deviceUsers(), 'list',
'deviceUsers', customer=customer, parent=parent,
page_message=page_message, pageSize=20, filter=device_filter)
for device_user in device_users:
for device in devices:
if device_user.get('name').startswith(device.get('name')):
if 'users' not in device:
device['users'] = []
device['users'].append(device_user)
break
for device in devices:
device = utils.flatten_json(device)
for a_key in device:
if a_key not in titles:

View File

@@ -230,7 +230,7 @@ def print_():
todrive = True
i += 1
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"
i += 2
elif myarg == 'delimiter':
@@ -501,7 +501,7 @@ def print_members():
)
i += 2
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"
i += 2
elif myarg in ['cigroup', 'cigroups']:
@@ -876,6 +876,13 @@ def update():
'cloudidentity.googleapis.com/groups.discussion_forum': ''
}
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':
body['labels'] = {
'cloudidentity.googleapis.com/groups.dynamic': '',

View File

@@ -35,7 +35,7 @@ def get_orgunit_id(orgunit):
'''build Cloud Identity API'''
def build():
return gapi_cloudidentity.build('cloudidentity_beta')
return gapi_cloudidentity.build('cloudidentity')
'''parse cmd for profile create/update'''
@@ -484,6 +484,16 @@ def update_assignment():
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'''
def info_assignment():
ci = build()

View File

@@ -16,7 +16,9 @@ NONSECURITY_GROUP_CONDITION = f'!{SECURITY_GROUP_CONDITION}'
def create():
cd = gapi_directory.build()
user = gam.normalizeEmailAddressOrUID(sys.argv[3])
body = {'assignedTo': gam.convertEmailAddressToUID(user, cd)}
body = {'assignedTo': gam.convertEmailAddressToUID(sys.argv[3],
cd=cd,
email_type='any')}
role = sys.argv[4]
body['roleId'] = gapi_directory_roles.getRoleId(role)
body['scopeType'] = sys.argv[5].upper()
@@ -70,7 +72,7 @@ def print_():
item_fields = ['roleAssignmentId', 'roleId', 'assignedTo', 'scopeType', 'orgUnitId']
titles = [
'roleAssignmentId', 'roleId', 'role', 'assignedTo', 'assignedToUser',
'scopeType', 'orgUnitId', 'orgUnit'
'assignedToGroup', 'scopeType', 'orgUnitId', 'orgUnit'
]
csvRows = []
i = 3
@@ -107,7 +109,21 @@ def print_():
admin_attrib = {}
for key, value in list(admin.items()):
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':
admin_attrib['role'] = gapi_directory_roles.role_from_roleid(value)
elif key == 'orgUnitId':

View File

@@ -4,5 +4,5 @@ import gam
def build(user=None):
if not user:
user = gam._get_admin_email()
userEmail = gam.convertUIDtoEmailAddress(user)
userEmail, _ = gam.convertUIDtoEmailAddress(user)
return (userEmail, gam.buildGAPIServiceObject('drive3', userEmail))

View File

@@ -204,6 +204,7 @@ def print_(returnFields=None,
],
page_message=page_message,
customerId=customer_id,
maxResults=100,
productId=product,
skuId=sku,
fields=fields)
@@ -234,6 +235,7 @@ def print_(returnFields=None,
],
page_message=page_message,
customerId=customer_id,
maxResults=100,
productId=productId,
fields=fields)
if countsOnly:

View File

@@ -511,7 +511,7 @@ def getHoldInfo():
account_type = 'group' if results['corpus'] == 'GROUPS' else 'user'
for i in range(0, len(results['accounts'])):
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
if 'orgUnit' in results:
results['orgUnit']['orgUnitPath'] = gapi_directory_orgunits.info(
@@ -792,7 +792,7 @@ def getMatterInfo():
cd = gam.buildGAPIObject('directory')
for i in range(0, len(result['matterPermissions'])):
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
display.print_json(result)

View File

@@ -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_Values
# Bump default retries
httplib2.RETRIES = 5
def create_http(cache=None,
timeout=None,

View File

@@ -8,7 +8,7 @@ import platform
import re
GAM_AUTHOR = 'Jay Lee <jay0lee@gmail.com>'
GAM_VERSION = '6.50'
GAM_VERSION = '6.58'
GAM_LICENSE = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
GAM_URL = 'https://jaylee.us/gam'
@@ -17,6 +17,18 @@ GAM_INFO = (
f'Python {platform.python_version()} {sys.version_info.releaselevel} / '
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_WIKI = 'https://github.com/GAM-team/GAM/wiki'
GAM_ALL_RELEASES = 'https://api.github.com/repos/GAM-team/GAM/releases'
@@ -62,6 +74,21 @@ SKUS = {
'aliases': ['cloudsearch'],
'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': {
'product': '101031',
'aliases': ['gsefe', 'e4e', 'gsuiteenterpriseeducation'],
@@ -300,6 +327,7 @@ PRODUCTID_NAME_MAPPINGS = {
'101035': 'Cloud Search',
'101036': 'Google Meet Global Dialing',
'101037': 'G Suite Workspace for Education',
'101038': 'AppSheet',
'101039': 'Assured Controls',
'101040': 'Beyond Corp',
'Google-Apps': 'Google Workspace',
@@ -641,7 +669,7 @@ GOOGLEDOC_VALID_EXTENSIONS_MAP = {
'.docx', '.html', '.odt', '.pdf', '.rtf', '.txt', '.zip'
],
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 = {
@@ -1331,7 +1359,7 @@ GC_TLS_MAX_VERSION = 'tls_max_ver'
# Path to certificate authority file for validating TLS hosts
GC_CA_FILE = 'ca_file'
TLS_MIN = 'TLSv1_3' if hasattr(ssl.SSLContext(), 'minimum_version') else None
TLS_MIN = 'TLSv1_3'
GC_Defaults = {
GC_ADMIN_EMAIL: '',
GC_AUTO_BATCH_MIN: 0,

View File

@@ -6,7 +6,6 @@ google-auth-httplib2
google-auth-oauthlib>=0.4.1
google-auth>=2.3.2
httplib2>=0.17.0
importlib.metadata; python_version < '3.8'
passlib>=1.7.2
pathvalidate
python-dateutil

View File

@@ -13,15 +13,15 @@ keywords = google, oauth2, gsuite, google-apps, google-admin-sdk, google-drive,
classifiers =
Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
License :: OSI Approved :: Apache License
[options]
packages = find:
python_requires = >= 3.7
python_requires = >= 3.8
install_requires =
cryptography
distro; sys_platform == 'linux'
@@ -31,11 +31,10 @@ install_requires =
google-auth-oauthlib >= 0.4.6
google-auth >= 2.3.3
httplib2 >= 0.20.2
importlib.metadata; python_version < '3.8'
passlib >= 1.7.4
python-dateutil
yubikey-manager >= 4.0.0
pathvalidate
python-dateutil
yubikey-manager >= 5.0
[options.package_data]
* = *.pem