mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-28 18:01:36 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b818002202 | ||
|
|
33065438d8 | ||
|
|
e4880a3814 | ||
|
|
a2bd409d51 | ||
|
|
352f09fad8 | ||
|
|
221a18fc51 | ||
|
|
158138f344 | ||
|
|
0d32e451e9 | ||
|
|
38780e2ba9 | ||
|
|
f3e6afbca4 | ||
|
|
cb1c7ad6fe | ||
|
|
3ee8bf2841 | ||
|
|
71d82f0f87 | ||
|
|
398a91b987 | ||
|
|
0cf17be7ca | ||
|
|
28d76c5aee |
131
.github/workflows/build.yml
vendored
131
.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
|
||||||
@@ -593,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:
|
||||||
@@ -601,34 +668,6 @@ 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@e8998f949152b193b063cb0ec769d69d929409be # 2.4.0
|
uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # 2.4.0
|
||||||
if: matrix.goal == 'build'
|
if: matrix.goal == 'build'
|
||||||
@@ -681,31 +720,17 @@ 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@e8998f949152b193b063cb0ec769d69d929409be # 2.4.0
|
uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # 2.4.0
|
||||||
|
|||||||
@@ -4549,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,18 @@
|
|||||||
|
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
|
7.19.00
|
||||||
|
|
||||||
Eliminated `drive_v3_beta` and `meet_v2_beta` from `gam.cfg` as the API betas are no longer used.
|
Eliminated `drive_v3_beta` and `meet_v2_beta` from `gam.cfg` as the API betas are no longer used.
|
||||||
|
|||||||
@@ -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.19.00'
|
__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
|
||||||
@@ -32228,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>
|
||||||
@@ -44907,6 +44907,12 @@ def waitForMailbox(entityList):
|
|||||||
Ind.Decrement()
|
Ind.Decrement()
|
||||||
|
|
||||||
def getUserLicenses(lic, user, skus):
|
def getUserLicenses(lic, user, skus):
|
||||||
|
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):
|
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:
|
||||||
@@ -44914,18 +44920,12 @@ def getUserLicenses(lic, user, skus):
|
|||||||
del sku_calls[request_id]
|
del sku_calls[request_id]
|
||||||
else:
|
else:
|
||||||
_, reason, _ = checkGAPIError(exception, softErrors=True)
|
_, reason, _ = checkGAPIError(exception, softErrors=True)
|
||||||
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
|
|
||||||
]
|
|
||||||
if reason in reasons_to_quit:
|
if reason in reasons_to_quit:
|
||||||
del sku_calls[request_id]
|
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 = {}
|
sku_calls = {}
|
||||||
for sku in skus:
|
for sku in skus:
|
||||||
svcparms = svcargs.copy()
|
svcparms = svcargs.copy()
|
||||||
@@ -44944,7 +44944,7 @@ def getUserLicenses(lic, user, skus):
|
|||||||
if try_count >= 5:
|
if try_count >= 5:
|
||||||
# give up and return what we have
|
# give up and return what we have
|
||||||
return licenses
|
return licenses
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
return licenses
|
return licenses
|
||||||
|
|
||||||
USER_NAME_PROPERTY_PRINT_ORDER = [
|
USER_NAME_PROPERTY_PRINT_ORDER = [
|
||||||
@@ -74689,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',
|
||||||
@@ -74718,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:
|
||||||
|
|||||||
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();
|
||||||
@@ -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.
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -10,14 +10,29 @@ 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
|
### 7.19.00
|
||||||
|
|
||||||
Eliminated `drive_v3_beta` and `meet_v2_beta` from `gam.cfg` as the API betas are no longer used.
|
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.
|
Updated `Meet API` scopes so that GAM can read metadata about additional Meet spaces.
|
||||||
```
|
```
|
||||||
[*] 34) Meet API - Display Meet Conference Records
|
[*] 34) Meet API - Manage/Display Meeting Spaces
|
||||||
[*] 35) Meet API - Manage/Display Meet Spaces
|
[*] 35) Meet API - Read Meeting Spaces metadata
|
||||||
```
|
```
|
||||||
|
|
||||||
### 7.18.07
|
### 7.18.07
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ 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.19.00 - 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.7 64-bit final
|
Python 3.13.7 64-bit final
|
||||||
MacOS Sequoia 15.6.1 x86_64
|
MacOS Sequoia 15.6.1 x86_64
|
||||||
@@ -990,7 +990,7 @@ writes the credentials into the file oauth2.txt.
|
|||||||
C:\>del C:\GAMConfig\oauth2.txt
|
C:\>del C:\GAMConfig\oauth2.txt
|
||||||
C:\>gam version
|
C:\>gam version
|
||||||
WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found
|
WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found
|
||||||
GAM 7.19.00 - 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.7 64-bit final
|
Python 3.13.7 64-bit final
|
||||||
Windows-10-10.0.17134 AMD64
|
Windows-10-10.0.17134 AMD64
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>)
|
||||||
|
|
||||||
|
|||||||
@@ -29,10 +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
|
||||||
...
|
...
|
||||||
[*] 34) Meet API - Display Meet Conference Records
|
[*] 34) Meet API - Manage/Display Meeting Spaces
|
||||||
[*] 35) Meet API - Manage/Display Meet Spaces
|
[*] 35) Meet API - Read Meeting Spaces metadata
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Definitions
|
## Definitions
|
||||||
* [`<UserTypeEntity>`](Collections-of-Users)
|
* [`<UserTypeEntity>`](Collections-of-Users)
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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,7 +3,7 @@
|
|||||||
Print the current version of Gam with details
|
Print the current version of Gam with details
|
||||||
```
|
```
|
||||||
gam version
|
gam version
|
||||||
GAM 7.19.00 - 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.7 64-bit final
|
Python 3.13.7 64-bit final
|
||||||
MacOS Sequoia 15.6.1 x86_64
|
MacOS Sequoia 15.6.1 x86_64
|
||||||
@@ -15,7 +15,7 @@ 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.19.00 - 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.7 64-bit final
|
Python 3.13.7 64-bit final
|
||||||
MacOS Sequoia 15.6.1 x86_64
|
MacOS Sequoia 15.6.1 x86_64
|
||||||
@@ -27,7 +27,7 @@ Your system time differs from www.googleapis.com by less than 1 second
|
|||||||
Print the current version of Gam with extended details and SSL information
|
Print the current version of Gam with extended details and SSL information
|
||||||
```
|
```
|
||||||
gam version extended
|
gam version extended
|
||||||
GAM 7.19.00 - 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.7 64-bit final
|
Python 3.13.7 64-bit final
|
||||||
MacOS Sequoia 15.6.1 x86_64
|
MacOS Sequoia 15.6.1 x86_64
|
||||||
@@ -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.19.00
|
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.19.00
|
7.19.02
|
||||||
```
|
```
|
||||||
In Linux/MacOS you can do:
|
In Linux/MacOS you can do:
|
||||||
```
|
```
|
||||||
@@ -82,7 +82,7 @@ echo $VER
|
|||||||
Print the current version of Gam and address of this Wiki
|
Print the current version of Gam and address of this Wiki
|
||||||
```
|
```
|
||||||
gam help
|
gam help
|
||||||
GAM 7.19.00 - 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.7 64-bit final
|
Python 3.13.7 64-bit final
|
||||||
MacOS Sequoia 15.6.1 x86_64
|
MacOS Sequoia 15.6.1 x86_64
|
||||||
|
|||||||
Reference in New Issue
Block a user