mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-28 09:51:36 +00:00
Compare commits
67 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a00256ee9f | ||
|
|
970697ec65 | ||
|
|
06d131ec55 | ||
|
|
094616e482 | ||
|
|
cbfae9226a | ||
|
|
c35cdcf4c3 | ||
|
|
d87ece177d | ||
|
|
41535666b6 | ||
|
|
20d1b18009 | ||
|
|
c27e48dd5c | ||
|
|
44fe8a22e0 | ||
|
|
21df315887 | ||
|
|
3a7c470e6e | ||
|
|
22feec5136 | ||
|
|
a30f8c325f | ||
|
|
0770c20992 | ||
|
|
cc7fb0df7b | ||
|
|
fec061c250 | ||
|
|
aef3b23061 | ||
|
|
3518fc8ad3 | ||
|
|
e5dab74336 | ||
|
|
3aef51781e | ||
|
|
23deb2526c | ||
|
|
1a77bbf706 | ||
|
|
40b8e58266 | ||
|
|
733110120a | ||
|
|
68b8c9108d | ||
|
|
9ddeaba79b | ||
|
|
582c1e4fa2 | ||
|
|
28383c1391 | ||
|
|
7ab959f27c | ||
|
|
a877ca6139 | ||
|
|
33da8016a2 | ||
|
|
36b2849f20 | ||
|
|
377201614b | ||
|
|
ff11bf33d1 | ||
|
|
59463dcc9a | ||
|
|
23b5eb6fd6 | ||
|
|
8f4aa19f13 | ||
|
|
6410691c0e | ||
|
|
1ad1f9b96c | ||
|
|
636e8dd11e | ||
|
|
caeab48dda | ||
|
|
1364991e5d | ||
|
|
6bdea8fa54 | ||
|
|
da24220d87 | ||
|
|
a7d537ebe2 | ||
|
|
74b285959c | ||
|
|
c138bc44f0 | ||
|
|
13f56afcd2 | ||
|
|
ba8242f480 | ||
|
|
01985d4381 | ||
|
|
77511b79c9 | ||
|
|
06f653db8f | ||
|
|
cea2099537 | ||
|
|
67a6d3f4de | ||
|
|
7987a94aab | ||
|
|
e6494b6747 | ||
|
|
401f5095e1 | ||
|
|
93323d12d3 | ||
|
|
c3e23fabf1 | ||
|
|
95eb36c5c2 | ||
|
|
17105d51f1 | ||
|
|
8dbc455407 | ||
|
|
7e7b8416a4 | ||
|
|
ed81501bf2 | ||
|
|
428f8f5987 |
534
.github/workflows/build.yml
vendored
534
.github/workflows/build.yml
vendored
@@ -86,18 +86,23 @@ jobs:
|
|||||||
freethreaded: false
|
freethreaded: false
|
||||||
goal: build
|
goal: build
|
||||||
name: Build x86_64 macOS 15
|
name: Build x86_64 macOS 15
|
||||||
- os: macos-26
|
- os: macos-26-intel
|
||||||
jid: 11
|
jid: 11
|
||||||
freethreaded: false
|
freethreaded: false
|
||||||
goal: build
|
goal: build
|
||||||
|
name: Build x86_64 macOS 26
|
||||||
|
- os: macos-26
|
||||||
|
jid: 12
|
||||||
|
freethreaded: false
|
||||||
|
goal: build
|
||||||
name: Build Arm MacOS 26
|
name: Build Arm MacOS 26
|
||||||
- os: windows-2025-vs2026
|
- os: windows-2025-vs2026
|
||||||
jid: 12
|
jid: 13
|
||||||
freethreaded: false
|
freethreaded: false
|
||||||
goal: build
|
goal: build
|
||||||
name: Build Intel Windows
|
name: Build Intel Windows
|
||||||
- os: windows-11-arm
|
- os: windows-11-arm
|
||||||
jid: 13
|
jid: 14
|
||||||
freethreaded: false
|
freethreaded: false
|
||||||
goal: build
|
goal: build
|
||||||
name: Build Arm Windows
|
name: Build Arm Windows
|
||||||
@@ -105,36 +110,42 @@ jobs:
|
|||||||
goal: test
|
goal: test
|
||||||
python: "3.10"
|
python: "3.10"
|
||||||
freethreaded: false
|
freethreaded: false
|
||||||
jid: 14
|
jid: 15
|
||||||
name: Test Python 3.10
|
name: Test Python 3.10
|
||||||
- os: ubuntu-24.04
|
- os: ubuntu-24.04
|
||||||
goal: test
|
goal: test
|
||||||
python: "3.11"
|
python: "3.11"
|
||||||
freethreaded: false
|
freethreaded: false
|
||||||
jid: 15
|
jid: 16
|
||||||
name: Test Python 3.11
|
name: Test Python 3.11
|
||||||
- os: ubuntu-24.04
|
- os: ubuntu-24.04
|
||||||
goal: test
|
goal: test
|
||||||
python: "3.12"
|
python: "3.12"
|
||||||
freethreaded: false
|
freethreaded: false
|
||||||
jid: 16
|
jid: 17
|
||||||
name: Test Python 3.12
|
name: Test Python 3.12
|
||||||
|
- os: ubuntu-24.04
|
||||||
|
goal: test
|
||||||
|
python: "3.13"
|
||||||
|
freethreaded: false
|
||||||
|
jid: 18
|
||||||
|
name: Test Python 3.13
|
||||||
- os: ubuntu-24.04
|
- os: ubuntu-24.04
|
||||||
goal: test
|
goal: test
|
||||||
python: "3.15-dev"
|
python: "3.15-dev"
|
||||||
freethreaded: false
|
freethreaded: false
|
||||||
jid: 17
|
jid: 19
|
||||||
name: Test Python 3.15-dev
|
name: Test Python 3.15-dev
|
||||||
- os: ubuntu-24.04
|
- os: ubuntu-24.04
|
||||||
goal: test
|
goal: test
|
||||||
python: "3.14"
|
python: "3.14"
|
||||||
freethreaded: true
|
freethreaded: true
|
||||||
jid: 18
|
jid: 20
|
||||||
name: Test Python 3.14 freethread
|
name: Test Python 3.14 freethread
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
@@ -148,12 +159,12 @@ jobs:
|
|||||||
|
|
||||||
- name: Cache multiple paths
|
- name: Cache multiple paths
|
||||||
if: matrix.goal == 'build'
|
if: matrix.goal == 'build'
|
||||||
uses: actions/cache@638ed79f9dc94c1de1baef91bcab5edaa19451f4 # v4.2.4
|
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
|
||||||
id: cache-python-ssl
|
id: cache-python-ssl
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
cache.tar.xz
|
cache.tar.xz
|
||||||
key: gam-${{ matrix.jid }}-20260213
|
key: gam-${{ matrix.jid }}-20260227
|
||||||
|
|
||||||
- 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'
|
||||||
@@ -163,7 +174,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Use pre-compiled Python for testing
|
- name: Use pre-compiled Python for testing
|
||||||
if: matrix.python != ''
|
if: matrix.python != ''
|
||||||
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python }}
|
python-version: ${{ matrix.python }}
|
||||||
allow-prereleases: true
|
allow-prereleases: true
|
||||||
@@ -247,7 +258,7 @@ jobs:
|
|||||||
|
|
||||||
- name: MacOS import developer certificates for signing
|
- name: MacOS import developer certificates for signing
|
||||||
if: runner.os == 'macOS'
|
if: runner.os == 'macOS'
|
||||||
uses: apple-actions/import-codesign-certs@11e1bb2d3771ad8ffa8459dfe527bc26b2dd4b62 # v5.0.3
|
uses: apple-actions/import-codesign-certs@b610f78488812c1e56b20e6df63ec42d833f2d14 # v6.0.0
|
||||||
with:
|
with:
|
||||||
p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
|
p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
|
||||||
p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}
|
p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}
|
||||||
@@ -471,7 +482,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Run Python
|
- name: Run Python
|
||||||
run: |
|
run: |
|
||||||
"${PYTHON}" -V
|
"${PYTHON}" -VV
|
||||||
"${PYTHON}" -c "import ssl; print(f'Using {ssl.OPENSSL_VERSION}')"
|
"${PYTHON}" -c "import ssl; print(f'Using {ssl.OPENSSL_VERSION}')"
|
||||||
|
|
||||||
- name: Create and use Python venv
|
- name: Create and use Python venv
|
||||||
@@ -732,7 +743,7 @@ jobs:
|
|||||||
$gam create signjwtserviceaccount
|
$gam create signjwtserviceaccount
|
||||||
|
|
||||||
- name: Attest gam executable was generated from this Action
|
- name: Attest gam executable was generated from this Action
|
||||||
uses: actions/attest-build-provenance@0b6e9809265278d02c58acf52849a95818a5a306 # v3.0.0
|
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
|
||||||
if: matrix.goal == 'build'
|
if: matrix.goal == 'build'
|
||||||
with:
|
with:
|
||||||
subject-path: ${{ env.gam }}
|
subject-path: ${{ env.gam }}
|
||||||
@@ -753,10 +764,10 @@ jobs:
|
|||||||
echo "GAM Archive ${GAM_ARCHIVE}"
|
echo "GAM Archive ${GAM_ARCHIVE}"
|
||||||
tar -C "${gampath}/.." --create --verbose --exclude-from "${GITHUB_WORKSPACE}/.github/actions/package_exclusions.txt" --file $GAM_ARCHIVE --xz gam7
|
tar -C "${gampath}/.." --create --verbose --exclude-from "${GITHUB_WORKSPACE}/.github/actions/package_exclusions.txt" --file $GAM_ARCHIVE --xz gam7
|
||||||
|
|
||||||
- name: Install Wix on Win ARM64
|
# - name: Install Wix on Win ARM64
|
||||||
if: runner.os == 'Windows' && runner.arch == 'ARM64'
|
# if: runner.os == 'Windows' && runner.arch == 'ARM64'
|
||||||
run: |
|
# run: |
|
||||||
choco install wixtoolset
|
# choco install wixtoolset
|
||||||
|
|
||||||
- name: Windows package zip
|
- name: Windows package zip
|
||||||
if: runner.os == 'Windows' && matrix.goal != 'test'
|
if: runner.os == 'Windows' && matrix.goal != 'test'
|
||||||
@@ -767,54 +778,80 @@ jobs:
|
|||||||
GAM_ARCHIVE="${GITHUB_WORKSPACE}/gam-${GAMVERSION}-windows-${arch}.zip"
|
GAM_ARCHIVE="${GITHUB_WORKSPACE}/gam-${GAMVERSION}-windows-${arch}.zip"
|
||||||
/c/Program\ Files/7-Zip/7z.exe a -tzip "$GAM_ARCHIVE" gam7 "-xr@${GITHUB_WORKSPACE}/.github/actions/package_exclusions.txt" -bb3
|
/c/Program\ Files/7-Zip/7z.exe a -tzip "$GAM_ARCHIVE" gam7 "-xr@${GITHUB_WORKSPACE}/.github/actions/package_exclusions.txt" -bb3
|
||||||
|
|
||||||
- name: Windows package MSI
|
- name: Windows package exe with Inno Setup
|
||||||
if: runner.os == 'Windows' && matrix.goal != 'test'
|
if: runner.os == 'Windows' && matrix.goal != 'test'
|
||||||
run: |
|
run: |
|
||||||
export MSI_FILENAME="${GITHUB_WORKSPACE}/gam-${GAMVERSION}-windows-${arch}.msi"
|
choco install innosetup
|
||||||
# auto-generate a lib.wxs based on the files PyInstaller created for the lib/ directory
|
export signtool="C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\signtool.exe"
|
||||||
/c/Program\ Files\ \(x86\)/WiX\ Toolset\ v3.14/bin/heat.exe dir "${gampath}/lib" -ke -srd -cg Lib -gg -dr lib -directoryid lib -out lib.wxs
|
iscc \
|
||||||
$PYTHON tools/gen-wix-xml-filelist.py lib.wxs
|
/S"gamsigntool=$signtool sign /sha1 $WINDOWS_CODESIGN_CERT_HASH /tr http://time.certum.pl /td SHA256 /fd SHA256 /v \$f" \
|
||||||
echo "-- begin lib.wxs --"
|
/O"$GITHUB_WORKSPACE" \
|
||||||
cat lib.wxs
|
gam.iss
|
||||||
echo "-- end lib.wxs --"
|
|
||||||
/c/Program\ Files\ \(x86\)/WiX\ Toolset\ v3.14/bin/candle.exe -arch "${WIX_ARCH}" gam.wxs lib.wxs
|
|
||||||
/c/Program\ Files\ \(x86\)/WiX\ Toolset\ v3.14/bin/light.exe -ext /c/Program\ Files\ \(x86\)/WiX\ Toolset\ v3.14/bin/WixUIExtension.dll gam.wixobj lib.wixobj -b "${gampath}/lib" -o "$MSI_FILENAME" || true;
|
|
||||||
rm -v -f *.wixpdb
|
|
||||||
rm -v -f *.wixobj
|
|
||||||
echo "MSI_FILENAME=${MSI_FILENAME}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Sign GAM MSI
|
#- name: Windows package MSI
|
||||||
if: runner.os == 'Windows'
|
# if: runner.os == 'Windows' && matrix.goal != 'test'
|
||||||
shell: pwsh
|
# run: |
|
||||||
run: |
|
# export MSI_FILENAME="${GITHUB_WORKSPACE}/gam-${GAMVERSION}-windows-${arch}.msi"
|
||||||
write-Host "Signing ${env:MSI_FILENAME}...."
|
# # auto-generate a lib.wxs based on the files PyInstaller created for the lib/ directory
|
||||||
|
# /c/Program\ Files\ \(x86\)/WiX\ Toolset\ v3.14/bin/heat.exe dir "${gampath}/lib" -ke -srd -cg Lib -gg -dr lib -directoryid lib -out lib.wxs
|
||||||
|
# $PYTHON tools/gen-wix-xml-filelist.py lib.wxs
|
||||||
|
# echo "-- begin lib.wxs --"
|
||||||
|
# cat lib.wxs
|
||||||
|
# echo "-- end lib.wxs --"
|
||||||
|
# /c/Program\ Files\ \(x86\)/WiX\ Toolset\ v3.14/bin/candle.exe -arch "${WIX_ARCH}" gam.wxs lib.wxs
|
||||||
|
# /c/Program\ Files\ \(x86\)/WiX\ Toolset\ v3.14/bin/light.exe -ext /c/Program\ Files\ \(x86\)/WiX\ Toolset\ v3.14/bin/WixUIExtension.dll gam.wixobj lib.wixobj -b "${gampath}/lib" -o "$MSI_FILENAME" || true;
|
||||||
|
# rm -v -f *.wixpdb
|
||||||
|
# rm -v -f *.wixobj
|
||||||
|
# echo "MSI_FILENAME=${MSI_FILENAME}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
#- name: Sign GAM MSI
|
||||||
|
# if: runner.os == 'Windows'
|
||||||
|
# shell: pwsh
|
||||||
|
# run: |
|
||||||
|
# write-Host "Signing ${env:MSI_FILENAME}...."
|
||||||
# Always explicitely use x64 version os signtool.exe, arm64 version apparently can't
|
# Always explicitely use x64 version os signtool.exe, arm64 version apparently can't
|
||||||
# see Certum certs since SimplySignDesktop is x64-only today.
|
# see Certum certs since SimplySignDesktop is x64-only today.
|
||||||
Start-Process -Wait -NoNewWindow -ErrorAction Continue -FilePath 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\signtool.exe' -ArgumentList "sign", "/sha1", "$env:WINDOWS_CODESIGN_CERT_HASH", "/tr", "http://time.certum.pl", "/td", "SHA256", "/fd", "SHA256", "/v", "$env:MSI_FILENAME"
|
# Start-Process -Wait -NoNewWindow -ErrorAction Continue -FilePath 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\signtool.exe' -ArgumentList "sign", "/sha1", "$env:WINDOWS_CODESIGN_CERT_HASH", "/tr", "http://time.certum.pl", "/td", "SHA256", "/fd", "SHA256", "/v", "$env:MSI_FILENAME"
|
||||||
write-Host "Verifying signature of ${env:MSI_FILENAME}...."
|
# write-Host "Verifying signature of ${env:MSI_FILENAME}...."
|
||||||
# verify signature. If we failed to sign we should fail to verify and die.
|
# # verify signature. If we failed to sign we should fail to verify and die.
|
||||||
& 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\signtool.exe' verify /pa /v "$env:MSI_FILENAME"
|
# & 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\signtool.exe' verify /pa /v "$env:MSI_FILENAME"
|
||||||
|
|
||||||
- name: Attest that gam package files were generated from this Action
|
- name: Attest that gam package files were generated from this Action
|
||||||
uses: actions/attest-build-provenance@0b6e9809265278d02c58acf52849a95818a5a306 # v3.0.0
|
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
|
||||||
if: (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && matrix.goal == 'build'
|
if: (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && matrix.goal == 'build'
|
||||||
with:
|
with:
|
||||||
subject-path: |
|
subject-path: |
|
||||||
gam*.tar.xz
|
gam*.tar.xz
|
||||||
gam*.zip
|
gam*.zip
|
||||||
gam*.msi
|
gam*.exe
|
||||||
|
# gam*.msi
|
||||||
|
|
||||||
- name: Archive production artifacts
|
- name: Archive tar.xz artifacts
|
||||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # 4.6.2
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # 7.0.0
|
||||||
#if: (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && matrix.goal != 'test'
|
if: runner.os != 'Windows'
|
||||||
if: always()
|
|
||||||
with:
|
with:
|
||||||
name: gam-binaries-${{ env.GAMOS }}-${{ env.arch }}-${{ matrix.jid }}
|
archive: false
|
||||||
|
if-no-files-found: ignore
|
||||||
path: |
|
path: |
|
||||||
gam*.tar.xz
|
gam*.tar.xz
|
||||||
|
|
||||||
|
- name: Archive zip artifacts
|
||||||
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # 7.0.0
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
with:
|
||||||
|
archive: false
|
||||||
|
if-no-files-found: ignore
|
||||||
|
path: |
|
||||||
gam*.zip
|
gam*.zip
|
||||||
gam*.msi
|
|
||||||
*.png
|
- name: Archive exe artifacts
|
||||||
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # 7.0.0
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
with:
|
||||||
|
archive: false
|
||||||
|
if-no-files-found: ignore
|
||||||
|
path: |
|
||||||
|
gam*.exe
|
||||||
|
|
||||||
- name: Basic Tests build jobs only
|
- name: Basic Tests build jobs only
|
||||||
if: matrix.goal != 'test' && steps.cache-python-ssl.outputs.cache-hit != 'true'
|
if: matrix.goal != 'test' && steps.cache-python-ssl.outputs.cache-hit != 'true'
|
||||||
@@ -839,19 +876,46 @@ jobs:
|
|||||||
- name: Live API tests
|
- name: Live API tests
|
||||||
if: (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
|
if: (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
|
||||||
run: |
|
run: |
|
||||||
|
run_gam() {
|
||||||
|
local allowed_codes="0"
|
||||||
|
if [[ "$1" == "-a" ]]; then
|
||||||
|
allowed_codes="$2"
|
||||||
|
shift 2
|
||||||
|
fi
|
||||||
|
echo "::group::Executing: gam $*"
|
||||||
|
local exit_code=0
|
||||||
|
$gam "$@" || exit_code=$?
|
||||||
|
echo "::endgroup::"
|
||||||
|
allowed_codes="${allowed_codes//,/ }"
|
||||||
|
local passed=false
|
||||||
|
for code in $allowed_codes; do
|
||||||
|
if [ "$exit_code" -eq "$code" ]; then
|
||||||
|
passed=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ "$passed" = true ]; then
|
||||||
|
echo "| \`gam $*\` | 🟢 Pass | $exit_code |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
return 0 # Mask the allowed non-zero exit code so GHA continues
|
||||||
|
else
|
||||||
|
echo "| \`gam $*\` | 🔴 Fail | $exit_code |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
exit $exit_code # Hard fail the step for unapproved errors
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
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 config customer_id "C03uzfv2s" save
|
run_gam config customer_id "C03uzfv2s" save
|
||||||
$gam config domain "pdl.jaylee.us" save
|
run_gam config domain "pdl.jaylee.us" save
|
||||||
$gam config admin_email "${gam_user}" save
|
run_gam config admin_email "${gam_user}" save
|
||||||
$gam config enable_dasa false save
|
run_gam config enable_dasa false save
|
||||||
$gam oauth info
|
run_gam oauth info
|
||||||
$gam oauth refresh
|
run_gam oauth refresh
|
||||||
$gam config enable_dasa true save
|
run_gam config enable_dasa true save
|
||||||
$gam checkconn
|
run_gam checkconn
|
||||||
$gam user "$gam_user" check serviceaccount
|
run_gam user "$gam_user" check serviceaccount
|
||||||
$gam info domain
|
run_gam info domain
|
||||||
$gam info user
|
run_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}"
|
||||||
export newuser="${newbase}@pdl.jaylee.us"
|
export newuser="${newbase}@pdl.jaylee.us"
|
||||||
@@ -860,24 +924,39 @@ jobs:
|
|||||||
export newbuilding="${newbase}-building"
|
export newbuilding="${newbase}-building"
|
||||||
export newresource="${newbase}-resource"
|
export newresource="${newbase}-resource"
|
||||||
export newou="aaaGithub Actions/${newbase}"
|
export newou="aaaGithub Actions/${newbase}"
|
||||||
|
|
||||||
|
echo "### GAM Execution Report" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| Command | Status |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| :--- | :---: |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
# cleanup old runs
|
# cleanup old runs
|
||||||
$gam config enable_dasa false save
|
run_gam config enable_dasa false save
|
||||||
$gam config csv_output_row_filter "name:regex:gha_test_${JID}_" print vaultholds | $gam csv - gam delete vaulthold "id:~~holdId~~" matter "id:~~matterId~~" || if [ $? != 55 ]; then exit $?; fi
|
run_gam config csv_output_row_filter "name:regex:gha_test_${JID}_" redirect csv ./vh.csv print vaultholds
|
||||||
$gam config csv_output_row_filter "name:regex:gha_test_${JID}_" print vaultmatters matterstate OPEN | $gam csv - gam update vaultmatter "id:~~matterId~~" action close
|
run_gam -a "0 55" csv ./vh.csv gam delete vaulthold "id:~~holdId~~" matter "id:~~matterId~~"
|
||||||
$gam config csv_output_row_filter "name:regex:gha_test_${JID}_" print vaultmatters matterstate CLOSED | $gam csv - gam update vaultmatter "id:~~matterId~~" action delete
|
run_gam config csv_output_row_filter "name:regex:gha_test_${JID}_" redirect csv ./vm-open.csv print vaultmatters matterstate OPEN
|
||||||
$gam config csv_output_row_filter "Emails.1.address:regex:^gha_test-${JID}_" print contacts | $gam csv - gam delete contact ~ContactID
|
run_gam csv ./vm-open.csv gam update vaultmatter "id:~~matterId~~" action close
|
||||||
$gam config enable_dasa true save
|
run_gam config csv_output_row_filter "name:regex:gha_test_${JID}_" redirect csv ./vm-closed.csv print vaultmatters matterstate CLOSED
|
||||||
$gam config csv_output_row_filter "name:regex:gha_test_${JID}_" print features | $gam csv - gam delete feature ~name
|
run_gam csv ./vm-closed.csv gam update vaultmatter "id:~~matterId~~" action delete
|
||||||
$gam config csv_output_row_filter "name:regex:^gha_test_${JID}_" user $gam_user print shareddrives asadmin | $gam csv - gam user $gam_user delete shareddrive ~id nukefromorbit
|
run_gam config csv_output_row_filter "Emails.1.address:regex:^gha_test-${JID}_" redirect csv ./contacts.csv print contacts
|
||||||
$gam print users query "gha.jid=$JID" | $gam csv - gam delete user ~primaryEmail
|
run_gam csv ./contacts.csv gam delete contact ~ContactID
|
||||||
$gam config csv_output_row_filter "name:regex:^gha_test_${JID}_" print ous fromparent "aaaGithub Actions" | $gam csv - gam delete ou ~orgUnitId
|
run_gam config enable_dasa true save
|
||||||
$gam config csv_output_row_filter "email:regex:^gha_test_${JID}_" print cigroups | $gam csv - gam delete cigroup ~email
|
run_gam config csv_output_row_filter "name:regex:gha_test_${JID}_" redirect csv ./features.csv print features
|
||||||
$gam config csv_output_row_filter "resourceId:regex:^gha_test_${JID}_" print resources | $gam csv - gam delete resource ~resourceId
|
run_gam csv ./features.csv gam delete feature ~name
|
||||||
$gam config csv_output_row_filter "buildingId:regex:^gha_test_${JID}_" print buildings | $gam csv - gam delete building ~buildingId
|
run_gam config csv_output_row_filter "name:regex:^gha_test_${JID}_" redirect csv ./sd.csv user $gam_user print shareddrives asadmin
|
||||||
|
run_gam csv ./sd.csv gam user $gam_user delete shareddrive ~id nukefromorbit
|
||||||
|
run_gam redirect csv ./users.csv print users query "gha.jid=$JID"
|
||||||
|
run_gam csv ./users.csv gam delete user ~primaryEmail
|
||||||
|
run_gam config csv_output_row_filter "name:regex:^gha_test_${JID}_" redirect csv ./ous.csv print ous fromparent "aaaGithub Actions"
|
||||||
|
run_gam csv ./ous.csv gam delete ou ~orgUnitId
|
||||||
|
run_gam config csv_output_row_filter "email:regex:^gha_test_${JID}_" redirect csv ./cigroups.csv print cigroups
|
||||||
|
run_gam csv ./cigroups.csv gam delete cigroup ~email
|
||||||
|
run_gam config csv_output_row_filter "resourceId:regex:^gha_test_${JID}_" redirect csv ./resources.csv print resources
|
||||||
|
run_gam csv ./resources.csv gam delete resource ~resourceId
|
||||||
|
run_gam config csv_output_row_filter "buildingId:regex:^gha_test_${JID}_" redirect csv ./buildings.csv print buildings
|
||||||
|
run_gam csv ./buildings.csv gam delete building ~buildingId
|
||||||
|
|
||||||
echo "Creating OrgUnit ${newou}"
|
echo "Creating OrgUnit ${newou}"
|
||||||
$gam create ou "${newou}"
|
run_gam create ou "${newou}"
|
||||||
export GAM_THREADS=5
|
export GAM_THREADS=5
|
||||||
echo email > sample.csv;
|
echo email > sample.csv;
|
||||||
for i in {1..10}; do
|
for i in {1..10}; do
|
||||||
@@ -885,156 +964,165 @@ jobs:
|
|||||||
done
|
done
|
||||||
driveid=$($gam user $gam_user add shareddrive "${newbase}" returnidonly)
|
driveid=$($gam user $gam_user add shareddrive "${newbase}" returnidonly)
|
||||||
echo "Created shared drive ${driveid}"
|
echo "Created shared drive ${driveid}"
|
||||||
$gam create user $newuser firstname GHA lastname $JID displayname "Github Actions ${JID}" password random recoveryphone 12125121110 recoveryemail jay0lee@gmail.com gha.jid $JID languages en+,en-GB- ou "${newou}"
|
run_gam create user $newuser firstname GHA lastname $JID displayname "Github Actions ${JID}" password random recoveryphone 12125121110 recoveryemail jay0lee@gmail.com gha.jid $JID languages en+,en-GB- ou "${newou}"
|
||||||
$gam user $newuser add license workspaceenterpriseplus
|
run_gam user $newuser add license workspaceenterpriseplus
|
||||||
$gam user $newuser update photo https://dummyimage.com/98x98/000/fff.jpg
|
run_gam user $newuser update photo https://dummyimage.com/98x98/000/fff.jpg
|
||||||
$gam user $newuser get photo
|
run_gam user $newuser get photo
|
||||||
$gam user $newuser delete photo
|
run_gam user $newuser delete photo
|
||||||
$gam create alias $newalias user $newuser
|
run_gam create alias $newalias user $newuser
|
||||||
$gam create group $newgroup name "GHA $JID group" description "This is a description" isarchived true
|
run_gam create group $newgroup name "GHA $JID group" description "This is a description" isarchived true
|
||||||
$gam user $gam_user sendemail recipient dev-null@pdl.jaylee.us subject "test message $newbase" message "GHA test message"
|
run_gam user $gam_user sendemail recipient dev-null@pdl.jaylee.us subject "test message $newbase" message "GHA test message"
|
||||||
$gam config enable_dasa false save
|
run_gam config enable_dasa false save
|
||||||
# don't expose policy output
|
# don't expose policy output
|
||||||
$gam show policies > policies.csv
|
run_gam show policies > policies.csv
|
||||||
$gam create contact firstname GHA lastname "$JID" email work "${newbase}@example.com" primary
|
run_gam create contact firstname GHA lastname "$JID" email work "${newbase}@example.com" primary
|
||||||
$gam print contacts
|
run_gam print contacts
|
||||||
$gam print privileges
|
run_gam print privileges
|
||||||
$gam config enable_dasa true save
|
run_gam config enable_dasa true save
|
||||||
$gam update cigroup $newgroup security memberrestriction 'member.type == 1 || member.customer_id == groupCustomerId()'
|
run_gam update cigroup $newgroup security memberrestriction 'member.type == 1 || member.customer_id == groupCustomerId()'
|
||||||
$gam info cigroup $newgroup
|
run_gam info cigroup $newgroup
|
||||||
$gam update group $newgroup add owner $gam_user
|
run_gam update group $newgroup add owner $gam_user
|
||||||
$gam update group $newgroup add member $newuser
|
run_gam update group $newgroup add member $newuser
|
||||||
$gam config enable_dasa false save
|
run_gam config enable_dasa false save
|
||||||
# 9/17/24 temp disable due to Google API sluggishness to see new users for admin commands
|
# 9/17/24 temp disable due to Google API sluggishness to see new users for admin commands
|
||||||
# $gam create admin $newuser _GROUPS_EDITOR_ROLE CUSTOMER # condition nonsecuritygroup
|
# run_gam create admin $newuser _GROUPS_EDITOR_ROLE CUSTOMER # condition nonsecuritygroup
|
||||||
# 9/13/25 temp disable due to hangs
|
# 9/13/25 temp disable due to hangs
|
||||||
# $gam create admin $newgroup _HELP_DESK_ADMIN_ROLE org_unit "${newou}"
|
# run_gam create admin $newgroup _HELP_DESK_ADMIN_ROLE org_unit "${newou}"
|
||||||
# $gam config csv_output_row_filter "assignedToUser:regex:${newuser}" print admins | $gam csv - gam delete admin "~roleAssignmentId"
|
# run_gam config csv_output_row_filter "assignedToUser:regex:${newuser}" print admins | run_gam csv - gam delete admin "~roleAssignmentId"
|
||||||
# $gam config csv_output_row_filter "assignedToGroup:regex:${newgroup}" print admins | $gam csv - gam delete admin "~roleAssignmentId"
|
# run_gam config csv_output_row_filter "assignedToGroup:regex:${newgroup}" print admins | run_gam csv - gam delete admin "~roleAssignmentId"
|
||||||
$gam config enable_dasa false save
|
run_gam config enable_dasa false save
|
||||||
$gam csv sample.csv gam create user ~~email~~ firstname "GHA Bulk" lastname ~~email~~ gha.jid $JID ou "${newou}"
|
run_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}"
|
run_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 ""
|
run_gam csv sample.csv gam update user ~~email~~ recoveryphone "" recoveryemail ""
|
||||||
$gam config enable_dasa false save
|
run_gam config enable_dasa false save
|
||||||
$gam csv sample.csv gam user ~email add license workspaceenterpriseplus
|
run_gam csv sample.csv gam user ~email add license workspaceenterpriseplus
|
||||||
#$gam user $newuser add contactdelegate "${newbase}-bulkuser-1"
|
#run_gam user $newuser add contactdelegate "${newbase}-bulkuser-1"
|
||||||
#$gam user $newuser print contactdelegates
|
#run_gam user $newuser print contactdelegates
|
||||||
$gam config enable_dasa true save
|
run_gam config enable_dasa true save
|
||||||
$gam csv sample.csv gam user $gam_user sendemail recipient ~~email~~@pdl.jaylee.us subject "test message $newbase" message "GHA test message"
|
run_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
|
run_gam csv sample.csv gam update group $newgroup add member ~email
|
||||||
$gam info group $newgroup
|
run_gam info group $newgroup
|
||||||
$gam info cigroup $newgroup membertree
|
run_gam info cigroup $newgroup membertree
|
||||||
# confirm mailbox is provisoned before continuing
|
# confirm mailbox is provisoned before continuing
|
||||||
$gam user $newuser waitformailbox retries 50
|
run_gam user $newuser waitformailbox retries 50
|
||||||
$gam user $newuser imap on
|
run_gam user $newuser imap on
|
||||||
$gam user $newuser show imap
|
run_gam user $newuser show imap
|
||||||
$gam user $newuser show delegates
|
run_gam user $newuser show delegates
|
||||||
export biohazard=$(echo -e '\xe2\x98\xa3')
|
export biohazard=$(echo -e '\xe2\x98\xa3')
|
||||||
$gam user $newuser label "$biohazard unicode biohazard $biohazard"
|
run_gam user $newuser label "$biohazard unicode biohazard $biohazard"
|
||||||
$gam user $newuser show labels
|
run_gam user $newuser show labels
|
||||||
$gam user $newuser show labels > labels.txt
|
run_gam user $newuser show labels > labels.txt
|
||||||
$gam user $gam_user importemail subject "GHA import $newbase" message "This is a test import" labels IMPORTANT,UNREAD,INBOX,STARRED
|
run_gam user $gam_user importemail subject "GHA import $newbase" message "This is a test import" labels IMPORTANT,UNREAD,INBOX,STARRED
|
||||||
$gam user $gam_user insertemail subject "GHA insert $newbase" file gam.py labels INBOX,UNREAD # yep body is gam code
|
run_gam user $gam_user insertemail subject "GHA insert $newbase" file gam.py labels INBOX,UNREAD # yep body is gam code
|
||||||
$gam user $gam_user sendemail recipient admin@pdl.jaylee.us subject "GHA send $gam_user $newbase" file gam.py
|
run_gam user $gam_user sendemail recipient admin@pdl.jaylee.us subject "GHA send $gam_user $newbase" file gam.py
|
||||||
$gam user $gam_user draftemail subject "GHA draft $newbase" message "Draft message test"
|
run_gam user $gam_user draftemail subject "GHA draft $newbase" message "Draft message test"
|
||||||
$gam csvfile sample.csv:email waitformailbox retries 20
|
run_gam csvfile sample.csv:email waitformailbox retries 20
|
||||||
$gam user $newuser delegate to "${newbase}-bulkuser-1" || if [ $? != 50 ]; then exit $?; fi # expect a 50 return code (delegation failed)
|
run_gam user $newuser delegate to "${newbase}-bulkuser-1" || if [ $? != 50 ]; then exit $?; fi # expect a 50 return code (delegation failed)
|
||||||
$gam users "$gam_user $newbase-bulkuser-1 $newbase-bulkuser-2 $newbase-bulkuser-3" delete messages query in:anywhere maxtodelete 99999 doit || if [ $? != 60 ]; then exit $?; fi # expect a 60 return code (no messages)
|
run_gam -a "0 60" users "$gam_user $newbase-bulkuser-1 $newbase-bulkuser-2 $newbase-bulkuser-3" delete messages query in:anywhere maxtodelete 99999 doit
|
||||||
$gam users "$newbase-bulkuser-4 $newbase-bulkuser-5 $newbase-bulkuser-6" trash messages query in:anywhere maxtotrash 99999 doit || if [ $? != 60 ]; then exit $?; fi # expect a 60 return code (no messages)
|
run_gam -a "0 60" users "$newbase-bulkuser-4 $newbase-bulkuser-5 $newbase-bulkuser-6" trash messages query in:anywhere maxtotrash 99999 doit
|
||||||
$gam users "$newbase-bulkuser-7 $newbase-bulkuser-8 $newbase-bulkuser-9" modify messages query in:anywhere maxtomodify 99999 addlabel IMPORTANT addlabel STARRED doit || if [ $? != 60 ]; then exit $?; fi # expect a 60 return code (no messages)
|
run_gam -a "0 60" users "$newbase-bulkuser-7 $newbase-bulkuser-8 $newbase-bulkuser-9" modify messages query in:anywhere maxtomodify 99999 addlabel IMPORTANT addlabel STARRED doit
|
||||||
$gam user $newuser delete label --ALL_LABELS--
|
run_gam user $newuser delete label --ALL_LABELS--
|
||||||
$gam config csv_output_row_filter "name:regex:gha-test-${JID}" print features | $gam csv - gam delete feature ~name
|
run_gam config csv_output_row_filter "name:regex:gha-test-${JID}" redirect csv ./features.csv print features
|
||||||
$gam create feature name VC-$newbase
|
run_gam csv ./features.csv gam delete feature ~name
|
||||||
$gam create feature name Whiteboard-$newbase
|
run_gam create feature name VC-$newbase
|
||||||
$gam create building "My Building - $newbase" id $newbuilding floors 1,2,3,4,5,6,7,8,9,10,11,12,14,15 description "No 13th floor here..."
|
run_gam create feature name Whiteboard-$newbase
|
||||||
$gam create resource $newresource "Resource Calendar $tstamp" capacity 25 features Whiteboard-$newbase,VC-$newbase building $newbuilding floor 15 type Room
|
run_gam create building "My Building - $newbase" id $newbuilding floors 1,2,3,4,5,6,7,8,9,10,11,12,14,15 description "No 13th floor here..."
|
||||||
$gam info resource $newresource
|
run_gam create resource $newresource "Resource Calendar $tstamp" capacity 25 features Whiteboard-$newbase,VC-$newbase building $newbuilding floor 15 type Room
|
||||||
$gam user $newuser add drivefile drivefilename "TPS Reports" mimetype gfolder
|
run_gam info resource $newresource
|
||||||
$gam user $newuser show filelist
|
run_gam user $newuser add drivefile drivefilename "TPS Reports" mimetype gfolder
|
||||||
$gam calendar $gam_user printacl | $gam csv - gam calendar $gam_user delete ~id # clear ACLs
|
run_gam user $newuser show filelist
|
||||||
$gam calendar $gam_user add read domain
|
run_gam redirect csv ./cal-acl.csv calendar $gam_user printacl
|
||||||
$gam calendar $gam_user add freebusy default
|
run_gam csv ./cal-acl.csv gam calendar $gam_user delete ~id # clear ACLs
|
||||||
$gam calendar $gam_user add editor $newuser
|
run_gam calendar $gam_user add read domain
|
||||||
$gam calendar $gam_user showacl
|
run_gam calendar $gam_user add freebusy default
|
||||||
$gam calendar $gam_user printacl | $gam csv - gam calendar $gam_user delete ~id
|
run_gam calendar $gam_user add editor $newuser
|
||||||
$gam calendar $gam_user addevent summary "GHA test event" start +1h end +2h attendee $newgroup hangoutsmeet guestscanmodify true sendupdates all
|
run_gam calendar $gam_user showacl
|
||||||
$gam calendar $gam_user printevents after -0d
|
run_gam redirect csv ./cal-acl.csv calendar $gam_user printacl
|
||||||
$gam config enable_dasa false save
|
run_gam csv ./cal-acl.csv gam calendar $gam_user delete ~id
|
||||||
|
run_gam calendar $gam_user addevent summary "GHA test event" start +1h end +2h attendee $newgroup hangoutsmeet guestscanmodify true sendupdates all
|
||||||
|
run_gam calendar $gam_user printevents after -0d
|
||||||
|
run_gam config enable_dasa false save
|
||||||
matterid=uid:$($gam create vaultmatter name "GHA matter $newbase" description "test matter" returnidonly)
|
matterid=uid:$($gam create vaultmatter name "GHA matter $newbase" description "test matter" returnidonly)
|
||||||
$gam create vaulthold matter $matterid name "GHA hold ${newbase}" corpus mail ou "$newou"
|
run_gam create vaulthold matter "$matterid" name "GHA hold ${newbase}" corpus mail ou "$newou"
|
||||||
$gam print vaultmatters matterstate open
|
run_gam print vaultmatters matterstate open
|
||||||
$gam print vaultholds matter $matterid
|
run_gam print vaultholds matter $matterid
|
||||||
$gam print vaultcount matter $matterid corpus mail everyone todrive tdnobrowser
|
run_gam print vaultcount matter $matterid corpus mail everyone todrive tdnobrowser
|
||||||
$gam create vaultexport matter $matterid name "GHA export $newbase" corpus mail ou "$newou"
|
run_gam create vaultexport matter $matterid name "GHA export $newbase" corpus mail ou "$newou"
|
||||||
$gam print exports matter $matterid | $gam csv - gam info export $matterid id:~~id~~
|
run_gam redirect csv ./exports.csv print exports matter $matterid
|
||||||
$gam config enable_dasa true save
|
run_gam csv ./exports.csv gam info export $matterid id:~~id~~
|
||||||
$gam csv sample.csv gam user ~email add calendar id:$newresource
|
run_gam config enable_dasa true save
|
||||||
$gam delete resource $newresource
|
run_gam csv sample.csv gam user ~email add calendar id:$newresource
|
||||||
$gam delete feature Whiteboard-$newbase
|
run_gam delete resource $newresource
|
||||||
$gam delete feature VC-$newbase
|
run_gam delete feature Whiteboard-$newbase
|
||||||
$gam delete building $newbuilding
|
run_gam delete feature VC-$newbase
|
||||||
$gam delete group $newgroup
|
run_gam delete building $newbuilding
|
||||||
$gam config enable_dasa false save
|
run_gam delete group $newgroup
|
||||||
|
run_gam config enable_dasa false save
|
||||||
echo start
|
echo start
|
||||||
$gam user $newuser delete license workspaceenterpriseplus
|
run_gam user $newuser delete license workspaceenterpriseplus
|
||||||
echo finish
|
echo finish
|
||||||
$gam config enable_dasa true save
|
run_gam config enable_dasa true save
|
||||||
$gam whatis $newuser || if [ $? != 20 ]; then exit $?; fi # expect a 20 return code (is a user)
|
run_gam -a "0 20" whatis $newuser
|
||||||
$gam user $gam_user show tokens
|
run_gam user $gam_user show tokens
|
||||||
$gam config enable_dasa false save
|
run_gam config enable_dasa false save
|
||||||
download_dir="${RUNNER_TEMP}/TEMP_DELETE_ME"
|
download_dir="${RUNNER_TEMP}/TEMP_DELETE_ME"
|
||||||
mkdir -v "$download_dir"
|
mkdir -v "$download_dir"
|
||||||
$gam print exports matter $matterid | $gam csv - gam download export $matterid id:~~id~~ targetfolder "$download_dir"
|
run_gam redirect csv ./exports.csv print exports matter $matterid
|
||||||
|
run_gam csv ./exports.csv gam download export $matterid id:~~id~~ targetfolder "$download_dir"
|
||||||
rm -rvf "$download_dir"
|
rm -rvf "$download_dir"
|
||||||
$gam delete hold "GHA hold $newbase" matter $matterid
|
run_gam delete hold "GHA hold $newbase" matter $matterid
|
||||||
$gam update matter $matterid action close
|
run_gam update matter $matterid action close
|
||||||
$gam update matter $matterid action delete
|
run_gam update matter $matterid action delete
|
||||||
# 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
|
run_gam redirect csv ./users.csv print users query "email:${newuser}" orgunitpath
|
||||||
$gam user $newuser show holds || if [ $? != 55 ]; then exit $?; fi # expect a 55 return code
|
run_gam csv ./users.csv gam update user ~primaryEmail ou ~orgUnitPath
|
||||||
|
run_gam user $newuser show holds || if [ $? != 55 ]; then exit $?; fi # expect a 55 return code
|
||||||
export sn="$JID$JID$JID$JID-$(openssl rand -base64 32 | sed 's/[^a-zA-Z0-9]//g')"
|
export sn="$JID$JID$JID$JID-$(openssl rand -base64 32 | sed 's/[^a-zA-Z0-9]//g')"
|
||||||
$gam create device serialnumber $sn devicetype android
|
run_gam create device serialnumber $sn devicetype android
|
||||||
$gam delete contacts emailmatchpattern "^${newbase}@example.com$"
|
run_gam delete contacts emailmatchpattern "^${newbase}@example.com$"
|
||||||
$gam config enable_dasa true save
|
run_gam config enable_dasa true save
|
||||||
$gam print users query "gha.jid=$JID" | $gam csv - gam delete user ~primaryEmail || if [ $? != 50 ]; then exit $?; fi # expect a 50 return code (vault hold on user)
|
run_gam redirect csv ./users.csv print users query "gha.jid=$JID"
|
||||||
$gam print mobile
|
run_gam -a "0 50" csv ./users.csv gam delete user ~primaryEmail
|
||||||
$gam print devices clientstates
|
run_gam print mobile
|
||||||
$gam print browsers
|
run_gam print devices clientstates
|
||||||
$gam print cros allfields orderby serialnumber
|
run_gam print browsers
|
||||||
$gam show crostelemetry storagepercentonly
|
run_gam print cros allfields orderby serialnumber
|
||||||
$gam report usageparameters customer
|
run_gam show crostelemetry storagepercentonly
|
||||||
$gam report usage customer parameters gmail:num_emails_sent,accounts:num_1day_logins
|
run_gam report usageparameters customer
|
||||||
$gam report customer todrive tdnobrowser
|
run_gam report usage customer parameters gmail:num_emails_sent,accounts:num_1day_logins
|
||||||
#$gam report users fields accounts:is_less_secure_apps_access_allowed,gmail:last_imap_time,gmail:last_pop_time filters "accounts:last_login_time>2025-01-01T00:00:00.000Z" todrive tdnobrowser
|
run_gam report customer todrive tdnobrowser
|
||||||
$gam report users todrive tdnobrowser
|
#run_gam report users fields accounts:is_less_secure_apps_access_allowed,gmail:last_imap_time,gmail:last_pop_time filters "accounts:last_login_time>2025-01-01T00:00:00.000Z" todrive tdnobrowser
|
||||||
$gam report admin start -3d todrive tdnobrowser
|
run_gam report users todrive tdnobrowser
|
||||||
$gam print devices nopersonaldevices nodeviceusers filter "serial:$JID$JID$JID$JID-" | $gam csv - gam delete device id ~name
|
run_gam report admin start -3d todrive tdnobrowser
|
||||||
$gam config enable_dasa false save
|
run_gam redirect csv ./devices.csv print devices nopersonaldevices nodeviceusers filter "serial:$JID$JID$JID$JID-"
|
||||||
$gam print userinvitations
|
run_gam csv ./devices.csv gam delete device id ~name
|
||||||
$gam print userinvitations | $gam csv - gam send userinvitation ~name
|
run_gam config enable_dasa false save
|
||||||
$gam config enable_dasa false save
|
run_gam print userinvitations
|
||||||
$gam create caalevel "zzz_${newbase}" basic condition ipsubnetworks 1.1.1.1/32,2.2.2.2/32 endcondition
|
run_gam redirect csv ./invitations.csv print userinvitations
|
||||||
$gam print caalevels
|
run_gam csv ./invitations.csv gam send userinvitation ~name
|
||||||
$gam delete caalevel "zzz_${newbase}"
|
run_gam config enable_dasa false save
|
||||||
$gam user $gam_user add drivefile localfile gam.py parentid "${driveid}"
|
run_gam create caalevel "zzz_${newbase}" basic condition ipsubnetworks 1.1.1.1/32,2.2.2.2/32 endcondition
|
||||||
$gam user $gam_user update shareddrive "${driveid}" ou "${newou}"
|
run_gam print caalevels
|
||||||
$gam user $gam_user show shareddrives asadmin
|
run_gam delete caalevel "zzz_${newbase}"
|
||||||
$gam user $gam_user update shareddrive "${driveid}" ou "aaaGithub Actions" # so we can delete our OU...
|
run_gam user $gam_user add drivefile localfile gam.py parentid "${driveid}"
|
||||||
$gam user $gam_user delete shareddrive "${driveid}" nukefromorbit
|
run_gam user $gam_user update shareddrive "${driveid}" ou "${newou}"
|
||||||
ssoprofile=$($gam config debug_level 1 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)
|
run_gam user $gam_user show shareddrives asadmin
|
||||||
|
run_gam user $gam_user update shareddrive "${driveid}" ou "aaaGithub Actions" # so we can delete our OU...
|
||||||
|
run_gam user $gam_user delete shareddrive "${driveid}" nukefromorbit
|
||||||
|
ssoprofile=$(run_gam config debug_level 1 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)
|
||||||
if [ ${ssoprofile} != 'inProgress' ]; then
|
if [ ${ssoprofile} != 'inProgress' ]; then
|
||||||
$gam create inboundssocredential profile "id:${ssoprofile}" generate_key
|
run_gam create inboundssocredential profile "id:${ssoprofile}" generate_key
|
||||||
#$gam create inboundssoassignment profile "id:${ssoprofile}" orgunit "${newou}" mode SAML_SSO
|
#run_gam create inboundssoassignment profile "id:${ssoprofile}" orgunit "${newou}" mode SAML_SSO
|
||||||
#$gam delete inboundssoassignment "orgunit:${newou}"
|
#run_gam delete inboundssoassignment "orgunit:${newou}"
|
||||||
$gam delete inboundssoprofile "id:${ssoprofile}"
|
run_gam delete inboundssoprofile "id:${ssoprofile}"
|
||||||
fi
|
fi
|
||||||
echo "printer model count:"
|
echo "printer model count:"
|
||||||
$gam print printermodels | wc -l
|
run_gam print printermodels | wc -l
|
||||||
$gam print printers
|
run_gam print printers
|
||||||
printerid=$($gam create printer displayname "${newbase}" uri ipp://localhost:631 driverless description "made by ${gam_user}" ou "${newou}" nodetails | awk '{print substr($2, 1, length($2)-1)}')
|
printerid=$($gam create printer displayname "${newbase}" uri ipp://localhost:631 driverless description "made by ${gam_user}" ou "${newou}" returnIdOnly)
|
||||||
$gam info printer "$printerid"
|
run_gam info printer "$printerid"
|
||||||
$gam delete printer "$printerid"
|
run_gam delete printer "$printerid"
|
||||||
$gam delete ou "${newou}"
|
run_gam delete ou "${newou}"
|
||||||
|
|
||||||
- name: Tar Cache archive
|
- name: Tar 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'
|
||||||
@@ -1048,24 +1136,10 @@ jobs:
|
|||||||
echo '.git*' > ./excludes.txt
|
echo '.git*' > ./excludes.txt
|
||||||
tar cJvvf cache.tar.xz --exclude-from=excludes.txt $tar_folders
|
tar cJvvf cache.tar.xz --exclude-from=excludes.txt $tar_folders
|
||||||
|
|
||||||
merge:
|
|
||||||
if: (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
needs: build
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
packages: write
|
|
||||||
steps:
|
|
||||||
- name: Merge Artifacts
|
|
||||||
uses: actions/upload-artifact/merge@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
|
||||||
with:
|
|
||||||
name: gam-binaries
|
|
||||||
pattern: gam-binaries-*
|
|
||||||
|
|
||||||
publish:
|
publish:
|
||||||
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
|
if: (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
needs: merge
|
needs: build
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
packages: write
|
packages: write
|
||||||
@@ -1073,13 +1147,17 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Download artifacts
|
- name: Download artifacts
|
||||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # 5.0.0
|
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
||||||
|
with:
|
||||||
|
path: gam-binaries/
|
||||||
|
merge-multiple: true
|
||||||
|
skip-decompress: true
|
||||||
|
|
||||||
- name: VirusTotal Scan
|
- name: VirusTotal Scan
|
||||||
uses: crazy-max/ghaction-virustotal@d34968c958ae283fe976efed637081b9f9dcf74f # 4.2.0
|
uses: crazy-max/ghaction-virustotal@d34968c958ae283fe976efed637081b9f9dcf74f # 4.2.0
|
||||||
@@ -1096,7 +1174,7 @@ jobs:
|
|||||||
echo "dateversion=${dateversion}" >> $GITHUB_OUTPUT
|
echo "dateversion=${dateversion}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- name: Publish draft release
|
- name: Publish draft release
|
||||||
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3
|
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
prerelease: false
|
prerelease: false
|
||||||
|
|||||||
48
.github/workflows/get-cacerts.yml
vendored
48
.github/workflows/get-cacerts.yml
vendored
@@ -17,7 +17,7 @@ defaults:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-certs:
|
check-certs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-slim
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
|
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
|
||||||
with:
|
with:
|
||||||
@@ -30,9 +30,51 @@ jobs:
|
|||||||
echo "Current hash is: ${CURRENT_HASH}"
|
echo "Current hash is: ${CURRENT_HASH}"
|
||||||
echo "CURRENT_HASH=${CURRENT_HASH}" >> $GITHUB_ENV
|
echo "CURRENT_HASH=${CURRENT_HASH}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Get latest cacerts.pem file from Google
|
- name: Generate GAM-specific bundle with LE + Google roots
|
||||||
run: |
|
run: |
|
||||||
curl -o ./cacerts.pem -vvvv https://pki.goog/roots.pem
|
OUTPUT_FILE="cacerts.pem"
|
||||||
|
> "$OUTPUT_FILE"
|
||||||
|
|
||||||
|
process_cert() {
|
||||||
|
local url="$1"
|
||||||
|
local op_ca="$2"
|
||||||
|
local label="$3"
|
||||||
|
local tmp_cert=$(mktemp)
|
||||||
|
curl "$url" > "$tmp_cert"
|
||||||
|
local issuer=$(openssl x509 -noout -issuer -in "$tmp_cert" | sed -e 's/^issuer= *//')
|
||||||
|
local subject=$(openssl x509 -noout -subject -in "$tmp_cert" | sed -e 's/^subject= *//')
|
||||||
|
local serial_hex=$(openssl x509 -noout -serial -in "$tmp_cert" | sed -e 's/^serial=//')
|
||||||
|
local serial_dec=$(python3 -c "print(int('$serial_hex', 16))")
|
||||||
|
local md5=$(openssl x509 -noout -fingerprint -md5 -in "$tmp_cert" | sed -e 's/.*=//' | tr '[:upper:]' '[:lower:]')
|
||||||
|
local sha1=$(openssl x509 -noout -fingerprint -sha1 -in "$tmp_cert" | sed -e 's/.*=//' | tr '[:upper:]' '[:lower:]')
|
||||||
|
local sha256=$(openssl x509 -noout -fingerprint -sha256 -in "$tmp_cert" | sed -e 's/.*=//' | tr '[:upper:]' '[:lower:]')
|
||||||
|
echo "# Operating CA: $op_ca" >> "$OUTPUT_FILE"
|
||||||
|
echo "# Issuer: $issuer" >> "$OUTPUT_FILE"
|
||||||
|
echo "# Subject: $subject" >> "$OUTPUT_FILE"
|
||||||
|
echo "# Label: \"$label\"" >> "$OUTPUT_FILE"
|
||||||
|
echo "# Serial: $serial_dec" >> "$OUTPUT_FILE"
|
||||||
|
echo "# MD5 Fingerprint: $md5" >> "$OUTPUT_FILE"
|
||||||
|
echo "# SHA1 Fingerprint: $sha1" >> "$OUTPUT_FILE"
|
||||||
|
echo "# SHA256 Fingerprint: $sha256" >> "$OUTPUT_FILE"
|
||||||
|
cat "$tmp_cert" >> "$OUTPUT_FILE"
|
||||||
|
echo "" >> "$OUTPUT_FILE"
|
||||||
|
rm "$tmp_cert"
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "#" >> "$OUTPUT_FILE"
|
||||||
|
echo "# This is a custom certificate authority bundle for GAM" >> "$OUTPUT_FILE"
|
||||||
|
echo "# It's composed of Let's Encrypt Root CAs and Google's" >> "$OUTPUT_FILE"
|
||||||
|
echo "# certificate bundle. This should be the minimal list of" >> "$OUTPUT_FILE"
|
||||||
|
echo "# CAs required to talk to Google and Github." >> "$OUTPUT_FILE"
|
||||||
|
echo"" >> "$OUTPUT_FILE"
|
||||||
|
echo "Processing Let's Encrypt ISRG Root X1..."
|
||||||
|
process_cert "https://letsencrypt.org/certs/isrgrootx1.pem" "Let's Encrypt" "ISRG Root X1"
|
||||||
|
echo "Processing Let's Encrypt ISRG Root X2..."
|
||||||
|
process_cert "https://letsencrypt.org/certs/isrg-root-x2.pem" "Let's Encrypt" "ISRG Root X2"
|
||||||
|
|
||||||
|
echo "Appending Google's roots.pem..."
|
||||||
|
curl -s https://pki.goog/roots.pem >> "$OUTPUT_FILE"
|
||||||
|
echo "Done! The new bundle has been saved to $OUTPUT_FILE."
|
||||||
|
|
||||||
- name: Compare hashes
|
- name: Compare hashes
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ authors = [
|
|||||||
#significant compile dependencies.
|
#significant compile dependencies.
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrow>=1.3.0",
|
"arrow>=1.3.0",
|
||||||
"chardet>=5.2.0",
|
"chardet==5.2.0",
|
||||||
"cryptography>=46.0.5",
|
"cryptography>=46.0.5",
|
||||||
"distro; sys_platform=='linux'",
|
"distro; sys_platform=='linux'",
|
||||||
"filelock>=3.18.0",
|
"filelock>=3.18.0",
|
||||||
@@ -31,11 +31,11 @@ requires-python = ">=3.10"
|
|||||||
classifiers = [
|
classifiers = [
|
||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
"Programming Language :: Python :: 3 :: Only",
|
"Programming Language :: Python :: 3 :: Only",
|
||||||
"Programming Language :: Python :: 3.9",
|
|
||||||
"Programming Language :: Python :: 3.10",
|
"Programming Language :: Python :: 3.10",
|
||||||
"Programming Language :: Python :: 3.11",
|
"Programming Language :: Python :: 3.11",
|
||||||
"Programming Language :: Python :: 3.12",
|
"Programming Language :: Python :: 3.12",
|
||||||
"Programming Language :: Python :: 3.13",
|
"Programming Language :: Python :: 3.13",
|
||||||
|
"Programming Language :: Python :: 3.14",
|
||||||
"Operating System :: OS Independent",
|
"Operating System :: OS Independent",
|
||||||
]
|
]
|
||||||
license = {text = "Apache License (2.0)"}
|
license = {text = "Apache License (2.0)"}
|
||||||
|
|||||||
@@ -4568,8 +4568,8 @@ gam check ou|org <OrgUnitItem> [todrive <ToDriveAttribute>*]
|
|||||||
usedriverlessconfig|
|
usedriverlessconfig|
|
||||||
<PrinterFieldNameList> ::= "<PrinterFieldName>(,<PrinterFieldName>)*"
|
<PrinterFieldNameList> ::= "<PrinterFieldName>(,<PrinterFieldName>)*"
|
||||||
|
|
||||||
gam create printer <PrinterAttribute>+
|
gam create printer <PrinterAttribute>+ [nodetails|returnidonly]
|
||||||
gam update printer <PrinterID> <PrinterAttribute>+
|
gam update printer <PrinterID> <PrinterAttribute>+ [nodetails|returnidonly]
|
||||||
gam delete printer
|
gam delete printer
|
||||||
<PrinterIDList>|
|
<PrinterIDList>|
|
||||||
<FileSelector>|
|
<FileSelector>|
|
||||||
@@ -5693,6 +5693,7 @@ gam download storagefile <StorageBucketObjectName>
|
|||||||
fullname|
|
fullname|
|
||||||
gender|
|
gender|
|
||||||
givenname|firstname|
|
givenname|firstname|
|
||||||
|
guestaccountinfo|
|
||||||
id|
|
id|
|
||||||
ims|im|
|
ims|im|
|
||||||
includeinglobaladdresslist|gal|
|
includeinglobaladdresslist|gal|
|
||||||
@@ -5700,6 +5701,7 @@ gam download storagefile <StorageBucketObjectName>
|
|||||||
isdelegatedadmin|admin|isadmin|
|
isdelegatedadmin|admin|isadmin|
|
||||||
isenforcedin2sv|is2svenforced|
|
isenforcedin2sv|is2svenforced|
|
||||||
isenrolledin2sv|is2svenrolled|
|
isenrolledin2sv|is2svenrolled|
|
||||||
|
isguestuser|
|
||||||
ismailboxsetup|
|
ismailboxsetup|
|
||||||
keyword|keywords|
|
keyword|keywords|
|
||||||
language|languages|
|
language|languages|
|
||||||
|
|||||||
@@ -1,3 +1,58 @@
|
|||||||
|
7.35.00
|
||||||
|
|
||||||
|
Updated cacerts.pem to avoid to following error in `gam checkconn`.
|
||||||
|
```
|
||||||
|
Checking raw.githubusercontent.com (185.199.110.133) (2)... ERROR
|
||||||
|
Certificate verification failed. If you are behind a firewall / proxy server that does TLS / SSL inspection you may need to point GAM at your certificate authority file by setting cacerts_pem = /path/to/your/certauth.pem in gam.cfg.
|
||||||
|
```
|
||||||
|
|
||||||
|
7.34.13
|
||||||
|
|
||||||
|
Fixed bug in `gam info policies <CIPolicyNameEntity> ... formatjson` where extraneous line
|
||||||
|
`Show Info 1 Policy` was displayed.
|
||||||
|
|
||||||
|
7.34.12
|
||||||
|
|
||||||
|
Fixed build errors that prevented Windows zip files from being created.
|
||||||
|
|
||||||
|
Added option `returnidonly` to `gam create|update printer` that causes GAM to return just the ID
|
||||||
|
of the printer.
|
||||||
|
|
||||||
|
7.34.11
|
||||||
|
|
||||||
|
Updated gam-install.sh script for macOS/Linux to properly config GAM when the answer to the following question is No.
|
||||||
|
```
|
||||||
|
Can you run a full browser on this machine? (usually Y for macOS, N for Linux if you SSH into this machine)
|
||||||
|
```
|
||||||
|
|
||||||
|
7.34.10
|
||||||
|
|
||||||
|
Fixed bug where `formatjson quotechar <Character>` on the command line did not override `redirect csv <FileName> multiprocess quotechar <Character>`.
|
||||||
|
|
||||||
|
7.34.09
|
||||||
|
|
||||||
|
Updated `gam <UserTypeEntity> update photo` to delete the user's existing photo
|
||||||
|
before performing the update as the API update will succeed but not replace a user's existing self-set photo.
|
||||||
|
|
||||||
|
7.34.08
|
||||||
|
|
||||||
|
Rebuild to avoid the following error:
|
||||||
|
```
|
||||||
|
requests/__init__.py:113: RequestsDependencyWarning: urllib3 (2.6.3) or chardet (6.0.0.post1)/charset_normalizer (3.4.4) doesn't match a supported version!
|
||||||
|
```
|
||||||
|
|
||||||
|
7.34.07
|
||||||
|
|
||||||
|
Added the following command to create a guest user.
|
||||||
|
* See: https://support.google.com/a/answer/16558545
|
||||||
|
```
|
||||||
|
gam create guestuser <EmailAddress>
|
||||||
|
```
|
||||||
|
|
||||||
|
Added the following items to `<UserFieldName>`:
|
||||||
|
* `guestaccountinfo` - Additional guest-related metadata fields
|
||||||
|
* `isguestuser` - Indicates if the inserted user is a guest
|
||||||
|
|
||||||
7.34.06
|
7.34.06
|
||||||
|
|
||||||
Added option `copyfolderpermissions [<Boolean>]` to `gam <UserTypeEntity> copy|move drivefile`.
|
Added option `copyfolderpermissions [<Boolean>]` to `gam <UserTypeEntity> copy|move drivefile`.
|
||||||
@@ -1499,7 +1554,7 @@ Re-run the command specify a new service account name with: saname <ServiceAccou
|
|||||||
|
|
||||||
Native support for Windows 11 Arm-based devices.
|
Native support for Windows 11 Arm-based devices.
|
||||||
|
|
||||||
Renamed some MacOS and Linux binary installer files to align on terminology. Everything is "arm64" now, no "aarch64".
|
Renamed some macOS and Linux binary installer files to align on terminology. Everything is "arm64" now, no "aarch64".
|
||||||
|
|
||||||
7.06.05
|
7.06.05
|
||||||
|
|
||||||
@@ -2108,7 +2163,7 @@ for `[R] 35) Meet API (supports readonly)` as it is a special case.
|
|||||||
|
|
||||||
7.00.39
|
7.00.39
|
||||||
|
|
||||||
Supported MacOS versions are now in the download filename.
|
Supported macOS versions are now in the download filename.
|
||||||
|
|
||||||
Minor code fixes.
|
Minor code fixes.
|
||||||
|
|
||||||
@@ -3911,11 +3966,11 @@ See: https://github.com/taers232c/GAMADV-XTD3/wiki/Users-Drive-Files-Display#fil
|
|||||||
|
|
||||||
6.65.12
|
6.65.12
|
||||||
|
|
||||||
Additional updates on MacOS when a `gam csv` command is interrupted with a contol-C.
|
Additional updates on macOS when a `gam csv` command is interrupted with a contol-C.
|
||||||
|
|
||||||
6.65.11
|
6.65.11
|
||||||
|
|
||||||
Updated multiprocessing to handle the following error that occurs on MacOS when a `gam csv` command
|
Updated multiprocessing to handle the following error that occurs on macOS when a `gam csv` command
|
||||||
is interrupted with a contol-C.
|
is interrupted with a contol-C.
|
||||||
```
|
```
|
||||||
multiprocessing/resource_tracker.py:224: UserWarning: resource_tracker: There appear to be N leaked semaphore objects to clean up at shutdown
|
multiprocessing/resource_tracker.py:224: UserWarning: resource_tracker: There appear to be N leaked semaphore objects to clean up at shutdown
|
||||||
@@ -6076,7 +6131,7 @@ Improved code for `gam [<UserTypeEntity>] create teamdrive <Name> ou <OrgUnitIte
|
|||||||
|
|
||||||
6.29.04
|
6.29.04
|
||||||
|
|
||||||
Updated multiprocessing on MacOS to use `spawn` instead of `fork` when starting subprocesses
|
Updated multiprocessing on macOS to use `spawn` instead of `fork` when starting subprocesses
|
||||||
as `fork` was unreliable when large numbers (>20) of threads were used; subprocesses would
|
as `fork` was unreliable when large numbers (>20) of threads were used; subprocesses would
|
||||||
hang and never complete.
|
hang and never complete.
|
||||||
|
|
||||||
@@ -6198,7 +6253,7 @@ then filters the list to only those in `<PeopleContactGroupItem>`; quota limits
|
|||||||
|
|
||||||
6.28.03
|
6.28.03
|
||||||
|
|
||||||
Build MacOS x86_64 and arm64 executables.
|
Build macOS x86_64 and arm64 executables.
|
||||||
|
|
||||||
6.28.02
|
6.28.02
|
||||||
|
|
||||||
@@ -6545,7 +6600,7 @@ Added command that allows checking if a user is a member of specific groups and
|
|||||||
|
|
||||||
6.26.00
|
6.26.00
|
||||||
|
|
||||||
Build MacOS universal version.
|
Build macOS universal version.
|
||||||
|
|
||||||
* Upgraded to OpenSSL 3.0.5 where possible.
|
* Upgraded to OpenSSL 3.0.5 where possible.
|
||||||
|
|
||||||
@@ -11387,7 +11442,7 @@ ID of the created Team Drive as output. This will be useful in scripts that crea
|
|||||||
want to perform subsequent GAM command on the Team Drive. This ID will only be valid when the return code
|
want to perform subsequent GAM command on the Team Drive. This ID will only be valid when the return code
|
||||||
of the command is 0; program accordingly.
|
of the command is 0; program accordingly.
|
||||||
```
|
```
|
||||||
Linux/MacOS
|
Linux/macOS
|
||||||
teamDriveId=`gam user user@domain.com create teamdrive ... returnidonly`
|
teamDriveId=`gam user user@domain.com create teamdrive ... returnidonly`
|
||||||
Windows PowerShell
|
Windows PowerShell
|
||||||
$teamDriveId = & gam user user@domain.com create teamdrive ... returnidonly`
|
$teamDriveId = & gam user user@domain.com create teamdrive ... returnidonly`
|
||||||
@@ -11479,7 +11534,7 @@ file ID of the created file as output. This will be useful in scripts that creat
|
|||||||
want to perform subsequent GAM command on the file. This file ID will only be valid when the return code
|
want to perform subsequent GAM command on the file. This file ID will only be valid when the return code
|
||||||
of the command is 0; program accordingly.
|
of the command is 0; program accordingly.
|
||||||
```
|
```
|
||||||
Linux/MacOS
|
Linux/macOS
|
||||||
fileId=`gam user user@domain.com create drivefile ... returnidonly`
|
fileId=`gam user user@domain.com create drivefile ... returnidonly`
|
||||||
Windows PowerShell
|
Windows PowerShell
|
||||||
$fileId = & gam user user@domain.com create drivefile ... returnidonly`
|
$fileId = & gam user user@domain.com create drivefile ... returnidonly`
|
||||||
@@ -15562,7 +15617,7 @@ gam print group-members [todrive [<ToDriveAttribute>]]
|
|||||||
|
|
||||||
4.55.44
|
4.55.44
|
||||||
|
|
||||||
Improve MacOS version of GAM's use of OpenSSL 1.0.2n.
|
Improve macOS version of GAM's use of OpenSSL 1.0.2n.
|
||||||
Recode pyinstaller .spec files.
|
Recode pyinstaller .spec files.
|
||||||
|
|
||||||
4.55.43
|
4.55.43
|
||||||
@@ -15589,7 +15644,7 @@ Fixed bug that made some gam print commands throw an exception.
|
|||||||
|
|
||||||
4.55.40
|
4.55.40
|
||||||
|
|
||||||
Update MacOS version of GAM to use OpenSSL 1.0.2n.
|
Update macOS version of GAM to use OpenSSL 1.0.2n.
|
||||||
|
|
||||||
4.55.39
|
4.55.39
|
||||||
|
|
||||||
@@ -18415,7 +18470,7 @@ Changed gam info user formatjson to show licenses in SKU ID (SKU Display Name) f
|
|||||||
4.42.00
|
4.42.00
|
||||||
|
|
||||||
Fixed problem where control-C was not recognized when multiple processes were running via gam batch/csv.
|
Fixed problem where control-C was not recognized when multiple processes were running via gam batch/csv.
|
||||||
Gam terminates cleanly on Linux/MacOS when you hit control-C in this situation; on Windows exceptions are
|
Gam terminates cleanly on Linux/macOS when you hit control-C in this situation; on Windows exceptions are
|
||||||
thrown but Gam does terminate.
|
thrown but Gam does terminate.
|
||||||
|
|
||||||
4.41.08
|
4.41.08
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ OPTIONS:
|
|||||||
-d Directory where gam folder will be installed. Default is \$HOME/bin/
|
-d Directory where gam folder will be installed. Default is \$HOME/bin/
|
||||||
-a Architecture to install (x86_64, arm64). Default is to detect your arch with "uname -m".
|
-a Architecture to install (x86_64, arm64). Default is to detect your arch with "uname -m".
|
||||||
-o OS we are running (linux, macos). Default is to detect your OS with "uname -s".
|
-o OS we are running (linux, macos). Default is to detect your OS with "uname -s".
|
||||||
-b OS version. Default is to detect on MacOS and Linux.
|
-b OS version. Default is to detect on macOS and Linux.
|
||||||
-l Just upgrade GAM to latest version. Skips project creation and auth.
|
-l Just upgrade GAM to latest version. Skips project creation and auth.
|
||||||
-p Profile update (true, false). Should script add gam command to environment. Default is true.
|
-p Profile update (true, false). Should script add gam command to environment. Default is true.
|
||||||
-u Admin user email address to use with GAM. Default is to prompt.
|
-u Admin user email address to use with GAM. Default is to prompt.
|
||||||
@@ -247,7 +247,7 @@ case $gamos in
|
|||||||
archgrep="-arm64\|-aarch64"
|
archgrep="-arm64\|-aarch64"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo_red "ERROR: this installer currently only supports x86_64 and arm64 MacOS. Looks like you're running on ${gamarch}. Exiting."
|
echo_red "ERROR: this installer currently only supports x86_64 and arm64 macOS. Looks like you're running on ${gamarch}. Exiting."
|
||||||
exit
|
exit
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -256,19 +256,19 @@ case $gamos in
|
|||||||
versionless_urls=$(echo -e "$gam_macos_urls" | \
|
versionless_urls=$(echo -e "$gam_macos_urls" | \
|
||||||
grep -e "-macos-")
|
grep -e "-macos-")
|
||||||
if [ "$versionless_urls" == "" ]; then
|
if [ "$versionless_urls" == "" ]; then
|
||||||
# versions after 7.00.38 include MacOS version info
|
# versions after 7.00.38 include macOS version info
|
||||||
gam_macos_vers=$(echo -e "$gam_macos_urls" | \
|
gam_macos_vers=$(echo -e "$gam_macos_urls" | \
|
||||||
grep --only-matching -e '-macos[0-9\.]*' | \
|
grep --only-matching -e '-macos[0-9\.]*' | \
|
||||||
cut -c 7-10)
|
cut -c 7-10)
|
||||||
for gam_mac_ver in $gam_macos_vers; do
|
for gam_mac_ver in $gam_macos_vers; do
|
||||||
if version_gt $currentversion $gam_mac_ver; then
|
if version_gt $currentversion $gam_mac_ver; then
|
||||||
download_url=$(echo -e "$gam_macos_urls" | grep "$gam_mac_ver")
|
download_url=$(echo -e "$gam_macos_urls" | grep "$gam_mac_ver")
|
||||||
echo_green "You are running MacOS ${currentversion} Using GAM compiled against ${gam_mac_ver}"
|
echo_green "You are running macOS ${currentversion} Using GAM compiled against ${gam_mac_ver}"
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
if [ -z ${download_url+x} ]; then
|
if [ -z ${download_url+x} ]; then
|
||||||
echo_red "Sorry, you are running MacOS ${osversion} but GAM on ${gamarch} requires MacOS ${gam_mac_ver} or newer. Exiting."
|
echo_red "Sorry, you are running macOS ${osversion} but GAM on ${gamarch} requires macOS ${gam_mac_ver} or newer. Exiting."
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
@@ -283,13 +283,13 @@ case $gamos in
|
|||||||
esac
|
esac
|
||||||
download_url=$(echo -e "$download_urls" | grep -e $archgrep)
|
download_url=$(echo -e "$download_urls" | grep -e $archgrep)
|
||||||
if version_gt "$osversion" "$minimum_version"; then
|
if version_gt "$osversion" "$minimum_version"; then
|
||||||
echo_green "You are running MacOS ${osversion}, good. Downloading GAM from ${download_url}."
|
echo_green "You are running macOS ${osversion}, good. Downloading GAM from ${download_url}."
|
||||||
else
|
else
|
||||||
echo_red "Sorry, you are running MacOS ${osversion} but GAM on ${gamarch} requires MacOS ${minimum_version}. Exiting."
|
echo_red "Sorry, you are running macOS ${osversion} but GAM on ${gamarch} requires macOS ${minimum_version}. Exiting."
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
if [ -z ${download_url+x} ]; then
|
if [ -z ${download_url+x} ]; then
|
||||||
echo_red "Sorry, you are running MacOS ${currentversion} but GAM on ${gamarch} requires MacOS ${minimum_version}. Exiting."
|
echo_red "Sorry, you are running macOS ${currentversion} but GAM on ${gamarch} requires macOS ${minimum_version}. Exiting."
|
||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
@@ -302,7 +302,7 @@ case $gamos in
|
|||||||
grep ".zip")
|
grep ".zip")
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo_red "Sorry, this installer currently only supports Linux and MacOS. Looks like you're running on ${gamos}. Exiting."
|
echo_red "Sorry, this installer currently only supports Linux and macOS. Looks like you're running on ${gamos}. Exiting."
|
||||||
exit
|
exit
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -368,18 +368,15 @@ if [ "$upgrade_only" = true ]; then
|
|||||||
exit
|
exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Set config command
|
|
||||||
#config_cmd="config no_browser false"
|
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
read -p "Can you run a full browser on this machine? (usually Y for MacOS, N for Linux if you SSH into this machine) " yn
|
read -p "Can you run a full browser on this machine? (usually Y for macOS, N for Linux if you SSH into this machine) " yn
|
||||||
case $yn in
|
case $yn in
|
||||||
[Yy]*)
|
[Yy]*)
|
||||||
|
"$target_gam" config no_browser false save
|
||||||
break
|
break
|
||||||
;;
|
;;
|
||||||
[Nn]*)
|
[Nn]*)
|
||||||
# config_cmd="config no_browser true"
|
"$target_gam" config no_browser true save
|
||||||
touch "$target_folder/nobrowser.txt" > /dev/null 2>&1
|
|
||||||
break
|
break
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
@@ -397,7 +394,6 @@ while true; do
|
|||||||
if [ "$adminuser" == "" ]; then
|
if [ "$adminuser" == "" ]; then
|
||||||
read -p "Please enter your Google Workspace admin email address: " adminuser
|
read -p "Please enter your Google Workspace admin email address: " adminuser
|
||||||
fi
|
fi
|
||||||
# "$target_gam" $config_cmd create project $adminuser
|
|
||||||
"$target_gam" create project $adminuser
|
"$target_gam" create project $adminuser
|
||||||
rc=$?
|
rc=$?
|
||||||
if (( $rc == 0 )); then
|
if (( $rc == 0 )); then
|
||||||
@@ -423,7 +419,6 @@ while $project_created; do
|
|||||||
read -p "Are you ready to authorize GAM to perform Google Workspace management operations as your admin account? (yes or no) " yn
|
read -p "Are you ready to authorize GAM to perform Google Workspace management operations as your admin account? (yes or no) " yn
|
||||||
case $yn in
|
case $yn in
|
||||||
[Yy]*)
|
[Yy]*)
|
||||||
# "$target_gam" $config_cmd oauth create $adminuser
|
|
||||||
"$target_gam" oauth create $adminuser
|
"$target_gam" oauth create $adminuser
|
||||||
rc=$?
|
rc=$?
|
||||||
if (( $rc == 0 )); then
|
if (( $rc == 0 )); then
|
||||||
@@ -453,7 +448,6 @@ while $admin_authorized; do
|
|||||||
read -p "Please enter the email address of a regular Google Workspace user: " regularuser
|
read -p "Please enter the email address of a regular Google Workspace user: " regularuser
|
||||||
fi
|
fi
|
||||||
echo_yellow "Great! Checking service account scopes.This will fail the first time. Follow the steps to authorize and retry. It can take a few minutes for scopes to PASS after they've been authorized in the admin console."
|
echo_yellow "Great! Checking service account scopes.This will fail the first time. Follow the steps to authorize and retry. It can take a few minutes for scopes to PASS after they've been authorized in the admin console."
|
||||||
# "$target_gam" $config_cmd user $regularuser check serviceaccount
|
|
||||||
"$target_gam" user $regularuser check serviceaccount
|
"$target_gam" user $regularuser check serviceaccount
|
||||||
rc=$?
|
rc=$?
|
||||||
if (( $rc == 0 )); then
|
if (( $rc == 0 )); then
|
||||||
@@ -475,7 +469,6 @@ while $admin_authorized; do
|
|||||||
done
|
done
|
||||||
|
|
||||||
echo_green "Here's information about your new GAM installation:"
|
echo_green "Here's information about your new GAM installation:"
|
||||||
#"$target_gam" $config_cmd save version extended
|
|
||||||
"$target_gam" version extended
|
"$target_gam" version extended
|
||||||
rc=$?
|
rc=$?
|
||||||
if (( $rc != 0 )); then
|
if (( $rc != 0 )); then
|
||||||
|
|||||||
116
src/gam.iss
Normal file
116
src/gam.iss
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
; --- 1. PREPROCESSOR DEFINITIONS ---
|
||||||
|
#define AppVersion GetEnv("GAMVERSION")
|
||||||
|
#if AppVersion == ""
|
||||||
|
#define AppVersion "7.0.0"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
; Pull architecture directly from GitHub Actions environment variable
|
||||||
|
#define RunnerArch GetEnv("RUNNER_ARCH")
|
||||||
|
|
||||||
|
[Setup]
|
||||||
|
; --- 2. CORE APPLICATION INFO ---
|
||||||
|
AppId={{D86B52B2-EFE9-4F9D-8BA3-9D84B9B2D319}
|
||||||
|
AppName=GAM7
|
||||||
|
AppVersion={#AppVersion}
|
||||||
|
AppPublisher=GAM Team - google-apps-manager@googlegroups.com
|
||||||
|
DefaultDirName={sd}\GAM7
|
||||||
|
LicenseFile=dist\gam\gam7\LICENSE
|
||||||
|
PrivilegesRequired=admin
|
||||||
|
ChangesEnvironment=yes
|
||||||
|
|
||||||
|
; Tell Inno Setup to use a custom signtool defined via the command line
|
||||||
|
SignTool=gamsigntool
|
||||||
|
|
||||||
|
; --- 3. COMPRESSION & OPTIMIZATION ---
|
||||||
|
Compression=lzma2/ultra64
|
||||||
|
SolidCompression=yes
|
||||||
|
|
||||||
|
; --- 4. DYNAMIC ARCHITECTURE CONFIGURATION ---
|
||||||
|
; GitHub Actions RUNNER_ARCH is typically uppercase "ARM64" or "X64"
|
||||||
|
#if RunnerArch == "ARM64" || RunnerArch == "arm64"
|
||||||
|
ArchitecturesAllowed=arm64
|
||||||
|
ArchitecturesInstallIn64BitMode=arm64
|
||||||
|
OutputBaseFilename=gam-{#AppVersion}-windows-arm64
|
||||||
|
#else
|
||||||
|
ArchitecturesAllowed=x64compatible
|
||||||
|
ArchitecturesInstallIn64BitMode=x64compatible
|
||||||
|
OutputBaseFilename=gam-{#AppVersion}-windows-x86_64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
[Messages]
|
||||||
|
; Custom error if an admin tries to run the ARM64 installer on an Intel machine
|
||||||
|
#if RunnerArch == "ARM64" || RunnerArch == "arm64"
|
||||||
|
WindowsVersionNotSupported=You downloaded the ARM64 version of GAM, but this computer has an Intel or AMD processor.%n%nPlease go back to the release page and download the x86_64 installer instead.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
[Files]
|
||||||
|
; --- 5. DYNAMIC FILE INCLUSION ---
|
||||||
|
Source: "dist\gam\gam7\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
|
||||||
|
|
||||||
|
[Registry]
|
||||||
|
; --- 6. PATH ENVIRONMENT VARIABLE ---
|
||||||
|
Root: HKLM; Subkey: "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"; \
|
||||||
|
ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};{app}"; \
|
||||||
|
Check: NeedsAddPath(ExpandConstant('{app}'))
|
||||||
|
|
||||||
|
[Code]
|
||||||
|
const
|
||||||
|
ERROR_SUCCESS = 0;
|
||||||
|
|
||||||
|
function MsiEnumRelatedProducts(lpUpgradeCode: string; dwReserved: Integer; iProductIndex: Integer; lpProductBuf: string): Integer;
|
||||||
|
external 'MsiEnumRelatedProductsW@msi.dll stdcall';
|
||||||
|
|
||||||
|
function UninstallWixMSI(): Boolean;
|
||||||
|
var
|
||||||
|
UpgradeCode: string;
|
||||||
|
ProductCode: string;
|
||||||
|
ResultCode: Integer;
|
||||||
|
begin
|
||||||
|
UpgradeCode := '{D86B52B2-EFE9-4F9D-8BA3-9D84B9B2D319}';
|
||||||
|
ProductCode := StringOfChar(' ', 39);
|
||||||
|
|
||||||
|
ResultCode := MsiEnumRelatedProducts(UpgradeCode, 0, 0, ProductCode);
|
||||||
|
|
||||||
|
if ResultCode = ERROR_SUCCESS then
|
||||||
|
begin
|
||||||
|
ProductCode := Trim(ProductCode);
|
||||||
|
Exec('msiexec.exe', '/x ' + ProductCode + ' /qn /norestart', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
|
||||||
|
end;
|
||||||
|
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function InitializeSetup(): Boolean;
|
||||||
|
begin
|
||||||
|
// --- Architecture Warning for Emulation ---
|
||||||
|
#if RunnerArch != "ARM64" && RunnerArch != "arm64"
|
||||||
|
if IsArm64() then
|
||||||
|
begin
|
||||||
|
if MsgBox('Notice: You are installing the Intel (x86_64) build of GAM on an ARM processor.' + #13#10#13#10 +
|
||||||
|
'While this will work via Windows emulation, it will perform worse than the native ARM64 version.' + #13#10#13#10 +
|
||||||
|
'Do you want to continue with the installation anyway?',
|
||||||
|
mbConfirmation, MB_YESNO) = idNo then
|
||||||
|
begin
|
||||||
|
Result := False;
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
UninstallWixMSI();
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function NeedsAddPath(Param: string): boolean;
|
||||||
|
var
|
||||||
|
OrigPath: string;
|
||||||
|
begin
|
||||||
|
if not RegQueryStringValue(HKEY_LOCAL_MACHINE,
|
||||||
|
'SYSTEM\CurrentControlSet\Control\Session Manager\Environment',
|
||||||
|
'Path', OrigPath)
|
||||||
|
then begin
|
||||||
|
Result := True;
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
Result := Pos(';' + Param + ';', ';' + OrigPath + ';') = 0;
|
||||||
|
end;
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
Manufacturer="GAM Team - google-apps-manager@googlegroups.com"
|
Manufacturer="GAM Team - google-apps-manager@googlegroups.com"
|
||||||
UpgradeCode="D86B52B2-EFE9-4F9D-8BA3-9D84B9B2D319">
|
UpgradeCode="D86B52B2-EFE9-4F9D-8BA3-9D84B9B2D319">
|
||||||
<Package
|
<Package
|
||||||
InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
|
InstallerVersion="500" Compressed="yes" InstallScope="perMachine" />
|
||||||
|
|
||||||
<MajorUpgrade
|
<MajorUpgrade
|
||||||
DowngradeErrorMessage=
|
DowngradeErrorMessage=
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
__author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
|
__author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
|
||||||
__version__ = '7.34.06'
|
__version__ = '7.35.00'
|
||||||
__license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
__license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
||||||
|
|
||||||
# pylint: disable=wrong-import-position
|
# pylint: disable=wrong-import-position
|
||||||
@@ -4688,7 +4688,7 @@ def writeClientCredentials(creds, filename):
|
|||||||
if filename != '-':
|
if filename != '-':
|
||||||
writeFile(filename, json.dumps(creds_data, indent=2, sort_keys=True)+'\n')
|
writeFile(filename, json.dumps(creds_data, indent=2, sort_keys=True)+'\n')
|
||||||
else:
|
else:
|
||||||
writeStdout(json.dumps(creds_data, ensure_ascii=False, sort_keys=True, indent=2)+'\n')
|
writeStdout(json.dumps(creds_data, ensure_ascii=False, indent=2, sort_keys=True)+'\n')
|
||||||
|
|
||||||
URL_SHORTENER_ENDPOINT = 'https://gam-shortn.appspot.com/create'
|
URL_SHORTENER_ENDPOINT = 'https://gam-shortn.appspot.com/create'
|
||||||
|
|
||||||
@@ -9499,6 +9499,33 @@ def getOSPlatform():
|
|||||||
pltfrm = platform.platform()
|
pltfrm = platform.platform()
|
||||||
return f'{myos} {pltfrm}'
|
return f'{myos} {pltfrm}'
|
||||||
|
|
||||||
|
def inspect_untrusted_cert(url):
|
||||||
|
"""Bypasses validation momentarily to extract the untrusted Issuer."""
|
||||||
|
parsed = urlparse(url if '://' in url else f'https://{url}')
|
||||||
|
host = parsed.hostname
|
||||||
|
port = parsed.port or 443
|
||||||
|
# Create an unverified context purely for diagnostic extraction
|
||||||
|
ctx = ssl.create_default_context()
|
||||||
|
ctx.check_hostname = False
|
||||||
|
ctx.verify_mode = ssl.CERT_NONE
|
||||||
|
try:
|
||||||
|
with socket.create_connection((host, port), timeout=5) as sock:
|
||||||
|
with ctx.wrap_socket(sock, server_hostname=host) as ssock:
|
||||||
|
der_cert = ssock.getpeercert(binary_form=True)
|
||||||
|
cert = x509.load_der_x509_certificate(der_cert, default_backend())
|
||||||
|
issuer = cert.issuer.rfc4514_string()
|
||||||
|
subject = cert.subject.rfc4514_string()
|
||||||
|
try:
|
||||||
|
san_ext = cert.extensions.get_extension_for_oid(x509.oid.ExtensionOID.SUBJECT_ALTERNATIVE_NAME)
|
||||||
|
# Loop through the list of SANs (DNS names, IP addresses, etc.)
|
||||||
|
sans = [str(name.value) for name in san_ext.value]
|
||||||
|
san_str = ", ".join(sans)
|
||||||
|
except x509.ExtensionNotFound:
|
||||||
|
san_str = "None"
|
||||||
|
return f"Untrusted Issuer: {issuer}\n Server Subject: {subject}\n SANs: {san_str}"
|
||||||
|
except Exception as e:
|
||||||
|
return f"Failed to retrieve diagnostic certificate: {e}"
|
||||||
|
|
||||||
# gam checkconnection
|
# gam checkconnection
|
||||||
def doCheckConnection():
|
def doCheckConnection():
|
||||||
|
|
||||||
@@ -9534,6 +9561,10 @@ def doCheckConnection():
|
|||||||
writeStdout(f'{not_okay}\n Connection reset by peer. {gen_firewall}\n')
|
writeStdout(f'{not_okay}\n Connection reset by peer. {gen_firewall}\n')
|
||||||
except httplib2.error.ServerNotFoundError:
|
except httplib2.error.ServerNotFoundError:
|
||||||
writeStdout(f'{not_okay}\n Failed to find server. Your DNS is probably misconfigured.\n')
|
writeStdout(f'{not_okay}\n Failed to find server. Your DNS is probably misconfigured.\n')
|
||||||
|
except ssl.SSLCertVerificationError as e:
|
||||||
|
diag_info = inspect_untrusted_cert(host)
|
||||||
|
# e.verify_message contains the specific OpenSSL error string
|
||||||
|
writeStdout(f'{not_okay}\n Certificate verification failed: {e.verify_message}\n Diagnostic Info:\n {diag_info}\nIf you are behind a firewall / proxy server that does TLS / SSL inspection you may need to point GAM at your certificate authority file by setting cacerts_pem = /path/to/your/certauth.pem in gam.cfg.\n')
|
||||||
except ssl.SSLError as e:
|
except ssl.SSLError as e:
|
||||||
if e.reason == 'SSLV3_ALERT_HANDSHAKE_FAILURE':
|
if e.reason == 'SSLV3_ALERT_HANDSHAKE_FAILURE':
|
||||||
writeStdout(f'{not_okay}\n GAM expects to connect with TLS 1.3 or newer and that failed. If your firewall / proxy server is not compatible with TLS 1.3 then you can tell GAM to allow TLS 1.2 by setting tls_min_version = TLSv1.2 in gam.cfg.\n')
|
writeStdout(f'{not_okay}\n GAM expects to connect with TLS 1.3 or newer and that failed. If your firewall / proxy server is not compatible with TLS 1.3 then you can tell GAM to allow TLS 1.2 by setting tls_min_version = TLSv1.2 in gam.cfg.\n')
|
||||||
@@ -9725,8 +9756,6 @@ def CSVFileQueueHandler(mpQueue, mpQueueStdout, mpQueueStderr, csvPF, datetimeNo
|
|||||||
GC.Values[GC.TIMEZONE] = tzinfo
|
GC.Values[GC.TIMEZONE] = tzinfo
|
||||||
GC.Values[GC.OUTPUT_TIMEFORMAT] = output_timeformat
|
GC.Values[GC.OUTPUT_TIMEFORMAT] = output_timeformat
|
||||||
clearRowFilters = False
|
clearRowFilters = False
|
||||||
# if sys.platform.startswith('win'):
|
|
||||||
# signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
||||||
if multiprocessing.get_start_method() != 'fork':
|
if multiprocessing.get_start_method() != 'fork':
|
||||||
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||||
Cmd = glclargs.GamCLArgs()
|
Cmd = glclargs.GamCLArgs()
|
||||||
@@ -9776,7 +9805,7 @@ def CSVFileQueueHandler(mpQueue, mpQueueStdout, mpQueueStderr, csvPF, datetimeNo
|
|||||||
GC.Values = dataItem
|
GC.Values = dataItem
|
||||||
csvPF.SetColumnDelimiter(GC.Values[GC.CSV_OUTPUT_COLUMN_DELIMITER])
|
csvPF.SetColumnDelimiter(GC.Values[GC.CSV_OUTPUT_COLUMN_DELIMITER])
|
||||||
csvPF.SetNoEscapeChar(GC.Values[GC.CSV_OUTPUT_NO_ESCAPE_CHAR])
|
csvPF.SetNoEscapeChar(GC.Values[GC.CSV_OUTPUT_NO_ESCAPE_CHAR])
|
||||||
csvPF.SetQuoteChar(GC.Values[GC.CSV_OUTPUT_QUOTE_CHAR])
|
# csvPF.SetQuoteChar(GC.Values[GC.CSV_OUTPUT_QUOTE_CHAR])
|
||||||
csvPF.SetSortHeaders(GC.Values[GC.CSV_OUTPUT_SORT_HEADERS])
|
csvPF.SetSortHeaders(GC.Values[GC.CSV_OUTPUT_SORT_HEADERS])
|
||||||
csvPF.SetTimestampColumn(GC.Values[GC.CSV_OUTPUT_TIMESTAMP_COLUMN])
|
csvPF.SetTimestampColumn(GC.Values[GC.CSV_OUTPUT_TIMESTAMP_COLUMN])
|
||||||
csvPF.SetHeaderFilter(GC.Values[GC.CSV_OUTPUT_HEADER_FILTER])
|
csvPF.SetHeaderFilter(GC.Values[GC.CSV_OUTPUT_HEADER_FILTER])
|
||||||
@@ -9843,8 +9872,6 @@ def StdQueueHandler(mpQueue, stdtype, gmGlobals, gcValues):
|
|||||||
except IOError as e:
|
except IOError as e:
|
||||||
systemErrorExit(FILE_ERROR_RC, fdErrorMessage(fd, GM.Globals[stdtype][GM.REDIRECT_NAME], e))
|
systemErrorExit(FILE_ERROR_RC, fdErrorMessage(fd, GM.Globals[stdtype][GM.REDIRECT_NAME], e))
|
||||||
|
|
||||||
# if sys.platform.startswith('win'):
|
|
||||||
# signal.signal(signal.SIGINT, signal.SIG_IGN)
|
|
||||||
if multiprocessing.get_start_method() != 'fork':
|
if multiprocessing.get_start_method() != 'fork':
|
||||||
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||||
GM.Globals = gmGlobals.copy()
|
GM.Globals = gmGlobals.copy()
|
||||||
@@ -9938,7 +9965,6 @@ def ProcessGAMCommandMulti(pid, numItems, logCmd, mpQueueCSVFile, mpQueueStdout,
|
|||||||
|
|
||||||
with mplock:
|
with mplock:
|
||||||
initializeLogging()
|
initializeLogging()
|
||||||
# if sys.platform.startswith('win'):
|
|
||||||
if multiprocessing.get_start_method() != 'fork':
|
if multiprocessing.get_start_method() != 'fork':
|
||||||
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||||
GM.Globals[GM.API_CALLS_RETRY_DATA] = {}
|
GM.Globals[GM.API_CALLS_RETRY_DATA] = {}
|
||||||
@@ -12480,7 +12506,7 @@ def checkServiceAccount(users):
|
|||||||
saScopes[API.DRIVE2] = saScopes[API.DRIVE3]
|
saScopes[API.DRIVE2] = saScopes[API.DRIVE3]
|
||||||
GM.Globals[GM.OAUTH2SERVICE_JSON_DATA][API.OAUTH2SA_SCOPES] = saScopes
|
GM.Globals[GM.OAUTH2SERVICE_JSON_DATA][API.OAUTH2SA_SCOPES] = saScopes
|
||||||
writeFile(GC.Values[GC.OAUTH2SERVICE_JSON],
|
writeFile(GC.Values[GC.OAUTH2SERVICE_JSON],
|
||||||
json.dumps(GM.Globals[GM.OAUTH2SERVICE_JSON_DATA], ensure_ascii=False, sort_keys=True, indent=2),
|
json.dumps(GM.Globals[GM.OAUTH2SERVICE_JSON_DATA], ensure_ascii=False, indent=2, sort_keys=True),
|
||||||
continueOnError=False)
|
continueOnError=False)
|
||||||
checkScopes = sorted(checkScopesSet)
|
checkScopes = sorted(checkScopesSet)
|
||||||
jcount = len(checkScopes)
|
jcount = len(checkScopes)
|
||||||
@@ -12967,7 +12993,7 @@ def doProcessSvcAcctKeys(mode=None, iam=None, projectId=None, clientEmail=None,
|
|||||||
except (IndexError, KeyError, SyntaxError, TypeError, ValueError) as e:
|
except (IndexError, KeyError, SyntaxError, TypeError, ValueError) as e:
|
||||||
invalidOauth2serviceJsonExit(str(e))
|
invalidOauth2serviceJsonExit(str(e))
|
||||||
GM.Globals[GM.OAUTH2SERVICE_JSON_DATA][API.OAUTH2SA_SCOPES] = GM.Globals[GM.SVCACCT_SCOPES]
|
GM.Globals[GM.OAUTH2SERVICE_JSON_DATA][API.OAUTH2SA_SCOPES] = GM.Globals[GM.SVCACCT_SCOPES]
|
||||||
oauth2service_data = json.dumps(GM.Globals[GM.OAUTH2SERVICE_JSON_DATA], ensure_ascii=False, sort_keys=True, indent=2)
|
oauth2service_data = json.dumps(GM.Globals[GM.OAUTH2SERVICE_JSON_DATA], ensure_ascii=False, indent=2, sort_keys=True)
|
||||||
writeFile(GC.Values[GC.OAUTH2SERVICE_JSON], oauth2service_data, continueOnError=False)
|
writeFile(GC.Values[GC.OAUTH2SERVICE_JSON], oauth2service_data, continueOnError=False)
|
||||||
Act.Set(Act.UPDATE)
|
Act.Set(Act.UPDATE)
|
||||||
entityActionPerformed([Ent.OAUTH2SERVICE_JSON_FILE, GC.Values[GC.OAUTH2SERVICE_JSON],
|
entityActionPerformed([Ent.OAUTH2SERVICE_JSON_FILE, GC.Values[GC.OAUTH2SERVICE_JSON],
|
||||||
@@ -13133,7 +13159,7 @@ def doCreateGCPServiceAccount():
|
|||||||
except GAPI.invalid as e:
|
except GAPI.invalid as e:
|
||||||
systemErrorExit(API_ACCESS_DENIED_RC, str(e))
|
systemErrorExit(API_ACCESS_DENIED_RC, str(e))
|
||||||
sa_info['client_id'] = token_info['issued_to']
|
sa_info['client_id'] = token_info['issued_to']
|
||||||
sa_output = json.dumps(sa_info, ensure_ascii=False, sort_keys=True, indent=2)
|
sa_output = json.dumps(sa_info, ensure_ascii=False, indent=2, sort_keys=True)
|
||||||
writeStdout(f'Writing SignJWT service account data:\n\n{sa_output}\n')
|
writeStdout(f'Writing SignJWT service account data:\n\n{sa_output}\n')
|
||||||
writeFile(GC.Values[GC.OAUTH2SERVICE_JSON], sa_output, continueOnError=False)
|
writeFile(GC.Values[GC.OAUTH2SERVICE_JSON], sa_output, continueOnError=False)
|
||||||
|
|
||||||
@@ -31661,6 +31687,7 @@ UPDATE_PRINTER_JSON_SKIP_FIELDS = ['id', 'name', 'createTime', 'orgUnitId', 'org
|
|||||||
def _getPrinterAttributes(cd, jsonDeleteFields):
|
def _getPrinterAttributes(cd, jsonDeleteFields):
|
||||||
'''get printer attributes for create/update commands'''
|
'''get printer attributes for create/update commands'''
|
||||||
body = {}
|
body = {}
|
||||||
|
returnIdOnly = False
|
||||||
showDetails = True
|
showDetails = True
|
||||||
while Cmd.ArgumentsRemaining():
|
while Cmd.ArgumentsRemaining():
|
||||||
myarg = getArgument()
|
myarg = getArgument()
|
||||||
@@ -31679,13 +31706,15 @@ def _getPrinterAttributes(cd, jsonDeleteFields):
|
|||||||
body['useDriverlessConfig'] = getBoolean()
|
body['useDriverlessConfig'] = getBoolean()
|
||||||
elif myarg == 'nodetails':
|
elif myarg == 'nodetails':
|
||||||
showDetails = False
|
showDetails = False
|
||||||
|
elif myarg == 'returnidonly':
|
||||||
|
returnIdOnly = True
|
||||||
elif myarg == 'json':
|
elif myarg == 'json':
|
||||||
body.update(getJSON(jsonDeleteFields))
|
body.update(getJSON(jsonDeleteFields))
|
||||||
else:
|
else:
|
||||||
unknownArgumentExit()
|
unknownArgumentExit()
|
||||||
if body.get('makeAndModel'):
|
if body.get('makeAndModel'):
|
||||||
body.pop('useDriverlessConfig', None)
|
body.pop('useDriverlessConfig', None)
|
||||||
return (body, showDetails)
|
return (body, showDetails, returnIdOnly)
|
||||||
|
|
||||||
PRINTER_FIELDS_CHOICE_MAP = {
|
PRINTER_FIELDS_CHOICE_MAP = {
|
||||||
'auxiliarymessages': 'auxiliaryMessages',
|
'auxiliarymessages': 'auxiliaryMessages',
|
||||||
@@ -31731,33 +31760,39 @@ def _showPrinter(cd, printer, FJQC, orgUnitId=None, showInherited=False, i=0, co
|
|||||||
showJSON(None, printer, timeObjects=PRINTER_TIME_OBJECTS)
|
showJSON(None, printer, timeObjects=PRINTER_TIME_OBJECTS)
|
||||||
Ind.Decrement()
|
Ind.Decrement()
|
||||||
|
|
||||||
# gam create printer <PrinterAttribute>+ [nodetails]
|
# gam create printer <PrinterAttribute>+ [nodetails|returnidonly]
|
||||||
def doCreatePrinter():
|
def doCreatePrinter():
|
||||||
cd = buildGAPIObject(API.DIRECTORY)
|
cd = buildGAPIObject(API.DIRECTORY)
|
||||||
parent = _getCustomersCustomerIdWithC()
|
parent = _getCustomersCustomerIdWithC()
|
||||||
body, showDetails = _getPrinterAttributes(cd, CREATE_PRINTER_JSON_SKIP_FIELDS)
|
body, showDetails, returnIdOnly = _getPrinterAttributes(cd, CREATE_PRINTER_JSON_SKIP_FIELDS)
|
||||||
if not body.get('orgUnitId'):
|
if not body.get('orgUnitId'):
|
||||||
missingArgumentExit('orgunit')
|
missingArgumentExit('orgunit')
|
||||||
try:
|
try:
|
||||||
printer = callGAPI(cd.customers().chrome().printers(), 'create',
|
printer = callGAPI(cd.customers().chrome().printers(), 'create',
|
||||||
throwReasons=[GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED],
|
throwReasons=[GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED],
|
||||||
parent=parent, body=body)
|
parent=parent, body=body)
|
||||||
|
if returnIdOnly:
|
||||||
|
writeStdout(f"{printer['id']}\n")
|
||||||
|
return
|
||||||
entityActionPerformed([Ent.PRINTER, printer['id']])
|
entityActionPerformed([Ent.PRINTER, printer['id']])
|
||||||
if showDetails:
|
if showDetails:
|
||||||
_showPrinter(cd, printer, None)
|
_showPrinter(cd, printer, None)
|
||||||
except (GAPI.invalidArgument, GAPI.permissionDenied) as e:
|
except (GAPI.invalidArgument, GAPI.permissionDenied) as e:
|
||||||
entityActionFailedWarning([Ent.PRINTER, None], str(e))
|
entityActionFailedWarning([Ent.PRINTER, None], str(e))
|
||||||
|
|
||||||
# gam update printer <PrinterID> <PrinterAttribute>+ [nodetails]
|
# gam update printer <PrinterID> <PrinterAttribute>+ [nodetails|returnidonly]
|
||||||
def doUpdatePrinter():
|
def doUpdatePrinter():
|
||||||
name, printerId, cd = _getPrinterID()
|
name, printerId, cd = _getPrinterID()
|
||||||
body, showDetails = _getPrinterAttributes(cd, UPDATE_PRINTER_JSON_SKIP_FIELDS)
|
body, showDetails, returnIdOnly = _getPrinterAttributes(cd, UPDATE_PRINTER_JSON_SKIP_FIELDS)
|
||||||
updateMask = ','.join(list(body.keys()))
|
updateMask = ','.join(list(body.keys()))
|
||||||
# note clearMask seems unnecessary. Updating field to '' clears it.
|
# note clearMask seems unnecessary. Updating field to '' clears it.
|
||||||
try:
|
try:
|
||||||
printer = callGAPI(cd.customers().chrome().printers(), 'patch',
|
printer = callGAPI(cd.customers().chrome().printers(), 'patch',
|
||||||
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED],
|
throwReasons=[GAPI.NOT_FOUND, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED],
|
||||||
name=name, updateMask=updateMask, body=body)
|
name=name, updateMask=updateMask, body=body)
|
||||||
|
if returnIdOnly:
|
||||||
|
writeStdout(f"{printer['id']}\n")
|
||||||
|
return
|
||||||
entityActionPerformed([Ent.PRINTER, printerId])
|
entityActionPerformed([Ent.PRINTER, printerId])
|
||||||
if showDetails:
|
if showDetails:
|
||||||
_showPrinter(cd, printer, None)
|
_showPrinter(cd, printer, None)
|
||||||
@@ -37770,8 +37805,7 @@ def _cleanPolicy(policy, add_warnings, no_appnames,
|
|||||||
def _showPolicy(policy, FJQC, i=0, count=0):
|
def _showPolicy(policy, FJQC, i=0, count=0):
|
||||||
if FJQC is not None and FJQC.formatJSON:
|
if FJQC is not None and FJQC.formatJSON:
|
||||||
printLine(json.dumps(cleanJSON(policy, timeObjects=CIPOLICY_TIME_OBJECTS),
|
printLine(json.dumps(cleanJSON(policy, timeObjects=CIPOLICY_TIME_OBJECTS),
|
||||||
ensure_ascii=False,
|
ensure_ascii=False, sort_keys=True))
|
||||||
sort_keys=True))
|
|
||||||
return
|
return
|
||||||
printEntity([Ent.POLICY, policy['name']], i, count)
|
printEntity([Ent.POLICY, policy['name']], i, count)
|
||||||
Ind.Increment()
|
Ind.Increment()
|
||||||
@@ -37784,10 +37818,11 @@ def _showPolicies(policies, FJQC, add_warnings, no_appnames,
|
|||||||
groupEmailPattern, orgUnitPathPattern,
|
groupEmailPattern, orgUnitPathPattern,
|
||||||
cd, groups_ci):
|
cd, groups_ci):
|
||||||
count = len(policies)
|
count = len(policies)
|
||||||
if groupEmailPattern is None and orgUnitPathPattern is None:
|
if FJQC is None or not FJQC.formatJSON:
|
||||||
performActionNumItems(count, Ent.POLICY)
|
if groupEmailPattern is None and orgUnitPathPattern is None:
|
||||||
else:
|
performActionNumItems(count, Ent.POLICY)
|
||||||
performActionModifierNumItems(Msg.MAXIMUM_OF, count, Ent.POLICY)
|
else:
|
||||||
|
performActionModifierNumItems(Msg.MAXIMUM_OF, count, Ent.POLICY)
|
||||||
Ind.Increment()
|
Ind.Increment()
|
||||||
i = 0
|
i = 0
|
||||||
for policy in policies:
|
for policy in policies:
|
||||||
@@ -37998,8 +38033,7 @@ def doPrintShowCIPolicies():
|
|||||||
elif csvPF.CheckRowTitles(row):
|
elif csvPF.CheckRowTitles(row):
|
||||||
csvPF.WriteRowNoFilter({'name': policy['name'],
|
csvPF.WriteRowNoFilter({'name': policy['name'],
|
||||||
'JSON': json.dumps(cleanJSON(policy, timeObjects=CIPOLICY_TIME_OBJECTS),
|
'JSON': json.dumps(cleanJSON(policy, timeObjects=CIPOLICY_TIME_OBJECTS),
|
||||||
ensure_ascii=False,
|
ensure_ascii=False, sort_keys=True)})
|
||||||
sort_keys=True)})
|
|
||||||
|
|
||||||
_checkPoliciesWithDASA()
|
_checkPoliciesWithDASA()
|
||||||
ci = buildGAPIObject(API.CLOUDIDENTITY_POLICY)
|
ci = buildGAPIObject(API.CLOUDIDENTITY_POLICY)
|
||||||
@@ -39636,8 +39670,7 @@ def doPrintShowBuildings():
|
|||||||
else:
|
else:
|
||||||
if (not csvPF.rowFilter and not csvPF.rowDropFilter) or csvPF.CheckRowTitles(row):
|
if (not csvPF.rowFilter and not csvPF.rowDropFilter) or csvPF.CheckRowTitles(row):
|
||||||
csvPF.WriteRowNoFilter({'buildingId': building['buildingId'],
|
csvPF.WriteRowNoFilter({'buildingId': building['buildingId'],
|
||||||
'JSON': json.dumps(cleanJSON(building),
|
'JSON': json.dumps(cleanJSON(building), ensure_ascii=False, sort_keys=True)})
|
||||||
ensure_ascii=False, sort_keys=True)})
|
|
||||||
if csvPF:
|
if csvPF:
|
||||||
csvPF.writeCSVfile('Buildings')
|
csvPF.writeCSVfile('Buildings')
|
||||||
|
|
||||||
@@ -40545,8 +40578,8 @@ def _printShowCalendarACLs(cal, user, entityType, calId, i, count, csvPF, FJQC,
|
|||||||
if not FJQC.formatJSON:
|
if not FJQC.formatJSON:
|
||||||
csvPF.WriteRowTitles(row)
|
csvPF.WriteRowTitles(row)
|
||||||
elif csvPF.CheckRowTitles(row):
|
elif csvPF.CheckRowTitles(row):
|
||||||
row = {'calendarId': calId, 'JSON': json.dumps(cleanJSON(rule),
|
row = {'calendarId': calId,
|
||||||
ensure_ascii=False, sort_keys=False)}
|
'JSON': json.dumps(cleanJSON(rule), ensure_ascii=False, sort_keys=False)}
|
||||||
if user:
|
if user:
|
||||||
row['primaryEmail'] = user
|
row['primaryEmail'] = user
|
||||||
if addCSVData:
|
if addCSVData:
|
||||||
@@ -40565,8 +40598,8 @@ def _printShowCalendarACLs(cal, user, entityType, calId, i, count, csvPF, FJQC,
|
|||||||
if not FJQC.formatJSON:
|
if not FJQC.formatJSON:
|
||||||
csvPF.WriteRowTitles(row)
|
csvPF.WriteRowTitles(row)
|
||||||
elif csvPF.CheckRowTitles(row):
|
elif csvPF.CheckRowTitles(row):
|
||||||
row = {'resourceId': user, 'resourceEmail': calId, 'JSON': json.dumps(cleanJSON(rule),
|
row = {'resourceId': user, 'resourceEmail': calId,
|
||||||
ensure_ascii=False, sort_keys=False)}
|
'JSON': json.dumps(cleanJSON(rule), ensure_ascii=False, sort_keys=False)}
|
||||||
if addCSVData:
|
if addCSVData:
|
||||||
row.update(addCSVData)
|
row.update(addCSVData)
|
||||||
csvPF.WriteRowNoFilter(row)
|
csvPF.WriteRowNoFilter(row)
|
||||||
@@ -46009,10 +46042,13 @@ def doCreateGuestUser():
|
|||||||
checkForExtraneousArguments()
|
checkForExtraneousArguments()
|
||||||
try:
|
try:
|
||||||
result = callGAPI(cd.users(), 'createGuest',
|
result = callGAPI(cd.users(), 'createGuest',
|
||||||
throwReasons=[GAPI.FAILED_PRECONDITION],
|
throwReasons=[GAPI.FAILED_PRECONDITION, GAPI.INVALID_ARGUMENT],
|
||||||
body=body)
|
body=body)
|
||||||
entityActionPerformed([Ent.GUEST_USER, result['primaryGuestEmail']])
|
entityActionPerformed([Ent.GUEST_USER, body['primaryGuestEmail']])
|
||||||
except (GAPI.failedPrecondition) as e:
|
Ind.Increment()
|
||||||
|
showJSON(None, result)
|
||||||
|
Ind.Decrement()
|
||||||
|
except (GAPI.failedPrecondition, GAPI.invalidArgument) as e:
|
||||||
entityActionFailedExit([Ent.GUEST_USER, body['primaryGuestEmail']], str(e))
|
entityActionFailedExit([Ent.GUEST_USER, body['primaryGuestEmail']], str(e))
|
||||||
|
|
||||||
# gam <UserTypeEntity> update user <UserAttribute>*
|
# gam <UserTypeEntity> update user <UserAttribute>*
|
||||||
@@ -46624,8 +46660,9 @@ USER_FIELDS_CHOICE_MAP = {
|
|||||||
'firstname': 'name.givenName',
|
'firstname': 'name.givenName',
|
||||||
'fullname': 'name.fullName',
|
'fullname': 'name.fullName',
|
||||||
'gal': 'includeInGlobalAddressList',
|
'gal': 'includeInGlobalAddressList',
|
||||||
'givenname': 'name.givenName',
|
|
||||||
'gender': ['gender.type', 'gender.customGender', 'gender.addressMeAs'],
|
'gender': ['gender.type', 'gender.customGender', 'gender.addressMeAs'],
|
||||||
|
'givenname': 'name.givenName',
|
||||||
|
'guestaccountinfo': 'guestAccountInfo',
|
||||||
'id': 'id',
|
'id': 'id',
|
||||||
'im': 'ims',
|
'im': 'ims',
|
||||||
'ims': 'ims',
|
'ims': 'ims',
|
||||||
@@ -46635,6 +46672,7 @@ USER_FIELDS_CHOICE_MAP = {
|
|||||||
'isdelegatedadmin': ['isAdmin', 'isDelegatedAdmin'],
|
'isdelegatedadmin': ['isAdmin', 'isDelegatedAdmin'],
|
||||||
'isenforcedin2sv': 'isEnforcedIn2Sv',
|
'isenforcedin2sv': 'isEnforcedIn2Sv',
|
||||||
'isenrolledin2sv': 'isEnrolledIn2Sv',
|
'isenrolledin2sv': 'isEnrolledIn2Sv',
|
||||||
|
'isguestuser': 'isGuestUser',
|
||||||
'is2svenforced': 'isEnforcedIn2Sv',
|
'is2svenforced': 'isEnforcedIn2Sv',
|
||||||
'is2svenrolled': 'isEnrolledIn2Sv',
|
'is2svenrolled': 'isEnrolledIn2Sv',
|
||||||
'ismailboxsetup': 'isMailboxSetup',
|
'ismailboxsetup': 'isMailboxSetup',
|
||||||
@@ -48318,8 +48356,7 @@ def doPrintShowInboundSSOProfiles():
|
|||||||
csvPF.WriteRowTitles(row)
|
csvPF.WriteRowTitles(row)
|
||||||
elif csvPF.CheckRowTitles(row):
|
elif csvPF.CheckRowTitles(row):
|
||||||
csvPF.WriteRowNoFilter({'name': profile['name'],
|
csvPF.WriteRowNoFilter({'name': profile['name'],
|
||||||
'JSON': json.dumps(cleanJSON(profile),
|
'JSON': json.dumps(cleanJSON(profile), ensure_ascii=False, sort_keys=True)})
|
||||||
ensure_ascii=False, sort_keys=True)})
|
|
||||||
if csvPF:
|
if csvPF:
|
||||||
csvPF.writeCSVfile('Inbound SSO Profiles')
|
csvPF.writeCSVfile('Inbound SSO Profiles')
|
||||||
|
|
||||||
@@ -48800,8 +48837,7 @@ def doPrintShowInboundSSOAssignments():
|
|||||||
csvPF.WriteRowTitles(row)
|
csvPF.WriteRowTitles(row)
|
||||||
elif csvPF.CheckRowTitles(row):
|
elif csvPF.CheckRowTitles(row):
|
||||||
csvPF.WriteRowNoFilter({'name': assignment['name'],
|
csvPF.WriteRowNoFilter({'name': assignment['name'],
|
||||||
'JSON': json.dumps(cleanJSON(assignment),
|
'JSON': json.dumps(cleanJSON(assignment), ensure_ascii=False, sort_keys=True)})
|
||||||
ensure_ascii=False, sort_keys=True)})
|
|
||||||
if csvPF:
|
if csvPF:
|
||||||
csvPF.writeCSVfile('Inbound SSO Assignments')
|
csvPF.writeCSVfile('Inbound SSO Assignments')
|
||||||
|
|
||||||
@@ -68168,8 +68204,7 @@ def printShowDriveLabelPermissions(users, useAdminAccess=False):
|
|||||||
csvPF.WriteRowTitles(row)
|
csvPF.WriteRowTitles(row)
|
||||||
elif csvPF.CheckRowTitles(row):
|
elif csvPF.CheckRowTitles(row):
|
||||||
row = {'User': user, 'name': labelperm['name']}
|
row = {'User': user, 'name': labelperm['name']}
|
||||||
row['JSON'] = json.dumps(cleanJSON(labelperm),
|
row['JSON'] = json.dumps(cleanJSON(labelperm), ensure_ascii=False, sort_keys=True)
|
||||||
ensure_ascii=False, sort_keys=True)
|
|
||||||
csvPF.WriteRowNoFilter(row)
|
csvPF.WriteRowNoFilter(row)
|
||||||
except (GAPI.permissionDenied, GAPI.notFound) as e:
|
except (GAPI.permissionDenied, GAPI.notFound) as e:
|
||||||
entityActionFailedWarning(kvList, str(e), j, jcount)
|
entityActionFailedWarning(kvList, str(e), j, jcount)
|
||||||
@@ -70905,8 +70940,7 @@ def printShowGroupTree(users):
|
|||||||
row = {'User': user, 'Group': groupEmail, 'Name': group['name']}
|
row = {'User': user, 'Group': groupEmail, 'Name': group['name']}
|
||||||
if rolesSet:
|
if rolesSet:
|
||||||
row['Role'] = role
|
row['Role'] = role
|
||||||
row['JSON'] = json.dumps(cleanJSON(groupInfo),
|
row['JSON'] = json.dumps(cleanJSON(groupInfo), ensure_ascii=False, sort_keys=True)
|
||||||
ensure_ascii=False, sort_keys=True)
|
|
||||||
csvPF.WriteRowNoFilter(row)
|
csvPF.WriteRowNoFilter(row)
|
||||||
Ind.Decrement()
|
Ind.Decrement()
|
||||||
if csvPF:
|
if csvPF:
|
||||||
@@ -71335,6 +71369,13 @@ def updatePhoto(users):
|
|||||||
continue
|
continue
|
||||||
body = {'photoData': base64.urlsafe_b64encode(image_data).decode(UTF8)}
|
body = {'photoData': base64.urlsafe_b64encode(image_data).decode(UTF8)}
|
||||||
try:
|
try:
|
||||||
|
try:
|
||||||
|
callGAPI(cd.users().photos(), 'delete',
|
||||||
|
bailOnInternalError=True,
|
||||||
|
throwReasons=[GAPI.USER_NOT_FOUND, GAPI.FORBIDDEN, GAPI.PHOTO_NOT_FOUND, GAPI.INTERNAL_ERROR],
|
||||||
|
userKey=user)
|
||||||
|
except (GAPI.photoNotFound, GAPI.internalError):
|
||||||
|
pass
|
||||||
callGAPI(cd.users().photos(), 'update',
|
callGAPI(cd.users().photos(), 'update',
|
||||||
throwReasons=[GAPI.USER_NOT_FOUND, GAPI.FORBIDDEN, GAPI.INVALID_INPUT, GAPI.CONDITION_NOT_MET],
|
throwReasons=[GAPI.USER_NOT_FOUND, GAPI.FORBIDDEN, GAPI.INVALID_INPUT, GAPI.CONDITION_NOT_MET],
|
||||||
userKey=user, body=body, fields='')
|
userKey=user, body=body, fields='')
|
||||||
@@ -78628,7 +78669,8 @@ def _showTask(tasklist, task, j=0, jcount=0, FJQC=None, compact=False):
|
|||||||
task['tasklistId'] = tasklist
|
task['tasklistId'] = tasklist
|
||||||
task['taskId'] = f"{tasklist}/{task['id']}"
|
task['taskId'] = f"{tasklist}/{task['id']}"
|
||||||
if FJQC is not None and FJQC.formatJSON:
|
if FJQC is not None and FJQC.formatJSON:
|
||||||
printLine(json.dumps(cleanJSON(task, skipObjects=TASK_SKIP_OBJECTS, timeObjects=TASK_TIME_OBJECTS), ensure_ascii=False, sort_keys=True))
|
printLine(json.dumps(cleanJSON(task, skipObjects=TASK_SKIP_OBJECTS, timeObjects=TASK_TIME_OBJECTS),
|
||||||
|
ensure_ascii=False, sort_keys=True))
|
||||||
return
|
return
|
||||||
printEntity([Ent.TASK, task['taskId']], j, jcount)
|
printEntity([Ent.TASK, task['taskId']], j, jcount)
|
||||||
Ind.Increment()
|
Ind.Increment()
|
||||||
@@ -78972,7 +79014,8 @@ TASKLIST_TIME_OBJECTS = ['updated']
|
|||||||
|
|
||||||
def _showTasklist(tasklist, j=0, jcount=0, FJQC=None):
|
def _showTasklist(tasklist, j=0, jcount=0, FJQC=None):
|
||||||
if FJQC is not None and FJQC.formatJSON:
|
if FJQC is not None and FJQC.formatJSON:
|
||||||
printLine(json.dumps(cleanJSON(tasklist, skipObjects=TASKLIST_SKIP_OBJECTS, timeObjects=TASKLIST_TIME_OBJECTS), ensure_ascii=False, sort_keys=True))
|
printLine(json.dumps(cleanJSON(tasklist, skipObjects=TASKLIST_SKIP_OBJECTS, timeObjects=TASKLIST_TIME_OBJECTS),
|
||||||
|
ensure_ascii=False, sort_keys=True))
|
||||||
return
|
return
|
||||||
printEntity([Ent.TASKLIST, tasklist['id']], j, jcount)
|
printEntity([Ent.TASKLIST, tasklist['id']], j, jcount)
|
||||||
Ind.Increment()
|
Ind.Increment()
|
||||||
|
|||||||
@@ -1,3 +1,72 @@
|
|||||||
|
#
|
||||||
|
# This is a custom certificate authority bundle for GAM
|
||||||
|
# It's composed of Let's Encrypt Root CAs and Google's
|
||||||
|
# certificate bundle. This should be the minimal list of
|
||||||
|
# CAs required to talk to Google and Github.
|
||||||
|
|
||||||
|
# Operating CA: Let's Encrypt
|
||||||
|
# Issuer: C = US, O = Internet Security Research Group, CN = ISRG Root X1
|
||||||
|
# Subject: C = US, O = Internet Security Research Group, CN = ISRG Root X1
|
||||||
|
# Label: "ISRG Root X1"
|
||||||
|
# Serial: 172886928669790476064670243504169061120
|
||||||
|
# MD5 Fingerprint: 0c:d2:f9:e0:da:17:73:e9:ed:86:4d:a5:e3:70:e7:4e
|
||||||
|
# SHA1 Fingerprint: ca:bd:2a:79:a1:07:6a:31:f2:1d:25:36:35:cb:03:9d:43:29:a5:e8
|
||||||
|
# SHA256 Fingerprint: 96:bc:ec:06:26:49:76:f3:74:60:77:9a:cf:28:c5:a7:cf:e8:a3:c0:aa:e1:1a:8f:fc:ee:05:c0:bd:df:08:c6
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
|
||||||
|
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
|
||||||
|
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
|
||||||
|
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
|
||||||
|
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
|
||||||
|
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
|
||||||
|
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
|
||||||
|
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
|
||||||
|
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
|
||||||
|
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
|
||||||
|
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
|
||||||
|
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
|
||||||
|
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
|
||||||
|
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
|
||||||
|
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
|
||||||
|
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
|
||||||
|
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
|
||||||
|
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
|
||||||
|
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
|
||||||
|
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
|
||||||
|
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
|
||||||
|
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
|
||||||
|
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
|
||||||
|
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
|
||||||
|
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
|
||||||
|
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
|
||||||
|
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
|
||||||
|
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
|
||||||
|
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
# Operating CA: Let's Encrypt
|
||||||
|
# Issuer: C = US, O = Internet Security Research Group, CN = ISRG Root X2
|
||||||
|
# Subject: C = US, O = Internet Security Research Group, CN = ISRG Root X2
|
||||||
|
# Label: "ISRG Root X2"
|
||||||
|
# Serial: 87493402998870891108772069816698636114
|
||||||
|
# MD5 Fingerprint: d3:9e:c4:1e:23:3c:a6:df:cf:a3:7e:6d:e0:14:e6:e5
|
||||||
|
# SHA1 Fingerprint: bd:b1:b9:3c:d5:97:8d:45:c6:26:14:55:f8:db:95:c7:5a:d1:53:af
|
||||||
|
# SHA256 Fingerprint: 69:72:9b:8e:15:a8:6e:fc:17:7a:57:af:b7:17:1d:fc:64:ad:d2:8c:2f:ca:8c:f1:50:7e:34:45:3c:cb:14:70
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIICGzCCAaGgAwIBAgIQQdKd0XLq7qeAwSxs6S+HUjAKBggqhkjOPQQDAzBPMQsw
|
||||||
|
CQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJuZXQgU2VjdXJpdHkgUmVzZWFyY2gg
|
||||||
|
R3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBYMjAeFw0yMDA5MDQwMDAwMDBaFw00
|
||||||
|
MDA5MTcxNjAwMDBaME8xCzAJBgNVBAYTAlVTMSkwJwYDVQQKEyBJbnRlcm5ldCBT
|
||||||
|
ZWN1cml0eSBSZXNlYXJjaCBHcm91cDEVMBMGA1UEAxMMSVNSRyBSb290IFgyMHYw
|
||||||
|
EAYHKoZIzj0CAQYFK4EEACIDYgAEzZvVn4CDCuwJSvMWSj5cz3es3mcFDR0HttwW
|
||||||
|
+1qLFNvicWDEukWVEYmO6gbf9yoWHKS5xcUy4APgHoIYOIvXRdgKam7mAHf7AlF9
|
||||||
|
ItgKbppbd9/w+kHsOdx1ymgHDB/qo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0T
|
||||||
|
AQH/BAUwAwEB/zAdBgNVHQ4EFgQUfEKWrt5LSDv6kviejM9ti6lyN5UwCgYIKoZI
|
||||||
|
zj0EAwMDaAAwZQIwe3lORlCEwkSHRhtFcP9Ymd70/aTSVaYgLXTWNLxBo1BfASdW
|
||||||
|
tL4ndQavEi51mI38AjEAi/V3bNTIZargCyzuFJ0nN6T5U6VR5CmD1/iQMVtCnwr1
|
||||||
|
/q4AaOeMSQ+2b1tbFfLn
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
# Operating CA: DigiCert
|
# Operating CA: DigiCert
|
||||||
# Issuer: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com
|
# Issuer: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com
|
||||||
# Subject: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com
|
# Subject: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
\deftab720
|
\deftab720
|
||||||
\pard\pardeftab720\sl276\slmult1\sa200\qc\partightenfactor0
|
\pard\pardeftab720\sl276\slmult1\sa200\qc\partightenfactor0
|
||||||
|
|
||||||
\f0\fs22 \cf0 Copyright 2025 Jay Lee\
|
\f0\fs22 \cf0 Copyright 2026 Jay Lee\
|
||||||
\pard\pardeftab720\sa200\qc\partightenfactor0
|
\pard\pardeftab720\sa200\qc\partightenfactor0
|
||||||
|
|
||||||
\f1\b \cf0 Licensed under the Apache License, Version 2.0 (the "License");\
|
\f1\b \cf0 Licensed under the Apache License, Version 2.0 (the "License");\
|
||||||
|
|||||||
@@ -99,14 +99,25 @@ Typically, you will enclose the entire list in double quotes and quote each item
|
|||||||
## Manage printers
|
## Manage printers
|
||||||
When creating a printer you must specify: `displayname`, `ou`, `uri` and `makeandmodel` or `driverless`.
|
When creating a printer you must specify: `displayname`, `ou`, `uri` and `makeandmodel` or `driverless`.
|
||||||
```
|
```
|
||||||
gam create printer <PrinterAttribute>+ [nodetails]
|
gam create printer <PrinterAttribute>+ [nodetails|returnidonly]
|
||||||
gam update printer <PrinterID> <PrinterAttribute>+ [nodetails]
|
gam update printer <PrinterID> <PrinterAttribute>+ [nodetails|returnidonly]
|
||||||
gam delete printer
|
gam delete printer
|
||||||
<PrinterIDList>|
|
<PrinterIDList>|
|
||||||
<FileSelector>|
|
<FileSelector>|
|
||||||
<CSVFileSelector>
|
<CSVFileSelector>
|
||||||
```
|
```
|
||||||
By default, when a printer is created/updated, GAM outputs details of the printer; the `nodetails` option suppresses this output.
|
By default, when a printer is created/updated, GAM outputs details of the printer.
|
||||||
|
* `nodetails` - Suppress the datails output.
|
||||||
|
* `returnidonly` - Display just the printer ID of the created printer as output
|
||||||
|
|
||||||
|
To retrieve the printer ID with `returnidonly`:
|
||||||
|
```
|
||||||
|
Linux/MacOS
|
||||||
|
printerId=$(gam create printer ... returnidonly)
|
||||||
|
Windows PowerShell
|
||||||
|
$printerId = & gam create printer ... returnidonly
|
||||||
|
```
|
||||||
|
The printer ID will only be valid when the return code of the command is 0; program accordingly.
|
||||||
|
|
||||||
## Display printers
|
## Display printers
|
||||||
Display information about a single printer.
|
Display information about a single printer.
|
||||||
|
|||||||
@@ -10,6 +10,73 @@ Add the `-s` option to the end of the above commands to suppress creating the `g
|
|||||||
|
|
||||||
See [Downloads-Installs-GAM7](https://github.com/GAM-team/GAM/wiki/Downloads-Installs) for Windows or other options, including manual installation
|
See [Downloads-Installs-GAM7](https://github.com/GAM-team/GAM/wiki/Downloads-Installs) for Windows or other options, including manual installation
|
||||||
|
|
||||||
|
### 7.34.13
|
||||||
|
|
||||||
|
Fixed bug in `gam info policies <CIPolicyNameEntity> ... formatjson` where extraneous line
|
||||||
|
`Show Info 1 Policy` was displayed.
|
||||||
|
|
||||||
|
### 7.34.12
|
||||||
|
|
||||||
|
Fixed build errors that prevented Windows zip files from being created.
|
||||||
|
|
||||||
|
Added option `returnidonly` to `gam create|update printer` that causes GAM to return just the ID
|
||||||
|
of the printer.
|
||||||
|
|
||||||
|
### 7.34.11
|
||||||
|
|
||||||
|
Updated gam-install.sh script for macOS/Linux to properly config GAM when the answer to the following question is No.
|
||||||
|
```
|
||||||
|
Can you run a full browser on this machine? (usually Y for macOS, N for Linux if you SSH into this machine)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.34.10
|
||||||
|
|
||||||
|
Fixed bug where `formatjson quotechar <Character>` on the command line did not override `redirect csv <FileName> multiprocess quotechar <Character>`.
|
||||||
|
|
||||||
|
### 7.34.09
|
||||||
|
|
||||||
|
Updated `gam <UserTypeEntity> update photo` to delete the user's existing photo
|
||||||
|
before performing the update as the API update will succeed but not replace a user's existing self-set photo.
|
||||||
|
|
||||||
|
### 7.34.08
|
||||||
|
|
||||||
|
Rebuild to avoid the following error:
|
||||||
|
```
|
||||||
|
requests/__init__.py:113: RequestsDependencyWarning: urllib3 (2.6.3) or chardet (6.0.0.post1)/charset_normalizer (3.4.4) doesn't match a supported version!
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.34.07
|
||||||
|
|
||||||
|
Added the following command to create a guest user.
|
||||||
|
* See: https://support.google.com/a/answer/16558545
|
||||||
|
```
|
||||||
|
gam create guestuser <EmailAddress>
|
||||||
|
```
|
||||||
|
|
||||||
|
Added the following items to `<UserFieldName>`:
|
||||||
|
* `guestaccountinfo` - Additional guest-related metadata fields
|
||||||
|
* `isguestuser` - Indicates if the inserted user is a guest
|
||||||
|
|
||||||
|
### 7.34.06
|
||||||
|
|
||||||
|
Added option `copyfolderpermissions [<Boolean>]` to `gam <UserTypeEntity> copy|move drivefile`.
|
||||||
|
|
||||||
|
When `copyfolderpermissions false` is specified, no folder permissions are copied; this simplifies
|
||||||
|
disabling all folder permission copying.
|
||||||
|
|
||||||
|
When not specified or `copyfolderpermissions [true]` is specified, folder permissions are copied based on the following options:
|
||||||
|
```
|
||||||
|
copymergewithparentfolderpermissions [<Boolean>]
|
||||||
|
copymergedtopfolderpermissions [<Boolean>]
|
||||||
|
copytopfolderpermissions [<Boolean>]
|
||||||
|
copytopfolderiheritedpermissions [<Boolean>]
|
||||||
|
copytopfoldernoniheritedpermissions never|always|syncallfolders|syncupdatedfolders
|
||||||
|
copymergedsubfolderpermissions [<Boolean>]
|
||||||
|
copysubfolderpermissions [<Boolean>]
|
||||||
|
copysubfolderinheritedpermissions [<Boolean>]
|
||||||
|
copysubfoldernoniheritedpermissions never|always|syncallfolders|syncupdatedfolders
|
||||||
|
```
|
||||||
|
|
||||||
### 7.34.05
|
### 7.34.05
|
||||||
|
|
||||||
Updated `gam report <ActivityApplictionName>` to perform a reverse chronological sort
|
Updated `gam report <ActivityApplictionName>` to perform a reverse chronological sort
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ writes the credentials into the file oauth2.txt.
|
|||||||
gamteam@server:/Users/gamteam$ rm -f /Users/gamteam/GAMConfig/oauth2.txt
|
gamteam@server:/Users/gamteam$ rm -f /Users/gamteam/GAMConfig/oauth2.txt
|
||||||
gamteam@server:/Users/gamteam$ gam version
|
gamteam@server:/Users/gamteam$ gam version
|
||||||
WARNING: Config File: /Users/gamteam/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/gamteam/GAMConfig/oauth2.txt, Not Found
|
WARNING: Config File: /Users/gamteam/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/gamteam/GAMConfig/oauth2.txt, Not Found
|
||||||
GAM 7.34.05 - https://github.com/GAM-team/GAM - pyinstaller
|
GAM 7.34.13 - https://github.com/GAM-team/GAM - pyinstaller
|
||||||
GAM Team <google-apps-manager@googlegroups.com>
|
GAM Team <google-apps-manager@googlegroups.com>
|
||||||
Python 3.14.3 64-bit final
|
Python 3.14.3 64-bit final
|
||||||
macOS Tahoe 26.3 arm64
|
macOS Tahoe 26.3 arm64
|
||||||
@@ -1036,7 +1036,7 @@ writes the credentials into the file oauth2.txt.
|
|||||||
C:\>del C:\GAMConfig\oauth2.txt
|
C:\>del C:\GAMConfig\oauth2.txt
|
||||||
C:\>gam version
|
C:\>gam version
|
||||||
WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found
|
WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found
|
||||||
GAM 7.34.05 - https://github.com/GAM-team/GAM - pythonsource
|
GAM 7.34.13 - https://github.com/GAM-team/GAM - pythonsource
|
||||||
GAM Team <google-apps-manager@googlegroups.com>
|
GAM Team <google-apps-manager@googlegroups.com>
|
||||||
Python 3.14.3 64-bit final
|
Python 3.14.3 64-bit final
|
||||||
Windows 11 10.0.26200 AMD64
|
Windows 11 10.0.26200 AMD64
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Organizational Units
|
# Organizational Units
|
||||||
- [API documentation](#api-documentation)
|
- [API documentation](#api-documentation)
|
||||||
- [Definitions](#definitions)
|
- [Definitions](#definitions)
|
||||||
|
- [Special character issues](#special-character-issues)
|
||||||
- [Special quoting](#special-quoting)
|
- [Special quoting](#special-quoting)
|
||||||
- [Manage organizational units](#manage-organizational-units)
|
- [Manage organizational units](#manage-organizational-units)
|
||||||
- [Add users to an organizational unit](#add-users-to-an-organizational-unit)
|
- [Add users to an organizational unit](#add-users-to-an-organizational-unit)
|
||||||
@@ -50,6 +51,15 @@ For `<UserTypeEntity>`, see: [Collections of Users](Collections-Of-Users)
|
|||||||
|
|
||||||
For `<CrOSTypeEntity>`, see: [Collections of ChromeOS Devices](Collections-of-ChromeOS-Devices)
|
For `<CrOSTypeEntity>`, see: [Collections of ChromeOS Devices](Collections-of-ChromeOS-Devices)
|
||||||
|
|
||||||
|
## Special character issues
|
||||||
|
If an organizational unit name contains a `#` or a `+`, these commands will not work due to a bug
|
||||||
|
that Google does not plan to fix.
|
||||||
|
```
|
||||||
|
gam update org|ou <OrgUnitPath>
|
||||||
|
gam delete org|ou <OrgUnitPath>
|
||||||
|
gam info org|ou <OrgUnitPath>
|
||||||
|
```
|
||||||
|
|
||||||
## Special quoting
|
## Special quoting
|
||||||
You specify a single organizational unit with `org <OrgUnitPath>` and a list of organizationsl units with `orgs <OrgUnitList>`.
|
You specify a single organizational unit with `org <OrgUnitPath>` and a list of organizationsl units with `orgs <OrgUnitList>`.
|
||||||
As organizational unit paths can contain spaces, some care must be used when entering `<OrgUnitPath>` and `<OrgUnitList>`.
|
As organizational unit paths can contain spaces, some care must be used when entering `<OrgUnitPath>` and `<OrgUnitList>`.
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ gam <UserTypeEntity> copy drivefile <DriveFileEntity>
|
|||||||
[copyfilepermissions [<Boolean>]]
|
[copyfilepermissions [<Boolean>]]
|
||||||
[copyfileinheritedpermissions [<Boolean>]
|
[copyfileinheritedpermissions [<Boolean>]
|
||||||
[copyfilenoninheritedpermissions [<Boolean>]
|
[copyfilenoninheritedpermissions [<Boolean>]
|
||||||
|
[copyfolderpermissions [<Boolean>]]
|
||||||
[copymergewithparentfolderpermissions [<Boolean>]]
|
[copymergewithparentfolderpermissions [<Boolean>]]
|
||||||
[copymergedtopfolderpermissions [<Boolean>]]
|
[copymergedtopfolderpermissions [<Boolean>]]
|
||||||
[copytopfolderpermissions [<Boolean>]]
|
[copytopfolderpermissions [<Boolean>]]
|
||||||
@@ -294,6 +295,8 @@ When a folder is copied, its permissions are not copied; these options control c
|
|||||||
of the form `option [<Boolean>]`; if `<Boolean>` is omitted, `true` is assumed.
|
of the form `option [<Boolean>]`; if `<Boolean>` is omitted, `true` is assumed.
|
||||||
|
|
||||||
When copied, a target folder inherits the permissions of its parent folder; these options control whether/how GAM copies the existing source folder permissions.
|
When copied, a target folder inherits the permissions of its parent folder; these options control whether/how GAM copies the existing source folder permissions.
|
||||||
|
* `copyfolderpermissions false` - The permissions of the source folders are not copied to the target folder.
|
||||||
|
* `copyfolderpermissions true` - The permissions of the source folders are copied to the target folder based on the following options; this is the default action.
|
||||||
|
|
||||||
When `mergewithparent` is `true`:
|
When `mergewithparent` is `true`:
|
||||||
* `copymergewithparentfolderpermissions false` - The permissions of the source top folder are not not copied to the target folder; this is the default action.
|
* `copymergewithparentfolderpermissions false` - The permissions of the source top folder are not not copied to the target folder; this is the default action.
|
||||||
@@ -571,6 +574,7 @@ gam <UserTypeEntity> move drivefile <DriveFileEntity> [newfilename <DriveFileNam
|
|||||||
[createshortcutsfornonmovablefiles [<Boolean>]]
|
[createshortcutsfornonmovablefiles [<Boolean>]]
|
||||||
[duplicatefiles overwriteolder|overwriteall|duplicatename|uniquename|skip]
|
[duplicatefiles overwriteolder|overwriteall|duplicatename|uniquename|skip]
|
||||||
[duplicatefolders merge|duplicatename|uniquename|skip]
|
[duplicatefolders merge|duplicatename|uniquename|skip]
|
||||||
|
[copyfolderpermissions [<Boolean>]]
|
||||||
[copymergewithparentfolderpermissions [<Boolean>]]
|
[copymergewithparentfolderpermissions [<Boolean>]]
|
||||||
[copymergedtopfolderpermissions [<Boolean>]]
|
[copymergedtopfolderpermissions [<Boolean>]]
|
||||||
[copytopfolderpermissions [<Boolean>]]
|
[copytopfolderpermissions [<Boolean>]]
|
||||||
@@ -660,6 +664,8 @@ When a folder is moved by recreating it, its permissions are not copied by the D
|
|||||||
For options of the form `option [<Boolean>]`; if `<Boolean>` is omitted, `true` is assumed.
|
For options of the form `option [<Boolean>]`; if `<Boolean>` is omitted, `true` is assumed.
|
||||||
|
|
||||||
When recreated, a target folder inherits the permissions of its parent folder; these options control whether/how GAM copies the existing source folder permissions;
|
When recreated, a target folder inherits the permissions of its parent folder; these options control whether/how GAM copies the existing source folder permissions;
|
||||||
|
* `copyfolderpermissions false` - The permissions of the source folders are not copied to the target folder.
|
||||||
|
* `copyfolderpermissions true` - The permissions of the source folders are copied to the target folder based on the following options; this is the default action.
|
||||||
|
|
||||||
When `mergewithparent` is `true`:
|
When `mergewithparent` is `true`:
|
||||||
* `copymergewithparentfolderpermissions false` - The permissions of the source top folder are not not copied to the target folder; this is the default action.
|
* `copymergewithparentfolderpermissions false` - The permissions of the source top folder are not not copied to the target folder; this is the default action.
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# Users - Photo
|
# Users - Photo
|
||||||
- [API documentation](#api-documentation)
|
- [API documentation](#api-documentation)
|
||||||
|
- [Notes](#notes)
|
||||||
- [Definitions](#definitions)
|
- [Definitions](#definitions)
|
||||||
- [Upload a user's photo from a default file](#upload-a-users-photo-from-a-default-file)
|
- [Upload a user's photo from a default file](#upload-a-users-photo-from-a-default-file)
|
||||||
- [Upload a user's photo specifying file name](#upload-a-users-photo-specifying-file-name)
|
- [Upload a user's photo specifying file name](#upload-a-users-photo-specifying-file-name)
|
||||||
@@ -7,11 +8,16 @@
|
|||||||
- [Upload a user's photo specifying a Google Drive owner and file name](#upload-a-users-photo-specifying-a-google-drive-owner-and-file-name)
|
- [Upload a user's photo specifying a Google Drive owner and file name](#upload-a-users-photo-specifying-a-google-drive-owner-and-file-name)
|
||||||
- [Download a user's photo](#download-a-users-photo)
|
- [Download a user's photo](#download-a-users-photo)
|
||||||
- [Delete a user's photo](#delete-a-users-photo)
|
- [Delete a user's photo](#delete-a-users-photo)
|
||||||
|
- [Update photo fails to change user's photo](#update-photo-fails-to-change-users-photo)
|
||||||
- [Download a user's profile photo](Users-Profile-Photo)
|
- [Download a user's profile photo](Users-Profile-Photo)
|
||||||
|
|
||||||
## API documentation
|
## API documentation
|
||||||
* [Directory API - Users Photos](https://developers.google.com/admin-sdk/directory/reference/rest/v1/users.photos)
|
* [Directory API - Users Photos](https://developers.google.com/admin-sdk/directory/reference/rest/v1/users.photos)
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
As of version 7.34.09, `gam <UserTypeEntity> update photo` was updated to delete the user's existing photo
|
||||||
|
before performing the update as the API update will succeed but not replace a user's existing self-set photo.
|
||||||
|
|
||||||
## Definitions
|
## Definitions
|
||||||
* [`<DriveFileEntity>`](Drive-File-Selection)
|
* [`<DriveFileEntity>`](Drive-File-Selection)
|
||||||
* [`<UserTypeEntity>`](Collections-of-Users)
|
* [`<UserTypeEntity>`](Collections-of-Users)
|
||||||
@@ -81,3 +87,7 @@ By default, the Base64 encoded data is dumped to stdout.
|
|||||||
```
|
```
|
||||||
gam <UserTypeEntity> delete|del photo
|
gam <UserTypeEntity> delete|del photo
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Update photo fails to change user's photo
|
||||||
|
If you use `gam <UserTypeEntity> update photo ...` to change a user's photo and the command succeeds
|
||||||
|
but the photo doesn't change, use `gam <UserTypeEntity> delete photo` first and then do the update.
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
- [Print user list](#print-user-list)
|
- [Print user list](#print-user-list)
|
||||||
- [Display user counts](#display-user-counts)
|
- [Display user counts](#display-user-counts)
|
||||||
- [Verify domain membership](#verify-domain-membership)
|
- [Verify domain membership](#verify-domain-membership)
|
||||||
|
- [Guest Users](#guest-users)
|
||||||
|
|
||||||
## API documentation
|
## API documentation
|
||||||
* [Directory API - Users](https://developers.google.com/admin-sdk/directory/reference/rest/v1/users)
|
* [Directory API - Users](https://developers.google.com/admin-sdk/directory/reference/rest/v1/users)
|
||||||
@@ -169,6 +170,7 @@ queries "`"orgUnitPath=\'/Students/Lower\ School/2027\'`",`"orgUnitPath=\'/Stude
|
|||||||
fullname|
|
fullname|
|
||||||
gender|
|
gender|
|
||||||
givenname|firstname|
|
givenname|firstname|
|
||||||
|
guestaccountinfo|
|
||||||
id|
|
id|
|
||||||
ims|im|
|
ims|im|
|
||||||
includeinglobaladdresslist|gal|
|
includeinglobaladdresslist|gal|
|
||||||
@@ -176,6 +178,7 @@ queries "`"orgUnitPath=\'/Students/Lower\ School/2027\'`",`"orgUnitPath=\'/Stude
|
|||||||
isdelegatedadmin|admin|isadmin|
|
isdelegatedadmin|admin|isadmin|
|
||||||
isenforcedin2sv|is2svenforced|
|
isenforcedin2sv|is2svenforced|
|
||||||
isenrolledin2sv|is2svenrolled|
|
isenrolledin2sv|is2svenrolled|
|
||||||
|
isguestuser|
|
||||||
ismailboxsetup|
|
ismailboxsetup|
|
||||||
keyword|keywords|
|
keyword|keywords|
|
||||||
language|languages|
|
language|languages|
|
||||||
@@ -1384,3 +1387,11 @@ testuser1@domain.com,118080758787650801331,True,Test User 1
|
|||||||
testuserxxx@domain.com,,False,Test User XXX
|
testuserxxx@domain.com,,False,Test User XXX
|
||||||
testuser2@domain.com,107344800159717682514,True,Test User 2
|
testuser2@domain.com,107344800159717682514,True,Test User 2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Guest Users
|
||||||
|
* See: https://support.google.com/a/answer/16558545
|
||||||
|
```
|
||||||
|
gam create guestuser <EmailAddress>
|
||||||
|
```
|
||||||
|
|
||||||
|
Guest users are in the OU "/Workspace guests".
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# Version and Help
|
\# Version and Help
|
||||||
|
|
||||||
Print the current version of Gam with details
|
Print the current version of Gam with details
|
||||||
```
|
```
|
||||||
gam version
|
gam version
|
||||||
GAM 7.34.05 - https://github.com/GAM-team/GAM - pyinstaller
|
GAM 7.34.13 - https://github.com/GAM-team/GAM - pyinstaller
|
||||||
GAM Team <google-apps-manager@googlegroups.com>
|
GAM Team <google-apps-manager@googlegroups.com>
|
||||||
Python 3.14.3 64-bit final
|
Python 3.14.3 64-bit final
|
||||||
macOS Tahoe 26.3 arm64
|
macOS Tahoe 26.3 arm64
|
||||||
@@ -15,7 +15,7 @@ Time: 2026-02-15T07:51:00-08:00
|
|||||||
Print the current version of Gam with details and time offset information
|
Print the current version of Gam with details and time offset information
|
||||||
```
|
```
|
||||||
gam version timeoffset
|
gam version timeoffset
|
||||||
GAM 7.34.05 - https://github.com/GAM-team/GAM - pyinstaller
|
GAM 7.34.13 - https://github.com/GAM-team/GAM - pyinstaller
|
||||||
GAM Team <google-apps-manager@googlegroups.com>
|
GAM Team <google-apps-manager@googlegroups.com>
|
||||||
Python 3.14.3 64-bit final
|
Python 3.14.3 64-bit final
|
||||||
macOS Tahoe 26.3 arm64
|
macOS Tahoe 26.3 arm64
|
||||||
@@ -27,7 +27,7 @@ Your system time differs from www.googleapis.com by less than 1 second
|
|||||||
Print the current version of Gam with extended details and SSL information
|
Print the current version of Gam with extended details and SSL information
|
||||||
```
|
```
|
||||||
gam version extended
|
gam version extended
|
||||||
GAM 7.34.05 - https://github.com/GAM-team/GAM - pyinstaller
|
GAM 7.34.13 - https://github.com/GAM-team/GAM - pyinstaller
|
||||||
GAM Team <google-apps-manager@googlegroups.com>
|
GAM Team <google-apps-manager@googlegroups.com>
|
||||||
Python 3.14.3 64-bit final
|
Python 3.14.3 64-bit final
|
||||||
macOS Tahoe 26.3 arm64
|
macOS Tahoe 26.3 arm64
|
||||||
@@ -68,7 +68,7 @@ MacOS High Sierra 10.13.6 x86_64
|
|||||||
Path: /Users/gamteam/bin/gam7
|
Path: /Users/gamteam/bin/gam7
|
||||||
Version Check:
|
Version Check:
|
||||||
Current: 5.35.08
|
Current: 5.35.08
|
||||||
Latest: 7.34.05
|
Latest: 7.34.13
|
||||||
echo $?
|
echo $?
|
||||||
1
|
1
|
||||||
```
|
```
|
||||||
@@ -76,7 +76,7 @@ echo $?
|
|||||||
Print the current version number without details
|
Print the current version number without details
|
||||||
```
|
```
|
||||||
gam version simple
|
gam version simple
|
||||||
7.34.05
|
7.34.13
|
||||||
```
|
```
|
||||||
In Linux/MacOS you can do:
|
In Linux/MacOS you can do:
|
||||||
```
|
```
|
||||||
@@ -86,7 +86,7 @@ echo $VER
|
|||||||
Print the current version of Gam and address of this Wiki
|
Print the current version of Gam and address of this Wiki
|
||||||
```
|
```
|
||||||
gam help
|
gam help
|
||||||
GAM 7.34.05 - https://github.com/GAM-team/GAM
|
GAM 7.34.13 - https://github.com/GAM-team/GAM
|
||||||
GAM Team <google-apps-manager@googlegroups.com>
|
GAM Team <google-apps-manager@googlegroups.com>
|
||||||
Python 3.14.3 64-bit final
|
Python 3.14.3 64-bit final
|
||||||
macOS Tahoe 26.3 arm64
|
macOS Tahoe 26.3 arm64
|
||||||
|
|||||||
Reference in New Issue
Block a user