mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-28 01:41:36 +00:00
Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b818002202 | ||
|
|
33065438d8 | ||
|
|
e4880a3814 | ||
|
|
a2bd409d51 | ||
|
|
352f09fad8 | ||
|
|
221a18fc51 | ||
|
|
158138f344 | ||
|
|
0d32e451e9 | ||
|
|
38780e2ba9 | ||
|
|
f3e6afbca4 | ||
|
|
cb1c7ad6fe | ||
|
|
3ee8bf2841 | ||
|
|
71d82f0f87 | ||
|
|
398a91b987 | ||
|
|
0cf17be7ca | ||
|
|
28d76c5aee | ||
|
|
04371eee53 | ||
|
|
70cf32f6ed | ||
|
|
d2495f0ed8 | ||
|
|
2d3c1e5aa8 | ||
|
|
7855447014 | ||
|
|
0028955412 | ||
|
|
c3b59a9c10 | ||
|
|
fbe1bb69c3 | ||
|
|
b2573b465b | ||
|
|
6171e3c2ef | ||
|
|
19e13a9052 | ||
|
|
b349d80be5 | ||
|
|
c5c4553ae0 | ||
|
|
ef77e6f7e9 | ||
|
|
66816863e0 | ||
|
|
7d67cd08f6 | ||
|
|
0d1b622831 | ||
|
|
a1000f7778 | ||
|
|
30fea18f93 | ||
|
|
8bbb9b6e85 | ||
|
|
4b5390f6cf | ||
|
|
941abe608a | ||
|
|
0d23175216 | ||
|
|
c9c9fbd604 | ||
|
|
9d0d7c5aa8 | ||
|
|
65b23113a9 | ||
|
|
4d3edf7203 | ||
|
|
3c5d3fc569 | ||
|
|
f0d984c693 | ||
|
|
6efe1a596f | ||
|
|
598e5cf315 | ||
|
|
3fb0b840a1 | ||
|
|
ee3fa9715c | ||
|
|
3369a7a506 | ||
|
|
a6d176c033 | ||
|
|
120d72e67a | ||
|
|
0082a5a0b9 | ||
|
|
9971cdbfb3 | ||
|
|
837bb9f84f | ||
|
|
805b196532 | ||
|
|
a23238f97f | ||
|
|
e88e2f0d7f | ||
|
|
13ed3fe80c | ||
|
|
37bfcc1251 | ||
|
|
cb7e70ce31 | ||
|
|
25006765de | ||
|
|
594164944e | ||
|
|
56ed6e8a81 | ||
|
|
159184be73 | ||
|
|
51f4f3c401 |
179
.github/workflows/build.yml
vendored
179
.github/workflows/build.yml
vendored
@@ -30,6 +30,7 @@ env:
|
|||||||
PYTHON_SOURCE_PATH: ${{ github.workspace }}/src/cpython
|
PYTHON_SOURCE_PATH: ${{ github.workspace }}/src/cpython
|
||||||
CRYPTOGRAPHY_BUILD_OPENSSL_NO_LEGACY: 1
|
CRYPTOGRAPHY_BUILD_OPENSSL_NO_LEGACY: 1
|
||||||
CRYPTOGRAPHY_OPENSSL_NO_LEGACY: 1
|
CRYPTOGRAPHY_OPENSSL_NO_LEGACY: 1
|
||||||
|
WINDOWS_CODESIGN_CERT_HASH: 590dc5bb10dfb31dbff38c0e2f9c35ef0f6d0e9e
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -76,7 +77,7 @@ jobs:
|
|||||||
jid: 9
|
jid: 9
|
||||||
goal: build
|
goal: build
|
||||||
name: Build Arm MacOS 15
|
name: Build Arm MacOS 15
|
||||||
- os: windows-2022
|
- os: windows-2025
|
||||||
jid: 10
|
jid: 10
|
||||||
goal: build
|
goal: build
|
||||||
name: Build Intel Windows
|
name: Build Intel Windows
|
||||||
@@ -107,26 +108,26 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # 5.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- id: auth
|
- id: auth
|
||||||
name: Authenticate to Google Cloud
|
name: Authenticate to Google Cloud
|
||||||
uses: google-github-actions/auth@v2
|
uses: google-github-actions/auth@6fc4af4b145ae7821d527454aa9bd537d1f2dc5f # 2.1.12
|
||||||
with:
|
with:
|
||||||
workload_identity_provider: projects/297925809119/locations/global/workloadIdentityPools/gha-pool/providers/gha-provider
|
workload_identity_provider: projects/297925809119/locations/global/workloadIdentityPools/gha-pool/providers/gha-provider
|
||||||
service_account: github-actions-testing-for-gam@gam-project-wyo-lub-ivl.iam.gserviceaccount.com
|
service_account: github-actions-testing-for-gam@gam-project-wyo-lub-ivl.iam.gserviceaccount.com
|
||||||
|
|
||||||
- name: Cache multiple paths
|
- name: Cache multiple paths
|
||||||
if: matrix.goal == 'build'
|
if: matrix.goal == 'build'
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # 4.2.4
|
||||||
id: cache-python-ssl
|
id: cache-python-ssl
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
cache.tar.xz
|
cache.tar.xz
|
||||||
key: gam-${{ matrix.jid }}-20250805
|
key: gam-${{ matrix.jid }}-20250814
|
||||||
|
|
||||||
- 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'
|
||||||
@@ -136,7 +137,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@v5
|
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # 5.6.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python }}
|
python-version: ${{ matrix.python }}
|
||||||
allow-prereleases: true
|
allow-prereleases: true
|
||||||
@@ -216,14 +217,13 @@ 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@v3
|
uses: apple-actions/import-codesign-certs@95e84a1a18f2bdbc5c6ab9b7f4429372e4b13a8b # 5.0.3
|
||||||
with:
|
with:
|
||||||
keychain: signing_temp
|
|
||||||
p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
|
p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
|
||||||
p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}
|
p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}
|
||||||
|
|
||||||
- name: Windows Configure VCode
|
- name: Windows Configure VCode
|
||||||
uses: ilammy/msvc-dev-cmd@v1
|
uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # 1.13.0
|
||||||
if: runner.os == 'Windows' && steps.cache-python-ssl.outputs.cache-hit != 'true'
|
if: runner.os == 'Windows' && steps.cache-python-ssl.outputs.cache-hit != 'true'
|
||||||
with:
|
with:
|
||||||
arch: ${{ runner.arch }}
|
arch: ${{ runner.arch }}
|
||||||
@@ -285,7 +285,7 @@ jobs:
|
|||||||
echo "COMPILED_OPENSSL_VERSION=${COMPILED_OPENSSL_VERSION}" >> $GITHUB_ENV
|
echo "COMPILED_OPENSSL_VERSION=${COMPILED_OPENSSL_VERSION}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Windows NASM Install
|
- name: Windows NASM Install
|
||||||
uses: ilammy/setup-nasm@v1
|
uses: ilammy/setup-nasm@72793074d3c8cdda771dba85f6deafe00623038b # 1.5.2
|
||||||
if: matrix.goal == 'build' && runner.os == 'Windows' && runner.arch == 'X64' && steps.cache-python-ssl.outputs.cache-hit != 'true'
|
if: matrix.goal == 'build' && runner.os == 'Windows' && runner.arch == 'X64' && steps.cache-python-ssl.outputs.cache-hit != 'true'
|
||||||
|
|
||||||
- name: Config OpenSSL
|
- name: Config OpenSSL
|
||||||
@@ -462,13 +462,6 @@ jobs:
|
|||||||
curl -O -L "$latest_crypt_whl"
|
curl -O -L "$latest_crypt_whl"
|
||||||
"$PYTHON" -m pip install cryptography*.whl
|
"$PYTHON" -m pip install cryptography*.whl
|
||||||
|
|
||||||
#- uses: actions-rust-lang/setup-rust-toolchain@v1
|
|
||||||
|
|
||||||
# - name: Compile cryptography from source (no legacy)
|
|
||||||
# if: runner.os != 'Windows' || runner.arch != 'ARM64'
|
|
||||||
# run: |
|
|
||||||
# pip install --no-binary ":all:" --force cryptography
|
|
||||||
|
|
||||||
- name: Install pip requirements
|
- name: Install pip requirements
|
||||||
run: |
|
run: |
|
||||||
echo "before anything..."
|
echo "before anything..."
|
||||||
@@ -601,6 +594,72 @@ jobs:
|
|||||||
echo "GAM Version ${GAMVERSION}"
|
echo "GAM Version ${GAMVERSION}"
|
||||||
echo "GAMVERSION=${GAMVERSION}" >> $GITHUB_ENV
|
echo "GAMVERSION=${GAMVERSION}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Install WinAppDriver
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
run: |
|
||||||
|
choco install -y winappdriver
|
||||||
|
|
||||||
|
- name: Enabled dev mode for WinAppDriver
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
shell: cmd
|
||||||
|
run : |
|
||||||
|
reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock" /t REG_DWORD /f /v "AllowDevelopmentWithoutDevLicense" /d "1"
|
||||||
|
|
||||||
|
- name: Install appium and totp tools
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
run: |
|
||||||
|
echo "Installing appium..."
|
||||||
|
npm install -g appium
|
||||||
|
echo "Installing totp-generator..."
|
||||||
|
npm install "totp-generator"
|
||||||
|
echo "Installing wdio..."
|
||||||
|
npm install @wdio/cli
|
||||||
|
echo "Installing appium win driver..."
|
||||||
|
appium driver install windows
|
||||||
|
|
||||||
|
- name: Install Certum MSI
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$url = "https://files.certum.eu/software/SimplySignDesktop/Windows/9.3.2.67/SimplySignDesktop-9.3.2.67-64-bit-en.msi"
|
||||||
|
$file = "SimplySignDesktop-9.3.2.67-64-bit-en.msi"
|
||||||
|
Invoke-WebRequest $url -OutFile $file
|
||||||
|
$log = "install.log"
|
||||||
|
$procMain = Start-Process "msiexec" "/i `"$file`" /qn /l*! `"$log`"" -NoNewWindow -PassThru
|
||||||
|
$procLog = Start-Process "powershell" "Get-Content -Path `"$log`" -Wait" -NoNewWindow -PassThru
|
||||||
|
$procMain.WaitForExit()
|
||||||
|
$procLog.Kill()
|
||||||
|
|
||||||
|
- name: Login to Certum
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
shell: pwsh
|
||||||
|
env:
|
||||||
|
TOTP_SECRET: ${{ secrets.TOTP_SECRET }}
|
||||||
|
run: |
|
||||||
|
# disable win private firewall that interferes with appium server
|
||||||
|
Set-NetFirewallProfile -Profile Private -Enabled False
|
||||||
|
$appiumCmd = Get-Command appium
|
||||||
|
$appiumPath = $appiumCmd.Path
|
||||||
|
Start-Process -Filepath "powershell.exe" -ArgumentList "-File", $appiumPath, "--address", "127.0.0.1", "--log-level", "error"
|
||||||
|
Start-Sleep -Seconds 10
|
||||||
|
write-host "appium started"
|
||||||
|
write-host "running SimplySignDesktop login..."
|
||||||
|
node tools/ssd.mjs --log-level warn
|
||||||
|
write-host "sleeping during login..."
|
||||||
|
Start-Sleep 10
|
||||||
|
|
||||||
|
- name: Sign gam.exe
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
write-Host "Signing ${env:gam}...."
|
||||||
|
# Always explicitely use x64 version os signtool.exe, arm64 version apparently can't
|
||||||
|
# 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", "590dc5bb10dfb31dbff38c0e2f9c35ef0f6d0e9e", "/tr", "http://time.certum.pl", "/td", "SHA256", "/fd", "SHA256", "/v", "$env:gam"
|
||||||
|
write-Host "Verifying signature of ${env:gam}...."
|
||||||
|
# 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:gam"
|
||||||
|
|
||||||
- name: Configure user and service account auth
|
- name: Configure user and service account auth
|
||||||
id: configserviceaccount
|
id: configserviceaccount
|
||||||
env:
|
env:
|
||||||
@@ -609,36 +668,8 @@ jobs:
|
|||||||
../.github/actions/decrypt.sh "${GAMCFGDIR}"
|
../.github/actions/decrypt.sh "${GAMCFGDIR}"
|
||||||
$gam create signjwtserviceaccount
|
$gam create signjwtserviceaccount
|
||||||
|
|
||||||
- name: Upload gam.exe Windows for signing
|
|
||||||
if: runner.os == 'Windows' && matrix.goal != 'test'
|
|
||||||
run: |
|
|
||||||
export folder_number=$(date +%s)
|
|
||||||
export folder_id=$($gam user gam-win-signer@pdl.jaylee.us add drivefile drivefilename "UPLOADING_FOR_SIGN ${folder_number}" parentid "1Xz3hYq4Mfa_r6D8EcBZHLDtHDFurYSvp" mimetype gfolder returnidonly)
|
|
||||||
$gam user gam-win-signer@pdl.jaylee.us add drivefile localfile "$gam" parentid "$folder_id"
|
|
||||||
$gam user gam-win-signer@pdl.jaylee.us update drivefile "$folder_id" newfilename "READYTOSIGN ${folder_number}"
|
|
||||||
export signed_folder="SIGNED ${folder_number}"
|
|
||||||
zero_results="gam-win-signer@pdl.jaylee.us,0"
|
|
||||||
while true; do
|
|
||||||
result_counts=$($gam user gam-win-signer@pdl.jaylee.us print filelist query "name = '${signed_folder}' and '1Xz3hYq4Mfa_r6D8EcBZHLDtHDFurYSvp' in parents and mimeType = 'application/vnd.google-apps.folder'" countsonly)
|
|
||||||
echo "$result_counts"
|
|
||||||
if [[ ! "$result_counts" =~ "$zero_results" ]]; then
|
|
||||||
echo "looks like we have results"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
echo "no results, sleeping 10..."
|
|
||||||
sleep 10
|
|
||||||
done
|
|
||||||
# download signed gam.exe
|
|
||||||
$gam user gam-win-signer@pdl.jaylee.us print filelist query "name = '${signed_folder}' and '1Xz3hYq4Mfa_r6D8EcBZHLDtHDFurYSvp' in parents and mimeType = 'application/vnd.google-apps.folder'" id | $gam csv - gam user gam-win-signer@pdl.jaylee.us print filelist query "'~~id~~' in parents and name = 'gam.exe'" id | $gam csv - gam user gam-win-signer@pdl.jaylee.us get drivefile ~id targetfolder "$gampath" targetname "signed-gam.exe" overwrite true acknowledgeabuse true
|
|
||||||
# delete signed folder on drive
|
|
||||||
$gam user gam-win-signer@pdl.jaylee.us print filelist query "name = '${signed_folder}' and '1Xz3hYq4Mfa_r6D8EcBZHLDtHDFurYSvp' in parents and mimeType = 'application/vnd.google-apps.folder'" id | $gam csv - gam user gam-win-signer@pdl.jaylee.us trash drivefile "~id"
|
|
||||||
# remove unsigned gam.exe and rename signed-gam.exe
|
|
||||||
rm -v -f "${gampath}/gam.exe"
|
|
||||||
mv -v -f "${gampath}/signed-gam.exe" "${gampath}/gam.exe"
|
|
||||||
#"/c/Program Files (x86)/Windows Kits/10/bin/10.0.22621.0/x64/signtool.exe" verify /v /pa "$gam"
|
|
||||||
|
|
||||||
- name: Attest gam executable was generated from this Action
|
- name: Attest gam executable was generated from this Action
|
||||||
uses: actions/attest-build-provenance@v1
|
uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # 2.4.0
|
||||||
if: matrix.goal == 'build'
|
if: matrix.goal == 'build'
|
||||||
with:
|
with:
|
||||||
subject-path: ${{ env.gam }}
|
subject-path: ${{ env.gam }}
|
||||||
@@ -689,34 +720,20 @@ jobs:
|
|||||||
rm -v -f *.wixobj
|
rm -v -f *.wixobj
|
||||||
echo "MSI_FILENAME=${MSI_FILENAME}" >> $GITHUB_ENV
|
echo "MSI_FILENAME=${MSI_FILENAME}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Upload gam MSI Windows for signing
|
- name: Sign GAM MSI
|
||||||
if: runner.os == 'Windows' && matrix.goal != 'test'
|
if: runner.os == 'Windows'
|
||||||
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
export folder_number=$(date +%s)
|
write-Host "Signing ${env:MSI_FILENAME}...."
|
||||||
export folder_id=$($gam user gam-win-signer@pdl.jaylee.us add drivefile drivefilename "UPLOADING_FOR_SIGN ${folder_number}" parentid "1Xz3hYq4Mfa_r6D8EcBZHLDtHDFurYSvp" mimetype gfolder returnidonly)
|
# Always explicitely use x64 version os signtool.exe, arm64 version apparently can't
|
||||||
$gam user gam-win-signer@pdl.jaylee.us add drivefile localfile "$MSI_FILENAME" parentid "$folder_id"
|
# see Certum certs since SimplySignDesktop is x64-only today.
|
||||||
rm -f -v "$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", "590dc5bb10dfb31dbff38c0e2f9c35ef0f6d0e9e", "/tr", "http://time.certum.pl", "/td", "SHA256", "/fd", "SHA256", "/v", "$env:MSI_FILENAME"
|
||||||
$gam user gam-win-signer@pdl.jaylee.us update drivefile "$folder_id" newfilename "READYTOSIGN ${folder_number}"
|
write-Host "Verifying signature of ${env:MSI_FILENAME}...."
|
||||||
export signed_folder="SIGNED ${folder_number}"
|
# verify signature. If we failed to sign we should fail to verify and die.
|
||||||
zero_results="gam-win-signer@pdl.jaylee.us,0"
|
& 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\signtool.exe' verify /pa /v "$env:MSI_FILENAME"
|
||||||
while true; do
|
|
||||||
result_counts=$($gam user gam-win-signer@pdl.jaylee.us print filelist query "name = '${signed_folder}' and '1Xz3hYq4Mfa_r6D8EcBZHLDtHDFurYSvp' in parents and mimeType = 'application/vnd.google-apps.folder'" countsonly)
|
|
||||||
echo "$result_counts"
|
|
||||||
if [[ ! "$result_counts" =~ "$zero_results" ]]; then
|
|
||||||
echo "looks like we have results"
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
echo "no results, sleeping 10..."
|
|
||||||
sleep 10
|
|
||||||
done
|
|
||||||
# download signed package
|
|
||||||
$gam user gam-win-signer@pdl.jaylee.us print filelist query "name = '${signed_folder}' and '1Xz3hYq4Mfa_r6D8EcBZHLDtHDFurYSvp' in parents and mimeType = 'application/vnd.google-apps.folder'" id | $gam csv - gam user gam-win-signer@pdl.jaylee.us print filelist query "'~~id~~' in parents and name contains '.msi'" id | $gam csv - gam user gam-win-signer@pdl.jaylee.us get drivefile ~id targetfolder "$GITHUB_WORKSPACE" targetname "$MSI_FILENAME" overwrite true acknowledgeabuse true
|
|
||||||
# delete signed folder on drive
|
|
||||||
$gam user gam-win-signer@pdl.jaylee.us print filelist query "name = '${signed_folder}' and '1Xz3hYq4Mfa_r6D8EcBZHLDtHDFurYSvp' in parents and mimeType = 'application/vnd.google-apps.folder'" id | $gam csv - gam user gam-win-signer@pdl.jaylee.us trash drivefile "~id"
|
|
||||||
#"/c/Program Files (x86)/Windows Kits/10/bin/10.0.22621.0/x64/signtool.exe" verify /v /pa "$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@v1
|
uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # 2.4.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: |
|
||||||
@@ -725,7 +742,7 @@ jobs:
|
|||||||
gam*.msi
|
gam*.msi
|
||||||
|
|
||||||
- name: Archive production artifacts
|
- name: Archive production artifacts
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # 4.6.2
|
||||||
if: (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && matrix.goal != 'test'
|
if: (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && matrix.goal != 'test'
|
||||||
with:
|
with:
|
||||||
name: gam-binaries-${{ env.GAMOS }}-${{ env.arch }}-${{ matrix.jid }}
|
name: gam-binaries-${{ env.GAMOS }}-${{ env.arch }}-${{ matrix.jid }}
|
||||||
@@ -989,16 +1006,16 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # 5.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Download artifacts
|
- name: Download artifacts
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # 5.0.0
|
||||||
|
|
||||||
- name: VirusTotal Scan
|
- name: VirusTotal Scan
|
||||||
uses: crazy-max/ghaction-virustotal@v4
|
uses: crazy-max/ghaction-virustotal@d34968c958ae283fe976efed637081b9f9dcf74f # 4.2.0
|
||||||
with:
|
with:
|
||||||
vt_api_key: ${{ secrets.VT_API_KEY }}
|
vt_api_key: ${{ secrets.VT_API_KEY }}
|
||||||
files: |
|
files: |
|
||||||
@@ -1011,12 +1028,14 @@ jobs:
|
|||||||
echo "Date version: ${dateversion}"
|
echo "Date version: ${dateversion}"
|
||||||
echo "dateversion=${dateversion}" >> $GITHUB_OUTPUT
|
echo "dateversion=${dateversion}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- uses: "marvinpinto/action-automatic-releases@latest"
|
- name: Publish draft release
|
||||||
name: Publish draft release
|
uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8 # 2.3.2
|
||||||
with:
|
with:
|
||||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
|
||||||
automatic_release_tag: "${{ steps.dateversion.outputs.dateversion }}"
|
|
||||||
prerelease: false
|
|
||||||
draft: true
|
draft: true
|
||||||
|
prerelease: false
|
||||||
|
tag_name: "${{ steps.dateversion.outputs.dateversion }}"
|
||||||
|
fail_on_unmatched_files: true
|
||||||
files: |
|
files: |
|
||||||
gam-binaries/*
|
gam-binaries/*
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
2
.github/workflows/get-cacerts.yml
vendored
2
.github/workflows/get-cacerts.yml
vendored
@@ -16,7 +16,7 @@ defaults:
|
|||||||
working-directory: src
|
working-directory: src
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-apis:
|
check-certs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@master
|
||||||
|
|||||||
@@ -539,6 +539,7 @@ If an item contains spaces, it should be surrounded by ".
|
|||||||
Must match this Python Regular Expression: [a-zA-Z0-9 '"!-]{4,30}
|
Must match this Python Regular Expression: [a-zA-Z0-9 '"!-]{4,30}
|
||||||
<PropertyKey> ::= <String>
|
<PropertyKey> ::= <String>
|
||||||
<PropertyValue> ::= <String>
|
<PropertyValue> ::= <String>
|
||||||
|
<PubSubTopicName> ::= <String>
|
||||||
<QueryAlert> ::= <String>
|
<QueryAlert> ::= <String>
|
||||||
See: https://developers.google.com/admin-sdk/alertcenter/guides/query-filters
|
See: https://developers.google.com/admin-sdk/alertcenter/guides/query-filters
|
||||||
<QueryBrowser> ::= <String>
|
<QueryBrowser> ::= <String>
|
||||||
@@ -1540,6 +1541,10 @@ gam show admins
|
|||||||
|
|
||||||
# Alert Center
|
# Alert Center
|
||||||
|
|
||||||
|
gam show alertsettings
|
||||||
|
gam update alertsettings <PubsubTopicName>
|
||||||
|
gam clear alertsettings
|
||||||
|
|
||||||
gam delete alert <AlertID>
|
gam delete alert <AlertID>
|
||||||
gam undelete alert <AlertID>
|
gam undelete alert <AlertID>
|
||||||
gam info alert <AlertID> [formatjson]
|
gam info alert <AlertID> [formatjson]
|
||||||
@@ -4277,19 +4282,19 @@ gam show policies
|
|||||||
<SSOProfileItem> ::= <SSOProfileDisplayName>|<SSOProfileName>
|
<SSOProfileItem> ::= <SSOProfileDisplayName>|<SSOProfileName>
|
||||||
<SSOProfileItemList> ::= "<SSOProfileItem>(,<SSOProfileItem>)*"
|
<SSOProfileItemList> ::= "<SSOProfileItem>(,<SSOProfileItem>)*"
|
||||||
|
|
||||||
gam create inboundssoprofile [name <SSOProfileDisplayName>]
|
gam create inboundssoprofile [saml|oidc] [name <SSOProfileDisplayName>]
|
||||||
[entityid <String>] [loginurl <URL>] [logouturl <URL>] [changepasswordurl <URL>]
|
[entityid <String>] [loginurl <URL>] [logouturl <URL>] [changepasswordurl <URL>]
|
||||||
[returnnameonly]
|
[returnnameonly]
|
||||||
gam update inboundssoprofile <SSOProfileItem>
|
gam update inboundssoprofile [saml|oidc] <SSOProfileItem>
|
||||||
[entityid <String>] [loginurl <URL>] [logouturl <URL>] [changepasswordurl <URL>]
|
[entityid <String>] [loginurl <URL>] [logouturl <URL>] [changepasswordurl <URL>]
|
||||||
[returnnameonly]
|
[returnnameonly]
|
||||||
gam delete inboundssoprofile <SSOProfileItem>
|
gam delete inboundssoprofile [saml|oidc] <SSOProfileItem>
|
||||||
|
|
||||||
gam info inboundssoprofile <SSOProfileItem>
|
gam info inboundssoprofile [all|saml|oidc] <SSOProfileItem>
|
||||||
[formatjson]
|
[formatjson]
|
||||||
gam show inboundssoprofiles
|
gam show inboundssoprofiles [all|saml|oidc]
|
||||||
[formatjson]
|
[formatjson]
|
||||||
gam print inboundssoprofiles [todrive <ToDriveAttribute>*]
|
gam print inboundssoprofiles [all|saml|oidc] [todrive <ToDriveAttribute>*]
|
||||||
[[formatjson [quotechar <Character>]]
|
[[formatjson [quotechar <Character>]]
|
||||||
|
|
||||||
<SSOCredentialsName> ::= [id:]inboundSamlSsoProfiles/<String>/idpCredentials/<String>
|
<SSOCredentialsName> ::= [id:]inboundSamlSsoProfiles/<String>/idpCredentials/<String>
|
||||||
@@ -4313,10 +4318,14 @@ gam print inboundssocredentials [profile|profiles <SSOProfileItemList>]
|
|||||||
orgunits/<String> |
|
orgunits/<String> |
|
||||||
orgunit:<OrgUnitPath>
|
orgunit:<OrgUnitPath>
|
||||||
|
|
||||||
gam create inboundssoassignment (group <GroupItem> rank <Number>)|(ou|org|orgunit <OrgUnitItem>)
|
gam create inboundssoassignment
|
||||||
(mode sso_off)|(mode saml_sso profile <SSOProfileItem>)(mode domain_wide_saml_if_enabled) [neverredirect]
|
(group <GroupItem> rank <Number>)|(ou|org|orgunit <OrgUnitItem>)
|
||||||
gam update inboundssoassignment [(group <GroupItem> rank <Number>)|(ou|org|orgunit <OrgUnitItem>)]
|
(mode sso_off)|(mode saml_sso profile <SSOProfileItem>)|(mode oidc_sso profile <SSOProfileName>}|(mode domain_wide_saml_if_enabled)
|
||||||
[(mode sso_off)|(mode saml_sso profile <SSOProfileItem>)(mode domain_wide_saml_if_enabled)] [neverredirect]
|
[neverredirect]
|
||||||
|
gam update inboundssoassignment <SSOAssignmentName>
|
||||||
|
[(group <GroupItem> rank <Number>)|(ou|org|orgunit <OrgUnitItem>)]
|
||||||
|
(mode sso_off)|(mode saml_sso profile <SSOProfileItem>)|(mode oidc_sso profile <SSOProfileName>}|(mode domain_wide_saml_if_enabled)
|
||||||
|
[neverredirect]
|
||||||
gam delete inboundssoassignment <SSOAssignmentSelector>
|
gam delete inboundssoassignment <SSOAssignmentSelector>
|
||||||
|
|
||||||
gam info inboundssoassignment <SSOAssignmentSelector>
|
gam info inboundssoassignment <SSOAssignmentSelector>
|
||||||
@@ -4540,11 +4549,12 @@ gam report usage customer [todrive <ToDriveAttribute>*]
|
|||||||
chrome|
|
chrome|
|
||||||
classroom|
|
classroom|
|
||||||
contextawareaccess|
|
contextawareaccess|
|
||||||
gplus|currents|google+|
|
|
||||||
datastudio|
|
datastudio|
|
||||||
drive|doc|docs|
|
drive|doc|docs|
|
||||||
gcp|cloud|
|
gcp|cloud|
|
||||||
geminiinworkspaceapps|gemini|geminiforworkspace|
|
geminiinworkspaceapps|gemini|geminiforworkspace|
|
||||||
|
gmail|
|
||||||
|
gplus|currents|google+|
|
||||||
groups|group|
|
groups|group|
|
||||||
groupsenterprise|enterprisegroups|
|
groupsenterprise|enterprisegroups|
|
||||||
jamboard|
|
jamboard|
|
||||||
|
|||||||
@@ -1,3 +1,61 @@
|
|||||||
|
7.19.02
|
||||||
|
|
||||||
|
Update `gam info user <UserItem>` to eliminate 5 second delay when getting license info.
|
||||||
|
|
||||||
|
Additional information:
|
||||||
|
* See: https://github.com/GAM-team/GAM/wiki/Licenses#info-user-performance
|
||||||
|
|
||||||
|
7.19.01
|
||||||
|
|
||||||
|
Updated `gam <UserTypeEntity> print|show signature` to handle the following error
|
||||||
|
that occurs when an alias is specified.
|
||||||
|
```
|
||||||
|
ERROR: 404: notFound - Requested entity was not found.
|
||||||
|
```
|
||||||
|
|
||||||
|
7.19.00
|
||||||
|
|
||||||
|
Eliminated `drive_v3_beta` and `meet_v2_beta` from `gam.cfg` as the API betas are no longer used.
|
||||||
|
|
||||||
|
Updated `Meet API` scopes so that GAM can read metadata about additional Meet spaces.
|
||||||
|
```
|
||||||
|
[*] 34) Meet API - Manage/Display Meeting Spaces
|
||||||
|
[*] 35) Meet API - Read Meeting Spaces metadata
|
||||||
|
```
|
||||||
|
|
||||||
|
7.18.07
|
||||||
|
|
||||||
|
Updated `gam <UserTypeEntity> print drivelastmodification` to put `addcsvdata` columns
|
||||||
|
after `User,id,name` rather than after the last column.
|
||||||
|
|
||||||
|
7.18.06
|
||||||
|
|
||||||
|
Updated `gam <UserTypeEntity> delete|modify messages` to improve the handling
|
||||||
|
of the following error.
|
||||||
|
```
|
||||||
|
quotaExceeded - User-rate limit exceeded
|
||||||
|
```
|
||||||
|
|
||||||
|
7.18.05
|
||||||
|
|
||||||
|
Added support for Inbound SSO OIDC profiles.
|
||||||
|
|
||||||
|
Currently, if you enter `gam select <SectionName>` and nothing else on the command line,
|
||||||
|
GAM performs no action. Now, it will be treated as if you entered:
|
||||||
|
`gam select <SectionName> save`
|
||||||
|
|
||||||
|
Updated to Python 3.13.7.
|
||||||
|
|
||||||
|
7.18.04
|
||||||
|
|
||||||
|
Added commands to display/manage Alert Center Pub/Sub notifications.
|
||||||
|
* See: https://github.com/GAM-team/GAM/wiki/Alert-Center#configuring-settings
|
||||||
|
|
||||||
|
7.18.03
|
||||||
|
|
||||||
|
Updated `gam oauth create` to give a warning if the number of selected scopes will
|
||||||
|
probably cause Google to generate a "Something went wrong" error.
|
||||||
|
|
||||||
7.18.02
|
7.18.02
|
||||||
|
|
||||||
Upgraded to OpenSSL 3.5.2.
|
Upgraded to OpenSSL 3.5.2.
|
||||||
|
|||||||
100
src/cacerts.pem
100
src/cacerts.pem
@@ -433,106 +433,6 @@ pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1
|
|||||||
mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0
|
mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
# Operating CA: GoDaddy
|
|
||||||
# Issuer: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority
|
|
||||||
# Subject: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority
|
|
||||||
# Label: "Starfield Class 2 CA"
|
|
||||||
# Serial: 0
|
|
||||||
# MD5 Fingerprint: 32:4a:4b:bb:c8:63:69:9b:be:74:9a:c6:dd:1d:46:24
|
|
||||||
# SHA1 Fingerprint: ad:7e:1c:28:b0:64:ef:8f:60:03:40:20:14:c3:d0:e3:37:0e:b5:8a
|
|
||||||
# SHA256 Fingerprint: 14:65:fa:20:53:97:b8:76:fa:a6:f0:a9:95:8e:55:90:e4:0f:cc:7f:aa:4f:b7:c2:c8:67:75:21:fb:5f:b6:58
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl
|
|
||||||
MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp
|
|
||||||
U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw
|
|
||||||
NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE
|
|
||||||
ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp
|
|
||||||
ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3
|
|
||||||
DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf
|
|
||||||
8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN
|
|
||||||
+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0
|
|
||||||
X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa
|
|
||||||
K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA
|
|
||||||
1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G
|
|
||||||
A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR
|
|
||||||
zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0
|
|
||||||
YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD
|
|
||||||
bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w
|
|
||||||
DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3
|
|
||||||
L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D
|
|
||||||
eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
|
|
||||||
xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp
|
|
||||||
VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY
|
|
||||||
WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q=
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
|
|
||||||
# Operating CA: GoDaddy
|
|
||||||
# Issuer: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority
|
|
||||||
# Subject: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority
|
|
||||||
# Label: "Go Daddy Class 2 CA"
|
|
||||||
# Serial: 0
|
|
||||||
# MD5 Fingerprint: 91:de:06:25:ab:da:fd:32:17:0c:bb:25:17:2a:84:67
|
|
||||||
# SHA1 Fingerprint: 27:96:ba:e6:3f:18:01:e2:77:26:1b:a0:d7:77:70:02:8f:20:ee:e4
|
|
||||||
# SHA256 Fingerprint: c3:84:6b:f2:4b:9e:93:ca:64:27:4c:0e:c6:7c:1e:cc:5e:02:4f:fc:ac:d2:d7:40:19:35:0e:81:fe:54:6a:e4
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh
|
|
||||||
MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE
|
|
||||||
YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3
|
|
||||||
MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo
|
|
||||||
ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg
|
|
||||||
MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN
|
|
||||||
ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA
|
|
||||||
PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w
|
|
||||||
wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi
|
|
||||||
EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY
|
|
||||||
avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+
|
|
||||||
YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE
|
|
||||||
sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h
|
|
||||||
/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5
|
|
||||||
IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj
|
|
||||||
YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
|
|
||||||
ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy
|
|
||||||
OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P
|
|
||||||
TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
|
|
||||||
HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER
|
|
||||||
dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf
|
|
||||||
ReYNnyicsbkqWletNw+vHX/bvZ8=
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
|
|
||||||
# Operating CA: Sectigo
|
|
||||||
# Issuer: CN=AAA Certificate Services O=Comodo CA Limited
|
|
||||||
# Subject: CN=AAA Certificate Services O=Comodo CA Limited
|
|
||||||
# Label: "Comodo AAA Services root"
|
|
||||||
# Serial: 1
|
|
||||||
# MD5 Fingerprint: 49:79:04:b0:eb:87:19:ac:47:b0:bc:11:51:9b:74:d0
|
|
||||||
# SHA1 Fingerprint: d1:eb:23:a4:6d:17:d6:8f:d9:25:64:c2:f1:f1:60:17:64:d8:e3:49
|
|
||||||
# SHA256 Fingerprint: d7:a7:a0:fb:5d:7e:27:31:d7:71:e9:48:4e:bc:de:f7:1d:5f:0c:3e:0a:29:48:78:2b:c8:3e:e0:ea:69:9e:f4
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb
|
|
||||||
MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow
|
|
||||||
GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj
|
|
||||||
YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL
|
|
||||||
MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
|
|
||||||
BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM
|
|
||||||
GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
|
||||||
ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua
|
|
||||||
BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe
|
|
||||||
3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4
|
|
||||||
YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR
|
|
||||||
rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm
|
|
||||||
ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU
|
|
||||||
oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
|
|
||||||
MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v
|
|
||||||
QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t
|
|
||||||
b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF
|
|
||||||
AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q
|
|
||||||
GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz
|
|
||||||
Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2
|
|
||||||
G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi
|
|
||||||
l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3
|
|
||||||
smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
|
|
||||||
# Operating CA: Sectigo
|
# Operating CA: Sectigo
|
||||||
# Issuer: CN=COMODO Certification Authority O=COMODO CA Limited
|
# Issuer: CN=COMODO Certification Authority O=COMODO CA Limited
|
||||||
# Subject: CN=COMODO Certification Authority O=COMODO CA Limited
|
# Subject: CN=COMODO Certification Authority O=COMODO CA Limited
|
||||||
|
|||||||
@@ -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.18.02'
|
__version__ = '7.19.02'
|
||||||
__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
|
||||||
@@ -3952,14 +3952,20 @@ def SetGlobalVariables():
|
|||||||
if checkArgumentPresent(Cmd.SELECT_CMD):
|
if checkArgumentPresent(Cmd.SELECT_CMD):
|
||||||
sectionName = _selectSection()
|
sectionName = _selectSection()
|
||||||
GM.Globals[GM.SECTION] = sectionName # Save section for inner gams
|
GM.Globals[GM.SECTION] = sectionName # Save section for inner gams
|
||||||
while Cmd.ArgumentsRemaining():
|
# If command line is simply: gam select <SectionName>
|
||||||
if checkArgumentPresent('save'):
|
# assume save
|
||||||
GM.Globals[GM.PARSER].set(configparser.DEFAULTSECT, GC.SECTION, sectionName)
|
if not Cmd.ArgumentsRemaining():
|
||||||
_writeGamCfgFile(GM.Globals[GM.PARSER], GM.Globals[GM.GAM_CFG_FILE], Act.SAVE)
|
GM.Globals[GM.PARSER].set(configparser.DEFAULTSECT, GC.SECTION, sectionName)
|
||||||
elif checkArgumentPresent('verify'):
|
_writeGamCfgFile(GM.Globals[GM.PARSER], GM.Globals[GM.GAM_CFG_FILE], Act.SAVE)
|
||||||
_verifyValues(sectionName, inputFilterSectionName, outputFilterSectionName)
|
else:
|
||||||
else:
|
while Cmd.ArgumentsRemaining():
|
||||||
break
|
if checkArgumentPresent('save'):
|
||||||
|
GM.Globals[GM.PARSER].set(configparser.DEFAULTSECT, GC.SECTION, sectionName)
|
||||||
|
_writeGamCfgFile(GM.Globals[GM.PARSER], GM.Globals[GM.GAM_CFG_FILE], Act.SAVE)
|
||||||
|
elif checkArgumentPresent('verify'):
|
||||||
|
_verifyValues(sectionName, inputFilterSectionName, outputFilterSectionName)
|
||||||
|
else:
|
||||||
|
break
|
||||||
GM.Globals[GM.GAM_CFG_SECTION_NAME] = sectionName
|
GM.Globals[GM.GAM_CFG_SECTION_NAME] = sectionName
|
||||||
# showsections
|
# showsections
|
||||||
if checkArgumentPresent(Cmd.SHOWSECTIONS_CMD):
|
if checkArgumentPresent(Cmd.SHOWSECTIONS_CMD):
|
||||||
@@ -4733,8 +4739,6 @@ def getAPIService(api, httpObj):
|
|||||||
discoveryServiceUrl=DISCOVERY_URIS[v2discovery], static_discovery=False)
|
discoveryServiceUrl=DISCOVERY_URIS[v2discovery], static_discovery=False)
|
||||||
|
|
||||||
def getService(api, httpObj):
|
def getService(api, httpObj):
|
||||||
### Drive v3beta
|
|
||||||
# mapDriveURL = api == API.DRIVE3 and GC.Values[GC.DRIVE_V3_BETA]
|
|
||||||
hasLocalJSON = API.hasLocalJSON(api)
|
hasLocalJSON = API.hasLocalJSON(api)
|
||||||
api, version, v2discovery = API.getVersion(api)
|
api, version, v2discovery = API.getVersion(api)
|
||||||
if api in GM.Globals[GM.CURRENT_API_SERVICES] and version in GM.Globals[GM.CURRENT_API_SERVICES][api]:
|
if api in GM.Globals[GM.CURRENT_API_SERVICES] and version in GM.Globals[GM.CURRENT_API_SERVICES][api]:
|
||||||
@@ -4750,9 +4754,6 @@ def getService(api, httpObj):
|
|||||||
discoveryServiceUrl=DISCOVERY_URIS[v2discovery], static_discovery=False)
|
discoveryServiceUrl=DISCOVERY_URIS[v2discovery], static_discovery=False)
|
||||||
GM.Globals[GM.CURRENT_API_SERVICES].setdefault(api, {})
|
GM.Globals[GM.CURRENT_API_SERVICES].setdefault(api, {})
|
||||||
GM.Globals[GM.CURRENT_API_SERVICES][api][version] = service._rootDesc.copy()
|
GM.Globals[GM.CURRENT_API_SERVICES][api][version] = service._rootDesc.copy()
|
||||||
### Drive v3beta
|
|
||||||
# if mapDriveURL:
|
|
||||||
# setattr(service, '_baseUrl', getattr(service, '_baseUrl').replace('/v3/', '/v3beta/'))
|
|
||||||
if GM.Globals[GM.CACHE_DISCOVERY_ONLY]:
|
if GM.Globals[GM.CACHE_DISCOVERY_ONLY]:
|
||||||
clearServiceCache(service)
|
clearServiceCache(service)
|
||||||
return service
|
return service
|
||||||
@@ -4794,7 +4795,10 @@ def defaultSvcAcctScopes():
|
|||||||
for scope in scopesList:
|
for scope in scopesList:
|
||||||
if not scope.get('offByDefault'):
|
if not scope.get('offByDefault'):
|
||||||
saScopes.setdefault(scope['api'], [])
|
saScopes.setdefault(scope['api'], [])
|
||||||
saScopes[scope['api']].append(scope['scope'])
|
if not isinstance(scope['scope'], list):
|
||||||
|
saScopes[scope['api']].append(scope['scope'])
|
||||||
|
else:
|
||||||
|
saScopes[scope['api']].extend(scope['scope'])
|
||||||
saScopes[API.DRIVEACTIVITY].append(API.DRIVE_SCOPE)
|
saScopes[API.DRIVEACTIVITY].append(API.DRIVE_SCOPE)
|
||||||
saScopes[API.DRIVE2] = saScopes[API.DRIVE3]
|
saScopes[API.DRIVE2] = saScopes[API.DRIVE3]
|
||||||
saScopes[API.DRIVETD] = saScopes[API.DRIVE3]
|
saScopes[API.DRIVETD] = saScopes[API.DRIVE3]
|
||||||
@@ -5609,8 +5613,6 @@ def buildGAPIServiceObject(api, user, i=0, count=0, displayError=True):
|
|||||||
userEmail = getSaUser(user)
|
userEmail = getSaUser(user)
|
||||||
httpObj = getHttpObj(cache=GM.Globals[GM.CACHE_DIR])
|
httpObj = getHttpObj(cache=GM.Globals[GM.CACHE_DIR])
|
||||||
service = getService(api, httpObj)
|
service = getService(api, httpObj)
|
||||||
if api == API.MEET_BETA:
|
|
||||||
api = API.MEET
|
|
||||||
credentials = getSvcAcctCredentials(api, userEmail)
|
credentials = getSvcAcctCredentials(api, userEmail)
|
||||||
request = transportCreateRequest(httpObj)
|
request = transportCreateRequest(httpObj)
|
||||||
triesLimit = 3
|
triesLimit = 3
|
||||||
@@ -10672,9 +10674,6 @@ Select all default scopes by entering an 's'; yields [*] for default scopes, [ ]
|
|||||||
Unselect all scopes by entering a 'u'; yields [ ] for all scopes
|
Unselect all scopes by entering a 'u'; yields [ ] for all scopes
|
||||||
Exit without changes/authorization by entering an 'e'
|
Exit without changes/authorization by entering an 'e'
|
||||||
Continue to authorization by entering a 'c'
|
Continue to authorization by entering a 'c'
|
||||||
'''
|
|
||||||
if clientAccess:
|
|
||||||
oauth2_menu += ''' Note, if all scopes are selected, Google will probably generate an authorization error
|
|
||||||
'''
|
'''
|
||||||
menu = oauth2_menu % tuple(range(numScopes))
|
menu = oauth2_menu % tuple(range(numScopes))
|
||||||
selectedScopes = ['*'] * numScopes
|
selectedScopes = ['*'] * numScopes
|
||||||
@@ -10710,14 +10709,21 @@ Continue to authorization by entering a 'c'
|
|||||||
api = a_scope['api']
|
api = a_scope['api']
|
||||||
possibleScope = a_scope['scope']
|
possibleScope = a_scope['scope']
|
||||||
if api in currentScopes:
|
if api in currentScopes:
|
||||||
for scope in currentScopes[api]:
|
if not isinstance(possibleScope, list):
|
||||||
if scope == possibleScope:
|
for scope in currentScopes[api]:
|
||||||
selectedScopes[i] = '*'
|
if scope == possibleScope:
|
||||||
break
|
selectedScopes[i] = '*'
|
||||||
if 'readonly' in a_scope['subscopes']:
|
|
||||||
if (scope == possibleScope+'.readonly') or (scope == a_scope.get('roscope')):
|
|
||||||
selectedScopes[i] = 'R'
|
|
||||||
break
|
break
|
||||||
|
if 'readonly' in a_scope['subscopes']:
|
||||||
|
if (scope == possibleScope+'.readonly') or (scope == a_scope.get('roscope')):
|
||||||
|
selectedScopes[i] = 'R'
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
for scope in possibleScope:
|
||||||
|
if scope not in currentScopes[api]:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
selectedScopes[i] = '*'
|
||||||
i += 1
|
i += 1
|
||||||
else:
|
else:
|
||||||
i = 0
|
i = 0
|
||||||
@@ -10776,7 +10782,25 @@ Continue to authorization by entering a 'c'
|
|||||||
break
|
break
|
||||||
sys.stdout.write(f'{ERROR_PREFIX}Invalid input "{choice}"\n')
|
sys.stdout.write(f'{ERROR_PREFIX}Invalid input "{choice}"\n')
|
||||||
if selection == 'c':
|
if selection == 'c':
|
||||||
break
|
if clientAccess:
|
||||||
|
numSelectedScopes = 0
|
||||||
|
i = 0
|
||||||
|
for a_scope in scopesList:
|
||||||
|
if selectedScopes[i] == '*':
|
||||||
|
if a_scope['scope']:
|
||||||
|
numSelectedScopes += 1
|
||||||
|
elif selectedScopes[i] != ' ':
|
||||||
|
numSelectedScopes += 1
|
||||||
|
i += 1
|
||||||
|
if numSelectedScopes <= API.NUM_CLIENT_SCOPES_ERROR_LIMIT:
|
||||||
|
break
|
||||||
|
# If number of scopes is > 48 we'll probably get an error
|
||||||
|
writeStdout(Msg.NUM_SELECTED_CLIENT_SCOPES.format(numSelectedScopes, API.NUM_CLIENT_SCOPES_ERROR_LIMIT))
|
||||||
|
choice = readStdin('\nPlease enter c to continue to authorization or any other key to amend selection: ')
|
||||||
|
if choice and choice.lower() == 'c':
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
break
|
||||||
return selectedScopes
|
return selectedScopes
|
||||||
|
|
||||||
def _localhost_to_ip():
|
def _localhost_to_ip():
|
||||||
@@ -11467,13 +11491,14 @@ def _createOauth2serviceJSON(httpObj, projectInfo, svcAcctInfo, create_key=True)
|
|||||||
iam = getAPIService(API.IAM, httpObj)
|
iam = getAPIService(API.IAM, httpObj)
|
||||||
try:
|
try:
|
||||||
service_account = callGAPI(iam.projects().serviceAccounts(), 'create',
|
service_account = callGAPI(iam.projects().serviceAccounts(), 'create',
|
||||||
throwReasons=[GAPI.NOT_FOUND, GAPI.PERMISSION_DENIED, GAPI.ALREADY_EXISTS],
|
throwReasons=[GAPI.FAILED_PRECONDITION, GAPI.NOT_FOUND,
|
||||||
|
GAPI.PERMISSION_DENIED, GAPI.ALREADY_EXISTS],
|
||||||
name=f'projects/{projectInfo["projectId"]}',
|
name=f'projects/{projectInfo["projectId"]}',
|
||||||
body={'accountId': svcAcctInfo['name'],
|
body={'accountId': svcAcctInfo['name'],
|
||||||
'serviceAccount': {'displayName': svcAcctInfo['displayName'],
|
'serviceAccount': {'displayName': svcAcctInfo['displayName'],
|
||||||
'description': svcAcctInfo['description']}})
|
'description': svcAcctInfo['description']}})
|
||||||
entityActionPerformed([Ent.PROJECT, projectInfo['projectId'], Ent.SVCACCT, service_account['name'].rsplit('/', 1)[-1]])
|
entityActionPerformed([Ent.PROJECT, projectInfo['projectId'], Ent.SVCACCT, service_account['name'].rsplit('/', 1)[-1]])
|
||||||
except (GAPI.notFound, GAPI.permissionDenied) as e:
|
except (GAPI.failedPrecondition, GAPI.notFound, GAPI.permissionDenied) as e:
|
||||||
entityActionFailedWarning([Ent.PROJECT, projectInfo['projectId']], str(e))
|
entityActionFailedWarning([Ent.PROJECT, projectInfo['projectId']], str(e))
|
||||||
return False
|
return False
|
||||||
except GAPI.alreadyExists as e:
|
except GAPI.alreadyExists as e:
|
||||||
@@ -12309,8 +12334,12 @@ def checkServiceAccount(users):
|
|||||||
for scope in scopesList:
|
for scope in scopesList:
|
||||||
if selectedScopes[i] == '*':
|
if selectedScopes[i] == '*':
|
||||||
saScopes.setdefault(scope['api'], [])
|
saScopes.setdefault(scope['api'], [])
|
||||||
saScopes[scope['api']].append(scope['scope'])
|
if not isinstance(scope['scope'], list):
|
||||||
checkScopesSet.add(scope['scope'])
|
saScopes[scope['api']].append(scope['scope'])
|
||||||
|
checkScopesSet.add(scope['scope'])
|
||||||
|
else:
|
||||||
|
saScopes[scope['api']].extend(scope['scope'])
|
||||||
|
checkScopesSet.update(scope['scope'])
|
||||||
elif selectedScopes[i] == 'R':
|
elif selectedScopes[i] == 'R':
|
||||||
saScopes.setdefault(scope['api'], [])
|
saScopes.setdefault(scope['api'], [])
|
||||||
if 'roscope' not in scope:
|
if 'roscope' not in scope:
|
||||||
@@ -13606,6 +13635,7 @@ REPORT_CHOICE_MAP = {
|
|||||||
'drive': 'drive',
|
'drive': 'drive',
|
||||||
'gcp': 'gcp',
|
'gcp': 'gcp',
|
||||||
'geminiinworkspaceapps': 'gemini_in_workspace_apps',
|
'geminiinworkspaceapps': 'gemini_in_workspace_apps',
|
||||||
|
'gmail': 'gmail',
|
||||||
'gplus': 'gplus',
|
'gplus': 'gplus',
|
||||||
'groups': 'groups',
|
'groups': 'groups',
|
||||||
'groupsenterprise': 'groups_enterprise',
|
'groupsenterprise': 'groups_enterprise',
|
||||||
@@ -27066,6 +27096,8 @@ def _getChatMemberEmail(cd, member):
|
|||||||
if member['member']['type'] == 'HUMAN':
|
if member['member']['type'] == 'HUMAN':
|
||||||
_, memberUid = member['member']['name'].split('/')
|
_, memberUid = member['member']['name'].split('/')
|
||||||
member['member']['email'], _ = convertUIDtoEmailAddressWithType(f'uid:{memberUid}', cd, None, emailTypes=['user'])
|
member['member']['email'], _ = convertUIDtoEmailAddressWithType(f'uid:{memberUid}', cd, None, emailTypes=['user'])
|
||||||
|
if member['member']['email'].find('@') == -1:
|
||||||
|
member['member']['email'] = 'id:'+member['member']['email']
|
||||||
elif 'groupMember' in member:
|
elif 'groupMember' in member:
|
||||||
_, memberUid = member['groupMember']['name'].split('/')
|
_, memberUid = member['groupMember']['name'].split('/')
|
||||||
member['groupMember']['email'], _ = convertUIDtoEmailAddressWithType(f'uid:{memberUid}', cd, None, emailTypes=['group'])
|
member['groupMember']['email'], _ = convertUIDtoEmailAddressWithType(f'uid:{memberUid}', cd, None, emailTypes=['group'])
|
||||||
@@ -28215,9 +28247,7 @@ def printShowChatEvents(users):
|
|||||||
if csvPF:
|
if csvPF:
|
||||||
csvPF.writeCSVfile('Chat Events')
|
csvPF.writeCSVfile('Chat Events')
|
||||||
|
|
||||||
def buildMeetServiceObject(api=API.MEET, user=None, i=0, count=0, entityTypeList=None):
|
def buildMeetServiceObject(api, user=None, i=0, count=0, entityTypeList=None):
|
||||||
if GC.Values[GC.MEET_V2_BETA]:
|
|
||||||
api = API.MEET_BETA
|
|
||||||
user, meet = buildGAPIServiceObject(api, user, i, count)
|
user, meet = buildGAPIServiceObject(api, user, i, count)
|
||||||
kvList = [Ent.USER, user]
|
kvList = [Ent.USER, user]
|
||||||
if entityTypeList is not None:
|
if entityTypeList is not None:
|
||||||
@@ -28326,7 +28356,7 @@ def createMeetSpace(users):
|
|||||||
i, count, users = getEntityArgument(users)
|
i, count, users = getEntityArgument(users)
|
||||||
for user in users:
|
for user in users:
|
||||||
i += 1
|
i += 1
|
||||||
user, meet, kvList = buildMeetServiceObject(API.MEET, user, i, count, [Ent.MEET_SPACE, None])
|
user, meet, kvList = buildMeetServiceObject(API.MEET_SPACES, user, i, count, [Ent.MEET_SPACE, None])
|
||||||
if not meet:
|
if not meet:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
@@ -28365,7 +28395,7 @@ def updateMeetSpace(users):
|
|||||||
i, count, users = getEntityArgument(users)
|
i, count, users = getEntityArgument(users)
|
||||||
for user in users:
|
for user in users:
|
||||||
i += 1
|
i += 1
|
||||||
user, meet, kvList = buildMeetServiceObject(API.MEET, user, i, count, [Ent.MEET_SPACE, name])
|
user, meet, kvList = buildMeetServiceObject(API.MEET_SPACES, user, i, count, [Ent.MEET_SPACE, name])
|
||||||
if not meet:
|
if not meet:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
@@ -28396,7 +28426,7 @@ def infoMeetSpace(users):
|
|||||||
i, count, users = getEntityArgument(users)
|
i, count, users = getEntityArgument(users)
|
||||||
for user in users:
|
for user in users:
|
||||||
i += 1
|
i += 1
|
||||||
user, meet, kvList = buildMeetServiceObject(API.MEET, user, i, count, [Ent.MEET_SPACE, name])
|
user, meet, kvList = buildMeetServiceObject(API.MEET_SPACES, user, i, count, [Ent.MEET_SPACE, name])
|
||||||
if not meet:
|
if not meet:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
@@ -28425,7 +28455,7 @@ def endMeetConference(users):
|
|||||||
i, count, users = getEntityArgument(users)
|
i, count, users = getEntityArgument(users)
|
||||||
for user in users:
|
for user in users:
|
||||||
i += 1
|
i += 1
|
||||||
user, meet, kvList = buildMeetServiceObject(API.MEET, user, i, count, [Ent.MEET_SPACE, name, Ent.MEET_CONFERENCE, None])
|
user, meet, kvList = buildMeetServiceObject(API.MEET_SPACES, user, i, count, [Ent.MEET_SPACE, name, Ent.MEET_CONFERENCE, None])
|
||||||
if not meet:
|
if not meet:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
@@ -28506,7 +28536,7 @@ def printShowMeetConferences(users):
|
|||||||
i, count, users = getEntityArgument(users)
|
i, count, users = getEntityArgument(users)
|
||||||
for user in users:
|
for user in users:
|
||||||
i += 1
|
i += 1
|
||||||
user, meet, kvList = buildMeetServiceObject(API.MEET, user, i, count, [Ent.MEET_CONFERENCE, None])
|
user, meet, kvList = buildMeetServiceObject(API.MEET_READONLY, user, i, count, [Ent.MEET_CONFERENCE, None])
|
||||||
if not meet:
|
if not meet:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
@@ -28582,7 +28612,7 @@ def _printShowMeetItems(users, entityType):
|
|||||||
i, count, users = getEntityArgument(users)
|
i, count, users = getEntityArgument(users)
|
||||||
for user in users:
|
for user in users:
|
||||||
i += 1
|
i += 1
|
||||||
user, meet, kvList = buildMeetServiceObject(API.MEET, user, i, count, [Ent.MEET_CONFERENCE, parent])
|
user, meet, kvList = buildMeetServiceObject(API.MEET_READONLY, user, i, count, [Ent.MEET_CONFERENCE, parent])
|
||||||
if not meet:
|
if not meet:
|
||||||
continue
|
continue
|
||||||
if entityType == Ent.MEET_PARTICIPANT:
|
if entityType == Ent.MEET_PARTICIPANT:
|
||||||
@@ -32198,9 +32228,9 @@ def doUpdateMobileDevices():
|
|||||||
[Msg.ACTION_APPLIED, body['action']], i, count)
|
[Msg.ACTION_APPLIED, body['action']], i, count)
|
||||||
except GAPI.internalError:
|
except GAPI.internalError:
|
||||||
entityActionFailedWarning([Ent.MOBILE_DEVICE, resourceId], Msg.DOES_NOT_EXIST, i, count)
|
entityActionFailedWarning([Ent.MOBILE_DEVICE, resourceId], Msg.DOES_NOT_EXIST, i, count)
|
||||||
except (GAPI.resourceIdNotFound, GAPI.badRequest, GAPI.resourceNotFound) as e:
|
except (GAPI.resourceIdNotFound, GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden) as e:
|
||||||
entityActionFailedWarning([Ent.MOBILE_DEVICE, resourceId], str(e), i, count)
|
entityActionFailedWarning([Ent.MOBILE_DEVICE, resourceId], str(e), i, count)
|
||||||
except (GAPI.forbidden, GAPI.permissionDenied) as e:
|
except GAPI.permissionDenied as e:
|
||||||
ClientAPIAccessDeniedExit(str(e))
|
ClientAPIAccessDeniedExit(str(e))
|
||||||
|
|
||||||
# gam delete mobile|mobiles <MobileDeviceEntity>
|
# gam delete mobile|mobiles <MobileDeviceEntity>
|
||||||
@@ -37783,6 +37813,54 @@ def doDeleteOrUndeleteAlert():
|
|||||||
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.permissionDenied):
|
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.permissionDenied):
|
||||||
userAlertsServiceNotEnabledWarning(user)
|
userAlertsServiceNotEnabledWarning(user)
|
||||||
|
|
||||||
|
def _showAlertSettings(settings):
|
||||||
|
notifications = settings.get('notifications', [])
|
||||||
|
count = len(notifications)
|
||||||
|
entityPerformAction([Ent.ALERT_SETTINGS, None])
|
||||||
|
i = 0
|
||||||
|
for notification in notifications:
|
||||||
|
i += 1
|
||||||
|
printEntity([Ent.NOTIFICATION, None], i, count)
|
||||||
|
Ind.Increment()
|
||||||
|
showJSON(None, notification)
|
||||||
|
Ind.Decrement()
|
||||||
|
|
||||||
|
# gam show alertsettings
|
||||||
|
def doShowAlertSettings():
|
||||||
|
checkForExtraneousArguments()
|
||||||
|
user, ac = buildGAPIServiceObject(API.ALERTCENTER, _getAdminEmail())
|
||||||
|
if not ac:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
settings = callGAPI(ac.v1beta1(), 'getSettings',
|
||||||
|
throwReasons=GAPI.ALERT_THROW_REASONS)
|
||||||
|
_showAlertSettings(settings)
|
||||||
|
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.permissionDenied):
|
||||||
|
userAlertsServiceNotEnabledWarning(user)
|
||||||
|
|
||||||
|
# gam update alertsettings <PubsubTopicName>
|
||||||
|
def doUpdateAlertSettings(clear=False):
|
||||||
|
if not clear:
|
||||||
|
body = {'notifications':
|
||||||
|
[{'cloudPubsubTopic': {'topicName': getString(Cmd.OB_PUBSUB_TOPIC_NAME)}}]}
|
||||||
|
else:
|
||||||
|
body = {'notifications': []}
|
||||||
|
checkForExtraneousArguments()
|
||||||
|
user, ac = buildGAPIServiceObject(API.ALERTCENTER, _getAdminEmail())
|
||||||
|
if not ac:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
settings = callGAPI(ac.v1beta1(), 'updateSettings',
|
||||||
|
throwReasons=GAPI.ALERT_THROW_REASONS,
|
||||||
|
body=body)
|
||||||
|
_showAlertSettings(settings)
|
||||||
|
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.permissionDenied):
|
||||||
|
userAlertsServiceNotEnabledWarning(user)
|
||||||
|
|
||||||
|
# gam clear alertsettings
|
||||||
|
def doClearAlertSettings():
|
||||||
|
doUpdateAlertSettings(clear=True)
|
||||||
|
|
||||||
ALERT_TIME_OBJECTS = {'createTime', 'startTime', 'endTime'}
|
ALERT_TIME_OBJECTS = {'createTime', 'startTime', 'endTime'}
|
||||||
|
|
||||||
def _showAlert(alert, FJQC, i=0, count=0):
|
def _showAlert(alert, FJQC, i=0, count=0):
|
||||||
@@ -44829,21 +44907,44 @@ def waitForMailbox(entityList):
|
|||||||
Ind.Decrement()
|
Ind.Decrement()
|
||||||
|
|
||||||
def getUserLicenses(lic, user, skus):
|
def getUserLicenses(lic, user, skus):
|
||||||
def _callbackGetLicense(_, response, exception):
|
reasons_to_quit = [
|
||||||
|
GAPI.ACCESS_NOT_CONFIGURED, # license API not turned on
|
||||||
|
GAPI.PERMISSION_DENIED, # Admin doesn't have rights to license assignments
|
||||||
|
GAPI.NOT_FOUND # API call succeeded, user does not have this license
|
||||||
|
]
|
||||||
|
|
||||||
|
def _callbackGetLicense(request_id, response, exception):
|
||||||
if exception is None:
|
if exception is None:
|
||||||
if response and 'skuId' in response:
|
if response and 'skuId' in response:
|
||||||
licenses.append(response['skuId'])
|
licenses.append(response['skuId'])
|
||||||
|
del sku_calls[request_id]
|
||||||
|
else:
|
||||||
|
_, reason, _ = checkGAPIError(exception, softErrors=True)
|
||||||
|
if reason in reasons_to_quit:
|
||||||
|
del sku_calls[request_id]
|
||||||
|
|
||||||
licenses = []
|
licenses = []
|
||||||
svcargs = dict([('userId', user['primaryEmail']), ('productId', None), ('skuId', None), ('fields', 'skuId')]+GM.Globals[GM.EXTRA_ARGS_LIST])
|
svcargs = dict([('userId', user['primaryEmail']), ('productId', None), ('skuId', None), ('fields', 'skuId')]+GM.Globals[GM.EXTRA_ARGS_LIST])
|
||||||
method = getattr(lic.licenseAssignments(), 'get')
|
method = getattr(lic.licenseAssignments(), 'get')
|
||||||
dbatch = lic.new_batch_http_request(callback=_callbackGetLicense)
|
sku_calls = {}
|
||||||
for sku in skus:
|
for sku in skus:
|
||||||
svcparms = svcargs.copy()
|
svcparms = svcargs.copy()
|
||||||
svcparms['productId'] = sku[0]
|
svcparms['productId'] = sku[0]
|
||||||
svcparms['skuId'] = sku[1]
|
sku_id = sku[1]
|
||||||
dbatch.add(method(**svcparms))
|
svcparms['skuId'] = sku_id
|
||||||
dbatch.execute()
|
sku_calls[sku_id] = method(**svcparms)
|
||||||
|
try_count = 0
|
||||||
|
while sku_calls:
|
||||||
|
try_count += 1
|
||||||
|
dbatch = lic.new_batch_http_request(callback=_callbackGetLicense)
|
||||||
|
for sku_id, sku_call in sku_calls.items():
|
||||||
|
dbatch.add(sku_call, request_id=sku_id)
|
||||||
|
dbatch.execute()
|
||||||
|
if sku_calls:
|
||||||
|
if try_count >= 5:
|
||||||
|
# give up and return what we have
|
||||||
|
return licenses
|
||||||
|
time.sleep(5)
|
||||||
return licenses
|
return licenses
|
||||||
|
|
||||||
USER_NAME_PROPERTY_PRINT_ORDER = [
|
USER_NAME_PROPERTY_PRINT_ORDER = [
|
||||||
@@ -46328,9 +46429,30 @@ def checkCIUserIsInvitable(users):
|
|||||||
return
|
return
|
||||||
csvPF.writeCSVfile('Invitable Users')
|
csvPF.writeCSVfile('Invitable Users')
|
||||||
|
|
||||||
|
INBOUNDSSO_INPUT_MODE_CHOICE_MAP = {
|
||||||
|
'saml': 'saml',
|
||||||
|
'samlsso': 'saml',
|
||||||
|
'oidc': 'oidc',
|
||||||
|
'oidcsso': 'oidc',
|
||||||
|
}
|
||||||
|
|
||||||
|
INBOUNDSSO_OUTPUT_MODE_CHOICE_MAP = {
|
||||||
|
'all': 'all',
|
||||||
|
'saml': 'saml',
|
||||||
|
'samlsso': 'saml',
|
||||||
|
'oidc': 'oidc',
|
||||||
|
'oidcsso': 'oidc',
|
||||||
|
}
|
||||||
|
|
||||||
|
INBOUNDSSO_ALL_SAML = {'all', 'saml'}
|
||||||
|
INBOUNDSSO_ALL_OIDC = {'all', 'oidc'}
|
||||||
|
|
||||||
INBOUNDSSO_MODE_CHOICE_MAP = {
|
INBOUNDSSO_MODE_CHOICE_MAP = {
|
||||||
'ssooff': 'SSO_OFF',
|
'ssooff': 'SSO_OFF',
|
||||||
|
'saml': 'SAML_SSO',
|
||||||
'samlsso': 'SAML_SSO',
|
'samlsso': 'SAML_SSO',
|
||||||
|
'oidc': 'OIDC_SSO',
|
||||||
|
'oidcsso': 'OIDC_SSO',
|
||||||
'domainwidesamlifenabled': 'DOMAIN_WIDE_SAML_IF_ENABLED'
|
'domainwidesamlifenabled': 'DOMAIN_WIDE_SAML_IF_ENABLED'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46340,29 +46462,49 @@ def getCIOrgunitID(cd, orgunit):
|
|||||||
ou_id = ou_id[3:]
|
ou_id = ou_id[3:]
|
||||||
return f'orgUnits/{ou_id}'
|
return f'orgUnits/{ou_id}'
|
||||||
|
|
||||||
def _getInboundSSOProfiles(ci):
|
def _getInboundSSOProfiles(ci, mode):
|
||||||
customer = normalizeChannelCustomerID(GC.Values[GC.CUSTOMER_ID])
|
customer = normalizeChannelCustomerID(GC.Values[GC.CUSTOMER_ID])
|
||||||
try:
|
profiles = []
|
||||||
return callGAPIpages(ci.inboundSamlSsoProfiles(), 'list', 'inboundSamlSsoProfiles',
|
if mode in INBOUNDSSO_ALL_SAML:
|
||||||
throwReasons=GAPI.CISSO_LIST_THROW_REASONS,
|
try:
|
||||||
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
profiles.extend(callGAPIpages(ci.inboundSamlSsoProfiles(), 'list', 'inboundSamlSsoProfiles',
|
||||||
bailOnInternalError=True,
|
throwReasons=GAPI.CISSO_LIST_THROW_REASONS,
|
||||||
filter=f'customer=="{customer}"')
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
||||||
except (GAPI.notFound, GAPI.domainNotFound, GAPI.domainCannotUseApis,
|
bailOnInternalError=True,
|
||||||
GAPI.forbidden, GAPI.badRequest, GAPI.invalid,
|
filter=f'customer=="{customer}"'))
|
||||||
GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e:
|
except (GAPI.notFound, GAPI.domainNotFound, GAPI.domainCannotUseApis,
|
||||||
entityActionFailedWarning([Ent.INBOUND_SSO_PROFILE, customer], str(e))
|
GAPI.forbidden, GAPI.badRequest, GAPI.invalid,
|
||||||
return []
|
GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e:
|
||||||
|
entityActionFailedWarning([Ent.INBOUND_SSO_PROFILE, customer], str(e))
|
||||||
|
if mode in INBOUNDSSO_ALL_OIDC:
|
||||||
|
try:
|
||||||
|
profiles.extend(callGAPIpages(ci.inboundOidcSsoProfiles(), 'list', 'inboundOidcSsoProfiles',
|
||||||
|
throwReasons=GAPI.CISSO_LIST_THROW_REASONS,
|
||||||
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
||||||
|
bailOnInternalError=True,
|
||||||
|
filter=f'customer=="{customer}"'))
|
||||||
|
except (GAPI.notFound, GAPI.domainNotFound, GAPI.domainCannotUseApis,
|
||||||
|
GAPI.forbidden, GAPI.badRequest, GAPI.invalid,
|
||||||
|
GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e:
|
||||||
|
entityActionFailedWarning([Ent.INBOUND_SSO_PROFILE, customer], str(e))
|
||||||
|
return profiles
|
||||||
|
|
||||||
def _convertInboundSSOProfileDisplaynameToName(ci=None, displayName=''):
|
def _convertInboundSSOProfileDisplaynameToName(ci, mode, displayName='',
|
||||||
|
entityType=Ent.INBOUND_SSO_PROFILE):
|
||||||
if displayName.lower().startswith('id:') or displayName.lower().startswith('uid:'):
|
if displayName.lower().startswith('id:') or displayName.lower().startswith('uid:'):
|
||||||
displayName = displayName.split(':', 1)[1]
|
displayName = displayName.split(':', 1)[1]
|
||||||
if not displayName.startswith('inboundSamlSsoProfiles/'):
|
if mode == 'all':
|
||||||
displayName = f'inboundSamlSsoProfiles/{displayName}'
|
if not (displayName.startswith('inboundSamlSsoProfiles/') and
|
||||||
|
displayName.startswith('inboundOidcSsoProfiles/')):
|
||||||
|
displayName = f'inboundSamlSsoProfiles/{displayName}'
|
||||||
|
elif mode == 'saml':
|
||||||
|
if not displayName.startswith('inboundSamlSsoProfiles/'):
|
||||||
|
displayName = f'inboundSamlSsoProfiles/{displayName}'
|
||||||
|
else:
|
||||||
|
if not displayName.startswith('inboundOidcSsoProfiles/'):
|
||||||
|
displayName = f'inboundOidcSsoProfiles/{displayName}'
|
||||||
return displayName
|
return displayName
|
||||||
if not ci:
|
profiles = _getInboundSSOProfiles(ci, mode)
|
||||||
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
|
|
||||||
profiles = _getInboundSSOProfiles(ci)
|
|
||||||
matches = []
|
matches = []
|
||||||
for profile in profiles:
|
for profile in profiles:
|
||||||
if displayName.lower() == profile.get('displayName', '').lower():
|
if displayName.lower() == profile.get('displayName', '').lower():
|
||||||
@@ -46370,30 +46512,50 @@ def _convertInboundSSOProfileDisplaynameToName(ci=None, displayName=''):
|
|||||||
if len(matches) == 1:
|
if len(matches) == 1:
|
||||||
return matches[0]['name']
|
return matches[0]['name']
|
||||||
if len(matches) == 0:
|
if len(matches) == 0:
|
||||||
usageErrorExit(Msg.NO_SSO_PROFILE_MATCHES.format(displayName))
|
errMsg = Msg.NO_SSO_PROFILE_MATCHES.format(displayName)
|
||||||
errMsg = Msg.MULTIPLE_SSO_PROFILES_MATCH.format(displayName)
|
else:
|
||||||
for m in matches:
|
errMsg = Msg.MULTIPLE_SSO_PROFILES_MATCH.format(displayName)
|
||||||
errMsg += f' {m["name"]} {m["displayName"]}\n'
|
for m in matches:
|
||||||
usageErrorExit(errMsg)
|
errMsg += f' {m["name"]} {m["displayName"]}\n'
|
||||||
|
entityActionFailedWarning([entityType, None], errMsg)
|
||||||
|
return None
|
||||||
|
|
||||||
def _getInboundSSOProfileArguments(body):
|
def _getInboundSSOProfileArguments(body, mode):
|
||||||
returnNameOnly = False
|
returnNameOnly = False
|
||||||
while Cmd.ArgumentsRemaining():
|
if mode == 'saml':
|
||||||
myarg = getArgument()
|
while Cmd.ArgumentsRemaining():
|
||||||
if myarg == 'name':
|
myarg = getArgument()
|
||||||
body['displayName'] = getString(Cmd.OB_STRING)
|
if myarg == 'name':
|
||||||
elif myarg == 'entityid':
|
body['displayName'] = getString(Cmd.OB_STRING)
|
||||||
body.setdefault('idpConfig', {})['entityId'] = getString(Cmd.OB_STRING)
|
elif myarg == 'entityid':
|
||||||
elif myarg == 'loginurl':
|
body.setdefault('idpConfig', {})['entityId'] = getString(Cmd.OB_STRING)
|
||||||
body.setdefault('idpConfig', {})['singleSignOnServiceUri'] = getString(Cmd.OB_STRING)
|
elif myarg == 'loginurl':
|
||||||
elif myarg == 'logouturl':
|
body.setdefault('idpConfig', {})['singleSignOnServiceUri'] = getString(Cmd.OB_STRING)
|
||||||
body.setdefault('idpConfig', {})['logoutRedirectUri'] = getString(Cmd.OB_STRING)
|
elif myarg == 'logouturl':
|
||||||
elif myarg == 'changepasswordurl':
|
body.setdefault('idpConfig', {})['logoutRedirectUri'] = getString(Cmd.OB_STRING)
|
||||||
body.setdefault('idpConfig', {})['changePasswordUri'] = getString(Cmd.OB_STRING)
|
elif myarg == 'changepasswordurl':
|
||||||
elif myarg == 'returnnameonly':
|
body.setdefault('idpConfig', {})['changePasswordUri'] = getString(Cmd.OB_STRING)
|
||||||
returnNameOnly = True
|
elif myarg == 'returnnameonly':
|
||||||
else:
|
returnNameOnly = True
|
||||||
unknownArgumentExit()
|
else:
|
||||||
|
unknownArgumentExit()
|
||||||
|
else:
|
||||||
|
while Cmd.ArgumentsRemaining():
|
||||||
|
myarg = getArgument()
|
||||||
|
if myarg == 'name':
|
||||||
|
body['displayName'] = getString(Cmd.OB_STRING)
|
||||||
|
elif myarg == 'issueruri':
|
||||||
|
body.setdefault('idpConfig', {})['issuerUri'] = getString(Cmd.OB_STRING)
|
||||||
|
elif myarg == 'changepasswordurl':
|
||||||
|
body.setdefault('idpConfig', {})['changePasswordUri'] = getString(Cmd.OB_STRING)
|
||||||
|
elif myarg == 'clientid':
|
||||||
|
body.setdefault('rpConfig', {})['clientId'] = getString(Cmd.OB_STRING)
|
||||||
|
elif myarg == 'clientsecret':
|
||||||
|
body.setdefault('rpConfig', {})['clientSecret'] = getString(Cmd.OB_STRING)
|
||||||
|
elif myarg == 'returnnameonly':
|
||||||
|
returnNameOnly = True
|
||||||
|
else:
|
||||||
|
unknownArgumentExit()
|
||||||
return (returnNameOnly, body)
|
return (returnNameOnly, body)
|
||||||
|
|
||||||
def _showInboundSSOProfile(profile, FJQC, i=0, count=0):
|
def _showInboundSSOProfile(profile, FJQC, i=0, count=0):
|
||||||
@@ -46424,18 +46586,24 @@ def _processInboundSSOProfileResult(result, returnNameOnly, kvlist, function):
|
|||||||
else:
|
else:
|
||||||
writeStdout('inProgress\n')
|
writeStdout('inProgress\n')
|
||||||
|
|
||||||
# gam create inboundssoprofile [name <SSOProfileName>]
|
def _getInboundSSOModeService(ci):
|
||||||
|
mode = getChoice(INBOUNDSSO_INPUT_MODE_CHOICE_MAP, defaultChoice='saml', mapChoice=True)
|
||||||
|
service = ci.inboundSamlSsoProfiles() if mode == 'saml' else ci.inboundOidcSsoProfiles()
|
||||||
|
return (mode, service)
|
||||||
|
|
||||||
|
# gam create inboundssoprofile [saml|oidc] [name <SSOProfileName>]
|
||||||
# [entityid <String>] [loginurl <URL>] [logouturl <URL>] [changepasswordurl <URL>]
|
# [entityid <String>] [loginurl <URL>] [logouturl <URL>] [changepasswordurl <URL>]
|
||||||
# [returnnameonly]
|
# [returnnameonly]
|
||||||
def doCreateInboundSSOProfile():
|
def doCreateInboundSSOProfile():
|
||||||
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
|
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
|
||||||
|
mode, service = _getInboundSSOModeService(ci)
|
||||||
body = {'customer': normalizeChannelCustomerID(GC.Values[GC.CUSTOMER_ID]),
|
body = {'customer': normalizeChannelCustomerID(GC.Values[GC.CUSTOMER_ID]),
|
||||||
'displayName': 'SSO Profile'
|
'displayName': 'SSO Profile'
|
||||||
}
|
}
|
||||||
returnNameOnly, body = _getInboundSSOProfileArguments(body)
|
returnNameOnly, body = _getInboundSSOProfileArguments(body, mode)
|
||||||
kvlist = [Ent.INBOUND_SSO_PROFILE, body['displayName']]
|
kvlist = [Ent.INBOUND_SSO_PROFILE, body['displayName']]
|
||||||
try:
|
try:
|
||||||
result = callGAPI(ci.inboundSamlSsoProfiles(), 'create',
|
result = callGAPI(service, 'create',
|
||||||
throwReasons=GAPI.CISSO_CREATE_THROW_REASONS,
|
throwReasons=GAPI.CISSO_CREATE_THROW_REASONS,
|
||||||
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
||||||
bailOnInternalError=True,
|
bailOnInternalError=True,
|
||||||
@@ -46446,16 +46614,19 @@ def doCreateInboundSSOProfile():
|
|||||||
GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e:
|
GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e:
|
||||||
entityActionFailedWarning(kvlist, str(e))
|
entityActionFailedWarning(kvlist, str(e))
|
||||||
|
|
||||||
# gam update inboundssoprofile <SSOProfileItem>
|
# gam update inboundssoprofile [saml|oidc] <SSOProfileItem>
|
||||||
# [entityid <String>] [loginurl <URL>] [logouturl <URL>] [changepasswordurl <URL>]
|
# [entityid <String>] [loginurl <URL>] [logouturl <URL>] [changepasswordurl <URL>]
|
||||||
# [returnnameonly]
|
# [returnnameonly]
|
||||||
def doUpdateInboundSSOProfile():
|
def doUpdateInboundSSOProfile():
|
||||||
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
|
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
|
||||||
name = _convertInboundSSOProfileDisplaynameToName(ci, getString(Cmd.OB_STRING))
|
mode, service = _getInboundSSOModeService(ci)
|
||||||
returnNameOnly, body = _getInboundSSOProfileArguments({})
|
name = _convertInboundSSOProfileDisplaynameToName(ci, mode, getString(Cmd.OB_STRING))
|
||||||
|
if not name:
|
||||||
|
return
|
||||||
|
returnNameOnly, body = _getInboundSSOProfileArguments({}, mode)
|
||||||
kvlist = [Ent.INBOUND_SSO_PROFILE, name]
|
kvlist = [Ent.INBOUND_SSO_PROFILE, name]
|
||||||
try:
|
try:
|
||||||
result = callGAPI(ci.inboundSamlSsoProfiles(), 'patch',
|
result = callGAPI(service, 'patch',
|
||||||
throwReasons=GAPI.CISSO_UPDATE_THROW_REASONS,
|
throwReasons=GAPI.CISSO_UPDATE_THROW_REASONS,
|
||||||
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
||||||
bailOnInternalError=True,
|
bailOnInternalError=True,
|
||||||
@@ -46468,14 +46639,17 @@ def doUpdateInboundSSOProfile():
|
|||||||
GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e:
|
GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e:
|
||||||
entityActionFailedWarning(kvlist, str(e))
|
entityActionFailedWarning(kvlist, str(e))
|
||||||
|
|
||||||
# gam delete inboundssoprofile <SSOProfileItem>
|
# gam delete inboundssoprofile [saml|oidc] <SSOProfileItem>
|
||||||
def doDeleteInboundSSOProfile():
|
def doDeleteInboundSSOProfile():
|
||||||
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
|
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
|
||||||
name = _convertInboundSSOProfileDisplaynameToName(ci, getString(Cmd.OB_STRING))
|
mode, service = _getInboundSSOModeService(ci)
|
||||||
|
name = _convertInboundSSOProfileDisplaynameToName(ci, mode, getString(Cmd.OB_STRING))
|
||||||
|
if not name:
|
||||||
|
return
|
||||||
checkForExtraneousArguments()
|
checkForExtraneousArguments()
|
||||||
kvlist = [Ent.INBOUND_SSO_PROFILE, name]
|
kvlist = [Ent.INBOUND_SSO_PROFILE, name]
|
||||||
try:
|
try:
|
||||||
result = callGAPI(ci.inboundSamlSsoProfiles(), 'delete',
|
result = callGAPI(service, 'delete',
|
||||||
throwReasons=GAPI.CISSO_UPDATE_THROW_REASONS,
|
throwReasons=GAPI.CISSO_UPDATE_THROW_REASONS,
|
||||||
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
||||||
bailOnInternalError=True,
|
bailOnInternalError=True,
|
||||||
@@ -46488,33 +46662,54 @@ def doDeleteInboundSSOProfile():
|
|||||||
GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e:
|
GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e:
|
||||||
entityActionFailedWarning(kvlist, str(e))
|
entityActionFailedWarning(kvlist, str(e))
|
||||||
|
|
||||||
def _getInboundSSOProfile(ci, name):
|
def _getInboundSSOProfileByName(ci, mode, name):
|
||||||
|
notFound = False
|
||||||
kvlist = [Ent.INBOUND_SSO_PROFILE, name]
|
kvlist = [Ent.INBOUND_SSO_PROFILE, name]
|
||||||
try:
|
if mode in INBOUNDSSO_ALL_SAML:
|
||||||
return callGAPI(ci.inboundSamlSsoProfiles(), 'get',
|
try:
|
||||||
throwReasons=GAPI.CISSO_GET_THROW_REASONS,
|
return callGAPI(ci.inboundSamlSsoProfiles(), 'get',
|
||||||
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
throwReasons=GAPI.CISSO_GET_THROW_REASONS,
|
||||||
bailOnInternalError=True,
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
||||||
name=name)
|
bailOnInternalError=True,
|
||||||
except GAPI.notFound:
|
name=name)
|
||||||
|
except GAPI.notFound:
|
||||||
|
notFound = True
|
||||||
|
except (GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.forbidden,
|
||||||
|
GAPI.badRequest, GAPI.invalid, GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e:
|
||||||
|
entityActionFailedWarning(kvlist, str(e))
|
||||||
|
if mode in INBOUNDSSO_ALL_OIDC:
|
||||||
|
try:
|
||||||
|
return callGAPI(ci.inboundOidcSsoProfiles(), 'get',
|
||||||
|
throwReasons=GAPI.CISSO_GET_THROW_REASONS,
|
||||||
|
retryReasons=GAPI.SERVICE_NOT_AVAILABLE_RETRY_REASONS,
|
||||||
|
bailOnInternalError=True,
|
||||||
|
name=name)
|
||||||
|
except GAPI.notFound:
|
||||||
|
notFound = True
|
||||||
|
except (GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.forbidden,
|
||||||
|
GAPI.badRequest, GAPI.invalid, GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e:
|
||||||
|
entityActionFailedWarning(kvlist, str(e))
|
||||||
|
if notFound:
|
||||||
entityActionFailedWarning(kvlist, Msg.DOES_NOT_EXIST)
|
entityActionFailedWarning(kvlist, Msg.DOES_NOT_EXIST)
|
||||||
except (GAPI.domainNotFound, GAPI.domainCannotUseApis, GAPI.forbidden,
|
|
||||||
GAPI.badRequest, GAPI.invalid, GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e:
|
|
||||||
entityActionFailedWarning(kvlist, str(e))
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# gam info inboundssoprofile <SSOProfileItem> [formatjson]
|
# gam info inboundssoprofile [all|saml|oidc] <SSOProfileItem> [formatjson]
|
||||||
def doInfoInboundSSOProfile():
|
def doInfoInboundSSOProfile():
|
||||||
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
|
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
|
||||||
name = _convertInboundSSOProfileDisplaynameToName(ci, getString(Cmd.OB_STRING))
|
mode = getChoice(INBOUNDSSO_OUTPUT_MODE_CHOICE_MAP, defaultChoice='all', mapChoice=True)
|
||||||
|
name = getString(Cmd.OB_STRING)
|
||||||
FJQC = FormatJSONQuoteChar(formatJSONOnly=True)
|
FJQC = FormatJSONQuoteChar(formatJSONOnly=True)
|
||||||
profile = _getInboundSSOProfile(ci, name)
|
name = _convertInboundSSOProfileDisplaynameToName(ci, mode, name)
|
||||||
|
if not name:
|
||||||
|
return
|
||||||
|
mode = 'saml' if name.startswith('inboundSamlSsoProfiles/') else 'oidc'
|
||||||
|
profile = _getInboundSSOProfileByName(ci, mode, name)
|
||||||
if profile:
|
if profile:
|
||||||
_showInboundSSOProfile(profile, FJQC)
|
_showInboundSSOProfile(profile, FJQC)
|
||||||
|
|
||||||
# gam show inboundssoprofile
|
# gam show inboundssoprofile [all|saml|oidc]
|
||||||
# [formatjson]
|
# [formatjson]
|
||||||
# gam print inboundssoprofile [todrive <ToDriveAttribute>*]
|
# gam print inboundssoprofile [all|saml|oidc] [todrive <ToDriveAttribute>*]
|
||||||
# [[formatjson [quotechar <Character>]]
|
# [[formatjson [quotechar <Character>]]
|
||||||
def doPrintShowInboundSSOProfiles():
|
def doPrintShowInboundSSOProfiles():
|
||||||
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
|
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
|
||||||
@@ -46522,6 +46717,7 @@ def doPrintShowInboundSSOProfiles():
|
|||||||
csvPF = CSVPrintFile(['name']) if Act.csvFormat() else None
|
csvPF = CSVPrintFile(['name']) if Act.csvFormat() else None
|
||||||
FJQC = FormatJSONQuoteChar(csvPF)
|
FJQC = FormatJSONQuoteChar(csvPF)
|
||||||
cfilter = f'customer=="{customer}"'
|
cfilter = f'customer=="{customer}"'
|
||||||
|
mode = getChoice(INBOUNDSSO_OUTPUT_MODE_CHOICE_MAP, defaultChoice='all', mapChoice=True)
|
||||||
while Cmd.ArgumentsRemaining():
|
while Cmd.ArgumentsRemaining():
|
||||||
myarg = getArgument()
|
myarg = getArgument()
|
||||||
if csvPF and myarg == 'todrive':
|
if csvPF and myarg == 'todrive':
|
||||||
@@ -46530,7 +46726,7 @@ def doPrintShowInboundSSOProfiles():
|
|||||||
FJQC.GetFormatJSONQuoteChar(myarg, True)
|
FJQC.GetFormatJSONQuoteChar(myarg, True)
|
||||||
if csvPF:
|
if csvPF:
|
||||||
printGettingAllAccountEntities(Ent.INBOUND_SSO_PROFILE, cfilter)
|
printGettingAllAccountEntities(Ent.INBOUND_SSO_PROFILE, cfilter)
|
||||||
profiles = _getInboundSSOProfiles(ci)
|
profiles = _getInboundSSOProfiles(ci, mode)
|
||||||
if not csvPF:
|
if not csvPF:
|
||||||
count = len(profiles)
|
count = len(profiles)
|
||||||
if not FJQC.formatJSON:
|
if not FJQC.formatJSON:
|
||||||
@@ -46600,6 +46796,7 @@ def _processInboundSSOCredentialsResult(result, kvlist, function):
|
|||||||
# (pemfile <FileName>)|(generatekey [keysize 1024|2048|4096]) [replaceolddest]
|
# (pemfile <FileName>)|(generatekey [keysize 1024|2048|4096]) [replaceolddest]
|
||||||
def doCreateInboundSSOCredential():
|
def doCreateInboundSSOCredential():
|
||||||
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
|
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
|
||||||
|
mode = 'saml'
|
||||||
profile = None
|
profile = None
|
||||||
generateKey = replaceOldest = False
|
generateKey = replaceOldest = False
|
||||||
keySize = 2048
|
keySize = 2048
|
||||||
@@ -46607,7 +46804,11 @@ def doCreateInboundSSOCredential():
|
|||||||
while Cmd.ArgumentsRemaining():
|
while Cmd.ArgumentsRemaining():
|
||||||
myarg = getArgument()
|
myarg = getArgument()
|
||||||
if myarg == 'profile':
|
if myarg == 'profile':
|
||||||
profile = _convertInboundSSOProfileDisplaynameToName(ci, getString(Cmd.OB_STRING))
|
profile = _convertInboundSSOProfileDisplaynameToName(ci, mode,
|
||||||
|
getString(Cmd.OB_STRING),
|
||||||
|
Ent.INBOUND_SSO_CREDENTIALS)
|
||||||
|
if not profile:
|
||||||
|
return
|
||||||
elif myarg == 'pemfile':
|
elif myarg == 'pemfile':
|
||||||
pemData = readFile(getString(Cmd.OB_FILE_NAME))
|
pemData = readFile(getString(Cmd.OB_FILE_NAME))
|
||||||
elif myarg == 'generatekey':
|
elif myarg == 'generatekey':
|
||||||
@@ -46711,15 +46912,24 @@ def doPrintShowInboundSSOCredentials():
|
|||||||
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
|
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
|
||||||
csvPF = CSVPrintFile(['name']) if Act.csvFormat() else None
|
csvPF = CSVPrintFile(['name']) if Act.csvFormat() else None
|
||||||
FJQC = FormatJSONQuoteChar(csvPF)
|
FJQC = FormatJSONQuoteChar(csvPF)
|
||||||
|
mode = 'saml'
|
||||||
profiles = []
|
profiles = []
|
||||||
while Cmd.ArgumentsRemaining():
|
while Cmd.ArgumentsRemaining():
|
||||||
myarg = getArgument()
|
myarg = getArgument()
|
||||||
if myarg in {'profile', 'profiles'}:
|
if myarg in {'profile', 'profiles'}:
|
||||||
profiles = [_convertInboundSSOProfileDisplaynameToName(ci, profile) for profile in getString(Cmd.OB_STRING_LIST).split(',')]
|
errors = 0
|
||||||
|
for profile in getEntityList(Cmd.OB_STRING_LIST, shlexSplit=True):
|
||||||
|
name = _convertInboundSSOProfileDisplaynameToName(ci, mode, profile, Ent.INBOUND_SSO_CREDENTIALS)
|
||||||
|
if name:
|
||||||
|
profiles.append(name)
|
||||||
|
else:
|
||||||
|
errors += 1
|
||||||
|
if errors:
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
FJQC.GetFormatJSONQuoteChar(myarg, True)
|
FJQC.GetFormatJSONQuoteChar(myarg, True)
|
||||||
if not profiles:
|
if not profiles:
|
||||||
profiles = [p['name'] for p in _getInboundSSOProfiles(ci)]
|
profiles = [p['name'] for p in _getInboundSSOProfiles(ci, mode)]
|
||||||
count = len(profiles)
|
count = len(profiles)
|
||||||
i = 0
|
i = 0
|
||||||
for profile in profiles:
|
for profile in profiles:
|
||||||
@@ -46812,6 +47022,7 @@ def _getInboundSSOAssignmentByTarget(ci, cd, target):
|
|||||||
usageErrorExit(Msg.NO_SSO_PROFILE_ASSIGNED.format(targetType, target))
|
usageErrorExit(Msg.NO_SSO_PROFILE_ASSIGNED.format(targetType, target))
|
||||||
|
|
||||||
def _getInboundSSOAssignmentArguments(ci, cd, body):
|
def _getInboundSSOAssignmentArguments(ci, cd, body):
|
||||||
|
mode = None
|
||||||
rank = 0
|
rank = 0
|
||||||
while Cmd.ArgumentsRemaining():
|
while Cmd.ArgumentsRemaining():
|
||||||
myarg = getArgument()
|
myarg = getArgument()
|
||||||
@@ -46819,9 +47030,19 @@ def _getInboundSSOAssignmentArguments(ci, cd, body):
|
|||||||
rank = getInteger(minVal=1)
|
rank = getInteger(minVal=1)
|
||||||
elif myarg == 'mode':
|
elif myarg == 'mode':
|
||||||
body['ssoMode'] = getChoice(INBOUNDSSO_MODE_CHOICE_MAP, mapChoice=True)
|
body['ssoMode'] = getChoice(INBOUNDSSO_MODE_CHOICE_MAP, mapChoice=True)
|
||||||
elif myarg == 'profile':
|
if body['ssoMode'] == 'SAML_SSO':
|
||||||
body['samlSsoInfo'] = {'inboundSamlSsoProfile':
|
mode = 'saml'
|
||||||
_convertInboundSSOProfileDisplaynameToName(ci, getString(Cmd.OB_STRING))}
|
profile = 'inboundSamlSsoProfile'
|
||||||
|
elif body['ssoMode'] == 'OIDC_SSO':
|
||||||
|
mode = 'oidc'
|
||||||
|
profile = 'inboundOidcSsoProfile'
|
||||||
|
elif mode and myarg == 'profile':
|
||||||
|
name = _convertInboundSSOProfileDisplaynameToName(ci, mode,
|
||||||
|
getString(Cmd.OB_STRING),
|
||||||
|
Ent.INBOUND_SSO_ASSIGNMENT)
|
||||||
|
if not name:
|
||||||
|
return None
|
||||||
|
body['samlSsoInfo'] = {profile: name}
|
||||||
elif myarg == 'neverredirect':
|
elif myarg == 'neverredirect':
|
||||||
body['signInBehavior'] = {'redirectCondition': 'NEVER'}
|
body['signInBehavior'] = {'redirectCondition': 'NEVER'}
|
||||||
elif myarg == 'group':
|
elif myarg == 'group':
|
||||||
@@ -46832,7 +47053,7 @@ def _getInboundSSOAssignmentArguments(ci, cd, body):
|
|||||||
unknownArgumentExit()
|
unknownArgumentExit()
|
||||||
if 'ssoMode' not in body:
|
if 'ssoMode' not in body:
|
||||||
missingArgumentExit('mode')
|
missingArgumentExit('mode')
|
||||||
if body['ssoMode'] == 'SAML_SSO' and 'samlSsoInfo' not in body:
|
if mode and 'samlSsoInfo' not in body:
|
||||||
missingArgumentExit('profile')
|
missingArgumentExit('profile')
|
||||||
if 'targetGroup' in body:
|
if 'targetGroup' in body:
|
||||||
if 'targetOrgUnit' in body:
|
if 'targetOrgUnit' in body:
|
||||||
@@ -46870,13 +47091,17 @@ def _processInboundSSOAssignmentResult(result, kvlist, ci, cd, function):
|
|||||||
else:
|
else:
|
||||||
entityActionPerformedMessage(kvlist, Msg.ACTION_IN_PROGRESS.format(f'{function} inboundssoassignment'))
|
entityActionPerformedMessage(kvlist, Msg.ACTION_IN_PROGRESS.format(f'{function} inboundssoassignment'))
|
||||||
|
|
||||||
# gam create inboundssoassignment (group <GroupItem> rank <Number>)|(ou|org|orgunit <OrgUnitItem>)
|
# gam create inboundssoassignment
|
||||||
# (mode sso_off)|(mode saml_sso profile <SSOProfileItem>)(mode domain_wide_saml_if_enabled) [neverredirect]
|
# (group <GroupItem> rank <Number>)|(ou|org|orgunit <OrgUnitItem>)
|
||||||
|
# (mode sso_off)|(mode saml_sso profile <SSOProfileItem>)|(mode oidc_sso profile <SSOProfileName>}|(mode domain_wide_saml_if_enabled)
|
||||||
|
# [neverredirect]
|
||||||
def doCreateInboundSSOAssignment():
|
def doCreateInboundSSOAssignment():
|
||||||
cd = buildGAPIObject(API.DIRECTORY)
|
cd = buildGAPIObject(API.DIRECTORY)
|
||||||
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
|
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
|
||||||
body = {'customer': normalizeChannelCustomerID(GC.Values[GC.CUSTOMER_ID])}
|
body = {'customer': normalizeChannelCustomerID(GC.Values[GC.CUSTOMER_ID])}
|
||||||
body = _getInboundSSOAssignmentArguments(ci, cd, body)
|
body = _getInboundSSOAssignmentArguments(ci, cd, body)
|
||||||
|
if not body:
|
||||||
|
return
|
||||||
kvlist = [Ent.INBOUND_SSO_ASSIGNMENT, body['customer']]
|
kvlist = [Ent.INBOUND_SSO_ASSIGNMENT, body['customer']]
|
||||||
try:
|
try:
|
||||||
result = callGAPI(ci.inboundSsoAssignments(), 'create',
|
result = callGAPI(ci.inboundSsoAssignments(), 'create',
|
||||||
@@ -46890,8 +47115,10 @@ def doCreateInboundSSOAssignment():
|
|||||||
GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e:
|
GAPI.systemError, GAPI.permissionDenied, GAPI.internalError, GAPI.serviceNotAvailable) as e:
|
||||||
entityActionFailedWarning(kvlist, str(e))
|
entityActionFailedWarning(kvlist, str(e))
|
||||||
|
|
||||||
# gam update inboundssoassignment [(group <GroupItem> rank <Number>)|(ou|org|orgunit <OrgUnitItem>)]
|
# gam update inboundssoassignment <SSOAssignmentName>
|
||||||
# [(mode sso_off)|(mode saml_sso profile <SSOProfileItem>)(mode domain_wide_saml_if_enabled)] [neverredirect]
|
# [(group <GroupItem> rank <Number>)|(ou|org|orgunit <OrgUnitItem>)]
|
||||||
|
# (mode sso_off)|(mode saml_sso profile <SSOProfileItem>)|(mode oidc_sso profile <SSOProfileName>}|(mode domain_wide_saml_if_enabled)
|
||||||
|
# [neverredirect]
|
||||||
def doUpdateInboundSSOAssignment():
|
def doUpdateInboundSSOAssignment():
|
||||||
cd = buildGAPIObject(API.DIRECTORY)
|
cd = buildGAPIObject(API.DIRECTORY)
|
||||||
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
|
ci = buildGAPIObject(API.CLOUDIDENTITY_INBOUND_SSO)
|
||||||
@@ -46948,14 +47175,20 @@ def doInfoInboundSSOAssignment():
|
|||||||
return
|
return
|
||||||
name = assignment.get('samlSsoInfo', {}).get('inboundSamlSsoProfile')
|
name = assignment.get('samlSsoInfo', {}).get('inboundSamlSsoProfile')
|
||||||
if name:
|
if name:
|
||||||
profile = _getInboundSSOProfile(ci, name)
|
profile = _getInboundSSOProfileByName(ci, 'saml', name)
|
||||||
if profile:
|
if profile:
|
||||||
assignment['samlSsoInfo']['inboundSamlSsoProfile'] = profile
|
assignment['samlSsoInfo']['inboundSamlSsoProfile'] = profile
|
||||||
|
else:
|
||||||
|
name = assignment.get('oidcSsoInfo', {}).get('inboundOidcSsoProfile')
|
||||||
|
if name:
|
||||||
|
profile = _getInboundSSOProfileByName(ci, 'oidc', name)
|
||||||
|
if profile:
|
||||||
|
assignment['oidcSsoInfo']['inboundOidcSsoProfile'] = profile
|
||||||
_showInboundSSOAssignment(assignment, FJQC, ci, cd)
|
_showInboundSSOAssignment(assignment, FJQC, ci, cd)
|
||||||
|
|
||||||
# gam show inboundssoassignment
|
# gam show inboundssoassignments
|
||||||
# [formatjson]
|
# [formatjson]
|
||||||
# gam print inboundssoassignment [todrive <ToDriveAttribute>*]
|
# gam print inboundssoassignments [todrive <ToDriveAttribute>*]
|
||||||
# [[formatjson [quotechar <Character>]]
|
# [[formatjson [quotechar <Character>]]
|
||||||
def doPrintShowInboundSSOAssignments():
|
def doPrintShowInboundSSOAssignments():
|
||||||
cd = buildGAPIObject(API.DIRECTORY)
|
cd = buildGAPIObject(API.DIRECTORY)
|
||||||
@@ -58086,11 +58319,11 @@ def printShowDrivelastModifications(users):
|
|||||||
DLP.Finalize(fileIdEntity)
|
DLP.Finalize(fileIdEntity)
|
||||||
if csvPF:
|
if csvPF:
|
||||||
sortTitles = ['User', 'id', 'name'] if fileIdEntity.get('shareddrive') else ['User']
|
sortTitles = ['User', 'id', 'name'] if fileIdEntity.get('shareddrive') else ['User']
|
||||||
|
if addCSVData:
|
||||||
|
sortTitles.extend(sorted(addCSVData.keys()))
|
||||||
sortTitles.extend(['lastModifiedFileId', 'lastModifiedFileName',
|
sortTitles.extend(['lastModifiedFileId', 'lastModifiedFileName',
|
||||||
'lastModifiedFileMimeType', 'lastModifiedFilePath',
|
'lastModifiedFileMimeType', 'lastModifiedFilePath',
|
||||||
'lastModifyingUser', 'lastModifiedTime'])
|
'lastModifyingUser', 'lastModifiedTime'])
|
||||||
if addCSVData:
|
|
||||||
sortTitles.extend(sorted(addCSVData.keys()))
|
|
||||||
csvPF.SetTitles(sortTitles)
|
csvPF.SetTitles(sortTitles)
|
||||||
csvPF.SetSortAllTitles()
|
csvPF.SetSortAllTitles()
|
||||||
pagesFields = getItemFieldsFromFieldsList('files', fieldsList)
|
pagesFields = getItemFieldsFromFieldsList('files', fieldsList)
|
||||||
@@ -71070,7 +71303,7 @@ def _processMessagesThreads(users, entityType):
|
|||||||
try:
|
try:
|
||||||
callGAPI(gmail.users().messages(), function,
|
callGAPI(gmail.users().messages(), function,
|
||||||
throwReasons=GAPI.GMAIL_THROW_REASONS+[GAPI.INVALID_MESSAGE_ID, GAPI.INVALID, GAPI.INVALID_ARGUMENT,
|
throwReasons=GAPI.GMAIL_THROW_REASONS+[GAPI.INVALID_MESSAGE_ID, GAPI.INVALID, GAPI.INVALID_ARGUMENT,
|
||||||
GAPI.FAILED_PRECONDITION, GAPI.PERMISSION_DENIED],
|
GAPI.FAILED_PRECONDITION, GAPI.PERMISSION_DENIED, GAPI.QUOTA_EXCEEDED],
|
||||||
userId='me', body=body)
|
userId='me', body=body)
|
||||||
for messageId in body['ids']:
|
for messageId in body['ids']:
|
||||||
mcount += 1
|
mcount += 1
|
||||||
@@ -71080,7 +71313,7 @@ def _processMessagesThreads(users, entityType):
|
|||||||
csvPF.WriteRow({'User': user, entityHeader: messageId, 'action': Act.Performed()})
|
csvPF.WriteRow({'User': user, entityHeader: messageId, 'action': Act.Performed()})
|
||||||
except GAPI.serviceNotAvailable:
|
except GAPI.serviceNotAvailable:
|
||||||
mcount += bcount
|
mcount += bcount
|
||||||
except (GAPI.invalid, GAPI.invalidArgument, GAPI.permissionDenied) as e:
|
except (GAPI.invalid, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.quotaExceeded) as e:
|
||||||
_processMessageFailed(user, idsList, f'{str(e)} ({mcount+1}-{mcount+bcount}/{jcount})')
|
_processMessageFailed(user, idsList, f'{str(e)} ({mcount+1}-{mcount+bcount}/{jcount})')
|
||||||
mcount += bcount
|
mcount += bcount
|
||||||
except GAPI.invalidMessageId:
|
except GAPI.invalidMessageId:
|
||||||
@@ -71093,7 +71326,8 @@ def _processMessagesThreads(users, entityType):
|
|||||||
|
|
||||||
_GMAIL_ERROR_REASON_TO_MESSAGE_MAP = {GAPI.NOT_FOUND: Msg.DOES_NOT_EXIST,
|
_GMAIL_ERROR_REASON_TO_MESSAGE_MAP = {GAPI.NOT_FOUND: Msg.DOES_NOT_EXIST,
|
||||||
GAPI.INVALID_MESSAGE_ID: Msg.INVALID_MESSAGE_ID,
|
GAPI.INVALID_MESSAGE_ID: Msg.INVALID_MESSAGE_ID,
|
||||||
GAPI.FAILED_PRECONDITION: Msg.FAILED_PRECONDITION}
|
GAPI.FAILED_PRECONDITION: Msg.FAILED_PRECONDITION,
|
||||||
|
GAPI.QUOTA_EXCEEDED: Msg.QUOTA_EXCEEDED}
|
||||||
|
|
||||||
def _callbackProcessMessage(request_id, _, exception):
|
def _callbackProcessMessage(request_id, _, exception):
|
||||||
ri = request_id.splitlines()
|
ri = request_id.splitlines()
|
||||||
@@ -71104,7 +71338,9 @@ def _processMessagesThreads(users, entityType):
|
|||||||
csvPF.WriteRow({'User': ri[RI_ENTITY], entityHeader: ri[RI_ITEM], 'action': Act.Performed()})
|
csvPF.WriteRow({'User': ri[RI_ENTITY], entityHeader: ri[RI_ITEM], 'action': Act.Performed()})
|
||||||
else:
|
else:
|
||||||
http_status, reason, message = checkGAPIError(exception)
|
http_status, reason, message = checkGAPIError(exception)
|
||||||
_processMessageFailed(ri[RI_ENTITY], ri[RI_ITEM], getHTTPError(_GMAIL_ERROR_REASON_TO_MESSAGE_MAP, http_status, reason, message), int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
_processMessageFailed(ri[RI_ENTITY], ri[RI_ITEM],
|
||||||
|
getHTTPError(_GMAIL_ERROR_REASON_TO_MESSAGE_MAP, http_status, reason, message),
|
||||||
|
int(ri[RI_J]), int(ri[RI_JCOUNT]))
|
||||||
|
|
||||||
def _batchProcessMessagesThreads(service, function, user, jcount, messageIds, **kwargs):
|
def _batchProcessMessagesThreads(service, function, user, jcount, messageIds, **kwargs):
|
||||||
svcargs = dict([('userId', 'me'), ('id', None), ('fields', '')]+list(kwargs.items())+GM.Globals[GM.EXTRA_ARGS_LIST])
|
svcargs = dict([('userId', 'me'), ('id', None), ('fields', '')]+list(kwargs.items())+GM.Globals[GM.EXTRA_ARGS_LIST])
|
||||||
@@ -71202,7 +71438,9 @@ def _processMessagesThreads(users, entityType):
|
|||||||
continue
|
continue
|
||||||
if parameters['messageEntity'] is None:
|
if parameters['messageEntity'] is None:
|
||||||
if parameters['maxToProcess'] and jcount > parameters['maxToProcess']:
|
if parameters['maxToProcess'] and jcount > parameters['maxToProcess']:
|
||||||
entityNumEntitiesActionNotPerformedWarning([Ent.USER, user], entityType, jcount, Msg.COUNT_N_EXCEEDS_MAX_TO_PROCESS_M.format(jcount, Act.ToPerform(), parameters['maxToProcess']), i, count)
|
entityNumEntitiesActionNotPerformedWarning([Ent.USER, user], entityType, jcount,
|
||||||
|
Msg.COUNT_N_EXCEEDS_MAX_TO_PROCESS_M.format(jcount, Act.ToPerform(), parameters['maxToProcess']),
|
||||||
|
i, count)
|
||||||
continue
|
continue
|
||||||
if not parameters['doIt']:
|
if not parameters['doIt']:
|
||||||
entityNumEntitiesActionNotPerformedWarning([Ent.USER, user], entityType, jcount, Msg.USE_DOIT_ARGUMENT_TO_PERFORM_ACTION, i, count)
|
entityNumEntitiesActionNotPerformedWarning([Ent.USER, user], entityType, jcount, Msg.USE_DOIT_ARGUMENT_TO_PERFORM_ACTION, i, count)
|
||||||
@@ -74451,7 +74689,7 @@ def printShowSignature(users):
|
|||||||
try:
|
try:
|
||||||
if selection is None:
|
if selection is None:
|
||||||
sendas = callGAPI(gmail.users().settings().sendAs(), 'get',
|
sendas = callGAPI(gmail.users().settings().sendAs(), 'get',
|
||||||
throwReasons=GAPI.GMAIL_THROW_REASONS,
|
throwReasons=GAPI.GMAIL_THROW_REASONS+[GAPI.NOT_FOUND],
|
||||||
userId='me', sendAsEmail=user)
|
userId='me', sendAsEmail=user)
|
||||||
else:
|
else:
|
||||||
results = callGAPIitems(gmail.users().settings().sendAs(), 'list', 'sendAs',
|
results = callGAPIitems(gmail.users().settings().sendAs(), 'list', 'sendAs',
|
||||||
@@ -74480,6 +74718,8 @@ def printShowSignature(users):
|
|||||||
if field in sendas[item]:
|
if field in sendas[item]:
|
||||||
row[f'smtpMsa{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}{field}'] = sendas[item][field]
|
row[f'smtpMsa{GC.Values[GC.CSV_OUTPUT_SUBFIELD_DELIMITER]}{field}'] = sendas[item][field]
|
||||||
csvPF.WriteRowTitles(row)
|
csvPF.WriteRowTitles(row)
|
||||||
|
except GAPI.notFound as e:
|
||||||
|
entityActionFailedWarning([Ent.USER, user], str(e), i, count)
|
||||||
except GAPI.serviceNotAvailable:
|
except GAPI.serviceNotAvailable:
|
||||||
userGmailServiceNotEnabledWarning(user, i, count)
|
userGmailServiceNotEnabledWarning(user, i, count)
|
||||||
if csvPF:
|
if csvPF:
|
||||||
@@ -77170,7 +77410,8 @@ MAIN_COMMANDS_WITH_OBJECTS = {
|
|||||||
),
|
),
|
||||||
'clear':
|
'clear':
|
||||||
(Act.CLEAR,
|
(Act.CLEAR,
|
||||||
{Cmd.ARG_CONTACT: doClearDomainContacts,
|
{Cmd.ARG_ALERTSETTINGS: doClearAlertSettings,
|
||||||
|
Cmd.ARG_CONTACT: doClearDomainContacts,
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
'close':
|
'close':
|
||||||
@@ -77491,6 +77732,7 @@ MAIN_COMMANDS_WITH_OBJECTS = {
|
|||||||
Cmd.ARG_ADMIN: doPrintShowAdmins,
|
Cmd.ARG_ADMIN: doPrintShowAdmins,
|
||||||
Cmd.ARG_ALERT: doPrintShowAlerts,
|
Cmd.ARG_ALERT: doPrintShowAlerts,
|
||||||
Cmd.ARG_ALERTFEEDBACK: doPrintShowAlertFeedback,
|
Cmd.ARG_ALERTFEEDBACK: doPrintShowAlertFeedback,
|
||||||
|
Cmd.ARG_ALERTSETTINGS: doShowAlertSettings,
|
||||||
Cmd.ARG_BROWSER: doPrintShowBrowsers,
|
Cmd.ARG_BROWSER: doPrintShowBrowsers,
|
||||||
Cmd.ARG_BROWSERTOKEN: doPrintShowBrowserTokens,
|
Cmd.ARG_BROWSERTOKEN: doPrintShowBrowserTokens,
|
||||||
Cmd.ARG_BUILDING: doPrintShowBuildings,
|
Cmd.ARG_BUILDING: doPrintShowBuildings,
|
||||||
@@ -77583,6 +77825,7 @@ MAIN_COMMANDS_WITH_OBJECTS = {
|
|||||||
'update':
|
'update':
|
||||||
(Act.UPDATE,
|
(Act.UPDATE,
|
||||||
{Cmd.ARG_ADMINROLE: doCreateUpdateAdminRoles,
|
{Cmd.ARG_ADMINROLE: doCreateUpdateAdminRoles,
|
||||||
|
Cmd.ARG_ALERTSETTINGS: doUpdateAlertSettings,
|
||||||
Cmd.ARG_ALIAS: doCreateUpdateAliases,
|
Cmd.ARG_ALIAS: doCreateUpdateAliases,
|
||||||
Cmd.ARG_BROWSER: doUpdateBrowsers,
|
Cmd.ARG_BROWSER: doUpdateBrowsers,
|
||||||
Cmd.ARG_BUILDING: doUpdateBuilding,
|
Cmd.ARG_BUILDING: doUpdateBuilding,
|
||||||
|
|||||||
@@ -433,106 +433,6 @@ pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1
|
|||||||
mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0
|
mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
# Operating CA: GoDaddy
|
|
||||||
# Issuer: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority
|
|
||||||
# Subject: O=Starfield Technologies, Inc. OU=Starfield Class 2 Certification Authority
|
|
||||||
# Label: "Starfield Class 2 CA"
|
|
||||||
# Serial: 0
|
|
||||||
# MD5 Fingerprint: 32:4a:4b:bb:c8:63:69:9b:be:74:9a:c6:dd:1d:46:24
|
|
||||||
# SHA1 Fingerprint: ad:7e:1c:28:b0:64:ef:8f:60:03:40:20:14:c3:d0:e3:37:0e:b5:8a
|
|
||||||
# SHA256 Fingerprint: 14:65:fa:20:53:97:b8:76:fa:a6:f0:a9:95:8e:55:90:e4:0f:cc:7f:aa:4f:b7:c2:c8:67:75:21:fb:5f:b6:58
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl
|
|
||||||
MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp
|
|
||||||
U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw
|
|
||||||
NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE
|
|
||||||
ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp
|
|
||||||
ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3
|
|
||||||
DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf
|
|
||||||
8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN
|
|
||||||
+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0
|
|
||||||
X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa
|
|
||||||
K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA
|
|
||||||
1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G
|
|
||||||
A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR
|
|
||||||
zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0
|
|
||||||
YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD
|
|
||||||
bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w
|
|
||||||
DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3
|
|
||||||
L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D
|
|
||||||
eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl
|
|
||||||
xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp
|
|
||||||
VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY
|
|
||||||
WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q=
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
|
|
||||||
# Operating CA: GoDaddy
|
|
||||||
# Issuer: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority
|
|
||||||
# Subject: O=The Go Daddy Group, Inc. OU=Go Daddy Class 2 Certification Authority
|
|
||||||
# Label: "Go Daddy Class 2 CA"
|
|
||||||
# Serial: 0
|
|
||||||
# MD5 Fingerprint: 91:de:06:25:ab:da:fd:32:17:0c:bb:25:17:2a:84:67
|
|
||||||
# SHA1 Fingerprint: 27:96:ba:e6:3f:18:01:e2:77:26:1b:a0:d7:77:70:02:8f:20:ee:e4
|
|
||||||
# SHA256 Fingerprint: c3:84:6b:f2:4b:9e:93:ca:64:27:4c:0e:c6:7c:1e:cc:5e:02:4f:fc:ac:d2:d7:40:19:35:0e:81:fe:54:6a:e4
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh
|
|
||||||
MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE
|
|
||||||
YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3
|
|
||||||
MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo
|
|
||||||
ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg
|
|
||||||
MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN
|
|
||||||
ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA
|
|
||||||
PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w
|
|
||||||
wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi
|
|
||||||
EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY
|
|
||||||
avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+
|
|
||||||
YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE
|
|
||||||
sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h
|
|
||||||
/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5
|
|
||||||
IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj
|
|
||||||
YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
|
|
||||||
ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy
|
|
||||||
OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P
|
|
||||||
TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ
|
|
||||||
HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER
|
|
||||||
dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf
|
|
||||||
ReYNnyicsbkqWletNw+vHX/bvZ8=
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
|
|
||||||
# Operating CA: Sectigo
|
|
||||||
# Issuer: CN=AAA Certificate Services O=Comodo CA Limited
|
|
||||||
# Subject: CN=AAA Certificate Services O=Comodo CA Limited
|
|
||||||
# Label: "Comodo AAA Services root"
|
|
||||||
# Serial: 1
|
|
||||||
# MD5 Fingerprint: 49:79:04:b0:eb:87:19:ac:47:b0:bc:11:51:9b:74:d0
|
|
||||||
# SHA1 Fingerprint: d1:eb:23:a4:6d:17:d6:8f:d9:25:64:c2:f1:f1:60:17:64:d8:e3:49
|
|
||||||
# SHA256 Fingerprint: d7:a7:a0:fb:5d:7e:27:31:d7:71:e9:48:4e:bc:de:f7:1d:5f:0c:3e:0a:29:48:78:2b:c8:3e:e0:ea:69:9e:f4
|
|
||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb
|
|
||||||
MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow
|
|
||||||
GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj
|
|
||||||
YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL
|
|
||||||
MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
|
|
||||||
BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM
|
|
||||||
GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
|
||||||
ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua
|
|
||||||
BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe
|
|
||||||
3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4
|
|
||||||
YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR
|
|
||||||
rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm
|
|
||||||
ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU
|
|
||||||
oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
|
|
||||||
MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v
|
|
||||||
QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t
|
|
||||||
b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF
|
|
||||||
AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q
|
|
||||||
GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz
|
|
||||||
Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2
|
|
||||||
G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi
|
|
||||||
l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3
|
|
||||||
smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==
|
|
||||||
-----END CERTIFICATE-----
|
|
||||||
|
|
||||||
# Operating CA: Sectigo
|
# Operating CA: Sectigo
|
||||||
# Issuer: CN=COMODO Certification Authority O=COMODO CA Limited
|
# Issuer: CN=COMODO Certification Authority O=COMODO CA Limited
|
||||||
# Subject: CN=COMODO Certification Authority O=COMODO CA Limited
|
# Subject: CN=COMODO Certification Authority O=COMODO CA Limited
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ IAM_CREDENTIALS = 'iamcredentials'
|
|||||||
KEEP = 'keep'
|
KEEP = 'keep'
|
||||||
LICENSING = 'licensing'
|
LICENSING = 'licensing'
|
||||||
LOOKERSTUDIO = 'datastudio'
|
LOOKERSTUDIO = 'datastudio'
|
||||||
MEET = 'meet'
|
MEET_SPACES = 'meet'
|
||||||
MEET_BETA = 'meetbeta'
|
MEET_READONLY = 'meetreadonly'
|
||||||
OAUTH2 = 'oauth2'
|
OAUTH2 = 'oauth2'
|
||||||
ORGPOLICY = 'orgpolicy'
|
ORGPOLICY = 'orgpolicy'
|
||||||
PEOPLE = 'people'
|
PEOPLE = 'people'
|
||||||
@@ -119,6 +119,7 @@ USERINFO_PROFILE_SCOPE = 'https://www.googleapis.com/auth/userinfo.profile' # pr
|
|||||||
VAULT_SCOPES = ['https://www.googleapis.com/auth/ediscovery', 'https://www.googleapis.com/auth/ediscovery.readonly']
|
VAULT_SCOPES = ['https://www.googleapis.com/auth/ediscovery', 'https://www.googleapis.com/auth/ediscovery.readonly']
|
||||||
REQUIRED_SCOPES = [USERINFO_EMAIL_SCOPE, USERINFO_PROFILE_SCOPE]
|
REQUIRED_SCOPES = [USERINFO_EMAIL_SCOPE, USERINFO_PROFILE_SCOPE]
|
||||||
REQUIRED_SCOPES_SET = set(REQUIRED_SCOPES)
|
REQUIRED_SCOPES_SET = set(REQUIRED_SCOPES)
|
||||||
|
NUM_CLIENT_SCOPES_ERROR_LIMIT = 48
|
||||||
#
|
#
|
||||||
JWT_APIS = {
|
JWT_APIS = {
|
||||||
ACCESSCONTEXTMANAGER: [CLOUD_PLATFORM_SCOPE],
|
ACCESSCONTEXTMANAGER: [CLOUD_PLATFORM_SCOPE],
|
||||||
@@ -266,8 +267,8 @@ _INFO = {
|
|||||||
KEEP: {'name': 'Keep API', 'version': 'v1', 'v2discovery': True},
|
KEEP: {'name': 'Keep API', 'version': 'v1', 'v2discovery': True},
|
||||||
LICENSING: {'name': 'License Manager API', 'version': 'v1', 'v2discovery': True},
|
LICENSING: {'name': 'License Manager API', 'version': 'v1', 'v2discovery': True},
|
||||||
LOOKERSTUDIO: {'name': 'Looker Studio API', 'version': 'v1', 'v2discovery': True, 'localjson': True},
|
LOOKERSTUDIO: {'name': 'Looker Studio API', 'version': 'v1', 'v2discovery': True, 'localjson': True},
|
||||||
MEET: {'name': 'Meet API', 'version': 'v2', 'v2discovery': True},
|
MEET_SPACES: {'name': 'Meet API - Manage/Display Meeting Spaces', 'version': 'v2', 'v2discovery': True},
|
||||||
MEET_BETA: {'name': 'Meet API Beta', 'version': 'v2beta', 'v2discovery': True, 'localjson': True, 'mappedAPI': MEET},
|
MEET_READONLY: {'name': 'Meet API - Read Meeting Spaces metadata', 'version': 'v2', 'v2discovery': True, 'mappedAPI': MEET_SPACES},
|
||||||
OAUTH2: {'name': 'OAuth2 API', 'version': 'v2', 'v2discovery': False},
|
OAUTH2: {'name': 'OAuth2 API', 'version': 'v2', 'v2discovery': False},
|
||||||
ORGPOLICY: {'name': 'Organization Policy API', 'version': 'v2', 'v2discovery': True},
|
ORGPOLICY: {'name': 'Organization Policy API', 'version': 'v2', 'v2discovery': True},
|
||||||
PEOPLE: {'name': 'People API', 'version': 'v1', 'v2discovery': True},
|
PEOPLE: {'name': 'People API', 'version': 'v1', 'v2discovery': True},
|
||||||
@@ -688,11 +689,15 @@ _SVCACCT_SCOPES = [
|
|||||||
'api': LOOKERSTUDIO,
|
'api': LOOKERSTUDIO,
|
||||||
'subscopes': READONLY,
|
'subscopes': READONLY,
|
||||||
'scope': 'https://www.googleapis.com/auth/datastudio'},
|
'scope': 'https://www.googleapis.com/auth/datastudio'},
|
||||||
{'name': 'Meet API',
|
{'name': 'Meet API - Manage/Display Meeting Spaces',
|
||||||
'api': MEET,
|
'api': MEET_SPACES,
|
||||||
'subscopes': READONLY,
|
'subscopes': [],
|
||||||
'scope': 'https://www.googleapis.com/auth/meetings.space.created',
|
'scope': ['https://www.googleapis.com/auth/meetings.space.created',
|
||||||
'roscope': 'https://www.googleapis.com/auth/meetings.space.readonly'},
|
'https://www.googleapis.com/auth/meetings.space.settings']},
|
||||||
|
{'name': 'Meet API - Read Meeting Spaces metadata',
|
||||||
|
'api': MEET_READONLY,
|
||||||
|
'subscopes': [],
|
||||||
|
'scope': 'https://www.googleapis.com/auth/meetings.space.readonly'},
|
||||||
{'name': 'OAuth2 API',
|
{'name': 'OAuth2 API',
|
||||||
'api': OAUTH2,
|
'api': OAUTH2,
|
||||||
'subscopes': [],
|
'subscopes': [],
|
||||||
@@ -843,4 +848,3 @@ def findAPIforScope(scopesList):
|
|||||||
if not requiredAPIs:
|
if not requiredAPIs:
|
||||||
requiredAPIs = scopesList
|
requiredAPIs = scopesList
|
||||||
return ' or '.join(requiredAPIs)
|
return ' or '.join(requiredAPIs)
|
||||||
|
|
||||||
|
|||||||
@@ -153,8 +153,6 @@ DOMAIN = 'domain'
|
|||||||
DRIVE_DIR = 'drive_dir'
|
DRIVE_DIR = 'drive_dir'
|
||||||
# When retrieving lists of Drive files/folders from API, how many should be retrieved in each chunk
|
# When retrieving lists of Drive files/folders from API, how many should be retrieved in each chunk
|
||||||
DRIVE_MAX_RESULTS = 'drive_max_results'
|
DRIVE_MAX_RESULTS = 'drive_max_results'
|
||||||
# Use Drive V3 beta
|
|
||||||
DRIVE_V3_BETA = 'drive_v3_beta'
|
|
||||||
# When processing email messages in batches, how many should be processed in each batch
|
# When processing email messages in batches, how many should be processed in each batch
|
||||||
EMAIL_BATCH_SIZE = 'email_batch_size'
|
EMAIL_BATCH_SIZE = 'email_batch_size'
|
||||||
# Enable Delegated Admin Service Account
|
# Enable Delegated Admin Service Account
|
||||||
@@ -177,8 +175,6 @@ INTER_BATCH_WAIT = 'inter_batch_wait'
|
|||||||
LICENSE_MAX_RESULTS = 'license_max_results'
|
LICENSE_MAX_RESULTS = 'license_max_results'
|
||||||
# License SKUs to process
|
# License SKUs to process
|
||||||
LICENSE_SKUS = 'license_skus'
|
LICENSE_SKUS = 'license_skus'
|
||||||
# Use Meet V2 beta
|
|
||||||
MEET_V2_BETA = 'meet_v2_beta'
|
|
||||||
# When retrieving lists of Google Group members from API, how many should be retrieved in each chunk
|
# When retrieving lists of Google Group members from API, how many should be retrieved in each chunk
|
||||||
MEMBER_MAX_RESULTS = 'member_max_results'
|
MEMBER_MAX_RESULTS = 'member_max_results'
|
||||||
# CI API Group members max page size when view=BASIC
|
# CI API Group members max page size when view=BASIC
|
||||||
@@ -379,7 +375,6 @@ Defaults = {
|
|||||||
DRIVE_DIR: '',
|
DRIVE_DIR: '',
|
||||||
ENFORCE_EXPANSIVE_ACCESS: TRUE,
|
ENFORCE_EXPANSIVE_ACCESS: TRUE,
|
||||||
DRIVE_MAX_RESULTS: '1000',
|
DRIVE_MAX_RESULTS: '1000',
|
||||||
DRIVE_V3_BETA: FALSE,
|
|
||||||
EMAIL_BATCH_SIZE: '50',
|
EMAIL_BATCH_SIZE: '50',
|
||||||
ENABLE_DASA: FALSE,
|
ENABLE_DASA: FALSE,
|
||||||
ENABLE_GCLOUD_REAUTH: FALSE,
|
ENABLE_GCLOUD_REAUTH: FALSE,
|
||||||
@@ -390,7 +385,6 @@ Defaults = {
|
|||||||
INTER_BATCH_WAIT: '0',
|
INTER_BATCH_WAIT: '0',
|
||||||
LICENSE_MAX_RESULTS: '100',
|
LICENSE_MAX_RESULTS: '100',
|
||||||
LICENSE_SKUS: '',
|
LICENSE_SKUS: '',
|
||||||
MEET_V2_BETA: FALSE,
|
|
||||||
MEMBER_MAX_RESULTS: '200',
|
MEMBER_MAX_RESULTS: '200',
|
||||||
MEMBER_MAX_RESULTS_CI_BASIC: '1000',
|
MEMBER_MAX_RESULTS_CI_BASIC: '1000',
|
||||||
MEMBER_MAX_RESULTS_CI_FULL: '500',
|
MEMBER_MAX_RESULTS_CI_FULL: '500',
|
||||||
@@ -547,7 +541,6 @@ VAR_INFO = {
|
|||||||
DRIVE_DIR: {VAR_TYPE: TYPE_DIRECTORY, VAR_ENVVAR: 'GAMDRIVEDIR'},
|
DRIVE_DIR: {VAR_TYPE: TYPE_DIRECTORY, VAR_ENVVAR: 'GAMDRIVEDIR'},
|
||||||
ENFORCE_EXPANSIVE_ACCESS: {VAR_TYPE: TYPE_BOOLEAN},
|
ENFORCE_EXPANSIVE_ACCESS: {VAR_TYPE: TYPE_BOOLEAN},
|
||||||
DRIVE_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 1000)},
|
DRIVE_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 1000)},
|
||||||
DRIVE_V3_BETA: {VAR_TYPE: TYPE_BOOLEAN},
|
|
||||||
EMAIL_BATCH_SIZE: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 100)},
|
EMAIL_BATCH_SIZE: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 100)},
|
||||||
ENABLE_DASA: {VAR_TYPE: TYPE_BOOLEAN, VAR_SIGFILE: 'enabledasa.txt', VAR_SFFT: (FALSE, TRUE)},
|
ENABLE_DASA: {VAR_TYPE: TYPE_BOOLEAN, VAR_SIGFILE: 'enabledasa.txt', VAR_SFFT: (FALSE, TRUE)},
|
||||||
ENABLE_GCLOUD_REAUTH: {VAR_TYPE: TYPE_BOOLEAN},
|
ENABLE_GCLOUD_REAUTH: {VAR_TYPE: TYPE_BOOLEAN},
|
||||||
@@ -558,7 +551,6 @@ VAR_INFO = {
|
|||||||
INTER_BATCH_WAIT: {VAR_TYPE: TYPE_FLOAT, VAR_LIMITS: (0.0, 60.0)},
|
INTER_BATCH_WAIT: {VAR_TYPE: TYPE_FLOAT, VAR_LIMITS: (0.0, 60.0)},
|
||||||
LICENSE_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (10, 1000)},
|
LICENSE_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (10, 1000)},
|
||||||
LICENSE_SKUS: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
|
LICENSE_SKUS: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
|
||||||
MEET_V2_BETA: {VAR_TYPE: TYPE_BOOLEAN},
|
|
||||||
MEMBER_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 200)},
|
MEMBER_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 200)},
|
||||||
MEMBER_MAX_RESULTS_CI_BASIC: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 1000)},
|
MEMBER_MAX_RESULTS_CI_BASIC: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 1000)},
|
||||||
MEMBER_MAX_RESULTS_CI_FULL: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 500)},
|
MEMBER_MAX_RESULTS_CI_FULL: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 500)},
|
||||||
|
|||||||
@@ -411,6 +411,7 @@ class GamCLArgs():
|
|||||||
ARG_ALERTFEEDBACK = 'alertfeedback'
|
ARG_ALERTFEEDBACK = 'alertfeedback'
|
||||||
ARG_ALERTFEEDBACKS = 'alertfeedbacks'
|
ARG_ALERTFEEDBACKS = 'alertfeedbacks'
|
||||||
ARG_ALERTSFEEDBACK = 'alertsfeedback'
|
ARG_ALERTSFEEDBACK = 'alertsfeedback'
|
||||||
|
ARG_ALERTSETTINGS = 'alertsettings'
|
||||||
ARG_ALIAS = 'alias'
|
ARG_ALIAS = 'alias'
|
||||||
ARG_ALIASES = 'aliases'
|
ARG_ALIASES = 'aliases'
|
||||||
ARG_ALIASDOMAIN = 'aliasdomain'
|
ARG_ALIASDOMAIN = 'aliasdomain'
|
||||||
@@ -1013,6 +1014,7 @@ class GamCLArgs():
|
|||||||
OB_PROJECT_ID_ENTITY = 'ProjectIDEntity'
|
OB_PROJECT_ID_ENTITY = 'ProjectIDEntity'
|
||||||
OB_PROPERTY_KEY = 'PropertyKey'
|
OB_PROPERTY_KEY = 'PropertyKey'
|
||||||
OB_PROPERTY_VALUE = 'PropertyValue'
|
OB_PROPERTY_VALUE = 'PropertyValue'
|
||||||
|
OB_PUBSUB_TOPIC_NAME = 'PubSubTopicName'
|
||||||
OB_QUERY = 'Query'
|
OB_QUERY = 'Query'
|
||||||
OB_QUERY_ITEM = 'QueryItem'
|
OB_QUERY_ITEM = 'QueryItem'
|
||||||
OB_QUERY_LIST = 'QueryList'
|
OB_QUERY_LIST = 'QueryList'
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ class GamEntity():
|
|||||||
ALERT_ID = 'alri'
|
ALERT_ID = 'alri'
|
||||||
ALERT_FEEDBACK = 'alfb'
|
ALERT_FEEDBACK = 'alfb'
|
||||||
ALERT_FEEDBACK_ID = 'alfi'
|
ALERT_FEEDBACK_ID = 'alfi'
|
||||||
|
ALERT_SETTINGS = 'alrs'
|
||||||
ALIAS = 'alia'
|
ALIAS = 'alia'
|
||||||
ALIAS_EMAIL = 'alie'
|
ALIAS_EMAIL = 'alie'
|
||||||
ALIAS_TARGET = 'alit'
|
ALIAS_TARGET = 'alit'
|
||||||
@@ -285,10 +286,11 @@ class GamEntity():
|
|||||||
MIMETYPE = 'mime'
|
MIMETYPE = 'mime'
|
||||||
MOBILE_DEVICE = 'mobi'
|
MOBILE_DEVICE = 'mobi'
|
||||||
NAME = 'name'
|
NAME = 'name'
|
||||||
|
NONEDITABLE_ALIAS = 'neal'
|
||||||
NOTE = 'note'
|
NOTE = 'note'
|
||||||
NOTE_ACL = 'nota'
|
NOTE_ACL = 'nota'
|
||||||
NOTES_ACLS = 'naac'
|
NOTES_ACLS = 'naac'
|
||||||
NONEDITABLE_ALIAS = 'neal'
|
NOTIFICATION = 'noti'
|
||||||
OAUTH2_TXT_FILE = 'oaut'
|
OAUTH2_TXT_FILE = 'oaut'
|
||||||
OAUTH2SERVICE_JSON_FILE = 'oau2'
|
OAUTH2SERVICE_JSON_FILE = 'oau2'
|
||||||
ORGANIZATIONAL_UNIT = 'orgu'
|
ORGANIZATIONAL_UNIT = 'orgu'
|
||||||
@@ -414,6 +416,7 @@ class GamEntity():
|
|||||||
ALERT_ID: ['Alert IDs', 'Alert ID'],
|
ALERT_ID: ['Alert IDs', 'Alert ID'],
|
||||||
ALERT_FEEDBACK: ['Alert Feedbacks', 'Alert Feedback'],
|
ALERT_FEEDBACK: ['Alert Feedbacks', 'Alert Feedback'],
|
||||||
ALERT_FEEDBACK_ID: ['Alert Feedback IDs', 'Alert Feedback ID'],
|
ALERT_FEEDBACK_ID: ['Alert Feedback IDs', 'Alert Feedback ID'],
|
||||||
|
ALERT_SETTINGS: ['Alert Settings', 'Alert Settings'],
|
||||||
ALIAS: ['Aliases', 'Alias'],
|
ALIAS: ['Aliases', 'Alias'],
|
||||||
ALIAS_EMAIL: ['Alias Emails', 'Alias Email'],
|
ALIAS_EMAIL: ['Alias Emails', 'Alias Email'],
|
||||||
ALIAS_TARGET: ['Alias Targets', 'Alias Target'],
|
ALIAS_TARGET: ['Alias Targets', 'Alias Target'],
|
||||||
@@ -645,10 +648,11 @@ class GamEntity():
|
|||||||
MIMETYPE: ['MIME Types', 'MIME Type'],
|
MIMETYPE: ['MIME Types', 'MIME Type'],
|
||||||
MOBILE_DEVICE: ['Mobile Devices', 'Mobile Device'],
|
MOBILE_DEVICE: ['Mobile Devices', 'Mobile Device'],
|
||||||
NAME: ['Names', 'Name'],
|
NAME: ['Names', 'Name'],
|
||||||
|
NONEDITABLE_ALIAS: ['Non-Editable Aliases', 'Non-Editable Alias'],
|
||||||
NOTE: ['Notes', 'Note'],
|
NOTE: ['Notes', 'Note'],
|
||||||
NOTE_ACL: ['Note ACLs', 'Note ACL'],
|
NOTE_ACL: ['Note ACLs', 'Note ACL'],
|
||||||
NOTES_ACLS: ["'Note's ACLs", "Note's ACLs"],
|
NOTES_ACLS: ["'Note's ACLs", "Note's ACLs"],
|
||||||
NONEDITABLE_ALIAS: ['Non-Editable Aliases', 'Non-Editable Alias'],
|
NOTIFICATION: ['Notifications', 'Notification'],
|
||||||
OAUTH2_TXT_FILE: ['Client OAuth2 File', 'Client OAuth2 File'],
|
OAUTH2_TXT_FILE: ['Client OAuth2 File', 'Client OAuth2 File'],
|
||||||
OAUTH2SERVICE_JSON_FILE: ['Service Account OAuth2 File', 'Service Account OAuth2 File'],
|
OAUTH2SERVICE_JSON_FILE: ['Service Account OAuth2 File', 'Service Account OAuth2 File'],
|
||||||
ORGANIZATIONAL_UNIT: ['Organizational Units', 'Organizational Unit'],
|
ORGANIZATIONAL_UNIT: ['Organizational Units', 'Organizational Unit'],
|
||||||
|
|||||||
@@ -433,6 +433,7 @@ NO_SVCACCT_ACCESS_ALLOWED = 'No Service Account Access allowed'
|
|||||||
NO_TRANSFER_LACK_OF_DISK_SPACE = 'Transfer not performed due to lack of target drive space.'
|
NO_TRANSFER_LACK_OF_DISK_SPACE = 'Transfer not performed due to lack of target drive space.'
|
||||||
NO_USAGE_PARAMETERS_DATA_AVAILABLE = 'No usage parameters data available.'
|
NO_USAGE_PARAMETERS_DATA_AVAILABLE = 'No usage parameters data available.'
|
||||||
NO_USER_COUNTS_DATA_AVAILABLE = 'No User counts data available.'
|
NO_USER_COUNTS_DATA_AVAILABLE = 'No User counts data available.'
|
||||||
|
NUM_SELECTED_CLIENT_SCOPES = '\n{0} scopes are selected, if more than {1} scopes are selected, Google will probably generate a "Something went wrong" error\n'
|
||||||
OAUTH2_GO_TO_LINK_MESSAGE = """
|
OAUTH2_GO_TO_LINK_MESSAGE = """
|
||||||
Go to the following link in a browser on this computer or on another computer:
|
Go to the following link in a browser on this computer or on another computer:
|
||||||
|
|
||||||
@@ -464,6 +465,7 @@ PROCESSING_ITEM_N = '{0},0,Processing item {1}\n'
|
|||||||
PROCESSING_ITEM_N_OF_M = '{0},0,Processing item {1}/{2}\n'
|
PROCESSING_ITEM_N_OF_M = '{0},0,Processing item {1}/{2}\n'
|
||||||
PROFILE_PHOTO_NOT_FOUND = 'Profile photo not found'
|
PROFILE_PHOTO_NOT_FOUND = 'Profile photo not found'
|
||||||
PROFILE_PHOTO_IS_DEFAULT = 'Profile photo is default'
|
PROFILE_PHOTO_IS_DEFAULT = 'Profile photo is default'
|
||||||
|
QUOTA_EXCEEDED = 'Quota exceeded'
|
||||||
REASON_ONLY_VALID_WITH_CONTENTRESTRICTIONS_READONLY_TRUE = 'reason only valid with contentrestrictions readonly true'
|
REASON_ONLY_VALID_WITH_CONTENTRESTRICTIONS_READONLY_TRUE = 'reason only valid with contentrestrictions readonly true'
|
||||||
REAUTHENTICATION_IS_NEEDED = 'Reauthentication is needed, please run\n\ngam oauth create'
|
REAUTHENTICATION_IS_NEEDED = 'Reauthentication is needed, please run\n\ngam oauth create'
|
||||||
RECOMMEND_RUNNING_GAM_ROTATE_SAKEY = 'Recommend running "gam rotate sakey" to get a new key\n'
|
RECOMMEND_RUNNING_GAM_ROTATE_SAKEY = 'Recommend running "gam rotate sakey" to get a new key\n'
|
||||||
|
|||||||
125
src/tools/ssd.mjs
Normal file
125
src/tools/ssd.mjs
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
// Node.js script that implements an Appium client which will launch
|
||||||
|
// Simply Sign Desktop app and log a user in. Once logged in it should
|
||||||
|
// be possible to use tools like signtool.exe to sign Windows EXE/MSI files
|
||||||
|
// with the Certum certificate.
|
||||||
|
|
||||||
|
import { Key, remote } from 'webdriverio';
|
||||||
|
import { exec } from 'child_process';
|
||||||
|
import { TOTP } from 'totp-generator';
|
||||||
|
|
||||||
|
async function screenshot(driver, filename) {
|
||||||
|
// uncomment to save .png screenshots
|
||||||
|
//await driver.saveScreenshot(filename);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
function sleep(ms) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function executeCommand(command) {
|
||||||
|
try {
|
||||||
|
let { stdout, stderr } = await exec(command);
|
||||||
|
return stdout;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error executing command: ${command}`);
|
||||||
|
console.error(`Error details: ${error}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runSSD() {
|
||||||
|
const opts = {
|
||||||
|
port: 4723,
|
||||||
|
logLevel: "silent",
|
||||||
|
capabilities: {
|
||||||
|
platformName: "Windows",
|
||||||
|
"appium:app": "C:\\Program Files\\Certum\\SimplySign Desktop\\SimplySignDesktop.exe",
|
||||||
|
"appium:automationName": "Windows",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let driver;
|
||||||
|
try {
|
||||||
|
driver = await remote(opts);
|
||||||
|
|
||||||
|
// Github Actions Win ARM64 is stuck on a OOB screen that steals focus
|
||||||
|
// These enter / escapes should dismiss it.
|
||||||
|
const runner_arch = process.env.RUNNER_ARCH;
|
||||||
|
if ( runner_arch === "ARM64" ) {
|
||||||
|
console.log('Running on ARM64...');
|
||||||
|
await sleep(3000); // Pause execution for 3 seconds
|
||||||
|
await screenshot(driver, 'oob1.png');
|
||||||
|
await driver.sendKeys([Key.Enter]);
|
||||||
|
await sleep(3000); // Pause execution for 3 seconds
|
||||||
|
await screenshot(driver, 'oob2.png');
|
||||||
|
await driver.sendKeys([Key.Enter]);
|
||||||
|
await sleep(3000); // Pause execution for 3 seconds
|
||||||
|
await screenshot(driver, 'oob3.png');
|
||||||
|
await driver.sendKeys([Key.Escape]);
|
||||||
|
await screenshot(driver, 'oob6.png');
|
||||||
|
} else {
|
||||||
|
console.log('NOT running on ARM64');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute SSD again to open login dialog
|
||||||
|
exec('"C:\\Program Files\\Certum\\SimplySign Desktop\\SimplySignDesktop.exe"', (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
console.error(`exec error: ${error}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await sleep(3000);
|
||||||
|
|
||||||
|
// Login
|
||||||
|
const windows = await driver.getWindowHandles();
|
||||||
|
const login_window = windows[0]
|
||||||
|
await driver.switchWindow(login_window);
|
||||||
|
await screenshot(driver, 'login01.png');
|
||||||
|
const id_value = 'jay0lee@gmail.com';
|
||||||
|
const id_arr = [...id_value];
|
||||||
|
await driver.sendKeys(id_arr);
|
||||||
|
await screenshot(driver, 'login02.png');
|
||||||
|
await driver.sendKeys([Key.Tab]);
|
||||||
|
// We wait until the last possible second to generate
|
||||||
|
// our TOTP to ensure it's still valid.
|
||||||
|
const token_value = TOTP.generate(process.env.TOTP_SECRET, {algorithm: 'SHA-256'}).otp;
|
||||||
|
const token_arr = [...token_value];
|
||||||
|
await driver.sendKeys(token_arr);
|
||||||
|
await screenshot(driver, 'login03.png');
|
||||||
|
await driver.sendKeys([Key.Enter]);
|
||||||
|
|
||||||
|
// TODO: it's expected that on successful login the window
|
||||||
|
// will close and these screenshots will error out. Figure
|
||||||
|
// out how to handle that gracefully.
|
||||||
|
await screenshot(driver, 'login04.png');
|
||||||
|
await sleep(500);
|
||||||
|
await screenshot(driver, 'login05.png');
|
||||||
|
await sleep(500);
|
||||||
|
await screenshot(driver, 'login06.png');
|
||||||
|
await sleep(500);
|
||||||
|
await screenshot(driver, 'login07.png');
|
||||||
|
await sleep(500);
|
||||||
|
await screenshot(driver, 'login08.png');
|
||||||
|
await sleep(500);
|
||||||
|
await screenshot(driver, 'login09.png');
|
||||||
|
await sleep(500);
|
||||||
|
await screenshot(driver, 'login10.png');
|
||||||
|
await sleep(500);
|
||||||
|
await screenshot(driver, 'login11.png');
|
||||||
|
await sleep(500);
|
||||||
|
await screenshot(driver, 'login12.png');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error during Appium run:", error.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// INTENTIONAL Keep driver open so tray icon for Certum doesn't close
|
||||||
|
// finally {
|
||||||
|
// if (driver) {
|
||||||
|
// await driver.deleteSession(); // Close the Appium session
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
runSSD();
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
- [Display alerts](#display-alerts)
|
- [Display alerts](#display-alerts)
|
||||||
- [Manage alert feedback](#manage-alert-feedback)
|
- [Manage alert feedback](#manage-alert-feedback)
|
||||||
- [Display alert feedback](#display-alert-feedback)
|
- [Display alert feedback](#display-alert-feedback)
|
||||||
|
- [Configuring settings](#configuring-settings)
|
||||||
|
|
||||||
## API documentation
|
## API documentation
|
||||||
* [Alert Center API](https://developers.google.com/admin-sdk/alertcenter/reference/rest/)
|
* [Alert Center API](https://developers.google.com/admin-sdk/alertcenter/reference/rest/)
|
||||||
@@ -18,6 +19,7 @@
|
|||||||
## Definitions
|
## Definitions
|
||||||
```
|
```
|
||||||
<AlertID> ::= <String>
|
<AlertID> ::= <String>
|
||||||
|
<PubSubTopicName> ::= <String>
|
||||||
<QueryAlert> ::= <String> See: https://developers.google.com/admin-sdk/alertcenter/guides/query-filters
|
<QueryAlert> ::= <String> See: https://developers.google.com/admin-sdk/alertcenter/guides/query-filters
|
||||||
```
|
```
|
||||||
## Introduction
|
## Introduction
|
||||||
@@ -95,3 +97,15 @@ the quote character itself, the column delimiter (comma by default) and new-line
|
|||||||
When using the `formatjson` option, double quotes are used extensively in the data resulting in hard to read/process output.
|
When using the `formatjson` option, double quotes are used extensively in the data resulting in hard to read/process output.
|
||||||
The `quotechar <Character>` option allows you to choose an alternate quote character, single quote for instance, that makes for readable/processable output.
|
The `quotechar <Character>` option allows you to choose an alternate quote character, single quote for instance, that makes for readable/processable output.
|
||||||
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.
|
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.
|
||||||
|
|
||||||
|
## Configuring settings
|
||||||
|
|
||||||
|
Alert Center can be configured to send notifications to a Google Cloud Pub/Sub topic, but it first requires configuration.
|
||||||
|
* See https://developers.google.com/workspace/admin/alertcenter/guides/notifications for information.
|
||||||
|
|
||||||
|
Gam can be used to display or modify the settings:
|
||||||
|
```
|
||||||
|
gam show alertsettings
|
||||||
|
gam update alertsettings <PubSubTopicName>
|
||||||
|
gam clear alertsettings
|
||||||
|
```
|
||||||
@@ -436,6 +436,7 @@
|
|||||||
Must match this Python Regular Expression: [a-zA-Z0-9 '"!-]{4,30}
|
Must match this Python Regular Expression: [a-zA-Z0-9 '"!-]{4,30}
|
||||||
<PropertyKey> ::= <String>
|
<PropertyKey> ::= <String>
|
||||||
<PropertyValue> ::= <String>
|
<PropertyValue> ::= <String>
|
||||||
|
<PubSubTopicName> ::= <String>
|
||||||
<QueryAlert> ::= <String>
|
<QueryAlert> ::= <String>
|
||||||
See: https://developers.google.com/admin-sdk/alertcenter/guides/query-filters
|
See: https://developers.google.com/admin-sdk/alertcenter/guides/query-filters
|
||||||
<QueryBrowser> ::= <String>
|
<QueryBrowser> ::= <String>
|
||||||
|
|||||||
@@ -239,6 +239,10 @@ bookmarks.json
|
|||||||
|
|
||||||
gam update chromepolicy chrome.users.ManagedBookmarksSetting json file bookmarks.json orgunit "/Students/
|
gam update chromepolicy chrome.users.ManagedBookmarksSetting json file bookmarks.json orgunit "/Students/
|
||||||
```
|
```
|
||||||
|
Allowlist the Google Translate extension for the Students OrgUnit
|
||||||
|
```
|
||||||
|
gam update chromepolicy chrome.users.apps.InstallType appInstallType ALLOWED app_id chrome:aapbdbdomjkkjkaonfhkkikfgjllcleb ou "/Students"
|
||||||
|
```
|
||||||
|
|
||||||
## Delete Chrome policy
|
## Delete Chrome policy
|
||||||
You can delete a policy for all devices/users within an OU, users with a group or for a specific printer or application within an OU.
|
You can delete a policy for all devices/users within an OU, users with a group or for a specific printer or application within an OU.
|
||||||
@@ -9946,4 +9950,4 @@ chrome.users.ZstdContentEncodingEnabled: Zstd compression.
|
|||||||
false: Do not allow zstd-compressed web content.
|
false: Do not allow zstd-compressed web content.
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -62,15 +62,6 @@ To use the `crostelemetry` commands you must authorize an additional scope:
|
|||||||
gam oauth create
|
gam oauth create
|
||||||
```
|
```
|
||||||
|
|
||||||
Many commands come in two forms:
|
|
||||||
```
|
|
||||||
gam <CrOSTypeEntity> <Command> ...
|
|
||||||
gam <Command> cros <CrOSEntity> ...
|
|
||||||
```
|
|
||||||
The first form allows more powerful selection of devices with `<CrOSTypeEntity>`.
|
|
||||||
|
|
||||||
The second form is backwards compatible with Legacy GAM and selection with `<CrOSEntity>` is limited.
|
|
||||||
|
|
||||||
## Definitions
|
## Definitions
|
||||||
* [`<CrOSTypeEntity>`](Collections-of-ChromeOS-Devices)
|
* [`<CrOSTypeEntity>`](Collections-of-ChromeOS-Devices)
|
||||||
|
|
||||||
@@ -332,7 +323,6 @@ gam select default config update_cros_ou_with_id true save
|
|||||||
|
|
||||||
```
|
```
|
||||||
gam <CrOSTypeEntity> update <CrOSAttribute>+ [quickcrosmove [<Boolean>]] [nobatchupdate]
|
gam <CrOSTypeEntity> update <CrOSAttribute>+ [quickcrosmove [<Boolean>]] [nobatchupdate]
|
||||||
gam update cros <CrOSEntity> <CrOSAttribute>+ [quickcrosmove [<Boolean>]] [nobatchupdate]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Google has introduced a new, faster method for moving CrOS devices to a new OU. The `quickcrosmove` option controls which method Gam uses.
|
Google has introduced a new, faster method for moving CrOS devices to a new OU. The `quickcrosmove` option controls which method Gam uses.
|
||||||
@@ -419,8 +409,6 @@ gam update ou csvkmd cros.csv keyfield OU datafield deviceId add croscsvdata dev
|
|||||||
|
|
||||||
gam <CrOSTypeEntity> update action <CrOSAction> [acknowledge_device_touch_requirement]
|
gam <CrOSTypeEntity> update action <CrOSAction> [acknowledge_device_touch_requirement]
|
||||||
[actionbatchsize <Integer>]
|
[actionbatchsize <Integer>]
|
||||||
gam update cros <CrOSEntity> action <CrOSAction> [acknowledge_device_touch_requirement]
|
|
||||||
[actionbatchsize <Integer>]
|
|
||||||
```
|
```
|
||||||
As of GAM version `6.67.00`, the new API function `batchChangeStatus` replaces the old API function `action`; ChromeOS devices are now processed in batches.
|
As of GAM version `6.67.00`, the new API function `batchChangeStatus` replaces the old API function `action`; ChromeOS devices are now processed in batches.
|
||||||
The batch size defaults to 10, the `actionbatchsize <Integer>` option can be used to set a batch size between 10 and 250.
|
The batch size defaults to 10, the `actionbatchsize <Integer>` option can be used to set a batch size between 10 and 250.
|
||||||
@@ -457,21 +445,18 @@ is configurable from 0 to some large number. If the status reaches `EXPIRED`, `C
|
|||||||
wipe_users|
|
wipe_users|
|
||||||
take_a_screenshot
|
take_a_screenshot
|
||||||
|
|
||||||
gam cros <CrOSTypeEntity> issuecommand command <CrOSCommand> [times_to_check_status <Integer>] [doit]
|
gam <CrOSTypeEntity> issuecommand command <CrOSCommand> [times_to_check_status <Integer>] [doit]
|
||||||
gam issuecommand cros <CrOSEntity> command <CrOSCommand> [times_to_check_status <Integer>] [doit]
|
|
||||||
```
|
```
|
||||||
If the final status is not reached before GAM exits, you can issue the following commands to continue checking the status.
|
If the final status is not reached before GAM exits, you can issue the following commands to continue checking the status.
|
||||||
```
|
```
|
||||||
gam cros <CrOSTypeEntity> getcommand commandid <CommandID> [times_to_check_status <Integer>]
|
gam <CrOSTypeEntity> getcommand commandid <CommandID> [times_to_check_status <Integer>]
|
||||||
gam getcommand cros <CrOSEntity> commandid <CommandID> [times_to_check_status <Integer>]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Action Examples
|
### Action Examples
|
||||||
Remove user profile data from the device; the device will remain enrolled and connected.
|
Remove user profile data from the device; the device will remain enrolled and connected.
|
||||||
User data not synced to the Cloud including Downloads, Android app data and Crostini Linux VMs will be permanently lost.
|
User data not synced to the Cloud including Downloads, Android app data and Crostini Linux VMs will be permanently lost.
|
||||||
Commands with issuecommand directly after gam will work with Legacy GAM & GAM7, whereas commands where the issuecommand is after the cros <CrOSTypeEntity> will work only with GAM7.
|
|
||||||
```
|
```
|
||||||
gam issuecommand cros dd1d659a-0ea4-4e94-905e-4726c7a5f1e9 command wipe_users doit
|
gam cros dd1d659a-0ea4-4e94-905e-4726c7a5f1e9 issuecommand command wipe_users doit
|
||||||
```
|
```
|
||||||
Remove profiles using the annotatedAssetID, which is a user editable field, in this example the device has an asset ID of CB1234.
|
Remove profiles using the annotatedAssetID, which is a user editable field, in this example the device has an asset ID of CB1234.
|
||||||
```
|
```
|
||||||
@@ -483,14 +468,12 @@ gam cros_queries "asset_id:CB1234,asset_id:CB5678" issuecommand command wipe_use
|
|||||||
```
|
```
|
||||||
Powerwash the device with serial number 143040348.
|
Powerwash the device with serial number 143040348.
|
||||||
```
|
```
|
||||||
gam issuecommand cros query:id:143040348 command remote_powerwash times_to_check_status 10 doit
|
|
||||||
gam cros_sn 143040348 issuecommand command remote_powerwash times_to_check_status 10 doit
|
gam cros_sn 143040348 issuecommand command remote_powerwash times_to_check_status 10 doit
|
||||||
```
|
```
|
||||||
|
|
||||||
Powerwash all devices in the /StudentCarts OrgUnit. Devices will need to be manually reconnected to WiFi which may mean entering a PSK.
|
Powerwash all devices in the /StudentCarts OrgUnit. Devices will need to be manually reconnected to WiFi which may mean entering a PSK.
|
||||||
Use `wipe_users` if that's going to create too much work for you.
|
Use `wipe_users` if that's going to create too much work for you.
|
||||||
```
|
```
|
||||||
gam issuecommand cros "query:orgunitpath:/StudentCarts" command remote_powerwash times_to_check_status 0 doit
|
|
||||||
gam cros_ou /StudentCarts issuecommand command remote_powerwash times_to_check_status 0 doit
|
gam cros_ou /StudentCarts issuecommand command remote_powerwash times_to_check_status 0 doit
|
||||||
```
|
```
|
||||||
## ChromeOS device lists
|
## ChromeOS device lists
|
||||||
@@ -829,7 +812,6 @@ The `quotechar <Character>` option allows you to choose an alternate quote chara
|
|||||||
|
|
||||||
```
|
```
|
||||||
gam <CrOSTypeEntity> info downloadfile latest|<Time> [targetfolder <FilePath>]
|
gam <CrOSTypeEntity> info downloadfile latest|<Time> [targetfolder <FilePath>]
|
||||||
gam info cros <CrOSEntity> downloadfile latest|<Time> [targetfolder <FilePath>]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Select the device file to download by its timestamp.
|
Select the device file to download by its timestamp.
|
||||||
|
|||||||
@@ -10,6 +10,68 @@ 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.19.02
|
||||||
|
|
||||||
|
Update `gam info user <UserItem>` to eliminate 5 second delay when getting license info.
|
||||||
|
|
||||||
|
Additional information:
|
||||||
|
* See: https://github.com/GAM-team/GAM/wiki/Licenses#info-user-performance
|
||||||
|
|
||||||
|
### 7.19.01
|
||||||
|
|
||||||
|
Updated `gam <UserTypeEntity> print|show signature` to handle the following error
|
||||||
|
that occurs when an alias is specified.
|
||||||
|
```
|
||||||
|
ERROR: 404: notFound - Requested entity was not found.
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.19.00
|
||||||
|
|
||||||
|
Eliminated `drive_v3_beta` and `meet_v2_beta` from `gam.cfg` as the API betas are no longer used.
|
||||||
|
|
||||||
|
Updated `Meet API` scopes so that GAM can read metadata about additional Meet spaces.
|
||||||
|
```
|
||||||
|
[*] 34) Meet API - Manage/Display Meeting Spaces
|
||||||
|
[*] 35) Meet API - Read Meeting Spaces metadata
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.18.07
|
||||||
|
|
||||||
|
Updated `gam <UserTypeEntity> print drivelastmodification` to put `addcsvdata` columns
|
||||||
|
after `User,id,name` rather than after the last column.
|
||||||
|
|
||||||
|
### 7.18.06
|
||||||
|
|
||||||
|
Updated `gam <UserTypeEntity> delete|modify messages` to improve the handling
|
||||||
|
of the following error.
|
||||||
|
```
|
||||||
|
quotaExceeded - User-rate limit exceeded
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.18.05
|
||||||
|
|
||||||
|
Added support for Inbound SSO OIDC profiles.
|
||||||
|
|
||||||
|
Currently, if you enter `gam select <SectionName>` and nothing else on the command line,
|
||||||
|
GAM performs no action. Now, it will be treated as if you entered:
|
||||||
|
`gam select <SectionName> save`
|
||||||
|
|
||||||
|
Updated to Python 3.13.7.
|
||||||
|
|
||||||
|
### 7.18.04
|
||||||
|
|
||||||
|
Added commands to display/manage Alert Center Pub/Sub notifications.
|
||||||
|
* See: https://github.com/GAM-team/GAM/wiki/Alert-Center#configuring-settings
|
||||||
|
|
||||||
|
### 7.18.03
|
||||||
|
|
||||||
|
Updated `gam oauth create` to give a warning if the number of selected scopes will
|
||||||
|
probably cause Google to generate a "Something went wrong" error.
|
||||||
|
|
||||||
|
### 7.18.02
|
||||||
|
|
||||||
|
Upgraded to OpenSSL 3.5.2.
|
||||||
|
|
||||||
### 7.18.01
|
### 7.18.01
|
||||||
|
|
||||||
Added option `nosystemroles` to `gam print|show adminroles` that causes GAM
|
Added option `nosystemroles` to `gam print|show adminroles` that causes GAM
|
||||||
|
|||||||
@@ -252,10 +252,10 @@ writes the credentials into the file oauth2.txt.
|
|||||||
admin@server:/Users/admin$ rm -f /Users/admin/GAMConfig/oauth2.txt
|
admin@server:/Users/admin$ rm -f /Users/admin/GAMConfig/oauth2.txt
|
||||||
admin@server:/Users/admin$ gam version
|
admin@server:/Users/admin$ gam version
|
||||||
WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/admin/GAMConfig/oauth2.txt, Not Found
|
WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/admin/GAMConfig/oauth2.txt, Not Found
|
||||||
GAM 7.18.01 - https://github.com/GAM-team/GAM - pyinstaller
|
GAM 7.19.02 - https://github.com/GAM-team/GAM - pyinstaller
|
||||||
GAM Team <google-apps-manager@googlegroups.com>
|
GAM Team <google-apps-manager@googlegroups.com>
|
||||||
Python 3.13.5 64-bit final
|
Python 3.13.7 64-bit final
|
||||||
MacOS Sequoia 15.6 x86_64
|
MacOS Sequoia 15.6.1 x86_64
|
||||||
Path: /Users/admin/bin/gam7
|
Path: /Users/admin/bin/gam7
|
||||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||||
|
|
||||||
@@ -990,9 +990,9 @@ 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.18.01 - https://github.com/GAM-team/GAM - pythonsource
|
GAM 7.19.02 - https://github.com/GAM-team/GAM - pythonsource
|
||||||
GAM Team <google-apps-manager@googlegroups.com>
|
GAM Team <google-apps-manager@googlegroups.com>
|
||||||
Python 3.13.5 64-bit final
|
Python 3.13.7 64-bit final
|
||||||
Windows-10-10.0.17134 AMD64
|
Windows-10-10.0.17134 AMD64
|
||||||
Path: C:\GAM7
|
Path: C:\GAM7
|
||||||
Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
# Inbound SSO
|
# Inbound SSO
|
||||||
- [Admin Console](#admin-console)
|
- [Setup SSO](https://support.google.com/a/answer/12032922)
|
||||||
|
- [Admin Console](https://admin.google.com/ac/security/sso)
|
||||||
- [API documentation](#api-documentation)
|
- [API documentation](#api-documentation)
|
||||||
- [Definitions](#definitions)
|
- [Definitions](#definitions)
|
||||||
|
- [Setup SSO](#setupsso)
|
||||||
- [Manage profiles](#manage-profiles)
|
- [Manage profiles](#manage-profiles)
|
||||||
- [Display profiles](#display-profiles)
|
- [Display profiles](#display-profiles)
|
||||||
- [Manage credentials](#manage-credentials)
|
- [Manage credentials](#manage-credentials)
|
||||||
@@ -9,12 +11,10 @@
|
|||||||
- [Manage assignments](#manage-assignments)
|
- [Manage assignments](#manage-assignments)
|
||||||
- [Display assignments](#display-assignments)
|
- [Display assignments](#display-assignments)
|
||||||
|
|
||||||
## Admin Console
|
|
||||||
* https://admin.google.com/ac/security/sso
|
|
||||||
|
|
||||||
## API documentation
|
## API documentation
|
||||||
* [Cloud Identity API - Inbound SAML SSO Profiles](https://cloud.google.com/identity/docs/reference/rest/v1beta1/inboundSamlSsoProfiles)
|
* [Cloud Identity API - Inbound SAML SSO Profiles](https://cloud.google.com/identity/docs/reference/rest/v1beta1/inboundSamlSsoProfiles)
|
||||||
* [Cloud Identity API - Inbound SAML SSO Profiles idp Credentials](https://cloud.google.com/identity/docs/reference/rest/v1beta1/inboundSamlSsoProfiles.idpCredentials)
|
* [Cloud Identity API - Inbound SAML SSO Profiles idp Credentials](https://cloud.google.com/identity/docs/reference/rest/v1beta1/inboundSamlSsoProfiles.idpCredentials)
|
||||||
|
* [Cloud Identity API - Inbound OIDC SSO Profiles](https://cloud.google.com/identity/docs/reference/rest/v1beta1/inboundOidcSsoProfiles)
|
||||||
* [Cloud Identity API - Inbound SSO Assignments](https://cloud.google.com/identity/docs/reference/rest/v1beta1/inboundSsoAssignments)
|
* [Cloud Identity API - Inbound SSO Assignments](https://cloud.google.com/identity/docs/reference/rest/v1beta1/inboundSsoAssignments)
|
||||||
|
|
||||||
## Definitions
|
## Definitions
|
||||||
@@ -41,46 +41,68 @@
|
|||||||
```
|
```
|
||||||
## Manage profiles
|
## Manage profiles
|
||||||
```
|
```
|
||||||
gam create inboundssoprofile [name <SSOProfileDisplayName>]
|
gam create inboundssoprofile [saml|oidc] [name <SSOProfileDisplayName>]
|
||||||
[entityid <String>] [loginurl <URL>] [logouturl <URL>] [changepasswordurl <URL>]
|
[entityid <String>] [loginurl <URL>] [logouturl <URL>] [changepasswordurl <URL>]
|
||||||
[returnnameonly]
|
[returnnameonly]
|
||||||
gam update inboundssoprofile <SSOProfileItem>
|
gam update inboundssoprofile [saml|oidc] <SSOProfileItem>
|
||||||
[entityid <String>] [loginurl <URL>] [logouturl <URL>] [changepasswordurl <URL>]
|
[entityid <String>] [loginurl <URL>] [logouturl <URL>] [changepasswordurl <URL>]
|
||||||
[returnnameonly]
|
[returnnameonly]
|
||||||
```
|
```
|
||||||
|
Select type of profile:
|
||||||
|
* `saml` - SAML profile; this is the default
|
||||||
|
* `oidc` - OIDC profile
|
||||||
|
|
||||||
By default, all fields of the created|updated profile are displayed;
|
By default, all fields of the created|updated profile are displayed;
|
||||||
use the `returnnameonly` option to have GAM display just the profile name of the created|updated profile.
|
use the `returnnameonly` option to have GAM display just the profile name of the created|updated profile.
|
||||||
This will be useful in scripts that create|update a profile and then want to perform subsequent GAM commands that
|
This will be useful in scripts that create|update a profile and then want to perform subsequent GAM commands that
|
||||||
reference the profile.
|
reference the profile.
|
||||||
|
|
||||||
If `returnnameonly is specified, `inProgress` is returned if the API does not return a complete result.
|
If `returnnameonly` is specified, `inProgress` is returned if the API does not return a complete result.
|
||||||
|
|
||||||
```
|
```
|
||||||
gam delete inboundssoprofile <SSOProfileItem>
|
gam delete inboundssoprofile [saml|oidc] <SSOProfileItem>
|
||||||
```
|
```
|
||||||
|
Select type of profile:
|
||||||
|
* `saml` - SAML profile; this is the default
|
||||||
|
* `oidc` - OIDC profile
|
||||||
|
|
||||||
## Display profiles
|
## Display profiles
|
||||||
Display a specific profile.
|
Display a specific profile.
|
||||||
```
|
```
|
||||||
gam info inboundssoprofile <SSOProfileItem>
|
gam info inboundssoprofile [all|saml|oidc] <SSOProfileItem>
|
||||||
[formatjson]
|
[formatjson]
|
||||||
```
|
```
|
||||||
|
Select type of profile:
|
||||||
|
* `all` - All profiles are displayed; this is the default
|
||||||
|
* `saml` - SAML profile
|
||||||
|
* `oidc` - OIDC profile
|
||||||
|
|
||||||
By default, Gam displays the information as an indented list of keys and values.
|
By default, Gam displays the information as an indented list of keys and values.
|
||||||
* `formatjson` - Display the fields in JSON format.
|
* `formatjson` - Display the fields in JSON format.
|
||||||
|
|
||||||
Display all profiles.
|
Display profiles.
|
||||||
```
|
```
|
||||||
gam show inboundssoprofiles
|
gam show inboundssoprofiles [all|saml|oidc]
|
||||||
[formatjson]
|
[formatjson]
|
||||||
```
|
```
|
||||||
|
Select profiles to display:
|
||||||
|
* `all` - All profiles are displayed; this is the default
|
||||||
|
* `saml` - Display SAML profiles
|
||||||
|
* `oidc` - Display OIDC profiles
|
||||||
|
|
||||||
By default, Gam displays the information as an indented list of keys and values.
|
By default, Gam displays the information as an indented list of keys and values.
|
||||||
* `formatjson` - Display the fields in JSON format.
|
* `formatjson` - Display the fields in JSON format.
|
||||||
|
|
||||||
Display all profiles in a CSV file.
|
Display profiles in a CSV file.
|
||||||
```
|
```
|
||||||
gam print inboundssoprofiles [todrive <ToDriveAttribute>*]
|
gam print inboundssoprofiles [all|saml|oidc] [todrive <ToDriveAttribute>*]
|
||||||
[[formatjson [quotechar <Character>]]
|
[[formatjson [quotechar <Character>]]
|
||||||
```
|
```
|
||||||
|
Select profiles to display:
|
||||||
|
* `all` - All profiles are displayed; this is the default
|
||||||
|
* `saml` - Display SAML profiles
|
||||||
|
* `oidc` - Display OIDC profiles
|
||||||
|
|
||||||
By default, Gam displays the information as columns of fields; the following option causes the output to be in JSON format,
|
By default, Gam displays the information as columns of fields; the following option causes the output to be in JSON format,
|
||||||
* `formatjson` - Display the fields in JSON format.
|
* `formatjson` - Display the fields in JSON format.
|
||||||
|
|
||||||
@@ -130,10 +152,14 @@ The `quotechar <Character>` option allows you to choose an alternate quote chara
|
|||||||
|
|
||||||
## Manage assignments
|
## Manage assignments
|
||||||
```
|
```
|
||||||
gam create inboundssoassignment (group <GroupItem> rank <Number>)|(ou|org|orgunit <OrgUnitItem>)
|
gam create inboundssoassignment
|
||||||
(mode sso_off)|(mode saml_sso profile <SSOProfileItem>)(mode domain_wide_saml_if_enabled) [neverredirect]
|
(group <GroupItem> rank <Number>)|(ou|org|orgunit <OrgUnitItem>)
|
||||||
gam update inboundssoassignment [(group <GroupItem> rank <Number>)|(ou|org|orgunit <OrgUnitItem>)]
|
(mode sso_off)|(mode saml_sso profile <SSOProfileItem>)(mode domain_wide_saml_if_enabled)
|
||||||
[(mode sso_off)|(mode saml_sso profile <SSOProfileItem>)(mode domain_wide_saml_if_enabled)] [neverredirect]
|
[neverredirect]
|
||||||
|
gam update inboundssoassignment <SSOAssignmentName>
|
||||||
|
[(group <GroupItem> rank <Number>)|(ou|org|orgunit <OrgUnitItem>)]
|
||||||
|
[(mode sso_off)|(mode saml_sso profile <SSOProfileItem>)(mode domain_wide_saml_if_enabled)]
|
||||||
|
[neverredirect]
|
||||||
gam delete inboundssoassignment <SSOAssignmentSelector>
|
gam delete inboundssoassignment <SSOAssignmentSelector>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
- [License Products and SKUs](#license-products-and-skus)
|
- [License Products and SKUs](#license-products-and-skus)
|
||||||
- [Definitions](#definitions)
|
- [Definitions](#definitions)
|
||||||
- [Notes](#Notes)
|
- [Notes](#Notes)
|
||||||
|
- [Info User Performance](#info-user-performance)
|
||||||
- [Display license counts](#display-license-counts)
|
- [Display license counts](#display-license-counts)
|
||||||
- [Display licenses](#display-licenses)
|
- [Display licenses](#display-licenses)
|
||||||
- [Add licenses](#add-licenses)
|
- [Add licenses](#add-licenses)
|
||||||
@@ -233,6 +234,27 @@ nv:<String>:<String>
|
|||||||
```
|
```
|
||||||
The first `<String>` is a Product and the second `<String>` is a SKU.
|
The first `<String>` is a Product and the second `<String>` is a SKU.
|
||||||
|
|
||||||
|
## Info User Performance
|
||||||
|
|
||||||
|
In GAM versions prior 7.18.05, when you did `gam info user`, GAM would make one attempt to get the user's licenses.
|
||||||
|
If something went wrong, you might not get the complete list.
|
||||||
|
|
||||||
|
The License Manager API doesn't have a call that returns the list of licenses that a user has; you have to ask:
|
||||||
|
```
|
||||||
|
Does user have license SKU 1?
|
||||||
|
Does user have license SKU 2?
|
||||||
|
Does user have license SKU 3?
|
||||||
|
...
|
||||||
|
Does user have license SKU 73?
|
||||||
|
```
|
||||||
|
If you do a couple of info user commands back to back, you start to run into quota issues.
|
||||||
|
|
||||||
|
You can help yourself in the following way: generate a list of all of the SKUs that exist in your workspace.
|
||||||
|
|
||||||
|
Then do (example, use actual list): gam config license_skus 1010020028,1010070001, ... save
|
||||||
|
Now, rather that asking 73 questions per user, GAM will only ask about the license SKUs in the list.
|
||||||
|
It is much less likely that quota issues will occur,
|
||||||
|
|
||||||
## Display license counts
|
## Display license counts
|
||||||
```
|
```
|
||||||
gam show licenses
|
gam show licenses
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ Select a section from gam.cfg and process a GAM command using values from that s
|
|||||||
- Print the variable values for the selected section
|
- Print the variable values for the selected section
|
||||||
- Values are determined in this order: Selected section, DEFAULT section, Program default
|
- Values are determined in this order: Selected section, DEFAULT section, Program default
|
||||||
|
|
||||||
|
If you enter `gam select <SectionName>` and nothing else on the command line,
|
||||||
|
it will be treated as if you entered: `gam select <SectionName> save`
|
||||||
|
|
||||||
### Display sections
|
### Display sections
|
||||||
Display all of the sections in gam.cfg and mark the currently selected section with a *.
|
Display all of the sections in gam.cfg and mark the currently selected section with a *.
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -392,7 +392,7 @@ Your command line will have: `embedimage file1.jpg image1 embedimage file2.jpg i
|
|||||||
|
|
||||||
## Send an email to users
|
## Send an email to users
|
||||||
```
|
```
|
||||||
gam <UserTypeEntity> sendemail [from <EmailAddress>]
|
gam <UserTypeEntity> sendemail from <EmailAddress>
|
||||||
[replyto <EmailAddress>]
|
[replyto <EmailAddress>]
|
||||||
[cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage]
|
[cc <RecipientEntity>] [bcc <RecipientEntity>] [singlemessage]
|
||||||
[subject <String>]
|
[subject <String>]
|
||||||
@@ -406,8 +406,6 @@ gam <UserTypeEntity> sendemail [from <EmailAddress>]
|
|||||||
```
|
```
|
||||||
Emails will be sent to the users in `<UserTypeEntity>`.
|
Emails will be sent to the users in `<UserTypeEntity>`.
|
||||||
|
|
||||||
By default, emails will be sent from the admin user named in oauth2.txt, override this with the `from <EmailAddress>` option.
|
|
||||||
|
|
||||||
When using the Gmail API/SMTP, GAM gets no/little indication as to the status of the message delivery; the from user will get a non-delivery receipt if the message
|
When using the Gmail API/SMTP, GAM gets no/little indication as to the status of the message delivery; the from user will get a non-delivery receipt if the message
|
||||||
could not be sent to the specified recipients.
|
could not be sent to the specified recipients.
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,7 @@
|
|||||||
(foregroundcolor <ColorValue>)|
|
(foregroundcolor <ColorValue>)|
|
||||||
(hidden <Boolean>)|
|
(hidden <Boolean>)|
|
||||||
(notification clear|(email <CalendarEmailNotificatonEventTypeList>))|
|
(notification clear|(email <CalendarEmailNotificatonEventTypeList>))|
|
||||||
(reminder clear|(email|popup <Number>)|(<Number> email|popup))|
|
(reminder clear|(email|popup <Number>)|(<Number> email|popup))*|
|
||||||
(selected <Boolean>)|
|
(selected <Boolean>)|
|
||||||
(summary <String>)
|
(summary <String>)
|
||||||
|
|
||||||
|
|||||||
@@ -464,7 +464,7 @@ gam <UserItem> delete chatmember asadmin <ChatSpace>
|
|||||||
|
|
||||||
Delete members from a chat space by specifying chatmember names, asadmin
|
Delete members from a chat space by specifying chatmember names, asadmin
|
||||||
```
|
```
|
||||||
gam <UserItem> remove chatmember members asadmin <ChatMemberList>
|
gam <UserItem> remove chatmember asadmin members <ChatMemberList>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Update a members role in a user's chat space
|
### Update a members role in a user's chat space
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
- [Move with ownership change](#move-with-ownership-change)
|
- [Move with ownership change](#move-with-ownership-change)
|
||||||
- [Complex moves](#complex-moves)
|
- [Complex moves](#complex-moves)
|
||||||
- [Move content of a Shared Drive to another Shared Drive](#move-content-of-a-shared-drive-to-another-shared-drive)
|
- [Move content of a Shared Drive to another Shared Drive](#move-content-of-a-shared-drive-to-another-shared-drive)
|
||||||
|
- [Move content of a Shared Drive to a My Drive](#move-content-of-a-shared-drive-to-a-my-drive)
|
||||||
|
|
||||||
## API documentation
|
## API documentation
|
||||||
* [Drive API - Files](https://developers.google.com/drive/api/v3/reference/files)
|
* [Drive API - Files](https://developers.google.com/drive/api/v3/reference/files)
|
||||||
@@ -673,8 +674,10 @@ gam user user@domain.com move drivefile teamdriveid 0AC_1AB teamdriveparentid 0A
|
|||||||
```
|
```
|
||||||
|
|
||||||
If you want the source Shared Drive with ID 0AC_1AB to be contained in a top level folder of the target Shared Drive with ID 0AE_9ZX, omit the `mergewithparent` argument.
|
If you want the source Shared Drive with ID 0AC_1AB to be contained in a top level folder of the target Shared Drive with ID 0AE_9ZX, omit the `mergewithparent` argument.
|
||||||
|
The folder on the target Shared Drive will have the same name as the name of the source Shared Drive; use the `newfilename <DriveFileName>` to use a different name.
|
||||||
```
|
```
|
||||||
gam user user@domain.com move drivefile teamdriveid 0AC_1AB teamdriveparentid 0AE_9ZX
|
gam user user@domain.com move drivefile teamdriveid 0AC_1AB teamdriveparentid 0AE_9ZX
|
||||||
|
gam user user@domain.com move drivefile teamdriveid 0AC_1AB teamdriveparentid 0AE_9ZX newfilename "Copy of source Shared Drive"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Inter-workspace moves
|
### Inter-workspace moves
|
||||||
@@ -692,3 +695,21 @@ User: user@domaina.com, Move 1 Drive File/Folder
|
|||||||
User: user@domaina.com, Drive Folder: Shared Drive A(<SharedDriveAID>), Retained
|
User: user@domaina.com, Drive Folder: Shared Drive A(<SharedDriveAID>), Retained
|
||||||
```
|
```
|
||||||
To get this to work, you must check `Allow people outside of Domain A to access files` on Shared Drive A in domaina.com
|
To get this to work, you must check `Allow people outside of Domain A to access files` on Shared Drive A in domaina.com
|
||||||
|
|
||||||
|
## Move content of a Shared Drive to a My Drive
|
||||||
|
Suppose you have a Shared Drive with ID 0AC_1AB with multiple files and folders, and want to move all of its content to the root of a My Drive.
|
||||||
|
|
||||||
|
The following command will change the parents of the top level files and folders from 0AC_1AB to the root of the My Drive; the sub files and folders will move along with their top level folder.
|
||||||
|
|
||||||
|
* No permissions are processed.
|
||||||
|
```
|
||||||
|
gam user user@domain.com move drivefile teamdriveid 0AC_1AB parentid root mergewithparent
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want the contents of Shared Drive with ID 0AC_1AB to be contained in a top level folder of the My Drive, omit the `mergewithparent` argument.
|
||||||
|
The folder on the My Drive will have the same name as the name of the Shared Drive; use the `newfilename <DriveFileName>` to use a different name.
|
||||||
|
```
|
||||||
|
gam user user@domain.com move drivefile teamdriveid 0AC_1AB parentid root
|
||||||
|
gam user user@domain.com move drivefile teamdriveid 0AC_1AB parentid root newfilename "Copy of Shared Drive"
|
||||||
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
- [Change shares to User1 to shares to User2](#change-shares-to-user1-to-shares-to-user2)
|
- [Change shares to User1 to shares to User2](#change-shares-to-user1-to-shares-to-user2)
|
||||||
- [Map All ACLs from an old domain to a new domain](#map-all-acls-from-an-old-domain-to-a-new-domain)
|
- [Map All ACLs from an old domain to a new domain](#map-all-acls-from-an-old-domain-to-a-new-domain)
|
||||||
- [Remove all ACLs for a specific user or group email address](#remove-all-ACLs-for-a-specific-user-or-group-email-address)
|
- [Remove all ACLs for a specific user or group email address](#remove-all-ACLs-for-a-specific-user-or-group-email-address)
|
||||||
|
- [Remove anyone-anyoneWithLink ACLs](#remove-anyone-anyonewithlink-acls)
|
||||||
|
|
||||||
## API documentation
|
## API documentation
|
||||||
* [Drive API - Permissions](https://developers.google.com/drive/api/v3/reference/permissions)
|
* [Drive API - Permissions](https://developers.google.com/drive/api/v3/reference/permissions)
|
||||||
@@ -391,15 +392,15 @@ gam config csv_input_row_filter "permission.type:regex:domain" redirect stdout .
|
|||||||
### My Drives
|
### My Drives
|
||||||
|
|
||||||
Get My Drive ACLs sharing to that email address:
|
Get My Drive ACLs sharing to that email address:
|
||||||
* Replace <Type> with user or group
|
* Replace `<Type>` with user or group
|
||||||
* Replace email@domain.com with actual address
|
* Replace `email@domain.com` with actual email address
|
||||||
```
|
```
|
||||||
gam config auto_batch_min 1 num_threads 20 redirect csv ./MyDriveShares.csv multiprocess redirect stderr - multiprocess all users print filelist fields id,name,mimetype,basicpermissions query "'email@domain.com' in readers or 'email@domain.com' in writers" pm notrole owner type <Type> emailaddress email@domain.com em pmfilter oneitemperrow
|
gam config auto_batch_min 1 num_threads 20 redirect csv ./MyDriveShares.csv multiprocess redirect stderr - multiprocess all users print filelist fields id,name,mimetype,basicpermissions query "'email@domain.com' in readers or 'email@domain.com' in writers" pm notrole owner type <Type> emailaddress email@domain.com em pmfilter oneitemperrow
|
||||||
```
|
```
|
||||||
|
|
||||||
Delete those My Drive ACLs.
|
Delete those My Drive ACLs.
|
||||||
```
|
```
|
||||||
gam config num_threads 20 redirect stdout ./DeleteMyDriveShares.txt multiprocess redirect stderr stdout csv MyDriveShares.csv gam user "~Owner" delete drivefleacl "~id" "id:~~permissions.id~~"
|
gam config num_threads 20 redirect stdout ./DeleteMyDriveShares.txt multiprocess redirect stderr stdout csv MyDriveShares.csv gam user "~Owner" delete drivefleacl "~id" "id:~~permission.id~~"
|
||||||
```
|
```
|
||||||
|
|
||||||
Add My Drive ACLs with a different email address and the same role.
|
Add My Drive ACLs with a different email address and the same role.
|
||||||
@@ -411,17 +412,18 @@ gam config num_threads 20 redirect stdout ./AddMyDriveShares.txt multiprocess re
|
|||||||
Get an organizer for each Shared Drive
|
Get an organizer for each Shared Drive
|
||||||
```
|
```
|
||||||
gam redirect csv ./SharedDriveOrganizers.csv print shareddriveorganizers
|
gam redirect csv ./SharedDriveOrganizers.csv print shareddriveorganizers
|
||||||
|
```
|
||||||
|
|
||||||
Get Shared Drive ACLs explicitly sharing to that email address:
|
Get Shared Drive ACLs explicitly sharing to that email address:
|
||||||
* Replace <Type> with user or group
|
* Replace `<Type>` with user or group
|
||||||
* Replace email@domain.com with actual address
|
* Replace `email@domain.com` with actual email address
|
||||||
```
|
```
|
||||||
gam config num_threads 20 csv_input_row_filter "organizers:regex:^.+$" redirect csv ./SharedDriveShares.csv multiprocess redirect stderr - multiprocess csv SharedDriveOrganizers.csv gam user "~organizers" print filelist select shareddriveid "~id" fields id,name,mimetype,basicpermissions,driveid showdrivename query "'email@domain.com' in readers or 'email@domain.com' in writers" pm type <Type> emailaddress email@domain.com inherited false em pmfilter oneitemperrow
|
gam config num_threads 20 csv_input_row_filter "organizers:regex:^.+$" redirect csv ./SharedDriveShares.csv multiprocess redirect stderr - multiprocess csv SharedDriveOrganizers.csv gam user "~organizers" print filelist select shareddriveid "~id" fields id,name,mimetype,basicpermissions,driveid showdrivename query "'email@domain.com' in readers or 'email@domain.com' in writers" pm type <Type> emailaddress email@domain.com inherited false em pmfilter oneitemperrow
|
||||||
```
|
```
|
||||||
|
|
||||||
Delete those Shared Drive ACLs.
|
Delete those Shared Drive ACLs.
|
||||||
```
|
```
|
||||||
gam config num_threads 20 redirect stdout ./DeleteSharedDriveShares.txt multiprocess redirect stderr stdout csv SharedDriveShares.csv gam user "~Owner" delete drivefleacl "~id" "id:~~permissions.id~~"
|
gam config num_threads 20 redirect stdout ./DeleteSharedDriveShares.txt multiprocess redirect stderr stdout csv SharedDriveShares.csv gam user "~Owner" delete drivefleacl "~id" "id:~~permission.id~~"
|
||||||
```
|
```
|
||||||
|
|
||||||
Add Shared Drive ACLs with a different email address and the same role.
|
Add Shared Drive ACLs with a different email address and the same role.
|
||||||
@@ -429,4 +431,39 @@ Add Shared Drive ACLs with a different email address and the same role.
|
|||||||
gam config num_threads 20 redirect stdout ./ReplaceSharedDriveShares.txt multiprocess redirect stderr stdout csv SharedDriveShares.csv gam user "~Owner" add drivefleacl "~id" "~permission.type" newemail@domain.rom role "~permission.role"
|
gam config num_threads 20 redirect stdout ./ReplaceSharedDriveShares.txt multiprocess redirect stderr stdout csv SharedDriveShares.csv gam user "~Owner" add drivefleacl "~id" "~permission.type" newemail@domain.rom role "~permission.role"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Remove anyone-anyoneWithLink ACLs
|
||||||
|
|
||||||
|
Here are the queries that will be used in these commands:
|
||||||
|
* anyone - query "visibility='anyoneCanFind'"
|
||||||
|
* anyoneWithLink - query "visibility='anyoneWithLink'"
|
||||||
|
* both - query "(visibility='anyoneCanFind' or visibility='anyoneWithLink')"
|
||||||
|
|
||||||
|
### My Drives
|
||||||
|
|
||||||
|
Get My Drive anyone/anyoneWithLink ACLs
|
||||||
|
```
|
||||||
|
gam config auto_batch_min 1 num_threads 20 redirect csv ./MyDriveShares.csv multiprocess redirect stderr - multiprocess all users print filelist fields id,name,mimetype,basicpermissions <Query> pm type anyone em pmfilter oneitemperrow
|
||||||
|
```
|
||||||
|
|
||||||
|
Delete those My Drive ACLs.
|
||||||
|
```
|
||||||
|
gam config num_threads 20 redirect stdout ./DeleteMyDriveShares.txt multiprocess redirect stderr stdout csv MyDriveShares.csv gam user "~Owner" delete drivefleacl "~id" "id:~~permission.id~~"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Shared Drives
|
||||||
|
Get an organizer for each Shared Drive
|
||||||
|
```
|
||||||
|
gam redirect csv ./SharedDriveOrganizers.csv print shareddriveorganizers
|
||||||
|
```
|
||||||
|
|
||||||
|
Get Shared Drive anyone/anyoneWithLink ACLs
|
||||||
|
```
|
||||||
|
gam config num_threads 20 csv_input_row_filter "organizers:regex:^.+$" redirect csv ./SharedDriveShares.csv multiprocess redirect stderr - multiprocess csv SharedDriveOrganizers.csv gam user "~organizers" print filelist select shareddriveid "~id" fields id,name,mimetype,basicpermissions,driveid showdrivename <Query> pm type anyone inherited false em pmfilter oneitemperrow
|
||||||
|
```
|
||||||
|
|
||||||
|
Delete those Shared Drive ACLs.
|
||||||
|
```
|
||||||
|
gam config num_threads 20 redirect stdout ./DeleteSharedDriveShares.txt multiprocess redirect stderr stdout csv SharedDriveShares.csv gam user "~Owner" delete drivefleacl "~id" "id:~~permission.id~~"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -29,9 +29,10 @@ To use these commands you must add the 'Meet API' to your project and update you
|
|||||||
gam update project
|
gam update project
|
||||||
gam user user@domain.com update serviceaccount
|
gam user user@domain.com update serviceaccount
|
||||||
...
|
...
|
||||||
[*] 36) Meet API (supports readonly)
|
[*] 34) Meet API - Manage/Display Meeting Spaces
|
||||||
|
[*] 35) Meet API - Read Meeting Spaces metadata
|
||||||
```
|
```
|
||||||
|
|
||||||
## Definitions
|
## Definitions
|
||||||
* [`<UserTypeEntity>`](Collections-of-Users)
|
* [`<UserTypeEntity>`](Collections-of-Users)
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -16,7 +16,10 @@ Delegated admin service accounts (DASA) are regular [GCP service accounts](https
|
|||||||
|
|
||||||
## Disadvantages
|
## Disadvantages
|
||||||
* DASA accounts can only be delegated admins. [If a task requires super admin rights to perform](https://support.google.com/a/answer/2405986#:~:text=Only%20super%20administrators%20can...), DASA accounts won’t be able to do it.
|
* DASA accounts can only be delegated admins. [If a task requires super admin rights to perform](https://support.google.com/a/answer/2405986#:~:text=Only%20super%20administrators%20can...), DASA accounts won’t be able to do it.
|
||||||
Not all Google Admin APIs work with DASA right now. For example, Google Vault API calls will fail with a DASA account; Classroom API calls do not return data.
|
Not all Google Admin APIs work with DASA right no:
|
||||||
|
* Google Vault API calls will fail with a DASA account
|
||||||
|
* Classroom API calls do not return data
|
||||||
|
* Cloud Identity Policies are not available
|
||||||
* DASA is a delegated admin and can make Workspace / Cloud Identity admin API calls, it does not replace domain-wide delegation (DwD) when using GAM7 commands that interact with Gmail, Drive and Calendar user data.
|
* DASA is a delegated admin and can make Workspace / Cloud Identity admin API calls, it does not replace domain-wide delegation (DwD) when using GAM7 commands that interact with Gmail, Drive and Calendar user data.
|
||||||
* GAM7 support for DASA is still experimental and some things may fail. Please report your findings to the [GAM group](https://groups.google.com/g/google-apps-manager).
|
* GAM7 support for DASA is still experimental and some things may fail. Please report your findings to the [GAM group](https://groups.google.com/g/google-apps-manager).
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,16 @@
|
|||||||
[Collections of Items](Collections-of-Items)
|
[Collections of Items](Collections-of-Items)
|
||||||
```
|
```
|
||||||
<AttendeeStatus> ::= accepted|declined|needsaction|tentative
|
<AttendeeStatus> ::= accepted|declined|needsaction|tentative
|
||||||
|
<Date> ::=
|
||||||
|
<Year>-<Month>-<Day> |
|
||||||
|
(+|-)<Number>(d|w|y) |
|
||||||
|
never|
|
||||||
|
today
|
||||||
|
<Time> ::=
|
||||||
|
<Year>-<Month>-<Day>(<Space>|T)<Hour>:<Minute>:<Second>[.<MilliSeconds>](Z|(+|-(<Hour>:<Minute>))) |
|
||||||
|
(+|-)<Number>(m|h|d|w|y) |
|
||||||
|
never|
|
||||||
|
now|today
|
||||||
<EmailItem> ::= <EmailAddress>|<UniqueID>|<String>
|
<EmailItem> ::= <EmailAddress>|<UniqueID>|<String>
|
||||||
<EmailItemList> ::= "<EmailItem>(,<EmailItem>)*"
|
<EmailItemList> ::= "<EmailItem>(,<EmailItem>)*"
|
||||||
<EmailAddressList> ::= "<EmailAddess>(,<EmailAddress>)*"
|
<EmailAddressList> ::= "<EmailAddess>(,<EmailAddress>)*"
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
Print the current version of Gam with details
|
Print the current version of Gam with details
|
||||||
```
|
```
|
||||||
gam version
|
gam version
|
||||||
GAM 7.18.01 - https://github.com/GAM-team/GAM - pyinstaller
|
GAM 7.19.02 - https://github.com/GAM-team/GAM - pyinstaller
|
||||||
GAM Team <google-apps-manager@googlegroups.com>
|
GAM Team <google-apps-manager@googlegroups.com>
|
||||||
Python 3.13.5 64-bit final
|
Python 3.13.7 64-bit final
|
||||||
MacOS Sequoia 15.6 x86_64
|
MacOS Sequoia 15.6.1 x86_64
|
||||||
Path: /Users/Admin/bin/gam7
|
Path: /Users/Admin/bin/gam7
|
||||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||||
Time: 2023-06-02T21:10:00-07:00
|
Time: 2023-06-02T21:10:00-07:00
|
||||||
@@ -15,10 +15,10 @@ Time: 2023-06-02T21:10:00-07: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.18.01 - https://github.com/GAM-team/GAM - pyinstaller
|
GAM 7.19.02 - https://github.com/GAM-team/GAM - pyinstaller
|
||||||
GAM Team <google-apps-manager@googlegroups.com>
|
GAM Team <google-apps-manager@googlegroups.com>
|
||||||
Python 3.13.5 64-bit final
|
Python 3.13.7 64-bit final
|
||||||
MacOS Sequoia 15.6 x86_64
|
MacOS Sequoia 15.6.1 x86_64
|
||||||
Path: /Users/Admin/bin/gam7
|
Path: /Users/Admin/bin/gam7
|
||||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||||
Your system time differs from www.googleapis.com by less than 1 second
|
Your system time differs from www.googleapis.com by less than 1 second
|
||||||
@@ -27,15 +27,15 @@ 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.18.01 - https://github.com/GAM-team/GAM - pyinstaller
|
GAM 7.19.02 - https://github.com/GAM-team/GAM - pyinstaller
|
||||||
GAM Team <google-apps-manager@googlegroups.com>
|
GAM Team <google-apps-manager@googlegroups.com>
|
||||||
Python 3.13.5 64-bit final
|
Python 3.13.7 64-bit final
|
||||||
MacOS Sequoia 15.6 x86_64
|
MacOS Sequoia 15.6.1 x86_64
|
||||||
Path: /Users/Admin/bin/gam7
|
Path: /Users/Admin/bin/gam7
|
||||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||||
Time: 2023-06-02T21:10:00-07:00
|
Time: 2023-06-02T21:10:00-07:00
|
||||||
Your system time differs from admin.googleapis.com by less than 1 second
|
Your system time differs from admin.googleapis.com by less than 1 second
|
||||||
OpenSSL 3.4.0 22 Oct Sep 2024
|
OpenSSL 3.5.2 5 ASug 2025
|
||||||
cryptography 43.0.3
|
cryptography 43.0.3
|
||||||
filelock 3.16.1
|
filelock 3.16.1
|
||||||
google-api-python-client 2.149.0
|
google-api-python-client 2.149.0
|
||||||
@@ -64,7 +64,7 @@ MacOS High Sierra 10.13.6 x86_64
|
|||||||
Path: /Users/Admin/bin/gam7
|
Path: /Users/Admin/bin/gam7
|
||||||
Version Check:
|
Version Check:
|
||||||
Current: 5.35.08
|
Current: 5.35.08
|
||||||
Latest: 7.18.01
|
Latest: 7.19.02
|
||||||
echo $?
|
echo $?
|
||||||
1
|
1
|
||||||
```
|
```
|
||||||
@@ -72,7 +72,7 @@ echo $?
|
|||||||
Print the current version number without details
|
Print the current version number without details
|
||||||
```
|
```
|
||||||
gam version simple
|
gam version simple
|
||||||
7.18.01
|
7.19.02
|
||||||
```
|
```
|
||||||
In Linux/MacOS you can do:
|
In Linux/MacOS you can do:
|
||||||
```
|
```
|
||||||
@@ -82,10 +82,10 @@ 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.18.01 - https://github.com/GAM-team/GAM
|
GAM 7.19.02 - https://github.com/GAM-team/GAM
|
||||||
GAM Team <google-apps-manager@googlegroups.com>
|
GAM Team <google-apps-manager@googlegroups.com>
|
||||||
Python 3.13.5 64-bit final
|
Python 3.13.7 64-bit final
|
||||||
MacOS Sequoia 15.6 x86_64
|
MacOS Sequoia 15.6.1 x86_64
|
||||||
Path: /Users/Admin/bin/gam7
|
Path: /Users/Admin/bin/gam7
|
||||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||||
Time: 2023-06-02T21:10:00-07:00
|
Time: 2023-06-02T21:10:00-07:00
|
||||||
|
|||||||
Reference in New Issue
Block a user