mirror of
https://github.com/GAM-team/GAM.git
synced 2026-02-04 21:56:27 -06:00
Compare commits
492 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea0886229e | ||
|
|
4e5462c704 | ||
|
|
ed2291bedf | ||
|
|
44ff2eba62 | ||
|
|
77fb2d0693 | ||
|
|
cea5a33856 | ||
|
|
3a7f84e49d | ||
|
|
5136c0a268 | ||
|
|
8e877a938e | ||
|
|
ed695b234f | ||
|
|
83179be184 | ||
|
|
8e105091d4 | ||
|
|
428be889a2 | ||
|
|
758519b1b3 | ||
|
|
47e2ba95eb | ||
|
|
881cbbea61 | ||
|
|
3ebfb98a98 | ||
|
|
0dbd95832f | ||
|
|
78acf64f7b | ||
|
|
4c7f03da7d | ||
|
|
3ac28ef231 | ||
|
|
7100abab12 | ||
|
|
1d531d13e4 | ||
|
|
df5af20b49 | ||
|
|
cfe6376c28 | ||
|
|
e271e18feb | ||
|
|
33d579985a | ||
|
|
887628ef87 | ||
|
|
4a969e716e | ||
|
|
363c041bbb | ||
|
|
2e151eb81b | ||
|
|
b7fe258aaf | ||
|
|
7a10f6627c | ||
|
|
613cae987f | ||
|
|
cd34c3d1e2 | ||
|
|
899200399c | ||
|
|
46c3f88280 | ||
|
|
3ddef79cbf | ||
|
|
546f31c123 | ||
|
|
443fca3f00 | ||
|
|
720d667b61 | ||
|
|
bb54fd113f | ||
|
|
aa05956d44 | ||
|
|
4b9694b760 | ||
|
|
804fb50ce4 | ||
|
|
22e0c1c355 | ||
|
|
4f6184503c | ||
|
|
fcdc333118 | ||
|
|
a09d849193 | ||
|
|
b53e6b9716 | ||
|
|
5796592288 | ||
|
|
9e32054d85 | ||
|
|
d588f7af43 | ||
|
|
01ce6319e8 | ||
|
|
49b6e03919 | ||
|
|
ac14037eb3 | ||
|
|
6135e5c2a2 | ||
|
|
540104161d | ||
|
|
3a235594b0 | ||
|
|
47eca41f2f | ||
|
|
eedb08513f | ||
|
|
8b4e38154e | ||
|
|
14e9b14bbf | ||
|
|
280805a917 | ||
|
|
5ce4ccdbeb | ||
|
|
a8f9fb7b81 | ||
|
|
aab8db0b84 | ||
|
|
5e6566618f | ||
|
|
415556603a | ||
|
|
f4566f4911 | ||
|
|
161a994dbf | ||
|
|
14f50e2433 | ||
|
|
a96db8cc88 | ||
|
|
ad45e31c27 | ||
|
|
9042bbaa30 | ||
|
|
1912765a83 | ||
|
|
0109e9b701 | ||
|
|
8973edf455 | ||
|
|
feb0a05d69 | ||
|
|
f7b5cdfb2b | ||
|
|
5b62fcb482 | ||
|
|
43dc914049 | ||
|
|
622d28af19 | ||
|
|
a0f0866551 | ||
|
|
d68d6711f5 | ||
|
|
d13001db1d | ||
|
|
0e393e0224 | ||
|
|
c1d13fa0f2 | ||
|
|
3445f648c0 | ||
|
|
677f683377 | ||
|
|
85e90ec56c | ||
|
|
53c9544456 | ||
|
|
be8ae5957b | ||
|
|
0d4e56822e | ||
|
|
865fe4ea60 | ||
|
|
c77d0806c9 | ||
|
|
d5deb0192b | ||
|
|
a7df79b75e | ||
|
|
25d88d825a | ||
|
|
5e309fbae9 | ||
|
|
a549dc4aa6 | ||
|
|
b61f2f0566 | ||
|
|
eb852b5935 | ||
|
|
1628a42569 | ||
|
|
df75bb9623 | ||
|
|
31d20985b7 | ||
|
|
c2ca6c0180 | ||
|
|
86ba64c65e | ||
|
|
d730e96c15 | ||
|
|
9d9f0e5595 | ||
|
|
97d5ede378 | ||
|
|
35ecb1d0d8 | ||
|
|
0dea4c3026 | ||
|
|
c477dbf67e | ||
|
|
185386e5e7 | ||
|
|
07b3e89809 | ||
|
|
3c09743f34 | ||
|
|
1dab9633aa | ||
|
|
57222b1a77 | ||
|
|
b54fef185c | ||
|
|
c36a71fe9e | ||
|
|
3491bd2286 | ||
|
|
36bc8837a7 | ||
|
|
395e01c1c9 | ||
|
|
b6528c4bf5 | ||
|
|
d361825165 | ||
|
|
17bc6c9f44 | ||
|
|
4058399672 | ||
|
|
e9c11f8b8d | ||
|
|
a08865b0cd | ||
|
|
8782865da4 | ||
|
|
8d79fdfe24 | ||
|
|
d86be2014c | ||
|
|
700bdfe8f8 | ||
|
|
0c50f4a794 | ||
|
|
5cf154d70b | ||
|
|
a13101ca8a | ||
|
|
1c8a3a3c57 | ||
|
|
4fc4a21909 | ||
|
|
061fead29e | ||
|
|
36887ba658 | ||
|
|
cecd9dc7ae | ||
|
|
4a952cbd16 | ||
|
|
624ed5f57c | ||
|
|
0925ff8bd4 | ||
|
|
2aecfc24dc | ||
|
|
d5d17676cc | ||
|
|
aa62ac4f3c | ||
|
|
e555e5440d | ||
|
|
122f5c3c0d | ||
|
|
4423a1ed0b | ||
|
|
e0f8789a8a | ||
|
|
944e2ed5f4 | ||
|
|
0e7c3bee6a | ||
|
|
86e827ee66 | ||
|
|
3491267f9d | ||
|
|
4b97accc21 | ||
|
|
44daf499b1 | ||
|
|
d5ac0b6b8a | ||
|
|
ce1697baff | ||
|
|
10350fbe2e | ||
|
|
dccc0443de | ||
|
|
4bf9741748 | ||
|
|
05cd06062e | ||
|
|
a56dab310a | ||
|
|
6b31831be8 | ||
|
|
0890ed1c9b | ||
|
|
8897a2dfb3 | ||
|
|
459dd59a03 | ||
|
|
5fe53106ba | ||
|
|
3f4a517ebe | ||
|
|
6185431a7d | ||
|
|
f6db0f8c7f | ||
|
|
4cb9e89197 | ||
|
|
55a0d699f8 | ||
|
|
64c3254d4c | ||
|
|
aa3649328c | ||
|
|
ab9c3e0e2c | ||
|
|
43b26c28a3 | ||
|
|
844e6442ac | ||
|
|
a5ff890a15 | ||
|
|
48d1b29010 | ||
|
|
28b3741133 | ||
|
|
d0b07febb1 | ||
|
|
f20520b3a7 | ||
|
|
c59398791b | ||
|
|
9349bc413d | ||
|
|
b2dd233dbf | ||
|
|
4142e5c293 | ||
|
|
56acae7c7d | ||
|
|
376c911e58 | ||
|
|
33a9e7e1fa | ||
|
|
360485b3aa | ||
|
|
59e425e41c | ||
|
|
e5f52289d2 | ||
|
|
19aab2b2ad | ||
|
|
6d07329e21 | ||
|
|
2ac6e361f0 | ||
|
|
d8c5d237b9 | ||
|
|
6b64879d56 | ||
|
|
213bf45942 | ||
|
|
158f24917c | ||
|
|
d2378d6e61 | ||
|
|
8ebc76905a | ||
|
|
bd367adc49 | ||
|
|
a9151c7248 | ||
|
|
b61d5f8f5d | ||
|
|
4150bef601 | ||
|
|
3a160874cf | ||
|
|
03ba798658 | ||
|
|
ab83d0204f | ||
|
|
902da7419d | ||
|
|
ff0536dedf | ||
|
|
c8bfee9544 | ||
|
|
625eddd73d | ||
|
|
b89a821252 | ||
|
|
5a45f816e8 | ||
|
|
5ad29b75e7 | ||
|
|
4da394b32a | ||
|
|
6a5052f8a2 | ||
|
|
dd5616ec0e | ||
|
|
46ad942637 | ||
|
|
6db53c6f4c | ||
|
|
87f601dc5e | ||
|
|
e3d940c548 | ||
|
|
90beada55e | ||
|
|
670e3525f0 | ||
|
|
4a4b154d3d | ||
|
|
8b182b7b37 | ||
|
|
e9d911b5cd | ||
|
|
c67a4c9147 | ||
|
|
e583b6e20c | ||
|
|
8c23cd8e06 | ||
|
|
75fa7155a0 | ||
|
|
90e11162a0 | ||
|
|
b11617c1ea | ||
|
|
cf59f9156e | ||
|
|
35c1e44568 | ||
|
|
5cc68247a3 | ||
|
|
906ee82417 | ||
|
|
3d13d4afd8 | ||
|
|
9d68ce1b46 | ||
|
|
bd0ba995e5 | ||
|
|
0ab4b6d5cd | ||
|
|
163433f15a | ||
|
|
3d6219b551 | ||
|
|
99e363b5d6 | ||
|
|
ed03da815f | ||
|
|
ef1a40afa8 | ||
|
|
cd56f353d8 | ||
|
|
3924722f1c | ||
|
|
3ce48a95c9 | ||
|
|
2dafbfbcfc | ||
|
|
e03086866a | ||
|
|
0422bf22ea | ||
|
|
f3d9f3d518 | ||
|
|
ea9fd3f363 | ||
|
|
bed9db37ad | ||
|
|
072dc4809a | ||
|
|
6db2309fc4 | ||
|
|
cbb0c81652 | ||
|
|
f68aca8361 | ||
|
|
d63fdb4ed9 | ||
|
|
226781766b | ||
|
|
434e30d57c | ||
|
|
2ab059926b | ||
|
|
5ae25495f7 | ||
|
|
20e226e57d | ||
|
|
b4677585bb | ||
|
|
3a1437872c | ||
|
|
602dce2f5a | ||
|
|
8ce930f01b | ||
|
|
9631882be0 | ||
|
|
32d2858e4b | ||
|
|
98370925e7 | ||
|
|
1ef5d030f6 | ||
|
|
d50b5fb61e | ||
|
|
e070e92be2 | ||
|
|
b3b6fff2f1 | ||
|
|
fea94fcc1c | ||
|
|
a0cd228110 | ||
|
|
acfcd8b723 | ||
|
|
a26494e5c6 | ||
|
|
5605e5d1b6 | ||
|
|
e0fdac6e17 | ||
|
|
53dc8e3265 | ||
|
|
993a0b403e | ||
|
|
2d7d118d32 | ||
|
|
f2bc704fd6 | ||
|
|
46e0c85308 | ||
|
|
9221d075fe | ||
|
|
12b84a5fcf | ||
|
|
6d411972ac | ||
|
|
d665a66d3e | ||
|
|
b2a340d99d | ||
|
|
c76164fbef | ||
|
|
3d22891052 | ||
|
|
48de06613f | ||
|
|
4d1879a9a8 | ||
|
|
454caa5a76 | ||
|
|
12ffa7e823 | ||
|
|
8fc41cbc64 | ||
|
|
dd16c29ee7 | ||
|
|
1a24b4c855 | ||
|
|
f9dfc7d094 | ||
|
|
bc64a292c3 | ||
|
|
524ef0df55 | ||
|
|
38f7f39b44 | ||
|
|
183e40ef4e | ||
|
|
ba43c4ea5f | ||
|
|
70c88dacf3 | ||
|
|
cc883b6bb7 | ||
|
|
4c320110b3 | ||
|
|
fe7c46e04d | ||
|
|
5b1c3a3a46 | ||
|
|
ce728a991f | ||
|
|
502bda4fe9 | ||
|
|
3f3d882c74 | ||
|
|
a1948eb3ca | ||
|
|
f0fb6336d1 | ||
|
|
71e5ef2399 | ||
|
|
9d9698a669 | ||
|
|
eeb180f1f2 | ||
|
|
6079ab20b3 | ||
|
|
6189ca92ab | ||
|
|
33b60c4b14 | ||
|
|
0c5f747c36 | ||
|
|
826619857c | ||
|
|
9a2880e411 | ||
|
|
95caeaba5e | ||
|
|
d8ad1b27a4 | ||
|
|
fefeae7c60 | ||
|
|
65f7b82d53 | ||
|
|
bebafb428d | ||
|
|
5e59363a0c | ||
|
|
4b2e0db720 | ||
|
|
938b2bf5a4 | ||
|
|
34ff0329c4 | ||
|
|
bed610405b | ||
|
|
1b0c8b75cb | ||
|
|
6eb7e59d56 | ||
|
|
5b4cf97702 | ||
|
|
997bd56bd6 | ||
|
|
e66db1a117 | ||
|
|
e3a5f33981 | ||
|
|
877465a82f | ||
|
|
7e9477c6ea | ||
|
|
1b2fc06f6f | ||
|
|
3d3b3eac85 | ||
|
|
882b930928 | ||
|
|
4d804177c4 | ||
|
|
de71baff60 | ||
|
|
79de854440 | ||
|
|
f0406af938 | ||
|
|
d51ca45626 | ||
|
|
00953b2984 | ||
|
|
735b131b44 | ||
|
|
cb6069bcb5 | ||
|
|
3a18143ba7 | ||
|
|
5021f685c1 | ||
|
|
2dd88a7d9e | ||
|
|
3496c2c96a | ||
|
|
98404e91b6 | ||
|
|
ddc36b42ba | ||
|
|
1cae3daa4a | ||
|
|
cc7b5c1a14 | ||
|
|
cd266ebec9 | ||
|
|
c1010d412b | ||
|
|
b41c49ea69 | ||
|
|
8617e9f57f | ||
|
|
b47f2fc4ea | ||
|
|
77f0d3abb3 | ||
|
|
71721d06f2 | ||
|
|
d51428f3dc | ||
|
|
b92239fb6f | ||
|
|
b50376656e | ||
|
|
7796baf685 | ||
|
|
6639e1be33 | ||
|
|
a8b666b32d | ||
|
|
97ce3e9b8d | ||
|
|
f9402cb21a | ||
|
|
1dc7868078 | ||
|
|
c0dc8ae790 | ||
|
|
8c12e33321 | ||
|
|
d1d48f3b90 | ||
|
|
3e52d6a924 | ||
|
|
c6e2031d45 | ||
|
|
186541d751 | ||
|
|
5efde2a967 | ||
|
|
83ed93a298 | ||
|
|
986672370a | ||
|
|
c313c5fa83 | ||
|
|
aaae733452 | ||
|
|
c810e1c8df | ||
|
|
e95ba0818e | ||
|
|
4b234c44a8 | ||
|
|
266bd68c94 | ||
|
|
36bf671251 | ||
|
|
ee71be86b5 | ||
|
|
0c3edfea62 | ||
|
|
4e85960954 | ||
|
|
61c23e2862 | ||
|
|
0613eb2c5f | ||
|
|
5b192a8f67 | ||
|
|
a3eedc360b | ||
|
|
c3225344ee | ||
|
|
dddf8a389d | ||
|
|
61847d0d89 | ||
|
|
e11510b2bc | ||
|
|
52b5745b85 | ||
|
|
82f5dd1864 | ||
|
|
b9ae49cf43 | ||
|
|
2cf9f1d5c6 | ||
|
|
544263099b | ||
|
|
085988dfde | ||
|
|
4a330ec1b6 | ||
|
|
7a69cd0b19 | ||
|
|
1be149bba2 | ||
|
|
7911317184 | ||
|
|
14f74b0d0a | ||
|
|
bb2635565d | ||
|
|
399149a946 | ||
|
|
b9237f9f63 | ||
|
|
dacd8f3c48 | ||
|
|
86b260d302 | ||
|
|
6396740269 | ||
|
|
e3931cff8d | ||
|
|
f5568ff474 | ||
|
|
38f8bdc910 | ||
|
|
10c1557494 | ||
|
|
71bd2f9cbc | ||
|
|
fc99f9b29b | ||
|
|
201f0b0eab | ||
|
|
9ad64c9efa | ||
|
|
4fb7448d40 | ||
|
|
993ab3d8d2 | ||
|
|
3befbf4419 | ||
|
|
06b2c83937 | ||
|
|
c171b6100b | ||
|
|
fa243f0894 | ||
|
|
59b61715aa | ||
|
|
2717908558 | ||
|
|
ae3c5f2ef6 | ||
|
|
0df08967ce | ||
|
|
6dbdc4db07 | ||
|
|
f4aed33e30 | ||
|
|
90fb2309ec | ||
|
|
9151de0f35 | ||
|
|
69c27d2553 | ||
|
|
1c433b69e4 | ||
|
|
b44e104b50 | ||
|
|
210d4720c2 | ||
|
|
e313006f54 | ||
|
|
ae058424f6 | ||
|
|
5c09998f9a | ||
|
|
985b6cc5e2 | ||
|
|
f7ac9aab21 | ||
|
|
5c2c049774 | ||
|
|
e290d6d200 | ||
|
|
006b885e7f | ||
|
|
bdfa8cbec7 | ||
|
|
a47ca4f602 | ||
|
|
9ba4eb88a6 | ||
|
|
69fd2ef738 | ||
|
|
a0733242ef | ||
|
|
7546ed2fe1 | ||
|
|
e7db6ac815 | ||
|
|
27d5a1c0b3 | ||
|
|
87dc1f2ec8 | ||
|
|
076f9dd376 | ||
|
|
74db8d503e | ||
|
|
b5298ec025 | ||
|
|
d01847ab25 | ||
|
|
c3b08d2d59 | ||
|
|
2cc22880f1 | ||
|
|
f2603f414c | ||
|
|
73d0d9e19a | ||
|
|
3f37bfe4cd | ||
|
|
47886770fd | ||
|
|
da3ab64267 | ||
|
|
35054b574b | ||
|
|
0640bca3e4 | ||
|
|
b148a821a3 | ||
|
|
f9e0e4b8bf | ||
|
|
6cbc47da13 | ||
|
|
66fcd01a63 | ||
|
|
4adb667db6 | ||
|
|
45f95a2dd7 | ||
|
|
cb70fe7e0d | ||
|
|
a66d53f6f3 | ||
|
|
f6a473ab43 | ||
|
|
158ec79880 |
2
.github/ISSUE_TEMPLATE/za-bug-report.md
vendored
2
.github/ISSUE_TEMPLATE/za-bug-report.md
vendored
@ -11,7 +11,7 @@ The issue tracker is for reporting product deficiencies. "How do I?" questions s
|
||||
|
||||
Please confirm the following:
|
||||
* I have upgraded to the latest GAM release from https://github.com/GAM-team/GAM/releases and I still have this issue.
|
||||
* I am typing the command as described in the GAM Wiki at https://github.com/jay0lee/gam/wiki
|
||||
* I am typing the command as described in the GAM Wiki at https://github.com/GAM-team/GAM/wiki.
|
||||
|
||||
Full steps to reproduce the issue:
|
||||
1.
|
||||
|
||||
178
.github/workflows/build.yml
vendored
178
.github/workflows/build.yml
vendored
@ -30,7 +30,7 @@ env:
|
||||
PYTHON_SOURCE_PATH: ${{ github.workspace }}/src/cpython
|
||||
CRYPTOGRAPHY_BUILD_OPENSSL_NO_LEGACY: 1
|
||||
CRYPTOGRAPHY_OPENSSL_NO_LEGACY: 1
|
||||
WINDOWS_CODESIGN_CERT_HASH: 590dc5bb10dfb31dbff38c0e2f9c35ef0f6d0e9e
|
||||
WINDOWS_CODESIGN_CERT_HASH: 3B11D9340A45CF078FF7FD984F1C3E30DA82FD05
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@ -41,93 +41,119 @@ jobs:
|
||||
include:
|
||||
- os: ubuntu-22.04
|
||||
jid: 1
|
||||
freethreaded: false
|
||||
goal: build
|
||||
name: Build Intel Ubuntu Jammy
|
||||
- os: ubuntu-24.04
|
||||
jid: 2
|
||||
freethreaded: false
|
||||
goal: build
|
||||
name: Build Intel Ubuntu Noble
|
||||
- os: ubuntu-24.04-arm
|
||||
jid: 3
|
||||
freethreaded: false
|
||||
goal: build
|
||||
name: Build Arm Ubuntu Noble
|
||||
- os: ubuntu-22.04-arm
|
||||
jid: 4
|
||||
freethreaded: false
|
||||
goal: build
|
||||
name: Build Arm Ubuntu Jammy
|
||||
- os: ubuntu-22.04
|
||||
jid: 5
|
||||
freethreaded: false
|
||||
goal: build
|
||||
staticx: yes
|
||||
name: Build Intel StaticX Legacy
|
||||
- os: ubuntu-22.04-arm
|
||||
jid: 6
|
||||
freethreaded: false
|
||||
goal: build
|
||||
staticx: yes
|
||||
name: Build Arm StaticX Legacy
|
||||
- os: macos-13
|
||||
jid: 7
|
||||
goal: build
|
||||
name: Build Intel MacOS
|
||||
- os: macos-14
|
||||
jid: 8
|
||||
freethreaded: false
|
||||
goal: build
|
||||
name: Build Arm MacOS 14
|
||||
- os: macos-15
|
||||
jid: 9
|
||||
freethreaded: false
|
||||
goal: build
|
||||
name: Build Arm MacOS 15
|
||||
- os: windows-2025
|
||||
- os: macos-15-intel
|
||||
jid: 10
|
||||
freethreaded: false
|
||||
goal: build
|
||||
name: Build x86_64 macOS 15
|
||||
- os: macos-26
|
||||
jid: 11
|
||||
freethreaded: false
|
||||
goal: build
|
||||
name: Build Arm MacOS 26
|
||||
- os: windows-2025
|
||||
jid: 12
|
||||
freethreaded: false
|
||||
goal: build
|
||||
name: Build Intel Windows
|
||||
- os: windows-11-arm
|
||||
jid: 11
|
||||
jid: 13
|
||||
freethreaded: false
|
||||
goal: build
|
||||
name: Build Arm Windows
|
||||
- os: ubuntu-24.04
|
||||
goal: test
|
||||
python: "3.10"
|
||||
jid: 12
|
||||
freethreaded: false
|
||||
jid: 14
|
||||
name: Test Python 3.10
|
||||
- os: ubuntu-24.04
|
||||
goal: test
|
||||
python: "3.11"
|
||||
jid: 13
|
||||
freethreaded: false
|
||||
jid: 15
|
||||
name: Test Python 3.11
|
||||
- os: ubuntu-24.04
|
||||
goal: test
|
||||
python: "3.12"
|
||||
jid: 14
|
||||
freethreaded: false
|
||||
jid: 16
|
||||
name: Test Python 3.12
|
||||
- os: ubuntu-24.04
|
||||
goal: test
|
||||
python: "3.14-dev"
|
||||
jid: 15
|
||||
name: Test Python 3.14-dev
|
||||
python: "3.15-dev"
|
||||
freethreaded: false
|
||||
jid: 17
|
||||
name: Test Python 3.15-dev
|
||||
- os: ubuntu-24.04
|
||||
goal: test
|
||||
python: "3.14"
|
||||
freethreaded: true
|
||||
jid: 18
|
||||
name: Test Python 3.14 freethread
|
||||
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # 5.0.0
|
||||
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
|
||||
- id: auth
|
||||
name: Authenticate to Google Cloud
|
||||
uses: google-github-actions/auth@6fc4af4b145ae7821d527454aa9bd537d1f2dc5f # 2.1.12
|
||||
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3.0.0
|
||||
with:
|
||||
workload_identity_provider: projects/297925809119/locations/global/workloadIdentityPools/gha-pool/providers/gha-provider
|
||||
service_account: github-actions-testing-for-gam@gam-project-wyo-lub-ivl.iam.gserviceaccount.com
|
||||
|
||||
- name: Cache multiple paths
|
||||
if: matrix.goal == 'build'
|
||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # 4.2.4
|
||||
uses: actions/cache@638ed79f9dc94c1de1baef91bcab5edaa19451f4 # v4.2.4
|
||||
id: cache-python-ssl
|
||||
with:
|
||||
path: |
|
||||
cache.tar.xz
|
||||
key: gam-${{ matrix.jid }}-20250814
|
||||
key: gam-${{ matrix.jid }}-20260129
|
||||
|
||||
- name: Untar Cache archive
|
||||
if: matrix.goal == 'build' && steps.cache-python-ssl.outputs.cache-hit == 'true'
|
||||
@ -137,17 +163,19 @@ jobs:
|
||||
|
||||
- name: Use pre-compiled Python for testing
|
||||
if: matrix.python != ''
|
||||
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # 5.6.0
|
||||
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
allow-prereleases: true
|
||||
check-latest: true
|
||||
freethreaded: ${{ matrix.freethreaded }}
|
||||
|
||||
- name: common variables for all runs
|
||||
env:
|
||||
JID: ${{ matrix.jid }}
|
||||
ACTIONS_CACHE: ${{ steps.cache-python-ssl.outputs.cache-hit }}
|
||||
ACTIONS_GOAL: ${{ matrix.goal }}
|
||||
freethreaded: ${{ matrix.freethreaded }}
|
||||
run: |
|
||||
case $RUNNER_ARCH in
|
||||
X64)
|
||||
@ -161,6 +189,12 @@ jobs:
|
||||
;;
|
||||
esac
|
||||
echo "JID=${JID}" >> $GITHUB_ENV
|
||||
echo "freethreaded=${freethreaded}" >> $GITHUB_ENV
|
||||
if "$freethreaded"; then
|
||||
# Hush some warnings while we test
|
||||
export PYTHON_GIL=0
|
||||
echo "PYTHON_GIL=${PYTHON_GIL}" >> $GITHUB_ENV
|
||||
fi
|
||||
echo "ACTIONS_CACHE=${ACTIONS_CACHE}" >> $GITHUB_ENV
|
||||
echo "ACTIONS_GOAL=${ACTIONS_GOAL}" >> $GITHUB_ENV
|
||||
curl_version=$(curl --version | head -n 1 | awk '{ print $2 }')
|
||||
@ -186,13 +220,9 @@ jobs:
|
||||
if: matrix.goal == 'test'
|
||||
run: |
|
||||
export PYTHON=$(which python3)
|
||||
export PIP=$(which pip3)
|
||||
export gam="${PYTHON} -m gam"
|
||||
export gampath="$(readlink -e .)"
|
||||
echo -e "PYTHON: ${PYTHON}\nPIP: ${PIP}\gam: ${gam}\ngampath: ${gampath}"
|
||||
export gampath="$(readlink -e .)/gam"
|
||||
echo -e "PYTHON: ${PYTHON}\ngam: ${gam}\ngampath: ${gampath}"
|
||||
echo "PYTHON=${PYTHON}" >> $GITHUB_ENV
|
||||
echo "PIP=${PIP}" >> $GITHUB_ENV
|
||||
echo "gam=${gam}" >> $GITHUB_ENV
|
||||
echo "gampath=${gampath}" >> $GITHUB_ENV
|
||||
|
||||
- name: Install necessary Github-hosted Linux packages
|
||||
@ -217,13 +247,13 @@ jobs:
|
||||
|
||||
- name: MacOS import developer certificates for signing
|
||||
if: runner.os == 'macOS'
|
||||
uses: apple-actions/import-codesign-certs@95e84a1a18f2bdbc5c6ab9b7f4429372e4b13a8b # 5.0.3
|
||||
uses: apple-actions/import-codesign-certs@11e1bb2d3771ad8ffa8459dfe527bc26b2dd4b62 # v5.0.3
|
||||
with:
|
||||
p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
|
||||
p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}
|
||||
|
||||
- name: Windows Configure VCode
|
||||
uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # 1.13.0
|
||||
uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0
|
||||
if: runner.os == 'Windows' && steps.cache-python-ssl.outputs.cache-hit != 'true'
|
||||
with:
|
||||
arch: ${{ runner.arch }}
|
||||
@ -253,14 +283,15 @@ jobs:
|
||||
MAKEOPT=""
|
||||
PERL="c:\strawberry\perl\bin\perl.exe"
|
||||
if [[ "$RUNNER_ARCH" == "ARM64" ]]; then
|
||||
PYEXTERNALS_PATH="arm64"
|
||||
PYEXTERNALS_ARCH="arm64"
|
||||
WIX_ARCH="arm64"
|
||||
elif [[ "$RUNNER_ARCH" == "X64" ]]; then
|
||||
PYEXTERNALS_PATH="amd64"
|
||||
PYEXTERNALS_ARCH="amd64"
|
||||
WIX_ARCH="x64"
|
||||
fi
|
||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${PYTHON_SOURCE_PATH}/PCbuild/${PYEXTERNALS_PATH}"
|
||||
echo "PYTHON=${PYTHON_SOURCE_PATH}/PCbuild/${PYEXTERNALS_PATH}/python.exe" >> $GITHUB_ENV
|
||||
PYEXTERNALS_PATH=$(cygpath -u "${PYTHON_SOURCE_PATH}/PCbuild/${PYEXTERNALS_ARCH}")
|
||||
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${PYEXTERNALS_PATH}"
|
||||
echo "PYTHON=${PYTHON_SOURCE_PATH}/PCbuild/${PYEXTERNALS_ARCH}/python.exe" >> $GITHUB_ENV
|
||||
echo "WIX_ARCH=${WIX_ARCH}" >> $GITHUB_ENV
|
||||
fi
|
||||
echo "We'll run make with: ${MAKEOPT}"
|
||||
@ -285,7 +316,7 @@ jobs:
|
||||
echo "COMPILED_OPENSSL_VERSION=${COMPILED_OPENSSL_VERSION}" >> $GITHUB_ENV
|
||||
|
||||
- name: Windows NASM Install
|
||||
uses: ilammy/setup-nasm@72793074d3c8cdda771dba85f6deafe00623038b # 1.5.2
|
||||
uses: ilammy/setup-nasm@72793074d3c8cdda771dba85f6deafe00623038b # v1.5.2
|
||||
if: matrix.goal == 'build' && runner.os == 'Windows' && runner.arch == 'X64' && steps.cache-python-ssl.outputs.cache-hit != 'true'
|
||||
|
||||
- name: Config OpenSSL
|
||||
@ -443,32 +474,50 @@ jobs:
|
||||
"${PYTHON}" -V
|
||||
"${PYTHON}" -c "import ssl; print(f'Using {ssl.OPENSSL_VERSION}')"
|
||||
|
||||
- name: Create and use Python venv
|
||||
run: |
|
||||
cd "$GITHUB_WORKSPACE"
|
||||
curl -o get-pip.py https://bootstrap.pypa.io/get-pip.py
|
||||
"$PYTHON" get-pip.py
|
||||
"$PYTHON" -m venv venv
|
||||
if [[ "$RUNNER_OS" == "Windows" ]]; then
|
||||
# pyscard seems to build outside venv but not in it.
|
||||
# build it so it's cached.
|
||||
"$PYTHON" -m pip install --upgrade --force-reinstall pyscard
|
||||
export PYTHON="${GITHUB_WORKSPACE}/venv/scripts/python.exe"
|
||||
else
|
||||
export PYTHON="${GITHUB_WORKSPACE}/venv/bin/python3"
|
||||
fi
|
||||
echo "PYTHON=${PYTHON}" >> $GITHUB_ENV
|
||||
if [[ "$ACTIONS_GOAL" == "test" ]]; then
|
||||
export gam="${PYTHON} gam.py"
|
||||
echo "gam=${gam}" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Upgrade pip, wheel, etc
|
||||
run: |
|
||||
curl $curl_retry -O https://bootstrap.pypa.io/get-pip.py
|
||||
"$PYTHON" get-pip.py
|
||||
"$PYTHON" -m pip install --upgrade pip
|
||||
"$PYTHON" -m pip install --upgrade wheel
|
||||
"$PYTHON" -m pip install setuptools
|
||||
"$PYTHON" -m pip install --upgrade setuptools
|
||||
"$PYTHON" -m pip install --upgrade importlib-metadata
|
||||
"$PYTHON" -m pip install --upgrade setuptools-scm
|
||||
"$PYTHON" -m pip install --upgrade packaging
|
||||
"$PYTHON" -m pip list
|
||||
|
||||
- name: Custom wheels for Win arm64
|
||||
if: runner.os == 'Windows' && runner.arch == 'ARM64'
|
||||
run: |
|
||||
latest_crypt_whl=$(curl https://api.github.com/repos/jay0lee/cryptography/releases/latest -s | jq -r .assets.[0].browser_download_url)
|
||||
echo "Downloading ${latest_crypt_whl}..."
|
||||
curl -O -L "$latest_crypt_whl"
|
||||
"$PYTHON" -m pip install cryptography*.whl
|
||||
|
||||
- name: Install pip requirements
|
||||
run: |
|
||||
echo "before anything..."
|
||||
"$PYTHON" -m pip list
|
||||
"$PYTHON" -m pip install --upgrade ..[yubikey]
|
||||
"$PYTHON" -m pip list
|
||||
#"$PYTHON" -m pip install --force-reinstall --no-deps --upgrade cryptography
|
||||
echo "--info--"
|
||||
"$PYTHON" -m pip cache info
|
||||
echo "--list--"
|
||||
"$PYTHON" -m pip cache list
|
||||
echo "--pip debug verbose--"
|
||||
"$PYTHON" -m pip debug --verbose
|
||||
echo "--------"
|
||||
"$PYTHON" -m pip install -vvv --upgrade ..[yubikey]
|
||||
echo "after everything..."
|
||||
"$PYTHON" -m pip list
|
||||
|
||||
@ -538,7 +587,7 @@ jobs:
|
||||
- name: Copy extra package files
|
||||
if: matrix.goal == 'build'
|
||||
run: |
|
||||
cp -v cacerts.pem "$gampath"
|
||||
cp -v gam/cacerts.pem "$gampath"
|
||||
cp -v LICENSE "$gampath"
|
||||
cp -v GamCommands.txt "$gampath"
|
||||
cp -v GamUpdate.txt "$gampath"
|
||||
@ -588,7 +637,6 @@ jobs:
|
||||
- name: Basic Tests all jobs
|
||||
id: basictests
|
||||
run: |
|
||||
$PYTHON -m unittest discover --start-directory ./ --pattern "*_test.py" --buffer || if [ $? != 5 ]; then exit $?; fi # exit 5 is no tests
|
||||
$gam version extended nooffseterror
|
||||
export GAMVERSION=$($gam version simple)
|
||||
echo "GAM Version ${GAMVERSION}"
|
||||
@ -621,8 +669,10 @@ jobs:
|
||||
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"
|
||||
#$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"
|
||||
$url = "https://files.certum.eu/software/SimplySignDesktop/Windows/9.3.4.72/SimplySignDesktop-9.3.4.72-64-bit-en.msi"
|
||||
$file = "SimplySignDesktop-9.3.4.72-64-bit-en.msi"
|
||||
Invoke-WebRequest $url -OutFile $file
|
||||
$log = "install.log"
|
||||
$procMain = Start-Process "msiexec" "/i `"$file`" /qn /l*! `"$log`"" -NoNewWindow -PassThru
|
||||
@ -655,7 +705,7 @@ jobs:
|
||||
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"
|
||||
Start-Process -Wait -NoNewWindow -ErrorAction Continue -FilePath 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\signtool.exe' -ArgumentList "sign", "/sha1", "$env:WINDOWS_CODESIGN_CERT_HASH", "/tr", "http://time.certum.pl", "/td", "SHA256", "/fd", "SHA256", "/v", "$env: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"
|
||||
@ -669,7 +719,7 @@ jobs:
|
||||
$gam create signjwtserviceaccount
|
||||
|
||||
- name: Attest gam executable was generated from this Action
|
||||
uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # 2.4.0
|
||||
uses: actions/attest-build-provenance@0b6e9809265278d02c58acf52849a95818a5a306 # v3.0.0
|
||||
if: matrix.goal == 'build'
|
||||
with:
|
||||
subject-path: ${{ env.gam }}
|
||||
@ -727,13 +777,13 @@ jobs:
|
||||
write-Host "Signing ${env:MSI_FILENAME}...."
|
||||
# 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:MSI_FILENAME"
|
||||
Start-Process -Wait -NoNewWindow -ErrorAction Continue -FilePath 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\signtool.exe' -ArgumentList "sign", "/sha1", "$env:WINDOWS_CODESIGN_CERT_HASH", "/tr", "http://time.certum.pl", "/td", "SHA256", "/fd", "SHA256", "/v", "$env:MSI_FILENAME"
|
||||
write-Host "Verifying signature of ${env:MSI_FILENAME}...."
|
||||
# verify signature. If we failed to sign we should fail to verify and die.
|
||||
& 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\signtool.exe' verify /pa /v "$env:MSI_FILENAME"
|
||||
|
||||
- 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@0b6e9809265278d02c58acf52849a95818a5a306 # v3.0.0
|
||||
if: (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') && matrix.goal == 'build'
|
||||
with:
|
||||
subject-path: |
|
||||
@ -743,13 +793,15 @@ jobs:
|
||||
|
||||
- name: Archive production artifacts
|
||||
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'
|
||||
if: always()
|
||||
with:
|
||||
name: gam-binaries-${{ env.GAMOS }}-${{ env.arch }}-${{ matrix.jid }}
|
||||
path: |
|
||||
gam*.tar.xz
|
||||
gam*.zip
|
||||
gam*.msi
|
||||
*.png
|
||||
|
||||
- name: Basic Tests build jobs only
|
||||
if: matrix.goal != 'test' && steps.cache-python-ssl.outputs.cache-hit != 'true'
|
||||
@ -842,9 +894,10 @@ jobs:
|
||||
$gam config enable_dasa false save
|
||||
# 9/17/24 temp disable due to Google API sluggishness to see new users for admin commands
|
||||
# $gam create admin $newuser _GROUPS_EDITOR_ROLE CUSTOMER # condition nonsecuritygroup
|
||||
$gam create admin $newgroup _HELP_DESK_ADMIN_ROLE org_unit "${newou}"
|
||||
$gam config csv_output_row_filter "assignedToUser:regex:${newuser}" print admins | $gam csv - gam delete admin "~roleAssignmentId"
|
||||
$gam config csv_output_row_filter "assignedToGroup:regex:${newgroup}" print admins | $gam csv - gam delete admin "~roleAssignmentId"
|
||||
# 9/13/25 temp disable due to hangs
|
||||
# $gam create admin $newgroup _HELP_DESK_ADMIN_ROLE org_unit "${newou}"
|
||||
# $gam config csv_output_row_filter "assignedToUser:regex:${newuser}" print admins | $gam csv - gam delete admin "~roleAssignmentId"
|
||||
# $gam config csv_output_row_filter "assignedToGroup:regex:${newgroup}" print admins | $gam csv - gam delete admin "~roleAssignmentId"
|
||||
$gam config enable_dasa false save
|
||||
$gam csv sample.csv gam create user ~~email~~ firstname "GHA Bulk" lastname ~~email~~ gha.jid $JID ou "${newou}"
|
||||
$gam csv sample.csv gam update user ~~email~~ recoveryphone 12125121110 recoveryemail jay0lee@gmail.com password random displayname "GitHub Actions Bulk ${JID}"
|
||||
@ -895,11 +948,11 @@ jobs:
|
||||
$gam calendar $gam_user printevents after -0d
|
||||
$gam config enable_dasa false save
|
||||
matterid=uid:$($gam create vaultmatter name "GHA matter $newbase" description "test matter" returnidonly)
|
||||
$gam create vaulthold matter $matterid name "GHA hold $newbase" corpus mail accounts $newuser
|
||||
$gam create vaulthold matter $matterid name "GHA hold ${newbase}" corpus mail ou "$newou"
|
||||
$gam print vaultmatters matterstate open
|
||||
$gam print vaultholds matter $matterid
|
||||
$gam print vaultcount matter $matterid corpus mail everyone todrive tdnobrowser
|
||||
$gam create vaultexport matter $matterid name "GHA export $newbase" corpus mail accounts $newuser
|
||||
$gam create vaultexport matter $matterid name "GHA export $newbase" corpus mail ou "$newou"
|
||||
$gam print exports matter $matterid | $gam csv - gam info export $matterid id:~~id~~
|
||||
$gam config enable_dasa true save
|
||||
$gam csv sample.csv gam user ~email add calendar id:$newresource
|
||||
@ -939,7 +992,7 @@ jobs:
|
||||
$gam report usageparameters customer
|
||||
$gam report usage customer parameters gmail:num_emails_sent,accounts:num_1day_logins
|
||||
$gam report customer todrive tdnobrowser
|
||||
#$gam report users fields accounts:is_less_secure_apps_access_allowed,gmail:last_imap_time,gmail:last_pop_time filters "accounts:last_login_time>2019-01-01T00:00:00.000Z" todrive tdnobrowser
|
||||
#$gam report users fields accounts:is_less_secure_apps_access_allowed,gmail:last_imap_time,gmail:last_pop_time filters "accounts:last_login_time>2025-01-01T00:00:00.000Z" todrive tdnobrowser
|
||||
$gam report users todrive tdnobrowser
|
||||
$gam report admin start -3d todrive tdnobrowser
|
||||
$gam print devices nopersonaldevices nodeviceusers filter "serial:$JID$JID$JID$JID-" | $gam csv - gam delete device id ~name
|
||||
@ -979,7 +1032,8 @@ jobs:
|
||||
else
|
||||
tar_folders="bin/"
|
||||
fi
|
||||
tar cJvvf cache.tar.xz $tar_folders
|
||||
echo '.git*' > ./excludes.txt
|
||||
tar cJvvf cache.tar.xz --exclude-from=excludes.txt $tar_folders
|
||||
|
||||
merge:
|
||||
if: (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch')
|
||||
@ -990,7 +1044,7 @@ jobs:
|
||||
packages: write
|
||||
steps:
|
||||
- name: Merge Artifacts
|
||||
uses: actions/upload-artifact/merge@v4
|
||||
uses: actions/upload-artifact/merge@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
with:
|
||||
name: gam-binaries
|
||||
pattern: gam-binaries-*
|
||||
@ -1006,7 +1060,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # 5.0.0
|
||||
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
@ -1029,7 +1083,7 @@ jobs:
|
||||
echo "dateversion=${dateversion}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Publish draft release
|
||||
uses: softprops/action-gh-release@72f2c25fcb47643c292f7107632f7a47c1df5cd8 # 2.3.2
|
||||
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3
|
||||
with:
|
||||
draft: true
|
||||
prerelease: false
|
||||
|
||||
4
.github/workflows/codeql-analysis.yml
vendored
4
.github/workflows/codeql-analysis.yml
vendored
@ -39,9 +39,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
|
||||
4
.github/workflows/get-cacerts.yml
vendored
4
.github/workflows/get-cacerts.yml
vendored
@ -13,13 +13,13 @@ on:
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
working-directory: src
|
||||
working-directory: src/gam
|
||||
|
||||
jobs:
|
||||
check-certs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal token
|
||||
fetch-depth: 0 # otherwise, you will failed to push refs to dest repo
|
||||
|
||||
4
.github/workflows/pushwiki.yml
vendored
4
.github/workflows/pushwiki.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
||||
git clone https://github.com/GAM-team/GAM
|
||||
|
||||
- name: Checkout Wiki source
|
||||
uses: actions/checkout@master
|
||||
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
|
||||
with:
|
||||
path: GAM.wiki
|
||||
repository: GAM-team/GAM.wiki
|
||||
@ -37,7 +37,7 @@ jobs:
|
||||
cd GAM.wiki
|
||||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "GitHub Action"
|
||||
git add *.md
|
||||
git add -A
|
||||
git commit -m "[no ci] Push Wiki changes"
|
||||
git status
|
||||
git push
|
||||
|
||||
4
.github/workflows/pypi.yml
vendored
4
.github/workflows/pypi.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
id-token: write
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
@ -30,6 +30,6 @@ jobs:
|
||||
python -m build
|
||||
|
||||
- name: Publish package distributions to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
|
||||
with:
|
||||
attestation: true
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
# See https://pre-commit.com for more information
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v2.5.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: double-quote-string-fixer
|
||||
- id: check-yaml
|
||||
- id: check-docstring-first
|
||||
- id: name-tests-test
|
||||
- id: requirements-txt-fixer
|
||||
- id: check-merge-conflict
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-yapf
|
||||
rev: v0.30.0
|
||||
hooks:
|
||||
- id: yapf
|
||||
args: [--style=google, --in-place]
|
||||
|
||||
- repo: https://github.com/PyCQA/pylint
|
||||
rev: pylint-2.5.0
|
||||
hooks:
|
||||
- id: pylint
|
||||
args: [--output-format=colorized]
|
||||
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v2.31.0
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args: [--py37-plus]
|
||||
@ -18,6 +18,11 @@ this will download GAM, install it and start setup.
|
||||
|
||||
Download the MSI Installer from the [GitHub Releases] page. Install the MSI and you'll be prompted to setup GAM.
|
||||
|
||||
## Use your own Python
|
||||
If you'd prefer to install GAM as a Python package you can install with pip:
|
||||
```
|
||||
pip install gam7
|
||||
```
|
||||
# Documentation
|
||||
|
||||
The GAM documentation is hosted in the [GitHub Wiki]
|
||||
|
||||
@ -10,23 +10,24 @@ authors = [
|
||||
# notice that yubikey-manager remains optional further down since it is less command and adds
|
||||
#significant compile dependencies.
|
||||
dependencies = [
|
||||
"arrow>=1.3.0",
|
||||
"chardet>=5.2.0",
|
||||
"cryptography>=44.0.2",
|
||||
"cryptography==46.0.3",
|
||||
"distro; sys_platform=='linux'",
|
||||
"filelock>=3.18.0",
|
||||
"google-api-python-client>=2.167.0",
|
||||
"google-auth-httplib2>=0.2.0",
|
||||
"google-auth-oauthlib>=1.2.2",
|
||||
"google-auth>=2.39.0",
|
||||
"httplib2>=0.22.0",
|
||||
"httplib2>=0.31.0",
|
||||
"lxml>=5.4.0",
|
||||
"passlib>=1.7.4",
|
||||
"pathvalidate>=3.2.3",
|
||||
"python-dateutil",
|
||||
"pysocks>=1.7.1",
|
||||
]
|
||||
description = "CLI tool to manage Google Workspace"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.9"
|
||||
requires-python = ">=3.10"
|
||||
classifiers = [
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,682 @@
|
||||
7.33.00
|
||||
|
||||
Added variable `developer_preview_apis` to `gam.cfg` that is a comma separated list of APIs requiring a Developer Preview key.
|
||||
Currently, `chat` is the only API that requires a Developer Preview key; it is required for the User Sections commands.
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Users-Chat#introduction
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Users-Chat#manage-chat-user-sections
|
||||
|
||||
7.32.07
|
||||
|
||||
Added option `includepermissionsforview published` to `gam <UserTypeEntity> print filelist` and
|
||||
`gam <UserTypeEntity> show fileinfo`. From the Drive API documentation:
|
||||
```
|
||||
Specifies which additional view's permissions to include in the response. Only published is supported.
|
||||
```
|
||||
|
||||
7.32.06
|
||||
|
||||
Added options to `gam <UserTypeEntity> copy drivefile ... copysubfiles` to limit copying
|
||||
to files whose `modifiedTime` meets specified requirements.
|
||||
* `start|starttime <Date>|<Time>` - If specified, `modifiedTime` must be >= the value
|
||||
* `end|endtime <Date>|<Time>` - If specified, `modifiedTime` must be <= the value
|
||||
* `range <Date>|<Time> <Date>|<Time>` - first value <= `modifiedTime` <= second value
|
||||
|
||||
7.32.05
|
||||
|
||||
Fixed bug in `gam <UserTypeEntity> print messages|threads ... headers <SMTPHeaderList>` where
|
||||
headers other than those specified in `<SMTPHeaderList>` were displayed.
|
||||
|
||||
Updated `gam info users <UserTypeEntity>` to display the following data when the Licensing API
|
||||
does not return data due to quota limits. Previously, no License data was displayed and
|
||||
there was no way to know if it was omitted due to API quota limits vs the user has no license?
|
||||
```
|
||||
Licenses: (1)
|
||||
Not available/incomplete
|
||||
```
|
||||
If a user has no licenses, this will be displayed.
|
||||
```
|
||||
Licenses: (0)
|
||||
```
|
||||
|
||||
You should use `license_skus = <SKUIDList>` in `gam.cfg` to list all of the licensing SKUs
|
||||
used in your workspace. Without this list, GAM has to make 70+ API calls to get the licenses
|
||||
for a user; this can cause quota limit errors.
|
||||
|
||||
7.32.04
|
||||
|
||||
Support for student groups in Google Classroom no longer requires Developer Preview membership.
|
||||
|
||||
Upgraded to OpenSSL 3.6.1.
|
||||
|
||||
7.32.03
|
||||
|
||||
Added option `template` as an additional formating option for `gam <UserTypeEntity> show signature`
|
||||
that displays just the HTML data; this simplifies capturing the data for use as input to GAM.
|
||||
```
|
||||
$ gam redirect stdout ./SigTemplate.html user user@domain.com show signature template
|
||||
$ more SigTemplate.html
|
||||
<div dir="ltr"><div dir="ltr"><div dir="ltr">
|
||||
<p style="margin:0px;font-size-adjust:none;font-stretch:normal;font-size:12px;line-height:normal;font-family:Monaco;color:rgb(0,0,0)">
|
||||
<span style="background-color:rgb(82,208,206)">{RT}Email: {Email}{/RT}</span></p>
|
||||
<p style="margin:0px;font-size-adjust:none;font-stretch:normal;font-size:12px;line-height:normal;font-family:Monaco;color:rgb(0,0,0)">
|
||||
<span style="background-color:rgb(82,208,206)">{RT}Phone: {Phone}{/RT}</span></p>
|
||||
<p style="margin:0px;font-size-adjust:none;font-stretch:normal;font-size:12px;line-height:normal;font-family:Monaco;color:rgb(0,0,0)">
|
||||
<span style="background-color:rgb(82,208,206)">{RT}Mobile: {Mobile}{/RT}</span></p>
|
||||
</div><br></div>\n</div>
|
||||
|
||||
```
|
||||
|
||||
7.32.02
|
||||
|
||||
Added variable `oauth2_txt_lock_mode` to `gam.cfg`, the default is 644 and valid values are: 644, 664, 666.
|
||||
This value is used to set the file permissions on the `oauth2.txt.lock` file. In very special cases where
|
||||
daemon processes, e.g. Apache/httpd, are running GAM, the value 666 may have to be used.
|
||||
|
||||
7.32.01
|
||||
|
||||
Added option `(addcsvdata <FieldName> <String>)*` to `gam <CrOSTypeEntity> issuecommand command <CrOSCommand> csv`
|
||||
and `gam <CrOSTypeEntity> getcommand commandid <CommandID> csv` that adds additional columns of data to the CSV file output.
|
||||
* See: https://github.com/GAM-team/GAM/wiki/ChromeOS-Devices#bulk-action-example
|
||||
|
||||
7.32.00
|
||||
|
||||
Added option `verifyallowexternal` to `gam print cigroup-members|group-members` that causes
|
||||
GAM to only display external members in groups with `allowExternalMembers=False'.
|
||||
This option can be used to help verify that internal-only groups don't have external members.
|
||||
|
||||
Updated option `internaldomains` for the following commands:
|
||||
```
|
||||
gam info|print groups
|
||||
gam print|show group-members
|
||||
gam info|print cigroups
|
||||
gam print|show cigroup-members
|
||||
gam <UserTypeEntity> print|show filesharecounts
|
||||
```
|
||||
Which domains are considered internal domains:
|
||||
* `internaldomains all` - All of your workspace domains; this is the default
|
||||
* `internaldomains primary` - Your workspace primary domain
|
||||
* `internaldomains <DomainNameList>` - A list of domain names
|
||||
|
||||
Added option `csv` to `gam <CrOSTypeEntity> issuecommand command <CrOSCommand>`
|
||||
and `gam <CrOSTypeEntity> getcommand commandid <CommandID>` so that command details are displayed in CSV format.
|
||||
This can be used to log commands issued to devices and then monitor the results.
|
||||
|
||||
Added option `filemimetype category <MimeTypeNameList>` to `gam <UserTypeEntity> copy drivefile` to support
|
||||
copying of files based on their MimeType category.
|
||||
|
||||
Added option `attendeeslist` to `gam calendars <CalendarEntity> print events` and `gam <UserTypeEntity> print events`
|
||||
that causes GAM to display the attendee email addresses in a single column `attendeesList`; no attendee details
|
||||
are displayed. The email addresses are separated by `csv_output_field_delimiter` from `gam.cfg`.
|
||||
|
||||
Fixed bug in `gam sendemail ... replyto <EmailAddress>` that caused a message delivery error if
|
||||
`<EmailAddress>` did not include a domain name.
|
||||
|
||||
Added support for users's chat sections.
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Users-Chat#manage-chat-users-sections
|
||||
* This is in Deveoper Preview; you must have a `developer_preview_api_key` in `gam.cfg` to use these commands.
|
||||
|
||||
7.31.06
|
||||
|
||||
Added option `batchsize <Integer>` to `gam calendar <CalendarEntity> delete|purge events` and
|
||||
`gam <UserTypeEntity> delete|purge events <UserCalendarEntity>` that causes GAM to delete events
|
||||
with batch API calls rather than with individual API calls.
|
||||
|
||||
7.31.05
|
||||
|
||||
Added option `variables <RESearchPattern>` to `gam select section <SectionName> verify` and `gam config verify`
|
||||
that causes GAM to only display variables with names selected by `<RESearchPattern>`.
|
||||
```
|
||||
gam select School verify variables "^(customer|domain)"
|
||||
Section: School
|
||||
customer_id = C03abc123
|
||||
domain = school.edu
|
||||
|
||||
gam config verify variables 'dir'
|
||||
Section: DEFAULT
|
||||
cache_dir = ~/GamConfig/gamcache ; /Users/gamteam/GamConfig/gamcache
|
||||
config_dir = ~/GamConfig ; /Users/gamteam/GamConfig
|
||||
drive_dir = ~/GamWork ; /Users/gamteam/GamWork
|
||||
gmail_cse_incert_dir = ~/GmailCSE/Certs ; /Users/gamteam/GmailCSE/Certs
|
||||
gmail_cse_inkey_dir = ~/GmailCSE/Keys ; /Users/gamteam/GmailCSE/Keys
|
||||
input_dir = .
|
||||
```
|
||||
|
||||
7.31.04
|
||||
|
||||
Fixed bug in `gam report admin|chrome` that caused to events to not be displayed.
|
||||
|
||||
Updated `gam <UserTypeEntity> print|show messages|threads ... query <QueryGmail>` to display the query.
|
||||
|
||||
7.31.03
|
||||
|
||||
Due to the following Calendar API update, the `gam <UserTypeEntity> transfer calendars` command has been removed.
|
||||
* See: https://developers.google.com/workspace/calendar/release-notes#October_27_2025
|
||||
Data ownership can be transferred in the Google Calendar UI.
|
||||
|
||||
7.31.02
|
||||
|
||||
Added the following options to `gam <UserTypeEntity> copy drivefile`
|
||||
to limit copying to those files owned by selected users.
|
||||
* `copysubfilesownedby users <EmailAddressList>` - Only files owned by users in `<EmailAddressList>` are copied.
|
||||
* `copysubfilesownedby notusers <EmailAddressList>` - Only files not owned by users in `<EmailAddressList>` are copied.
|
||||
* `copysubfilesownedby regex <REMatchPattern>` - Only files owned by users whose email addresses match `<REMatchPattern>` are copied.
|
||||
* `copysubfilesownedby notregex <REMatchPattern>` - Only files owned by users whose email addresses do not match `<REMatchPattern>` are copied.
|
||||
|
||||
7.31.01
|
||||
|
||||
Code cleanup for `addcsvdata <FieldName> <String>`.
|
||||
|
||||
7.31.00
|
||||
|
||||
Fixed bug in `gam report chrome (user <UserItem>)|(select <UserTypeEntity>)` where no activities were returned.
|
||||
`report chrome` does not use the parameter `userKey=<EmailAddress>` as do other applications but requires
|
||||
parameter `filter DEVICE_USER==<EmailAddress>`.
|
||||
|
||||
Updated `gam report admin (user <UserItem>)|(select <UserTypeEntity>)` to use parameter `filter USER_EMAIL==<EmailAddress>`
|
||||
to display activiities affecting the user `<EmailAddress>`. Use option `userisactor` to use the parameter `userKey=<EmailAddress>`
|
||||
that displays activities where user `<EmailAddress>` executed the command that generated the activity.
|
||||
|
||||
Fixed bug in `gam print cros|filelist|users ... (addcsvdata <FieldName> <String>)+ formatjson` where the `addcsvdata` columns
|
||||
were not displayed but the additional field values were included in the JSON data. Now, the `addcsvdata` columns
|
||||
are displayed but the additional field values are only included in the JSON data when option `includdecsvdatainjson` is specified.
|
||||
|
||||
Added option `addcsvdata <FieldName> <String>` to `gam <UserTypeEntity> print cigroups|groups`
|
||||
that adds additional columns of data to the CSV file output.
|
||||
|
||||
Added option `addcsvdata <FieldName> <String>` to `gam <UserTypeEntity> print cigroupmembere|group-members`
|
||||
that adds additional columns of data to the CSV file output.
|
||||
|
||||
7.30.05
|
||||
|
||||
Added option `gmaileventtypes <NumberRangeList>` to `gam report gmail` that can be used to limit the event types displayed.
|
||||
```
|
||||
<NumberRange> ::= <Number>|(<Number>/<Number>)
|
||||
<NumberRangeList> ::= "<NumberRange>(,<NumberRange>)*"
|
||||
|
||||
gam report gmail user user@domain.com gmaileventtypes 1,10/11
|
||||
```
|
||||
* See: https://developers.google.com/workspace/admin/reports/v1/appendix/activity/gmail
|
||||
|
||||
Updated sorting of column headers in `gam report <ActivityApplicationName>`.
|
||||
|
||||
7.30.04
|
||||
|
||||
Updated `gam report gmail` to avoid the following error when incomplete start/end time information is provided.
|
||||
```
|
||||
ERROR: Invalid request: Start time and end time should both be provided, and the scan duration should not be greater than 30 days.
|
||||
```
|
||||
* No time information provided - GAM sets `range -30d today`
|
||||
* Only `start <Time>` provided - GAM sets `end <Time>+30d`
|
||||
* Only `end <Time>` provided - GAM sets `start <Time>-30d`
|
||||
|
||||
7.30.03
|
||||
|
||||
Updated `gam report <ActivityApplicationName>` to reflect the changes described here:
|
||||
* See: https://workspaceupdates.googleblog.com/2025/12/google-workspace-audit-log-api.html
|
||||
|
||||
Added option `resourcedetailsfilter <String>` to `gam report <ActivityApplicationName>` described here:
|
||||
* See: https://developers.google.com/workspace/admin/reports/reference/rest/v1/activities/list#query-parameters
|
||||
|
||||
For `gam <UserTypeEntity> print <Objects>`, expanded the list of `<Objects>` covered by `gam.cfg csv_output_users_audit = True`.
|
||||
|
||||
7.30.02
|
||||
|
||||
Added option `conferencedata meet <MeetID>` to `<EventAttribute>` that allows specifying
|
||||
a reference to an existing Google Meet when creating/updating a calendar event.
|
||||
|
||||
Upgraded to Python 3.14.2.
|
||||
|
||||
7.30.01
|
||||
|
||||
Fixed bug introduced in 7.30.00 that caused errors when reading CSV files.
|
||||
|
||||
Added the following options to `gam <UserTypeEntity> create focustime|outofoffice`:
|
||||
```
|
||||
((date yyyy-mm-dd)|
|
||||
(range yyyy-mm-dd yyyy-mm-dd)|
|
||||
(daily yyyy-mm-dd N)|
|
||||
(weekly yyyy-mm-dd N))
|
||||
```
|
||||
|
||||
Added the following options to `gam <UserTypeEntity> create focustime|outofoffice|workinglocation`:
|
||||
```
|
||||
noreminders|(reminder email|popup <Number>)+
|
||||
```
|
||||
|
||||
7.30.00
|
||||
|
||||
Added `input_dir` directory variable to `gam.cfg` that is used to select a directory for reading files with non-absolute file names.
|
||||
The default is '.' that matches the current behavior where these files are read from the current working directory.
|
||||
This will be most useful in multiple domain situations where each domain will have distinct `drive_dir` and `input_dir` values.
|
||||
|
||||
Added support for the new resource calendar setting `autoAcceptInvitations`.
|
||||
|
||||
7.29.04
|
||||
|
||||
Updated `gam delete chromepolicy chrome.users.apps.InstallType ou <OrgUnitItem> appid <AppID>`
|
||||
to allow deleting an app, i.e., explicitly remove it from management. `<OrgUnitItem>` must specify where it was added for management.
|
||||
|
||||
7.29.03
|
||||
|
||||
Remove debugging message from `gam <UserTypeEntity> move drivefile <DriveFileEntity>`.
|
||||
|
||||
7.29.02
|
||||
|
||||
Fixed bug in `gam <UserTypeEntity> move drivefile <DriveFileEntity>` where the following options
|
||||
were only applied to top level files or folders re-created in the destination. Now, domain
|
||||
and email address mappings apply to all moved files/folders.
|
||||
```
|
||||
excludepermissionsfromdomains <DomainNameList>
|
||||
includepermissionsfromdomains <DomainNameList>
|
||||
mappermissionsdomain <DomainName> <DomainName>
|
||||
mappermissionsemail <EmailAddress> <EmailAddress>
|
||||
mappermissionsemailfile <CSVFileInput> endcsv
|
||||
```
|
||||
|
||||
Upgraded to Python 3.14.1.
|
||||
|
||||
7.29.01
|
||||
|
||||
Added option `oneitemperrow` to `gam <UserTypeEntity> print calendars ... permissions` to have each of a
|
||||
calendar's permissions displayed on a separate row with all of the other calendar fields.
|
||||
|
||||
Updated `gam yubikey reset_piv` to handle YubiKey firmware updates that caused an error.
|
||||
|
||||
7.29.00
|
||||
|
||||
Added options `mappermissionsemail <EmailAddress> <EmailAddress>` and ` mappermissionsemailfile <CSVFileInput> endcsv`
|
||||
to these commands:
|
||||
```
|
||||
gam [<UserTypeEntity>] copy shareddriveacls <SharedDriveEntity> to <SharedDriveEntity>
|
||||
gam [<UserTypeEntity>] sync shareddriveacls <SharedDriveEntity> with <SharedDriveEntity>
|
||||
gam <UserTypeEntity> copy drivefile <DriveFileEntity>
|
||||
gam <UserTypeEntity> move drivefile <DriveFileEntity>
|
||||
```
|
||||
When `mappermissionsemail <EmailAddress> <EmailAddress>` is specifed, an ACL that references the first `<EmailAddress>`
|
||||
in the source will be modified to reference the second `<EmailAddress>` in the destination.
|
||||
|
||||
Bulk permission email address mapping can be specified with `mappermissionsemailfile <CSVFileInput> endcsv`.
|
||||
`<CSVFileInput>` must include these columns: `sourceEmail` and `destinationEmail`.
|
||||
|
||||
These options will be most useful with inter-workspace Shared Drive copies and moves.
|
||||
|
||||
7.28.13
|
||||
|
||||
Added option `addcsvdata <FieldName> <String>` to `gam <UserTypeEntity> print messages`
|
||||
that adds additional columns of data to the CSV file output.
|
||||
|
||||
7.28.12
|
||||
|
||||
Updated `gam delete project` to handle the following error:
|
||||
```
|
||||
ERROR: 400: failedPrecondition - Project not active
|
||||
```
|
||||
|
||||
7.28.11
|
||||
|
||||
Removed all options/fields referencing inheritance from `gam create|update|info|print org` as this option/field is deprecated.
|
||||
|
||||
7.28.10
|
||||
|
||||
Added a command `gam print course-counts` that dsplays the count of the number of courses in which a teacher or student is a participant.
|
||||
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Classroom-Membership#display-course-counts-for-teachers-students
|
||||
|
||||
7.28.09
|
||||
|
||||
Fixed bug in `gam print cigroups ... descriptionmatchpattern [not] <REMatchPattern>` that caused a trap.
|
||||
|
||||
7.28.08
|
||||
|
||||
Updated `gam <UserTypeEntity> print|show chatmessages` to cache the sender UID to email address
|
||||
map so that each sender UID only has to be looked up once; this improves performance.
|
||||
|
||||
7.28.07
|
||||
|
||||
Fixed bug in `gam report users ... aggregatebydate|aggregatebyuser` where `accounts:used_quota_in_percentage` was incorrectly displayed.
|
||||
|
||||
7.28.06
|
||||
|
||||
Updated `gam <UserTypeEntity> info|print|show calendars` and
|
||||
`gam calendars <CalendarEntity> print|show settings` to display the
|
||||
new `dataOwner` field as described under `Additional details` below.
|
||||
|
||||
* See: https://workspaceupdates.googleblog.com/2025/11/secondary-calendar-management-with-dedicated-owners.html
|
||||
|
||||
7.28.04
|
||||
|
||||
Updated commands that display Chrome device counts to display the date in the output.
|
||||
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Chrome-Device-Counts
|
||||
|
||||
7.28.03
|
||||
|
||||
Improved commands to display Chrome device counts.
|
||||
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Chrome-Device-Counts
|
||||
|
||||
7.28.02
|
||||
|
||||
Added commands to display Chrome device counts.
|
||||
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Chrome-Device-Counts
|
||||
|
||||
7.28.01
|
||||
|
||||
Updated `gam <UserTypeEntity> show fileinfo <DriveFileEntity>` to display `displayName` as the key field
|
||||
of a `permission` not `deleted`.
|
||||
|
||||
7.28.00
|
||||
|
||||
Added option `addcsvdata <FieldName> <String>` to `gam report [usage] customers|users`
|
||||
that adds additional columns of data to the CSV file output. This will be most useful
|
||||
when reading a CSV of user information and you want to include some of the user information,
|
||||
e.g., orgUnitPath, in the output.
|
||||
```
|
||||
gam redirect csv ./Users.csv print users fields ou
|
||||
gam redirect csv ./UserStorageInfo.csv multiprocess csv Users.csv gam report users user "~primaryEmail" parameters accounts:drive_used_quota_in_mb,accounts:gmail_used_quota_in_mb,accounts:gplus_photos_used_quota_in_mb,accounts:total_quota_in_mb,accounts:used_quota_in_mb,accounts:used_quota_in_percentage addcsvdata orgUnitPath "~orgUnitPath"
|
||||
```
|
||||
|
||||
7.27.05
|
||||
|
||||
Added option `addcsvdata <FieldName> <String>` to `gam print courses`
|
||||
that adds additional columns of data to the CSV file output.
|
||||
|
||||
The following scope is no longer necessary: `Cloud Identity API - Groups Beta (Enables group locking/unlocking)`
|
||||
as this scope `Cloud Identity API - Groups` now provides group locking/unlocking.
|
||||
|
||||
7.27.04
|
||||
|
||||
Added options to `gam <UserTypeEntity> create delegate` that support
|
||||
sending email notifications when a user adds a delegate.
|
||||
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Users-Gmail-Delegates#delegation-notification
|
||||
|
||||
7.27.03
|
||||
|
||||
Updated `gam <UserTypeEntity> create|update|sync chatmember` role specification to `role member|manager|owner`.
|
||||
This is the mapping between the Chat UI and Chat API; GAM uses the Chat UI role names.
|
||||
```
|
||||
UI: Member, API: ROLE_MEMBER
|
||||
UI: Manager, API: ROLE_ASSISTANT_MANAGER
|
||||
UI: Owner, API: ROLE_MANAGER
|
||||
```
|
||||
|
||||
Updated `gam <UserTypeEntity> update chatspace` options for permission settings.
|
||||
```
|
||||
[managemembersandgroups owners|managers|members]
|
||||
[modifyspacedetails owners|managers|members]
|
||||
[togglehistory owners|managers|members]
|
||||
[useatmentionall owners|managers|members]
|
||||
[manageapps owners|managers|members]
|
||||
[managewebhooks owners|managers|members]
|
||||
[replymessages owners|managers|members]
|
||||
```
|
||||
|
||||
7.27.02
|
||||
|
||||
Added option `clearattachments <String>` to `gam [<UserTypeMessage>] update chatmessage`
|
||||
that clears all attachments from a Chat message. If `<ChatContent>` is not specified,
|
||||
the current message text is retained and `<String>` is appended; `<String>` must be specified
|
||||
but can be empty in which case the current message test is preserved as-is.
|
||||
|
||||
7.27.01
|
||||
|
||||
Fixed bug in `gam <UserTypeEntity> claim ownership <DriveFileEntity> ... onlyUsers|skipusers <UserTypeEntity>`
|
||||
where the email addresses in `onlyUsers|skipusers <UserTypeEntity>` were not normalized.
|
||||
|
||||
7.27.00
|
||||
|
||||
Added `debug_redaction` Boolean variable to `gam.cfg`. When True, the default,
|
||||
sensitive data like access/refresh tokens, client secret and authorization codes
|
||||
are redacted from debug output. This allows you to post debug output without
|
||||
compromising your account information. Even with debug redaction,
|
||||
anything shared publicly should be double-checked for sensitive content.
|
||||
|
||||
7.25.01
|
||||
|
||||
Fixed bug in `gam config timezone <String>` to handle timezone abbreviations correctly;
|
||||
they were incorrectly shifted to lowercase.
|
||||
|
||||
7.25.00
|
||||
|
||||
Removed a capabilty added in 7.24.00 that allowed reading command data from Google Docs and Sheets
|
||||
when a user's service account access to Drive and Sheets had been disabled. Jay was concerned
|
||||
that this change could be exploited to give access to all user's files.
|
||||
|
||||
This capability has been replaced by issuing the following commands. The admin specified in `gam oauth create`
|
||||
can read command data from Docs and Sheets to which it has access.
|
||||
```
|
||||
gam config commanddata_clientaccess true save
|
||||
gam oauth create
|
||||
Enable the following and proceed to authorization.
|
||||
|
||||
[*] 42) Drive API - commanddata_clientaccess
|
||||
[*] 54) Sheets API - commanddata_clientaccess
|
||||
```
|
||||
|
||||
Fixed in bug in `gam report` that caused a trap with either of the `thismonth` or `previousmonths` options were used.
|
||||
|
||||
Upgraded to Python 3.14.0.
|
||||
|
||||
7.24.01
|
||||
|
||||
Updated GAM to handle the following error that occurs when GAM tries to authenticate
|
||||
as a user that has been disabled by Google.
|
||||
```
|
||||
ERROR: Authentication Token Error - invalid_account: Forbidden
|
||||
```
|
||||
|
||||
7.24.00
|
||||
|
||||
If you want to disable a user's service account access to Drive and Sheets but still allow reading command data from Google Docs and Sheets,
|
||||
issue the following command and make these settings:
|
||||
```
|
||||
gam user user@domain.com update serviceaccount
|
||||
|
||||
[ ] 20) Drive API (supports readonly)
|
||||
[*] 21) Drive API - read command data
|
||||
[ ] 42) Sheets API (supports readonly)
|
||||
[*] 43) Sheets API - read command data
|
||||
```
|
||||
|
||||
7.23.07
|
||||
|
||||
Fixed bug in `gam print|show admins` where all admin assignments were not displayed when
|
||||
`types <AdminAssigneeTypeList>` was not specified, i.e., all assignments should be displayed.
|
||||
|
||||
7.23.06
|
||||
|
||||
Added option `types <AdminAssigneeTypeList>` to `gam print|show admins` that allows filtering
|
||||
of admin assignments by the type of the assignee; by default, all assignee types are displayed.
|
||||
```
|
||||
<AdminAssigneeType> ::= group|user|serviceaccount|unknown
|
||||
<AdminAssigneeTypeList> ::= "<AdminAssigneeType>(,<AdminAssigneeType>)*"
|
||||
```
|
||||
|
||||
7.23.05
|
||||
|
||||
Added option `recursive` that will display assignments to the members
|
||||
of security groups assigned to roles; the security group membership is recursively expanded.
|
||||
|
||||
7.23.04
|
||||
|
||||
Added option `addcsvdata <FieldName> <String>` to `gam <UserTypeEntity> print events`
|
||||
and `gam calendars <CalendarEntity> print events` that adds additional columns of data to the CSV file output.
|
||||
An example would be to get the calendar name in addition to the calendar ID when printing events.
|
||||
```
|
||||
gam redirect csv ./Resources.csv print resources fields email,name
|
||||
gam redirect csv ./ResourceEventCounts.csv multiprocess redirect stderr - multiprocess csv Resources.csv gam calendar "~resourceEmail" print events starttime -1y countsonly addcsvdata calendarName "~resourceName"
|
||||
```
|
||||
|
||||
Upgraded to OpenSSL 3.6.0.
|
||||
|
||||
7.23.03
|
||||
|
||||
Upgraded to OpenSSL 3.5.4.
|
||||
|
||||
7.23.02
|
||||
|
||||
Added option `oneitemperrow` to 'gam print course-materials|course-work` to have each of a
|
||||
course's materials displayed on a separate row with all of the other course fields.
|
||||
This produces a CSV file that can be used in subsequent commands to process the materials without further script processing.
|
||||
|
||||
7.23.00
|
||||
|
||||
Added `chat_max_results` variable to `gam.cfg`.
|
||||
```
|
||||
chat_max_results
|
||||
When retrieving lists of Chat items from API,
|
||||
how many should be retrieved in each API call
|
||||
Default: 100
|
||||
Range: 1 - 1000
|
||||
```
|
||||
Previously, this vaule was always set to 1000 which could cause errors.
|
||||
|
||||
7.22.07
|
||||
|
||||
Added options `showdetails` and `returnidonly` to `gam create|copy vaultquery`.
|
||||
|
||||
Added option `<JSONData>` to `gam create vaultexport|vaultquery` and `gam print vaultcounts`.
|
||||
|
||||
7.22.06
|
||||
|
||||
Added commands to create, copy and delete Vault saved queries.
|
||||
```
|
||||
gam create vaultquery <MatterItem> [name <String>]
|
||||
corpus calendar|drive|gemini|groups|hangouts_chat|mail|voice
|
||||
[scope all_data|held_data|unprocessed_data]
|
||||
(accounts <EmailAddressEntity>) | (orgunit|org|ou <OrgUnitPath>) | everyone
|
||||
(documentids (<DriveFileIDList>|(select <FileSelector>|<CSVFileSelector>))) |
|
||||
(shareddrives|teamdrives (<SharedDriveIDList>|(select <FileSelector>|<CSVFileSelector>))) |
|
||||
[(includeshareddrives <Boolean>)|(shareddrivesoption included|included_if_account_is_not_a_member|not_included)]
|
||||
(sitesurl (<URLList>||(select <FileSelector>|<CSVFileSelector>)))
|
||||
[driveversiondate <Date>|<Time>]
|
||||
[includerooms <Boolean>]
|
||||
(rooms (<ChatSpaceList>|(select <FileSelector>|<CSVFileSelector>))) |
|
||||
[terms <String>] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>] [timezone <TimeZone>]
|
||||
[locationquery <StringList>] [peoplequery <StringList>] [minuswords <StringList>]
|
||||
[responsestatuses <AttendeeStatus>(,<AttendeeStatus>)*] [calendarversiondate <Date>|<Time>]
|
||||
(covereddata calllogs|textmessages|voicemails)*
|
||||
[shownames] [formatjson]
|
||||
|
||||
gam copy vaultquery <MatterItem> <QueryItem> [targetmatter <MatterItem>] [name <String>]
|
||||
[shownames] [formatjson]
|
||||
|
||||
gam delete vaultquery <QueryItem> matter <MatterItem>
|
||||
gam delete vaultquery <MatterItem> <QueryItem>
|
||||
```
|
||||
|
||||
Added a variant of `gam print vaultcounts` that gets its query parameters from a saved Vault query.
|
||||
```
|
||||
gam print vaultcounts [todrive <ToDriveAttributes>*]
|
||||
matter <MatterItem> <QueryItem>
|
||||
[wait <Integer>]
|
||||
```
|
||||
|
||||
7.22.05
|
||||
|
||||
Added a variant of `gam create vaultexport` that gets its query parameters from a saved Vault query.
|
||||
|
||||
```
|
||||
gam create vaultexport|export matter <MatterItem> [name <String>]
|
||||
vaultquery <QueryItem>
|
||||
[driveclientsideencryption any|encrypted|unencrypted]
|
||||
[includeaccessinfo <Boolean>]
|
||||
[excludedrafts <Boolean>] [mailclientsideencryption any|encrypted|unencrypted]
|
||||
[showconfidentialmodecontent <Boolean>] [usenewexport <Boolean>] [exportlinkeddrivefiles <Boolean>]
|
||||
[format ics|mbox|pst|xml]
|
||||
[region any|europe|us] [showdetails|returnidonly]
|
||||
```
|
||||
|
||||
7.22.04
|
||||
|
||||
Added a variant of `gam create vaulthold` that gets its parameters from a saved Vault query.
|
||||
```
|
||||
gam create vaulthold matter <MatterItem> [name <String>]
|
||||
vaultquery <QueryItem>
|
||||
[showdetails|returnidonly]
|
||||
```
|
||||
|
||||
7.22.03
|
||||
|
||||
Fix backwards compatability bug introduced in 7.22.00 for `gam print users` that changed `suspended`
|
||||
from a field name to a query option; it is now correctly interpreted as a field name.
|
||||
|
||||
7.22.02
|
||||
|
||||
An update to the httplib2 library caused GAM proxy connections to fail; this has been fixed
|
||||
by including the pysocks library needed by the latest httplib2 library.
|
||||
|
||||
7.22.00
|
||||
|
||||
Expanded `<UserTypeEntity>` to allow specification of non-archived/archived users.
|
||||
* See [Collections of Users](Collections-of-Users)
|
||||
|
||||
These commands have been updated:
|
||||
* `gam print aliases`
|
||||
* `gam update groups`
|
||||
* `gam info orgs`
|
||||
* `gam print orgs`
|
||||
* `gam print users`
|
||||
|
||||
Added `datetime <DateTimeFormat>` command that can be embedded in Gam batch files. The current time is formatted with `<DateTimeFormat>`
|
||||
and subsequent lines in `<BatchContent>` will have `%datetime%` replaced with the formatted time value.
|
||||
|
||||
See: https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes
|
||||
|
||||
7.21.03
|
||||
|
||||
Added option `notifyrecoveryemail` to `gam create user` and `gam <UserTypeEntity> update user password <String>`
|
||||
that sends the passsword notification email to the user's recovery email address (if defined).
|
||||
|
||||
7.21.02
|
||||
|
||||
GAM now builds on macOS 26 Tahoe and properly identifies the OS.
|
||||
|
||||
A custom build of the cryptography library is no longer needed for Windows arm64 builds as the project now releases their own build for the OS.
|
||||
|
||||
Upgraded to OpenSSL 3.5.3.
|
||||
|
||||
7.21.01
|
||||
|
||||
Replaced datetime, dateutil, calendar and iso8601 Python libraries with arrow library.
|
||||
This should have no performance impact; report any problems.
|
||||
|
||||
You can now use timezone names when setting `timezone` in `gam.cfg`.
|
||||
* See: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
||||
```
|
||||
gam config timezone America/Los_Angeles save
|
||||
```
|
||||
|
||||
7.20.04
|
||||
|
||||
Cleaned up Python library imports: googleapiclient, iso8601.
|
||||
|
||||
7.20.03
|
||||
|
||||
Rebranded license SKU `1010470004` from `Gemini Education` to `Google AI Pro for Education`.
|
||||
|
||||
Additional updates to student groups in Google Classroom.
|
||||
|
||||
7.20.02
|
||||
|
||||
Upgraded `gam create course-studentgroups` to allow specification of multiple student group titles;
|
||||
multiple student groups can be created in a single command.
|
||||
* `((title <String>)|(select <StringEntity))+`
|
||||
|
||||
7.20.01
|
||||
|
||||
Added option `showaccesssettings` to `gam [<UserTypeEntity>] print|show chatspaces`. When listing
|
||||
Chat Spaces, the Chat API does not return the `accessSettings` field; if you need to see this field,
|
||||
add `showaccesssettings` to the command. This requires an additional Chat API call per chat space of type `SPACE`
|
||||
to get the `accessSettings` field.
|
||||
|
||||
7.20.00
|
||||
|
||||
Added initial support for student groups in Google Classroom. This is a work in progress and requires Developer Preview membership.
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Classroom-StudentGroups#notes
|
||||
|
||||
7.19.03
|
||||
|
||||
Fixed bug where specifying a `<UserItem>` as `id:1234567890` could lead to errors like this:
|
||||
@ -16969,7 +17648,7 @@ Current version of Gam with drive_v3_name_names = false
|
||||
Owner,title,parents,parents.0.id,parents.1.id
|
||||
testuser@domain.com,TestFile,2,PPPP1111,PPPP2222
|
||||
|
||||
Current version of Gam with drive_v3_name_names = true
|
||||
Current version of Gam with drive_v3_name_names = true
|
||||
Owner,name,parents
|
||||
testuser@domain.com,TestFile,PPPP1111 PPPP2222
|
||||
|
||||
|
||||
747
src/cacerts.pem
747
src/cacerts.pem
@ -1,747 +0,0 @@
|
||||
# Operating CA: DigiCert
|
||||
# Issuer: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com
|
||||
# Subject: CN=DigiCert Assured ID Root CA O=DigiCert Inc OU=www.digicert.com
|
||||
# Label: "DigiCert Assured ID Root CA"
|
||||
# Serial: 17154717934120587862167794914071425081
|
||||
# MD5 Fingerprint: 87:ce:0b:7b:2a:0e:49:00:e1:58:71:9b:37:a8:93:72
|
||||
# SHA1 Fingerprint: 05:63:b8:63:0d:62:d7:5a:bb:c8:ab:1e:4b:df:b5:a8:99:b2:4d:43
|
||||
# SHA256 Fingerprint: 3e:90:99:b5:01:5e:8f:48:6c:00:bc:ea:9d:11:1e:e7:21:fa:ba:35:5a:89:bc:f1:df:69:56:1e:3d:c6:32:5c
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||
d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
|
||||
b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG
|
||||
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
|
||||
cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi
|
||||
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c
|
||||
JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP
|
||||
mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+
|
||||
wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4
|
||||
VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/
|
||||
AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB
|
||||
AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
|
||||
BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun
|
||||
pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC
|
||||
dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf
|
||||
fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm
|
||||
NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx
|
||||
H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe
|
||||
+o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Operating CA: DigiCert
|
||||
# Issuer: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com
|
||||
# Subject: CN=DigiCert Assured ID Root G2 O=DigiCert Inc OU=www.digicert.com
|
||||
# Label: "DigiCert Assured ID Root G2"
|
||||
# Serial: 15385348160840213938643033620894905419
|
||||
# MD5 Fingerprint: 92:38:b9:f8:63:24:82:65:2c:57:33:e6:fe:81:8f:9d
|
||||
# SHA1 Fingerprint: a1:4b:48:d9:43:ee:0a:0e:40:90:4f:3c:e0:a4:c0:91:93:51:5d:3f
|
||||
# SHA256 Fingerprint: 7d:05:eb:b6:82:33:9f:8c:94:51:ee:09:4e:eb:fe:fa:79:53:a1:14:ed:b2:f4:49:49:45:2f:ab:7d:2f:c1:85
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||
d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv
|
||||
b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG
|
||||
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
|
||||
cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi
|
||||
MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA
|
||||
n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc
|
||||
biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp
|
||||
EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA
|
||||
bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu
|
||||
YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB
|
||||
AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW
|
||||
BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI
|
||||
QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I
|
||||
0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni
|
||||
lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9
|
||||
B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv
|
||||
ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo
|
||||
IhNzbM8m9Yop5w==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Operating CA: DigiCert
|
||||
# Issuer: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com
|
||||
# Subject: CN=DigiCert Assured ID Root G3 O=DigiCert Inc OU=www.digicert.com
|
||||
# Label: "DigiCert Assured ID Root G3"
|
||||
# Serial: 15459312981008553731928384953135426796
|
||||
# MD5 Fingerprint: 7c:7f:65:31:0c:81:df:8d:ba:3e:99:e2:5c:ad:6e:fb
|
||||
# SHA1 Fingerprint: f5:17:a2:4f:9a:48:c6:c9:f8:a2:00:26:9f:dc:0f:48:2c:ab:30:89
|
||||
# SHA256 Fingerprint: 7e:37:cb:8b:4c:47:09:0c:ab:36:55:1b:a6:f4:5d:b8:40:68:0f:ba:16:6a:95:2d:b1:00:71:7f:43:05:3f:c2
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw
|
||||
CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
|
||||
ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg
|
||||
RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV
|
||||
UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu
|
||||
Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq
|
||||
hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf
|
||||
Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q
|
||||
RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/
|
||||
BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD
|
||||
AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY
|
||||
JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv
|
||||
6pZjamVFkpUBtA==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Operating CA: DigiCert
|
||||
# Issuer: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com
|
||||
# Subject: CN=DigiCert Global Root CA O=DigiCert Inc OU=www.digicert.com
|
||||
# Label: "DigiCert Global Root CA"
|
||||
# Serial: 10944719598952040374951832963794454346
|
||||
# MD5 Fingerprint: 79:e4:a9:84:0d:7d:3a:96:d7:c0:4f:e2:43:4c:89:2e
|
||||
# SHA1 Fingerprint: a8:98:5d:3a:65:e5:e5:c4:b2:d7:d6:6d:40:c6:dd:2f:b1:9c:54:36
|
||||
# SHA256 Fingerprint: 43:48:a0:e9:44:4c:78:cb:26:5e:05:8d:5e:89:44:b4:d8:4f:96:62:bd:26:db:25:7f:89:34:a4:43:c7:01:61
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
|
||||
QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
|
||||
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
|
||||
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
|
||||
9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
|
||||
CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
|
||||
nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
|
||||
43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
|
||||
T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
|
||||
gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
|
||||
BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
|
||||
TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
|
||||
DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
|
||||
hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
|
||||
06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
|
||||
PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
|
||||
YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
|
||||
CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Operating CA: DigiCert
|
||||
# Issuer: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com
|
||||
# Subject: CN=DigiCert Global Root G2 O=DigiCert Inc OU=www.digicert.com
|
||||
# Label: "DigiCert Global Root G2"
|
||||
# Serial: 4293743540046975378534879503202253541
|
||||
# MD5 Fingerprint: e4:a6:8a:c8:54:ac:52:42:46:0a:fd:72:48:1b:2a:44
|
||||
# SHA1 Fingerprint: df:3c:24:f9:bf:d6:66:76:1b:26:80:73:fe:06:d1:cc:8d:4f:82:a4
|
||||
# SHA256 Fingerprint: cb:3c:cb:b7:60:31:e5:e0:13:8f:8d:d3:9a:23:f9:de:47:ff:c3:5e:43:c1:14:4c:ea:27:d4:6a:5a:b1:cb:5f
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
|
||||
MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT
|
||||
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
|
||||
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG
|
||||
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI
|
||||
2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx
|
||||
1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ
|
||||
q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz
|
||||
tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ
|
||||
vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP
|
||||
BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV
|
||||
5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY
|
||||
1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4
|
||||
NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG
|
||||
Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91
|
||||
8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe
|
||||
pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl
|
||||
MrY=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Operating CA: DigiCert
|
||||
# Issuer: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com
|
||||
# Subject: CN=DigiCert Global Root G3 O=DigiCert Inc OU=www.digicert.com
|
||||
# Label: "DigiCert Global Root G3"
|
||||
# Serial: 7089244469030293291760083333884364146
|
||||
# MD5 Fingerprint: f5:5d:a4:50:a5:fb:28:7e:1e:0f:0d:cc:96:57:56:ca
|
||||
# SHA1 Fingerprint: 7e:04:de:89:6a:3e:66:6d:00:e6:87:d3:3f:fa:d9:3b:e8:3d:34:9e
|
||||
# SHA256 Fingerprint: 31:ad:66:48:f8:10:41:38:c7:38:f3:9e:a4:32:01:33:39:3e:3a:18:cc:02:29:6e:f9:7c:2a:c9:ef:67:31:d0
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw
|
||||
CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
|
||||
ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe
|
||||
Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw
|
||||
EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x
|
||||
IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF
|
||||
K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG
|
||||
fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO
|
||||
Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd
|
||||
BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx
|
||||
AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/
|
||||
oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8
|
||||
sycX
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Operating CA: DigiCert
|
||||
# Issuer: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com
|
||||
# Subject: CN=DigiCert High Assurance EV Root CA O=DigiCert Inc OU=www.digicert.com
|
||||
# Label: "DigiCert High Assurance EV Root CA"
|
||||
# Serial: 3553400076410547919724730734378100087
|
||||
# MD5 Fingerprint: d4:74:de:57:5c:39:b2:d3:9c:85:83:c5:c0:65:49:8a
|
||||
# SHA1 Fingerprint: 5f:b7:ee:06:33:e2:59:db:ad:0c:4c:9a:e6:d3:8f:1a:61:c7:dc:25
|
||||
# SHA256 Fingerprint: 74:31:e5:f4:c3:c1:ce:46:90:77:4f:0b:61:e0:54:40:88:3b:a9:a0:1e:d0:0b:a6:ab:d7:80:6e:d3:b1:18:cf
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
|
||||
ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
|
||||
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
|
||||
LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
|
||||
RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
|
||||
+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
|
||||
PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
|
||||
xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
|
||||
Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
|
||||
hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
|
||||
EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
|
||||
MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
|
||||
FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
|
||||
nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
|
||||
eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
|
||||
hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
|
||||
Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
|
||||
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
|
||||
+OkuE6N36B9K
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Operating CA: DigiCert
|
||||
# Issuer: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com
|
||||
# Subject: CN=DigiCert Trusted Root G4 O=DigiCert Inc OU=www.digicert.com
|
||||
# Label: "DigiCert Trusted Root G4"
|
||||
# Serial: 7451500558977370777930084869016614236
|
||||
# MD5 Fingerprint: 78:f2:fc:aa:60:1f:2f:b4:eb:c9:37:ba:53:2e:75:49
|
||||
# SHA1 Fingerprint: dd:fb:16:cd:49:31:c9:73:a2:03:7d:3f:c8:3a:4d:7d:77:5d:05:e4
|
||||
# SHA256 Fingerprint: 55:2f:7b:dc:f1:a7:af:9e:6c:e6:72:01:7f:4f:12:ab:f7:72:40:c7:8e:76:1a:c2:03:d1:d9:d2:0a:c8:99:88
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||
d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg
|
||||
RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV
|
||||
UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu
|
||||
Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG
|
||||
SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y
|
||||
ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If
|
||||
xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV
|
||||
ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO
|
||||
DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ
|
||||
jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/
|
||||
CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi
|
||||
EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM
|
||||
fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY
|
||||
uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK
|
||||
chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t
|
||||
9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB
|
||||
hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD
|
||||
ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2
|
||||
SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd
|
||||
+SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc
|
||||
fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa
|
||||
sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N
|
||||
cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N
|
||||
0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie
|
||||
4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI
|
||||
r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1
|
||||
/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm
|
||||
gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Operating CA: GlobalSign
|
||||
# Issuer: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA
|
||||
# Subject: CN=GlobalSign Root CA O=GlobalSign nv-sa OU=Root CA
|
||||
# Label: "GlobalSign Root CA"
|
||||
# Serial: 4835703278459707669005204
|
||||
# MD5 Fingerprint: 3e:45:52:15:09:51:92:e1:b7:5d:37:9f:b1:87:29:8a
|
||||
# SHA1 Fingerprint: b1:bc:96:8b:d4:f4:9d:62:2a:a8:9a:81:f2:15:01:52:a4:1d:82:9c
|
||||
# SHA256 Fingerprint: eb:d4:10:40:e4:bb:3e:c7:42:c9:e3:81:d3:1e:f2:a4:1a:48:b6:68:5c:96:e7:ce:f3:c1:df:6c:d4:33:1c:99
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG
|
||||
A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv
|
||||
b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw
|
||||
MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i
|
||||
YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT
|
||||
aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ
|
||||
jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp
|
||||
xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp
|
||||
1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG
|
||||
snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ
|
||||
U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8
|
||||
9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E
|
||||
BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B
|
||||
AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz
|
||||
yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE
|
||||
38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP
|
||||
AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
|
||||
DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
|
||||
HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Operating CA: GlobalSign
|
||||
# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3
|
||||
# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R3
|
||||
# Label: "GlobalSign Root CA - R3"
|
||||
# Serial: 4835703278459759426209954
|
||||
# MD5 Fingerprint: c5:df:b8:49:ca:05:13:55:ee:2d:ba:1a:c3:3e:b0:28
|
||||
# SHA1 Fingerprint: d6:9b:56:11:48:f0:1c:77:c5:45:78:c1:09:26:df:5b:85:69:76:ad
|
||||
# SHA256 Fingerprint: cb:b5:22:d7:b7:f1:27:ad:6a:01:13:86:5b:df:1c:d4:10:2e:7d:07:59:af:63:5a:7c:f4:72:0d:c9:63:c5:3b
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
|
||||
A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
|
||||
Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
|
||||
MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
|
||||
A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
|
||||
hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
|
||||
RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
|
||||
gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
|
||||
KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
|
||||
QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
|
||||
XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
|
||||
DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
|
||||
LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
|
||||
RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
|
||||
jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
|
||||
6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
|
||||
mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
|
||||
Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
|
||||
WD9f
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Operating CA: GlobalSign
|
||||
# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5
|
||||
# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign ECC Root CA - R5
|
||||
# Label: "GlobalSign ECC Root CA - R5"
|
||||
# Serial: 32785792099990507226680698011560947931244
|
||||
# MD5 Fingerprint: 9f:ad:3b:1c:02:1e:8a:ba:17:74:38:81:0c:a2:bc:08
|
||||
# SHA1 Fingerprint: 1f:24:c6:30:cd:a4:18:ef:20:69:ff:ad:4f:dd:5f:46:3a:1b:69:aa
|
||||
# SHA256 Fingerprint: 17:9f:bc:14:8a:3d:d0:0f:d2:4e:a1:34:58:cc:43:bf:a7:f5:9c:81:82:d7:83:a5:13:f6:eb:ec:10:0c:89:24
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk
|
||||
MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH
|
||||
bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX
|
||||
DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD
|
||||
QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu
|
||||
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc
|
||||
8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke
|
||||
hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD
|
||||
VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI
|
||||
KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg
|
||||
515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO
|
||||
xwy8p2Fp8fc74SrL+SvzZpA3
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Operating CA: GlobalSign
|
||||
# Issuer: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6
|
||||
# Subject: CN=GlobalSign O=GlobalSign OU=GlobalSign Root CA - R6
|
||||
# Label: "GlobalSign Root CA - R6"
|
||||
# Serial: 1417766617973444989252670301619537
|
||||
# MD5 Fingerprint: 4f:dd:07:e4:d4:22:64:39:1e:0c:37:42:ea:d1:c6:ae
|
||||
# SHA1 Fingerprint: 80:94:64:0e:b5:a7:a1:ca:11:9c:1f:dd:d5:9f:81:02:63:a7:fb:d1
|
||||
# SHA256 Fingerprint: 2c:ab:ea:fe:37:d0:6c:a2:2a:ba:73:91:c0:03:3d:25:98:29:52:c4:53:64:73:49:76:3a:3a:b5:ad:6c:cf:69
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEg
|
||||
MB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2Jh
|
||||
bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQx
|
||||
MjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSNjET
|
||||
MBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCAiIwDQYJ
|
||||
KoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQssgrRI
|
||||
xutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1k
|
||||
ZguSgMpE3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxD
|
||||
aNc9PIrFsmbVkJq3MQbFvuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJw
|
||||
LnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqMPKq0pPbzlUoSB239jLKJz9CgYXfIWHSw
|
||||
1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+azayOeSsJDa38O+2HBNX
|
||||
k7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05OWgtH8wY2
|
||||
SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/h
|
||||
bguyCLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4n
|
||||
WUx2OVvq+aWh2IMP0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpY
|
||||
rZxCRXluDocZXFSxZba/jJvcE+kNb7gu3GduyYsRtYQUigAZcIN5kZeR1Bonvzce
|
||||
MgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD
|
||||
AQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNVHSMEGDAWgBSu
|
||||
bAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN
|
||||
nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGt
|
||||
Ixg93eFyRJa0lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr61
|
||||
55wsTLxDKZmOMNOsIeDjHfrYBzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLj
|
||||
vUYAGm0CuiVdjaExUd1URhxN25mW7xocBFymFe944Hn+Xds+qkxV/ZoVqW/hpvvf
|
||||
cDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr3TsTjxKM4kEaSHpz
|
||||
oHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB10jZp
|
||||
nOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfs
|
||||
pA9MRf/TuTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+v
|
||||
JJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R
|
||||
8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDfLRVpOoERIyNiwmcUVhAn21klJwGW4
|
||||
5hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Note: "GlobalSign Root CA - R7" not added on purpose. It is P-521.
|
||||
|
||||
# Operating CA: GoDaddy
|
||||
# Issuer: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc.
|
||||
# Subject: CN=Go Daddy Root Certificate Authority - G2 O=GoDaddy.com, Inc.
|
||||
# Label: "Go Daddy Root Certificate Authority - G2"
|
||||
# Serial: 0
|
||||
# MD5 Fingerprint: 80:3a:bc:22:c1:e6:fb:8d:9b:3b:27:4a:32:1b:9a:01
|
||||
# SHA1 Fingerprint: 47:be:ab:c9:22:ea:e8:0e:78:78:34:62:a7:9f:45:c2:54:fd:e6:8b
|
||||
# SHA256 Fingerprint: 45:14:0b:32:47:eb:9c:c8:c5:b4:f0:d7:b5:30:91:f7:32:92:08:9e:6e:5a:63:e2:74:9d:d3:ac:a9:19:8e:da
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx
|
||||
EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT
|
||||
EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp
|
||||
ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz
|
||||
NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH
|
||||
EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE
|
||||
AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw
|
||||
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD
|
||||
E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH
|
||||
/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy
|
||||
DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh
|
||||
GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR
|
||||
tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA
|
||||
AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE
|
||||
FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX
|
||||
WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu
|
||||
9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr
|
||||
gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo
|
||||
2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO
|
||||
LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI
|
||||
4uJEvlz36hz1
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Operating CA: GoDaddy
|
||||
# Issuer: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc.
|
||||
# Subject: CN=Starfield Root Certificate Authority - G2 O=Starfield Technologies, Inc.
|
||||
# Label: "Starfield Root Certificate Authority - G2"
|
||||
# Serial: 0
|
||||
# MD5 Fingerprint: d6:39:81:c6:52:7e:96:69:fc:fc:ca:66:ed:05:f2:96
|
||||
# SHA1 Fingerprint: b5:1c:06:7c:ee:2b:0c:3d:f8:55:ab:2d:92:f4:fe:39:d4:e7:0f:0e
|
||||
# SHA256 Fingerprint: 2c:e1:cb:0b:f9:d2:f9:e1:02:99:3f:be:21:51:52:c3:b2:dd:0c:ab:de:1c:68:e5:31:9b:83:91:54:db:b7:f5
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx
|
||||
EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT
|
||||
HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs
|
||||
ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw
|
||||
MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6
|
||||
b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj
|
||||
aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp
|
||||
Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
|
||||
ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg
|
||||
nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1
|
||||
HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N
|
||||
Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN
|
||||
dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0
|
||||
HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO
|
||||
BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G
|
||||
CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU
|
||||
sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3
|
||||
4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg
|
||||
8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K
|
||||
pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1
|
||||
mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Operating CA: Sectigo
|
||||
# Issuer: CN=COMODO Certification Authority O=COMODO CA Limited
|
||||
# Subject: CN=COMODO Certification Authority O=COMODO CA Limited
|
||||
# Label: "COMODO Certification Authority"
|
||||
# Serial: 43390818032842818540635488309124489234
|
||||
# MD5 Fingerprint: 20:E7:4F:82:C2:7E:94:80:34:82:8A:13:A9:17:1D:97
|
||||
# SHA1 Fingerprint EE:86:93:87:FF:FD:83:49:AB:5A:D1:43:22:58:87:89:A4:57:B0:12
|
||||
# SHA256 Fingerprint: 1A:0D:20:44:5D:E5:BA:18:62:D1:9E:F8:80:85:8C:BC:E5:01:02:B3:6E:8F:0A:04:0C:3C:69:E7:45:22:FE:6E
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID0DCCArigAwIBAgIQIKTEf93f4cdTYwcTiHdgEjANBgkqhkiG9w0BAQUFADCB
|
||||
gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
|
||||
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV
|
||||
BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xMTAxMDEwMDAw
|
||||
MDBaFw0zMDEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl
|
||||
YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P
|
||||
RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0
|
||||
aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3
|
||||
UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI
|
||||
2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8
|
||||
Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp
|
||||
+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+
|
||||
DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O
|
||||
nKVIrLsm9wIDAQABo0IwQDAdBgNVHQ4EFgQUC1jli8ZMFTekQKkwqSG+RzZaVv8w
|
||||
DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD
|
||||
ggEBAC/JxBwHO89hAgCx2SFRdXIDMLDEFh9sAIsQrK/xR9SuEDwMGvjUk2ysEDd8
|
||||
t6aDZK3N3w6HM503sMZ7OHKx8xoOo/lVem0DZgMXlUrxsXrfViEGQo+x06iF3u6X
|
||||
HWLrp+cxEmbDD6ZLLkGC9/3JG6gbr+48zuOcrigHoSybJMIPIyaDMouGDx8rEkYl
|
||||
Fo92kANr3ryqImhrjKGsKxE5pttwwn1y6TPn/CbxdFqR5p2ErPioBhlG5qfpqjQi
|
||||
pKGfeq23sqSaM4hxAjwu1nqyH6LKwN0vEJT9s4yEIHlG1QXUEOTS22RPuFvuG8Ug
|
||||
R1uUq27UlTMdphVx8fiUylQ5PsE=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Operating CA: Sectigo
|
||||
# Issuer: CN=COMODO ECC Certification Authority O=COMODO CA Limited
|
||||
# Subject: CN=COMODO ECC Certification Authority O=COMODO CA Limited
|
||||
# Label: "COMODO ECC Certification Authority"
|
||||
# Serial: 41578283867086692638256921589707938090
|
||||
# MD5 Fingerprint: 7c:62:ff:74:9d:31:53:5e:68:4a:d5:78:aa:1e:bf:23
|
||||
# SHA1 Fingerprint: 9f:74:4e:9f:2b:4d:ba:ec:0f:31:2c:50:b6:56:3b:8e:2d:93:c3:11
|
||||
# SHA256 Fingerprint: 17:93:92:7a:06:14:54:97:89:ad:ce:2f:8f:34:f7:f0:b6:6d:0f:3a:e3:a3:b8:4d:21:ec:15:db:ba:4f:ad:c7
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL
|
||||
MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE
|
||||
BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT
|
||||
IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw
|
||||
MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy
|
||||
ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N
|
||||
T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv
|
||||
biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR
|
||||
FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J
|
||||
cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW
|
||||
BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/
|
||||
BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm
|
||||
fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv
|
||||
GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Operating CA: Sectigo
|
||||
# Issuer: CN=COMODO RSA Certification Authority O=COMODO CA Limited
|
||||
# Subject: CN=COMODO RSA Certification Authority O=COMODO CA Limited
|
||||
# Label: "COMODO RSA Certification Authority"
|
||||
# Serial: 101909084537582093308941363524873193117
|
||||
# MD5 Fingerprint: 1b:31:b0:71:40:36:cc:14:36:91:ad:c4:3e:fd:ec:18
|
||||
# SHA1 Fingerprint: af:e5:d2:44:a8:d1:19:42:30:ff:47:9f:e2:f8:97:bb:cd:7a:8c:b4
|
||||
# SHA256 Fingerprint: 52:f0:e1:c4:e5:8e:c6:29:29:1b:60:31:7f:07:46:71:b8:5d:7e:a8:0d:5b:07:27:34:63:53:4b:32:b4:02:34
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB
|
||||
hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
|
||||
A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV
|
||||
BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5
|
||||
MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT
|
||||
EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
|
||||
Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh
|
||||
dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR
|
||||
6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X
|
||||
pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC
|
||||
9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV
|
||||
/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf
|
||||
Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z
|
||||
+pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w
|
||||
qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah
|
||||
SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC
|
||||
u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf
|
||||
Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq
|
||||
crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E
|
||||
FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB
|
||||
/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl
|
||||
wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM
|
||||
4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV
|
||||
2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna
|
||||
FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ
|
||||
CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK
|
||||
boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke
|
||||
jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL
|
||||
S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb
|
||||
QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl
|
||||
0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB
|
||||
NVOFBkpdn627G190
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Operating CA: Sectigo
|
||||
# Issuer: CN=USERTrust ECC Certification Authority O=The USERTRUST Network
|
||||
# Subject: CN=USERTrust ECC Certification Authority O=The USERTRUST Network
|
||||
# Label: "USERTrust ECC Certification Authority"
|
||||
# Serial: 123013823720199481456569720443997572134
|
||||
# MD5 Fingerprint: fa:68:bc:d9:b5:7f:ad:fd:c9:1d:06:83:28:cc:24:c1
|
||||
# SHA1 Fingerprint: d1:cb:ca:5d:b2:d5:2a:7f:69:3b:67:4d:e5:f0:5a:1d:0c:95:7d:f0
|
||||
# SHA256 Fingerprint: 4f:f4:60:d5:4b:9c:86:da:bf:bc:fc:57:12:e0:40:0d:2b:ed:3f:bc:4d:4f:bd:aa:86:e0:6a:dc:d2:a9:ad:7a
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL
|
||||
MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl
|
||||
eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT
|
||||
JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx
|
||||
MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
|
||||
Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg
|
||||
VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm
|
||||
aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo
|
||||
I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng
|
||||
o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G
|
||||
A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD
|
||||
VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB
|
||||
zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW
|
||||
RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg=
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Operating CA: Sectigo
|
||||
# Issuer: CN=USERTrust RSA Certification Authority O=The USERTRUST Network
|
||||
# Subject: CN=USERTrust RSA Certification Authority O=The USERTRUST Network
|
||||
# Label: "USERTrust RSA Certification Authority"
|
||||
# Serial: 2645093764781058787591871645665788717
|
||||
# MD5 Fingerprint: 1b:fe:69:d1:91:b7:19:33:a3:72:a8:0f:e1:55:e5:b5
|
||||
# SHA1 Fingerprint: 2b:8f:1b:57:33:0d:bb:a2:d0:7a:6c:51:f7:0e:e9:0d:da:b9:ad:8e
|
||||
# SHA256 Fingerprint: e7:93:c9:b0:2f:d8:aa:13:e2:1c:31:22:8a:cc:b0:81:19:64:3b:74:9c:89:89:64:b1:74:6d:46:c3:d4:cb:d2
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB
|
||||
iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
|
||||
cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV
|
||||
BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw
|
||||
MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV
|
||||
BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU
|
||||
aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy
|
||||
dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK
|
||||
AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B
|
||||
3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY
|
||||
tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/
|
||||
Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2
|
||||
VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT
|
||||
79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6
|
||||
c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT
|
||||
Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l
|
||||
c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee
|
||||
UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE
|
||||
Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd
|
||||
BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G
|
||||
A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF
|
||||
Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO
|
||||
VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3
|
||||
ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs
|
||||
8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR
|
||||
iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze
|
||||
Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ
|
||||
XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/
|
||||
qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB
|
||||
VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB
|
||||
L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG
|
||||
jjxDah2nGN59PRbxYvnKkKj9
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Operating CA: Google Trust Services LLC
|
||||
# Subject: C = US, O = Google Trust Services LLC, CN = GTS Root R1
|
||||
# Issuer: C = US, O = Google Trust Services LLC, CN = GTS Root R1
|
||||
# Label: "GTS Root R1"
|
||||
# Serial: 0203E5936F31B01349886BA217
|
||||
# MD5 Fingerprint: 05:FE:D0:BF:71:A8:A3:76:63:DA:01:E0:D8:52:DC:40
|
||||
# SHA1 Fingerprint: E5:8C:1C:C4:91:3B:38:63:4B:E9:10:6E:E3:AD:8E:6B:9D:D9:81:4A
|
||||
# SHA256 Fingerprint: D9:47:43:2A:BD:E7:B7:FA:90:FC:2E:6B:59:10:1B:12:80:E0:E1:C7:E4:E4:0F:A3:C6:88:7F:FF:57:A7:F4:CF
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw
|
||||
CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
|
||||
MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
|
||||
MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
|
||||
Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA
|
||||
A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo
|
||||
27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w
|
||||
Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw
|
||||
TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl
|
||||
qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH
|
||||
szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8
|
||||
Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk
|
||||
MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92
|
||||
wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p
|
||||
aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN
|
||||
VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID
|
||||
AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
|
||||
FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb
|
||||
C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe
|
||||
QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy
|
||||
h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4
|
||||
7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J
|
||||
ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef
|
||||
MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/
|
||||
Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT
|
||||
6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ
|
||||
0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm
|
||||
2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb
|
||||
bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Operating CA: Google Trust Services LLC
|
||||
# Subject: C = US, O = Google Trust Services LLC, CN = GTS Root R2
|
||||
# Issuer: C = US, O = Google Trust Services LLC, CN = GTS Root R2
|
||||
# Label: "GTS Root R2"
|
||||
# Serial: 0203E5AEC58D04251AAB1125AA
|
||||
# MD5 Fingerprint=1E:39:C0:53:E6:1E:29:82:0B:CA:52:55:36:5D:57:DC
|
||||
# SHA1 Fingerprint=9A:44:49:76:32:DB:DE:FA:D0:BC:FB:5A:7B:17:BD:9E:56:09:24:94
|
||||
# SHA256 Fingerprint=8D:25:CD:97:22:9D:BF:70:35:6B:DA:4E:B3:CC:73:40:31:E2:4C:F0:0F:AF:CF:D3:2D:C7:6E:B5:84:1C:7E:A8
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFVzCCAz+gAwIBAgINAgPlrsWNBCUaqxElqjANBgkqhkiG9w0BAQwFADBHMQsw
|
||||
CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
|
||||
MBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
|
||||
MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
|
||||
Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEBAQUA
|
||||
A4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3LvCvpt
|
||||
nfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3KgGjSY
|
||||
6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9BuXvAu
|
||||
MC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOdre7k
|
||||
RXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXuPuWg
|
||||
f9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1mKPV
|
||||
+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K8Yzo
|
||||
dDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqjx5RW
|
||||
Ir9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsRnTKa
|
||||
G73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0kzCq
|
||||
gc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9OktwID
|
||||
AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
|
||||
FgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBAB/Kzt3H
|
||||
vqGf2SdMC9wXmBFqiN495nFWcrKeGk6c1SuYJF2ba3uwM4IJvd8lRuqYnrYb/oM8
|
||||
0mJhwQTtzuDFycgTE1XnqGOtjHsB/ncw4c5omwX4Eu55MaBBRTUoCnGkJE+M3DyC
|
||||
B19m3H0Q/gxhswWV7uGugQ+o+MePTagjAiZrHYNSVc61LwDKgEDg4XSsYPWHgJ2u
|
||||
NmSRXbBoGOqKYcl3qJfEycel/FVL8/B/uWU9J2jQzGv6U53hkRrJXRqWbTKH7QMg
|
||||
yALOWr7Z6v2yTcQvG99fevX4i8buMTolUVVnjWQye+mew4K6Ki3pHrTgSAai/Gev
|
||||
HyICc/sgCq+dVEuhzf9gR7A/Xe8bVr2XIZYtCtFenTgCR2y59PYjJbigapordwj6
|
||||
xLEokCZYCDzifqrXPW+6MYgKBesntaFJ7qBFVHvmJ2WZICGoo7z7GJa7Um8M7YNR
|
||||
TOlZ4iBgxcJlkoKM8xAfDoqXvneCbT+PHV28SSe9zE8P4c52hgQjxcCMElv924Sg
|
||||
JPFI/2R80L5cFtHvma3AH/vLrrw4IgYmZNralw4/KBVEqE8AyvCazM90arQ+POuV
|
||||
7LXTWtiBmelDGDfrs7vRWGJB82bSj6p4lVQgw1oudCvV0b4YacCs1aTPObpRhANl
|
||||
6WLAYv7YTVWW4tAR+kg0Eeye7QUd5MjWHYbL
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Operating CA: Google Trust Services LLC
|
||||
# Subject: C = US, O = Google Trust Services LLC, CN = GTS Root R3
|
||||
# Issuer: C = US, O = Google Trust Services LLC, CN = GTS Root R3
|
||||
# Label: "GTS Root R3"
|
||||
# Serial: 0203E5B882EB20F825276D3D66
|
||||
# MD5 Fingerprint: 3E:E7:9D:58:02:94:46:51:94:E5:E0:22:4A:8B:E7:73
|
||||
# SHA1 Fingerprint: ED:E5:71:80:2B:C8:92:B9:5B:83:3C:D2:32:68:3F:09:CD:A0:1E:46
|
||||
# SHA256 Fingerprint: 34:D8:A7:3E:E2:08:D9:BC:DB:0D:95:65:20:93:4B:4E:40:E6:94:82:59:6E:8B:6F:73:C8:42:6B:01:0A:6F:48
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICCTCCAY6gAwIBAgINAgPluILrIPglJ209ZjAKBggqhkjOPQQDAzBHMQswCQYD
|
||||
VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG
|
||||
A1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw
|
||||
WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz
|
||||
IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQAIgNi
|
||||
AAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout736G
|
||||
jOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2ADDL2
|
||||
4CejQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
|
||||
BBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEA9uEglRR7
|
||||
VKOQFhG/hMjqb2sXnh5GmCCbn9MN2azTL818+FsuVbu/3ZL3pAzcMeGiAjEA/Jdm
|
||||
ZuVDFhOD3cffL74UOO0BzrEXGhF16b0DjyZ+hOXJYKaV11RZt+cRLInUue4X
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Operating CA: Google Trust Services LLC
|
||||
# Subject: C = US, O = Google Trust Services LLC, CN = GTS Root R4
|
||||
# Issuer: C = US, O = Google Trust Services LLC, CN = GTS Root R4
|
||||
# Label: "GTS Root R4"
|
||||
# Serial: 0203E5C068EF631A9C72905052
|
||||
# MD5 Fingerprint=43:96:83:77:19:4D:76:B3:9D:65:52:E4:1D:22:A5:E8
|
||||
# SHA1 Fingerprint=77:D3:03:67:B5:E0:0C:15:F6:0C:38:61:DF:7C:E1:3B:92:46:4D:47
|
||||
# SHA256 Fingerprint=34:9D:FA:40:58:C5:E2:63:12:3B:39:8A:E7:95:57:3C:4E:13:13:C8:3F:E6:8F:93:55:6C:D5:E8:03:1B:3C:7D
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICCTCCAY6gAwIBAgINAgPlwGjvYxqccpBQUjAKBggqhkjOPQQDAzBHMQswCQYD
|
||||
VQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEUMBIG
|
||||
A1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAwMDAw
|
||||
WjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2Vz
|
||||
IExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQAIgNi
|
||||
AATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzuhXyi
|
||||
QHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/lxKvR
|
||||
HYqjQjBAMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
|
||||
BBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNpADBmAjEA6ED/g94D
|
||||
9J+uHXqnLrmvT/aDHQ4thQEd0dlq7A/Cr8deVl5c1RxYIigL9zC2L7F8AjEA8GE8
|
||||
p/SgguMh1YQdc4acLa/KNJvxn7kjNuK8YAOdgLOaVsjh4rsUecrNIdSUtUlD
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
# Operating CA: Google Trust Services LLC
|
||||
# Subject: OU = GlobalSign ECC Root CA - R4, O = GlobalSign, CN = GlobalSign
|
||||
# Issuer: OU = GlobalSign ECC Root CA - R4, O = GlobalSign, CN = GlobalSign
|
||||
# Label: "GlobalSign R4"
|
||||
# Serial: 0203E57EF53F93FDA50921B2A6
|
||||
# MD5 Fingerprint: 26:29:F8:6D:E1:88:BF:A2:65:7F:AA:C4:CD:0F:7F:FC
|
||||
# SHA1 Fingerprint: 6B:A0:B0:98:E1:71:EF:5A:AD:FE:48:15:80:77:10:F4:BD:6F:0B:28
|
||||
# SHA256 Fingerprint: B0:85:D7:0B:96:4F:19:1A:73:E4:AF:0D:54:AE:7A:0E:07:AA:FD:AF:9B:71:DD:08:62:13:8A:B7:32:5A:24:A2
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIB3DCCAYOgAwIBAgINAgPlfvU/k/2lCSGypjAKBggqhkjOPQQDAjBQMSQwIgYD
|
||||
VQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0gUjQxEzARBgNVBAoTCkdsb2Jh
|
||||
bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTIxMTEzMDAwMDAwWhcNMzgw
|
||||
MTE5MDMxNDA3WjBQMSQwIgYDVQQLExtHbG9iYWxTaWduIEVDQyBSb290IENBIC0g
|
||||
UjQxEzARBgNVBAoTCkdsb2JhbFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wWTAT
|
||||
BgcqhkjOPQIBBggqhkjOPQMBBwNCAAS4xnnTj2wlDp8uORkcA6SumuU5BwkWymOx
|
||||
uYb4ilfBV85C+nOh92VC/x7BALJucw7/xyHlGKSq2XE/qNS5zowdo0IwQDAOBgNV
|
||||
HQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUVLB7rUW44kB/
|
||||
+wpu+74zyTyjhNUwCgYIKoZIzj0EAwIDRwAwRAIgIk90crlgr/HmnKAWBVBfw147
|
||||
bmF0774BxL4YSFlhgjICICadVGNA3jdgUM/I2O2dgq43mLyjj0xMqTQrbO/7lZsm
|
||||
-----END CERTIFICATE-----
|
||||
@ -83,13 +83,8 @@ echo -e '\x1B[0m'
|
||||
|
||||
version_gt()
|
||||
{
|
||||
# MacOS < 10.13 doesn't support sort -V
|
||||
echo "" | sort -V > /dev/null 2>&1
|
||||
vsort_failed=$?
|
||||
if [ "${1}" = "${2}" ]; then
|
||||
true
|
||||
elif (( $vsort_failed != 0 )); then
|
||||
false
|
||||
else
|
||||
test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"
|
||||
fi
|
||||
|
||||
@ -33,7 +33,6 @@ datas += [('gam/contactdelegation-v1.json', '.')]
|
||||
datas += [('gam/datastudio-v1.json', '.')]
|
||||
datas += [('gam/meet-v2beta.json', '.')]
|
||||
datas += [('gam/serviceaccountlookup-v1.json', '.')]
|
||||
datas += [('cacerts.pem', '.')]
|
||||
hiddenimports = [
|
||||
'gam.gamlib.yubikey',
|
||||
]
|
||||
@ -49,7 +48,7 @@ a = Analysis(
|
||||
binaries=[],
|
||||
datas=datas,
|
||||
hiddenimports=hiddenimports,
|
||||
hookspath=[],
|
||||
hookspath=['tools/hooks'],
|
||||
hooksconfig={},
|
||||
runtime_hooks=runtime_hooks,
|
||||
excludes=excludes,
|
||||
|
||||
5707
src/gam/__init__.py
5707
src/gam/__init__.py
File diff suppressed because it is too large
Load Diff
@ -34,7 +34,12 @@ def main():
|
||||
|
||||
# Run from command line
|
||||
if __name__ == '__main__':
|
||||
if platform.system() != 'Linux':
|
||||
if getattr(sys, 'frozen', False): # we're frozen:
|
||||
multiprocessing.freeze_support()
|
||||
if platform.system() == 'Linux':
|
||||
# set explictly since it's not default in Python < 3.14, forkserver should
|
||||
# be safer than fork and less likely to see bulk command hangs.
|
||||
multiprocessing.set_start_method('forkserver')
|
||||
else:
|
||||
multiprocessing.set_start_method('spawn')
|
||||
main()
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2025 Ross Scroggs All Rights Reserved.
|
||||
# Copyright (C) 2026 Ross Scroggs All Rights Reserved.
|
||||
#
|
||||
# All Rights Reserved.
|
||||
#
|
||||
@ -32,6 +32,7 @@ CHAT_EVENTS = 'chatevents'
|
||||
CHAT_MEMBERSHIPS = 'chatmemberships'
|
||||
CHAT_MEMBERSHIPS_ADMIN = 'chatmembershipsadmin'
|
||||
CHAT_MESSAGES = 'chatmessages'
|
||||
CHAT_SECTIONS = 'chatsections'
|
||||
CHAT_SPACES = 'chatspaces'
|
||||
CHAT_SPACES_ADMIN = 'chatspacesadmin'
|
||||
CHAT_SPACES_DELETE = 'chatspacesdelete'
|
||||
@ -46,11 +47,11 @@ CLASSROOM = 'classroom'
|
||||
CLOUDCHANNEL = 'cloudchannel'
|
||||
CLOUDIDENTITY_DEVICES = 'cloudidentitydevices'
|
||||
CLOUDIDENTITY_GROUPS = 'cloudidentitygroups'
|
||||
CLOUDIDENTITY_GROUPS_BETA = 'cloudidentitygroupsbeta'
|
||||
CLOUDIDENTITY_INBOUND_SSO = 'cloudidentityinboundsso'
|
||||
CLOUDIDENTITY_ORGUNITS = 'cloudidentityorgunits'
|
||||
CLOUDIDENTITY_ORGUNITS_BETA = 'cloudidentityorgunitsbeta'
|
||||
CLOUDIDENTITY_POLICY = 'cloudidentitypolicy'
|
||||
CLOUDIDENTITY_POLICY_BETA = 'cloudidentitypolicybeta'
|
||||
CLOUDIDENTITY_USERINVITATIONS = 'cloudidentityuserinvitations'
|
||||
CLOUDRESOURCEMANAGER = 'cloudresourcemanager'
|
||||
CONTACTS = 'contacts'
|
||||
@ -105,6 +106,8 @@ YOUTUBE = 'youtube'
|
||||
BUSINESSACCOUNTMANAGEMENT_SCOPE = 'https://www.googleapis.com/auth/business.manage'
|
||||
CHROMEVERSIONHISTORY_URL = 'https://versionhistory.googleapis.com/v1/chrome/platforms'
|
||||
DRIVE_SCOPE = 'https://www.googleapis.com/auth/drive'
|
||||
DRIVE_FILE_SCOPE = 'https://www.googleapis.com/auth/drive.file'
|
||||
DRIVE_READONLY_SCOPE = 'https://www.googleapis.com/auth/drive.readonly'
|
||||
GMAIL_SEND_SCOPE = 'https://www.googleapis.com/auth/gmail.send'
|
||||
GOOGLE_AUTH_PROVIDER_X509_CERT_URL = 'https://www.googleapis.com/oauth2/v1/certs'
|
||||
GOOGLE_OAUTH2_ENDPOINT = 'https://accounts.google.com/o/oauth2/v2/auth'
|
||||
@ -156,6 +159,7 @@ OAUTH2_TOKEN_ERRORS = [
|
||||
'access_denied: Account restricted',
|
||||
'internal_failure: Backend Error',
|
||||
'internal_failure: None',
|
||||
'invalid_account: Forbidden',
|
||||
'invalid_grant',
|
||||
'invalid_grant: Bad Request',
|
||||
'invalid_grant: Invalid email or User ID',
|
||||
@ -226,6 +230,7 @@ _INFO = {
|
||||
CHAT_MEMBERSHIPS: {'name': 'Chat API - Memberships', 'version': 'v1', 'v2discovery': True, 'mappedAPI': CHAT},
|
||||
CHAT_MEMBERSHIPS_ADMIN: {'name': 'Chat API - Memberships Admin', 'version': 'v1', 'v2discovery': True, 'mappedAPI': CHAT},
|
||||
CHAT_MESSAGES: {'name': 'Chat API - Messages', 'version': 'v1', 'v2discovery': True, 'mappedAPI': CHAT},
|
||||
CHAT_SECTIONS: {'name': 'Chat API - User Sections', 'version': 'v1', 'v2discovery': True, 'mappedAPI': CHAT},
|
||||
CHAT_SPACES: {'name': 'Chat API - Spaces', 'version': 'v1', 'v2discovery': True, 'mappedAPI': CHAT},
|
||||
CHAT_SPACES_ADMIN: {'name': 'Chat API - Spaces Admin', 'version': 'v1', 'v2discovery': True, 'mappedAPI': CHAT},
|
||||
CHAT_SPACES_DELETE: {'name': 'Chat API - Spaces Delete', 'version': 'v1', 'v2discovery': True, 'mappedAPI': CHAT},
|
||||
@ -239,11 +244,11 @@ _INFO = {
|
||||
CLOUDCHANNEL: {'name': 'Cloud Channel API', 'version': 'v1', 'v2discovery': True},
|
||||
CLOUDIDENTITY_DEVICES: {'name': 'Cloud Identity API - Devices', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
|
||||
CLOUDIDENTITY_GROUPS: {'name': 'Cloud Identity API - Groups', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
|
||||
CLOUDIDENTITY_GROUPS_BETA: {'name': 'Cloud Identity API - Groups Beta', 'version': 'v1beta1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
|
||||
CLOUDIDENTITY_INBOUND_SSO: {'name': 'Cloud Identity API - Inbound SSO Settings', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
|
||||
CLOUDIDENTITY_ORGUNITS: {'name': 'Cloud Identity API - OrgUnits', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
|
||||
CLOUDIDENTITY_ORGUNITS_BETA: {'name': 'Cloud Identity API - OrgUnits Beta', 'version': 'v1beta1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
|
||||
CLOUDIDENTITY_POLICY: {'name': 'Cloud Identity API - Policy', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
|
||||
CLOUDIDENTITY_POLICY_BETA: {'name': 'Cloud Identity API - Policy Beta', 'version': 'v1beta1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
|
||||
CLOUDIDENTITY_USERINVITATIONS: {'name': 'Cloud Identity API - User Invitations', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
|
||||
CLOUDRESOURCEMANAGER: {'name': 'Cloud Resource Manager API v3', 'version': 'v3', 'v2discovery': True},
|
||||
CONTACTS: {'name': 'Contacts API', 'version': 'v3', 'v2discovery': False},
|
||||
@ -253,7 +258,7 @@ _INFO = {
|
||||
DOCS: {'name': 'Docs API', 'version': 'v1', 'v2discovery': True},
|
||||
DRIVE2: {'name': 'Drive API v2', 'version': 'v2', 'v2discovery': False, 'mappedAPI': 'drive'},
|
||||
DRIVE3: {'name': 'Drive API v3', 'version': 'v3', 'v2discovery': False, 'mappedAPI': 'drive'},
|
||||
DRIVETD: {'name': 'Drive API v3 - todrive', 'version': 'v3', 'v2discovery': False, 'mappedAPI': 'drive'},
|
||||
DRIVETD: {'name': 'Drive API v3 - write todrive data', 'version': 'v3', 'v2discovery': False, 'mappedAPI': 'drive'},
|
||||
DRIVEACTIVITY: {'name': 'Drive Activity API v2', 'version': 'v2', 'v2discovery': True},
|
||||
DRIVELABELS_ADMIN: {'name': 'Drive Labels API - Admin', 'version': 'v2', 'v2discovery': True, 'mappedAPI': DRIVELABELS},
|
||||
DRIVELABELS_USER: {'name': 'Drive Labels API - User', 'version': 'v2', 'v2discovery': True, 'mappedAPI': DRIVELABELS},
|
||||
@ -283,7 +288,7 @@ _INFO = {
|
||||
SERVICEMANAGEMENT: {'name': 'Service Management API', 'version': 'v1', 'v2discovery': True},
|
||||
SERVICEUSAGE: {'name': 'Service Usage API', 'version': 'v1', 'v2discovery': True},
|
||||
SHEETS: {'name': 'Sheets API', 'version': 'v4', 'v2discovery': True},
|
||||
SHEETSTD: {'name': 'Sheets API - todrive', 'version': 'v4', 'v2discovery': True, 'mappedAPI': SHEETS},
|
||||
SHEETSTD: {'name': 'Sheets API - write todrive data', 'version': 'v4', 'v2discovery': True, 'mappedAPI': SHEETS},
|
||||
SITEVERIFICATION: {'name': 'Site Verification API', 'version': 'v1', 'v2discovery': True},
|
||||
STORAGE: {'name': 'Cloud Storage API', 'version': 'v1', 'v2discovery': True},
|
||||
STORAGEREAD: {'name': 'Cloud Storage API - Read', 'version': 'v1', 'v2discovery': True, 'mappedAPI': STORAGE},
|
||||
@ -384,10 +389,6 @@ _CLIENT_SCOPES = [
|
||||
'api': CLOUDIDENTITY_GROUPS,
|
||||
'subscopes': READONLY,
|
||||
'scope': 'https://www.googleapis.com/auth/cloud-identity.groups'},
|
||||
{'name': 'Cloud Identity API - Groups Beta (Enables group locking/unlocking)',
|
||||
'api': CLOUDIDENTITY_GROUPS_BETA,
|
||||
'subscopes': [],
|
||||
'scope': 'https://www.googleapis.com/auth/cloud-identity.groups'},
|
||||
{'name': 'Cloud Identity API - Inbound SSO Settings',
|
||||
'api': CLOUDIDENTITY_INBOUND_SSO,
|
||||
'subscopes': READONLY,
|
||||
@ -400,8 +401,12 @@ _CLIENT_SCOPES = [
|
||||
'api': CLOUDIDENTITY_POLICY,
|
||||
'subscopes': READONLY,
|
||||
'roByDefault': True,
|
||||
'scope': 'https://www.googleapis.com/auth/cloud-identity.policies'
|
||||
},
|
||||
'scope': 'https://www.googleapis.com/auth/cloud-identity.policies'},
|
||||
{'name': 'Cloud Identity API - Policy Beta',
|
||||
'api': CLOUDIDENTITY_POLICY_BETA,
|
||||
'subscopes': [],
|
||||
'offByDefault': True,
|
||||
'scope': 'https://www.googleapis.com/auth/cloud-identity.policies'},
|
||||
{'name': 'Cloud Identity API - User Invitations',
|
||||
'api': CLOUDIDENTITY_USERINVITATIONS,
|
||||
'subscopes': READONLY,
|
||||
@ -416,7 +421,7 @@ _CLIENT_SCOPES = [
|
||||
'subscopes': [],
|
||||
'offByDefault': True,
|
||||
'scope': STORAGE_READWRITE_SCOPE},
|
||||
{'name': 'Contacts API - Domain Shared Contacts and GAL',
|
||||
{'name': 'Contacts API - Domain Shared Contacts',
|
||||
'api': CONTACTS,
|
||||
'subscopes': [],
|
||||
'scope': 'https://www.google.com/m8/feeds'},
|
||||
@ -530,6 +535,17 @@ _CLIENT_SCOPES = [
|
||||
'scope': 'https://www.googleapis.com/auth/ediscovery'},
|
||||
]
|
||||
|
||||
_COMMANDDATA_CLIENT_SCOPES = [
|
||||
{'name': 'Drive API - commanddata_clientaccess',
|
||||
'api': DRIVE3,
|
||||
'subscopes': [],
|
||||
'scope': DRIVE_READONLY_SCOPE},
|
||||
{'name': 'Sheets API - commanddata_clientaccess',
|
||||
'api': SHEETS,
|
||||
'subscopes': [],
|
||||
'scope': 'https://www.googleapis.com/auth/spreadsheets.readonly'},
|
||||
]
|
||||
|
||||
_TODRIVE_CLIENT_SCOPES = [
|
||||
{'name': 'Drive API - todrive_clientaccess',
|
||||
'api': DRIVE3,
|
||||
@ -538,7 +554,7 @@ _TODRIVE_CLIENT_SCOPES = [
|
||||
{'name': 'Drive File API - todrive_clientaccess',
|
||||
'api': DRIVE3,
|
||||
'subscopes': [],
|
||||
'scope': 'https://www.googleapis.com/auth/drive.file'},
|
||||
'scope': DRIVE_FILE_SCOPE},
|
||||
{'name': 'Gmail API - todrive_clientaccess',
|
||||
'api': GMAIL,
|
||||
'subscopes': [],
|
||||
@ -580,6 +596,11 @@ _SVCACCT_SCOPES = [
|
||||
'api': CHAT_MESSAGES,
|
||||
'subscopes': READONLY,
|
||||
'scope': 'https://www.googleapis.com/auth/chat.messages'},
|
||||
{'name': 'Chat API - User Sections',
|
||||
'api': CHAT_SECTIONS,
|
||||
'offByDefault': True,
|
||||
'subscopes': READONLY,
|
||||
'scope': 'https://www.googleapis.com/auth/chat.users.sections'},
|
||||
{'name': 'Chat API - Spaces',
|
||||
'api': CHAT_SPACES,
|
||||
'subscopes': READONLY,
|
||||
@ -643,7 +664,8 @@ _SVCACCT_SCOPES = [
|
||||
{'name': 'Drive Activity API v2 - must pair with Drive API',
|
||||
'api': DRIVEACTIVITY,
|
||||
'subscopes': [],
|
||||
'scope': 'https://www.googleapis.com/auth/drive.activity'},
|
||||
'scope': [DRIVE_READONLY_SCOPE,
|
||||
'https://www.googleapis.com/auth/drive.activity']},
|
||||
{'name': 'Drive Labels API - Admin',
|
||||
'api': DRIVELABELS_ADMIN,
|
||||
'subscopes': READONLY,
|
||||
@ -656,10 +678,12 @@ _SVCACCT_SCOPES = [
|
||||
'api': DOCS,
|
||||
'subscopes': READONLY,
|
||||
'scope': 'https://www.googleapis.com/auth/documents'},
|
||||
{'name': 'Forms API',
|
||||
{'name': 'Forms API - must pair with Drive API',
|
||||
'api': FORMS,
|
||||
'subscopes': [],
|
||||
'scope': DRIVE_SCOPE},
|
||||
'scope': [DRIVE_READONLY_SCOPE,
|
||||
'https://www.googleapis.com/auth/forms.body',
|
||||
'https://www.googleapis.com/auth/forms.responses.readonly']},
|
||||
{'name': 'Gmail API - Full Access (Labels, Messages)',
|
||||
'api': GMAIL,
|
||||
'subscopes': [],
|
||||
@ -750,9 +774,10 @@ _SVCACCT_SCOPES = [
|
||||
]
|
||||
|
||||
_SVCACCT_SPECIAL_SCOPES = [
|
||||
{'name': 'Drive API - todrive',
|
||||
{'name': 'Drive API - write todrive data - has access to all Drive',
|
||||
'api': DRIVETD,
|
||||
'subscopes': [],
|
||||
'offByDefault': True,
|
||||
'scope': DRIVE_SCOPE},
|
||||
{'name': 'Gmail API - Full Access - read only',
|
||||
'api': GMAIL,
|
||||
@ -764,8 +789,9 @@ _SVCACCT_SPECIAL_SCOPES = [
|
||||
'subscopes': [],
|
||||
'offByDefault': True,
|
||||
'scope': GMAIL_SEND_SCOPE},
|
||||
{'name': 'Sheets API - todrive',
|
||||
{'name': 'Sheets API - write todrive data - has access to all Sheets',
|
||||
'api': SHEETSTD,
|
||||
'offByDefault': True,
|
||||
'subscopes': [],
|
||||
'scope': 'https://www.googleapis.com/auth/spreadsheets'},
|
||||
]
|
||||
@ -786,17 +812,27 @@ def getVersion(api):
|
||||
api = _INFO[api].get('mappedAPI', api)
|
||||
return (api, version, v2discovery)
|
||||
|
||||
def getAPIsList():
|
||||
apisList = set()
|
||||
for api, value in _INFO.items():
|
||||
apisList.add(value.get('mappedAPI', api))
|
||||
return apisList
|
||||
|
||||
def getClientScopesSet(api):
|
||||
return {scope['scope'] for scope in _CLIENT_SCOPES if scope['api'] == api}
|
||||
|
||||
def getClientScopesList(todriveClientAccess):
|
||||
def getClientScopesList(commanddataClientAccess, todriveClientAccess):
|
||||
caScopes = _CLIENT_SCOPES[:]
|
||||
if commanddataClientAccess:
|
||||
caScopes.extend(_COMMANDDATA_CLIENT_SCOPES)
|
||||
if todriveClientAccess:
|
||||
caScopes.extend(_TODRIVE_CLIENT_SCOPES)
|
||||
return sorted(caScopes, key=lambda k: k['name'])
|
||||
|
||||
def getClientScopesURLs(todriveClientAccess):
|
||||
def getClientScopesURLs(commanddataClientAccess, todriveClientAccess):
|
||||
caScopes = _CLIENT_SCOPES[:]
|
||||
if commanddataClientAccess:
|
||||
caScopes.extend(_COMMANDDATA_CLIENT_SCOPES)
|
||||
if todriveClientAccess:
|
||||
caScopes.extend(_TODRIVE_CLIENT_SCOPES)
|
||||
return sorted({scope['scope'] for scope in _CLIENT_SCOPES})
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2025 Ross Scroggs All Rights Reserved.
|
||||
# Copyright (C) 2026 Ross Scroggs All Rights Reserved.
|
||||
#
|
||||
# All Rights Reserved.
|
||||
#
|
||||
@ -69,6 +69,8 @@ CACHE_DISCOVERY_ONLY = 'cache_discovery_only'
|
||||
CHANNEL_CUSTOMER_ID = 'channel_customer_id'
|
||||
# Character set of batch, csv, data files
|
||||
CHARSET = 'charset'
|
||||
# When retrieving lists of Chat items from API, how many should be retrieved in each chunk
|
||||
CHAT_MAX_RESULTS = 'chat_max_results'
|
||||
# When retrieving lists of Google Classroom items from API, how many should be retrieved in each chunk
|
||||
CLASSROOM_MAX_RESULTS = 'classroom_max_results'
|
||||
# Path to client_secrets.json
|
||||
@ -83,6 +85,8 @@ CMDLOG_MAX__BACKUPS = 'cmdlog_max__backups'
|
||||
CMDLOG_MAX_BACKUPS = 'cmdlog_max_backups'
|
||||
# Command logging max kilo bytes per log file
|
||||
CMDLOG_MAX_KILO_BYTES = 'cmdlog_max_kilo_bytes'
|
||||
# Use client access for command data from Google Docs/Sheets
|
||||
COMMANDDATA_CLIENTACCESS = 'commanddata_clientaccess'
|
||||
# GAM config directory containing client_secrets.json, oauth2.txt, oauth2service.json, extra_args.txt
|
||||
CONFIG_DIR = 'config_dir'
|
||||
# When retrieving lists of Google Contacts from API, how many should be retrieved in each chunk
|
||||
@ -145,11 +149,17 @@ CSV_OUTPUT_USERS_AUDIT = 'csv_output_users_audit'
|
||||
CUSTOMER_ID = 'customer_id'
|
||||
# If debug_level > 0: extra_args['prettyPrint'] = True, httplib2.debuglevel = gam_debug_level, appsObj.debug = True
|
||||
DEBUG_LEVEL = 'debug_level'
|
||||
# redact sensitive credentials from debug output
|
||||
DEBUG_REDACTION = 'debug_redaction'
|
||||
# Developer Preview APIs
|
||||
DEVELOPER_PREVIEW_APIS = 'developer_preview_apis'
|
||||
# Developer Preview API Key
|
||||
DEVELOPER_PREVIEW_API_KEY = 'developer_preview_api_key'
|
||||
# When retrieving lists of ChromeOS devices from API, how many should be retrieved in each chunk
|
||||
DEVICE_MAX_RESULTS = 'device_max_results'
|
||||
# Domain obtained from gam.cfg or oauth2.txt
|
||||
DOMAIN = 'domain'
|
||||
# Google Drive download directory
|
||||
# directory for file output
|
||||
DRIVE_DIR = 'drive_dir'
|
||||
# When retrieving lists of Drive files/folders from API, how many should be retrieved in each chunk
|
||||
DRIVE_MAX_RESULTS = 'drive_max_results'
|
||||
@ -169,6 +179,8 @@ EXTRA_ARGS = 'extra_args'
|
||||
GMAIL_CSE_INCERT_DIR = 'gmail_cse_incert_dir'
|
||||
# Gmail CSE KACL wrapped key files
|
||||
GMAIL_CSE_INKEY_DIR = 'gmail_cse_inkey_dir'
|
||||
# directory for file input
|
||||
INPUT_DIR = 'input_dir'
|
||||
# When processing items in batches, how many seconds should GAM wait between batches
|
||||
INTER_BATCH_WAIT = 'inter_batch_wait'
|
||||
# When retrieving lists of licenses from API, how many should be retrieved in each chunk
|
||||
@ -208,6 +220,8 @@ NUM_TBATCH_THREADS = 'num_tbatch_threads'
|
||||
NUM_THREADS = 'num_threads'
|
||||
# Path to oauth2.txt
|
||||
OAUTH2_TXT = 'oauth2_txt'
|
||||
# File permissions for oauth2.txt.lock
|
||||
OAUTH2_TXT_LOCK_MODE = 'oauth2_txt_lock_mode'
|
||||
# Path to oauth2service.json
|
||||
OAUTH2SERVICE_JSON = 'oauth2service_json'
|
||||
# Output date format, empty defalts to ISOFormat
|
||||
@ -333,12 +347,14 @@ Defaults = {
|
||||
CACHE_DISCOVERY_ONLY: TRUE,
|
||||
CHARSET: DEFAULT_CHARSET,
|
||||
CHANNEL_CUSTOMER_ID: '',
|
||||
CHAT_MAX_RESULTS: '100',
|
||||
CLASSROOM_MAX_RESULTS: '0',
|
||||
CLIENT_SECRETS_JSON: FN_CLIENT_SECRETS_JSON,
|
||||
CLOCK_SKEW_IN_SECONDS: '10',
|
||||
CMDLOG: '',
|
||||
CMDLOG_MAX_BACKUPS: 5,
|
||||
CMDLOG_MAX_KILO_BYTES: 1000,
|
||||
COMMANDDATA_CLIENTACCESS: FALSE,
|
||||
CONFIG_DIR: '',
|
||||
CONTACT_MAX_RESULTS: '100',
|
||||
CSV_INPUT_COLUMN_DELIMITER: ',',
|
||||
@ -370,6 +386,9 @@ Defaults = {
|
||||
CSV_OUTPUT_USERS_AUDIT: FALSE,
|
||||
CUSTOMER_ID: MY_CUSTOMER,
|
||||
DEBUG_LEVEL: '0',
|
||||
DEBUG_REDACTION: TRUE,
|
||||
DEVELOPER_PREVIEW_APIS: '',
|
||||
DEVELOPER_PREVIEW_API_KEY: '',
|
||||
DEVICE_MAX_RESULTS: '200',
|
||||
DOMAIN: '',
|
||||
DRIVE_DIR: '',
|
||||
@ -382,6 +401,7 @@ Defaults = {
|
||||
EXTRA_ARGS: '',
|
||||
GMAIL_CSE_INCERT_DIR: '',
|
||||
GMAIL_CSE_INKEY_DIR: '',
|
||||
INPUT_DIR: '.',
|
||||
INTER_BATCH_WAIT: '0',
|
||||
LICENSE_MAX_RESULTS: '100',
|
||||
LICENSE_SKUS: '',
|
||||
@ -401,6 +421,7 @@ Defaults = {
|
||||
NUM_TBATCH_THREADS: '2',
|
||||
NUM_THREADS: '5',
|
||||
OAUTH2_TXT: FN_OAUTH2_TXT,
|
||||
OAUTH2_TXT_LOCK_MODE: '644',
|
||||
OAUTH2SERVICE_JSON: FN_OAUTH2SERVICE_JSON,
|
||||
OUTPUT_DATEFORMAT: '',
|
||||
OUTPUT_TIMEFORMAT: '',
|
||||
@ -499,12 +520,14 @@ VAR_INFO = {
|
||||
CACHE_DISCOVERY_ONLY: {VAR_TYPE: TYPE_BOOLEAN, VAR_SIGFILE: 'allcache.txt', VAR_SFFT: (TRUE, FALSE)},
|
||||
CHARSET: {VAR_TYPE: TYPE_STRING, VAR_ENVVAR: 'GAM_CHARSET', VAR_LIMITS: (1, None)},
|
||||
CHANNEL_CUSTOMER_ID: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
|
||||
CHAT_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 1000)},
|
||||
CLASSROOM_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (0, 1000)},
|
||||
CLIENT_SECRETS_JSON: {VAR_TYPE: TYPE_FILE, VAR_ENVVAR: 'CLIENTSECRETS', VAR_ACCESS: os.R_OK},
|
||||
CLOCK_SKEW_IN_SECONDS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (10, 3600)},
|
||||
CMDLOG: {VAR_TYPE: TYPE_FILE, VAR_ACCESS: os.W_OK},
|
||||
CMDLOG_MAX_BACKUPS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 10)},
|
||||
CMDLOG_MAX_KILO_BYTES: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (100, 10000)},
|
||||
COMMANDDATA_CLIENTACCESS: {VAR_TYPE: TYPE_BOOLEAN},
|
||||
CONFIG_DIR: {VAR_TYPE: TYPE_DIRECTORY, VAR_ENVVAR: 'GAMUSERCONFIGDIR'},
|
||||
CONTACT_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 10000)},
|
||||
CSV_INPUT_COLUMN_DELIMITER: {VAR_TYPE: TYPE_CHARACTER},
|
||||
@ -536,6 +559,9 @@ VAR_INFO = {
|
||||
CSV_OUTPUT_USERS_AUDIT: {VAR_TYPE: TYPE_BOOLEAN},
|
||||
CUSTOMER_ID: {VAR_TYPE: TYPE_STRING, VAR_ENVVAR: 'CUSTOMER_ID', VAR_LIMITS: (0, None)},
|
||||
DEBUG_LEVEL: {VAR_TYPE: TYPE_INTEGER, VAR_SIGFILE: 'debug.gam', VAR_LIMITS: (0, None), VAR_SFFT: ('0', '4')},
|
||||
DEBUG_REDACTION: {VAR_TYPE: TYPE_BOOLEAN},
|
||||
DEVELOPER_PREVIEW_APIS: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
|
||||
DEVELOPER_PREVIEW_API_KEY: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
|
||||
DEVICE_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 200)},
|
||||
DOMAIN: {VAR_TYPE: TYPE_STRING, VAR_ENVVAR: 'GA_DOMAIN', VAR_LIMITS: (0, None)},
|
||||
DRIVE_DIR: {VAR_TYPE: TYPE_DIRECTORY, VAR_ENVVAR: 'GAMDRIVEDIR'},
|
||||
@ -548,6 +574,7 @@ VAR_INFO = {
|
||||
EXTRA_ARGS: {VAR_TYPE: TYPE_FILE, VAR_SIGFILE: FN_EXTRA_ARGS_TXT, VAR_SFFT: ('', FN_EXTRA_ARGS_TXT), VAR_ACCESS: os.R_OK},
|
||||
GMAIL_CSE_INCERT_DIR: {VAR_TYPE: TYPE_DIRECTORY},
|
||||
GMAIL_CSE_INKEY_DIR: {VAR_TYPE: TYPE_DIRECTORY},
|
||||
INPUT_DIR: {VAR_TYPE: TYPE_DIRECTORY},
|
||||
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_SKUS: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
|
||||
@ -567,6 +594,7 @@ VAR_INFO = {
|
||||
NUM_TBATCH_THREADS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (1, 1000)},
|
||||
NUM_THREADS: {VAR_TYPE: TYPE_INTEGER, VAR_ENVVAR: 'GAM_THREADS', VAR_LIMITS: (1, 1000)},
|
||||
OAUTH2_TXT: {VAR_TYPE: TYPE_FILE, VAR_ENVVAR: 'OAUTHFILE', VAR_ACCESS: os.R_OK | os.W_OK},
|
||||
OAUTH2_TXT_LOCK_MODE: {VAR_TYPE: TYPE_CHOICE, VAR_CHOICES: {'644': 0o644, '664': 0o664, '666': 0o666}},
|
||||
OAUTH2SERVICE_JSON: {VAR_TYPE: TYPE_FILE, VAR_ENVVAR: 'OAUTHSERVICEFILE', VAR_ACCESS: os.R_OK | os.W_OK},
|
||||
OUTPUT_DATEFORMAT: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
|
||||
OUTPUT_TIMEFORMAT: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2025 Ross Scroggs All Rights Reserved.
|
||||
# Copyright (C) 2026 Ross Scroggs All Rights Reserved.
|
||||
#
|
||||
# All Rights Reserved.
|
||||
#
|
||||
@ -49,43 +49,71 @@ class GamCLArgs():
|
||||
ENTITY_CROS_OUS_AND_CHILDREN_QUERIES = 'cros_ous_and_children_queries'
|
||||
ENTITY_CROS_SN = 'cros_sn'
|
||||
ENTITY_DOMAINS = 'domains'
|
||||
ENTITY_DOMAINS_NA = 'domains_na'
|
||||
ENTITY_DOMAINS_ARCH = 'domains_arch'
|
||||
ENTITY_DOMAINS_NS = 'domains_ns'
|
||||
ENTITY_DOMAINS_SUSP = 'domains_susp'
|
||||
ENTITY_DOMAINS_NA_NS = 'domains_na_ns'
|
||||
ENTITY_GROUP = 'group'
|
||||
ENTITY_GROUP_INDE = 'group_inde'
|
||||
ENTITY_GROUP_NA = 'group_na'
|
||||
ENTITY_GROUP_ARCH = 'group_arch'
|
||||
ENTITY_GROUP_NS = 'group_ns'
|
||||
ENTITY_GROUP_SUSP = 'group_susp'
|
||||
ENTITY_GROUP_NA_NS = 'group_na_ns'
|
||||
ENTITY_GROUPS = 'groups'
|
||||
ENTITY_GROUPS_INDE = 'groups_inde'
|
||||
ENTITY_GROUPS_NA = 'groups_na'
|
||||
ENTITY_GROUPS_ARCH = 'groups_arch'
|
||||
ENTITY_GROUPS_NS = 'groups_ns'
|
||||
ENTITY_GROUPS_SUSP = 'groups_susp'
|
||||
ENTITY_GROUPS_NA_NS = 'groups_na_ns'
|
||||
ENTITY_GROUP_USERS = 'group_users'
|
||||
ENTITY_GROUP_USERS_NA = 'group_users_na'
|
||||
ENTITY_GROUP_USERS_ARCH = 'group_users_arch'
|
||||
ENTITY_GROUP_USERS_NS = 'group_users_ns'
|
||||
ENTITY_GROUP_USERS_SUSP = 'group_users_susp'
|
||||
ENTITY_GROUP_USERS_NA_NS = 'group_users_na_ns'
|
||||
ENTITY_GROUP_USERS_SELECT = 'group_users_select'
|
||||
ENTITY_LICENSES = 'licenses'
|
||||
ENTITY_OAUTHUSER = 'oauthuser'
|
||||
ENTITY_OU = 'ou'
|
||||
ENTITY_OU_NA = 'ou_na'
|
||||
ENTITY_OU_ARCH = 'ou_arch'
|
||||
ENTITY_OU_NS = 'ou_ns'
|
||||
ENTITY_OU_SUSP = 'ou_susp'
|
||||
ENTITY_OU_NA_NS = 'ou_na_ns'
|
||||
ENTITY_OU_AND_CHILDREN = 'ou_and_children'
|
||||
ENTITY_OU_AND_CHILDREN_NA = 'ou_and_children_na'
|
||||
ENTITY_OU_AND_CHILDREN_ARCH = 'ou_and_children_arch'
|
||||
ENTITY_OU_AND_CHILDREN_NS = 'ou_and_children_ns'
|
||||
ENTITY_OU_AND_CHILDREN_SUSP = 'ou_and_children_susp'
|
||||
ENTITY_OU_AND_CHILDREN_NA_NS = 'ou_and_children_na_ns'
|
||||
ENTITY_OUS = 'ous'
|
||||
ENTITY_OUS_NA = 'ous_na'
|
||||
ENTITY_OUS_ARCH = 'ous_arch'
|
||||
ENTITY_OUS_NS = 'ous_ns'
|
||||
ENTITY_OUS_SUSP = 'ous_susp'
|
||||
ENTITY_OUS_NA_NS = 'ous_na_ns'
|
||||
ENTITY_OUS_AND_CHILDREN = 'ous_and_children'
|
||||
ENTITY_OUS_AND_CHILDREN_NA = 'ous_and_children_na'
|
||||
ENTITY_OUS_AND_CHILDREN_ARCH = 'ous_and_children_arch'
|
||||
ENTITY_OUS_AND_CHILDREN_NS = 'ous_and_children_ns'
|
||||
ENTITY_OUS_AND_CHILDREN_SUSP = 'ous_and_children_susp'
|
||||
ENTITY_OUS_AND_CHILDREN_NA_NS = 'ous_and_children_na_ns'
|
||||
ENTITY_QUERIES = 'queries'
|
||||
ENTITY_QUERY = 'query'
|
||||
ENTITY_STUDENTS = 'students'
|
||||
ENTITY_TEACHERS = 'teachers'
|
||||
ENTITY_USER = 'user'
|
||||
ENTITY_USERS = 'users'
|
||||
ENTITY_USERS_NA = 'users_na'
|
||||
ENTITY_USERS_ARCH = 'users_arch'
|
||||
ENTITY_USERS_NS = 'users_ns'
|
||||
ENTITY_USERS_NS_SUSP = 'users_ns_susp'
|
||||
ENTITY_USERS_SUSP = 'users_susp'
|
||||
ENTITY_USERS_NA_NS = 'users_na_ns'
|
||||
ENTITY_USERS_ARCH_OR_SUSP = 'users_arch_or_susp'
|
||||
ENTITY_USERS_NS_SUSP = 'users_ns_susp'
|
||||
#
|
||||
BROWSER_ENTITIES = [
|
||||
ENTITY_BROWSER,
|
||||
@ -118,34 +146,58 @@ class GamCLArgs():
|
||||
ENTITY_CIGROUP_USERS,
|
||||
ENTITY_COURSEPARTICIPANTS,
|
||||
ENTITY_DOMAINS,
|
||||
ENTITY_DOMAINS_NA,
|
||||
ENTITY_DOMAINS_ARCH,
|
||||
ENTITY_DOMAINS_NS,
|
||||
ENTITY_DOMAINS_SUSP,
|
||||
ENTITY_DOMAINS_NA_NS,
|
||||
ENTITY_GROUP,
|
||||
ENTITY_GROUP_INDE,
|
||||
ENTITY_GROUP_NA,
|
||||
ENTITY_GROUP_ARCH,
|
||||
ENTITY_GROUP_NS,
|
||||
ENTITY_GROUP_SUSP,
|
||||
ENTITY_GROUP_NA_NS,
|
||||
ENTITY_GROUPS,
|
||||
ENTITY_GROUPS_INDE,
|
||||
ENTITY_GROUPS_NA,
|
||||
ENTITY_GROUPS_ARCH,
|
||||
ENTITY_GROUPS_NS,
|
||||
ENTITY_GROUPS_SUSP,
|
||||
ENTITY_GROUPS_NA_NS,
|
||||
ENTITY_GROUP_USERS,
|
||||
ENTITY_GROUP_USERS_NA,
|
||||
ENTITY_GROUP_USERS_ARCH,
|
||||
ENTITY_GROUP_USERS_NS,
|
||||
ENTITY_GROUP_USERS_SUSP,
|
||||
ENTITY_GROUP_USERS_NA_NS,
|
||||
ENTITY_GROUP_USERS_SELECT,
|
||||
ENTITY_LICENSES,
|
||||
ENTITY_OAUTHUSER,
|
||||
ENTITY_OU,
|
||||
ENTITY_OU_NA,
|
||||
ENTITY_OU_ARCH,
|
||||
ENTITY_OU_NS,
|
||||
ENTITY_OU_SUSP,
|
||||
ENTITY_OU_NA_NS,
|
||||
ENTITY_OU_AND_CHILDREN,
|
||||
ENTITY_OU_AND_CHILDREN_NA,
|
||||
ENTITY_OU_AND_CHILDREN_ARCH,
|
||||
ENTITY_OU_AND_CHILDREN_NS,
|
||||
ENTITY_OU_AND_CHILDREN_SUSP,
|
||||
ENTITY_OU_AND_CHILDREN_NA_NS,
|
||||
ENTITY_OUS,
|
||||
ENTITY_OUS_NA,
|
||||
ENTITY_OUS_ARCH,
|
||||
ENTITY_OUS_NS,
|
||||
ENTITY_OUS_SUSP,
|
||||
ENTITY_OUS_NA_NS,
|
||||
ENTITY_OUS_AND_CHILDREN,
|
||||
ENTITY_OUS_AND_CHILDREN_NA,
|
||||
ENTITY_OUS_AND_CHILDREN_ARCH,
|
||||
ENTITY_OUS_AND_CHILDREN_NS,
|
||||
ENTITY_OUS_AND_CHILDREN_SUSP,
|
||||
ENTITY_OUS_AND_CHILDREN_NA_NS,
|
||||
ENTITY_QUERIES,
|
||||
ENTITY_QUERY,
|
||||
ENTITY_STUDENTS,
|
||||
@ -222,29 +274,53 @@ class GamCLArgs():
|
||||
'licence': ENTITY_LICENSES,
|
||||
'licences': ENTITY_LICENSES,
|
||||
'org': ENTITY_OU,
|
||||
'org_na': ENTITY_OU_NA,
|
||||
'org_arch': ENTITY_OU_ARCH,
|
||||
'org_ns': ENTITY_OU_NS,
|
||||
'org_susp': ENTITY_OU_SUSP,
|
||||
'org_na_ns': ENTITY_OU_NA_NS,
|
||||
'org_and_child': ENTITY_OU_AND_CHILDREN,
|
||||
'org_and_child_na': ENTITY_OU_AND_CHILDREN_NA,
|
||||
'org_and_child_arch': ENTITY_OU_AND_CHILDREN_ARCH,
|
||||
'org_and_child_ns': ENTITY_OU_AND_CHILDREN_NS,
|
||||
'org_and_child_susp': ENTITY_OU_AND_CHILDREN_SUSP,
|
||||
'org_and_child_na_ns': ENTITY_OU_AND_CHILDREN_NA_NS,
|
||||
'org_and_children': ENTITY_OU_AND_CHILDREN,
|
||||
'org_and_children_na': ENTITY_OU_AND_CHILDREN_NA,
|
||||
'org_and_children_arch': ENTITY_OU_AND_CHILDREN_ARCH,
|
||||
'org_and_children_ns': ENTITY_OU_AND_CHILDREN_NS,
|
||||
'org_and_children_susp': ENTITY_OU_AND_CHILDREN_SUSP,
|
||||
'org_and_children_na_ns': ENTITY_OU_AND_CHILDREN_NA_NS,
|
||||
'orgs': ENTITY_OUS,
|
||||
'orgs_na': ENTITY_OUS_NA,
|
||||
'orgs_arch': ENTITY_OUS_ARCH,
|
||||
'orgs_ns': ENTITY_OUS_NS,
|
||||
'orgs_susp': ENTITY_OUS_SUSP,
|
||||
'orgs_na_ns': ENTITY_OUS_NA_NS,
|
||||
'orgs_and_child': ENTITY_OUS_AND_CHILDREN,
|
||||
'orgs_and_child_na': ENTITY_OUS_AND_CHILDREN_NA,
|
||||
'orgs_and_child_arch': ENTITY_OUS_AND_CHILDREN_ARCH,
|
||||
'orgs_and_child_ns': ENTITY_OUS_AND_CHILDREN_NS,
|
||||
'orgs_and_child_susp': ENTITY_OUS_AND_CHILDREN_SUSP,
|
||||
'orgs_and_child_na_ns': ENTITY_OUS_AND_CHILDREN_NA_NS,
|
||||
'orgs_and_children': ENTITY_OUS_AND_CHILDREN,
|
||||
'orgs_and_children_na': ENTITY_OUS_AND_CHILDREN_NA,
|
||||
'orgs_and_children_arch': ENTITY_OUS_AND_CHILDREN_ARCH,
|
||||
'orgs_and_children_ns': ENTITY_OUS_AND_CHILDREN_NS,
|
||||
'orgs_and_children_susp': ENTITY_OUS_AND_CHILDREN_SUSP,
|
||||
'orgs_and_children_na_ns': ENTITY_OUS_AND_CHILDREN_NA_NS,
|
||||
'ou_and_child': ENTITY_OU_AND_CHILDREN,
|
||||
'ou_and_child_na': ENTITY_OU_AND_CHILDREN_NA,
|
||||
'ou_and_child_arch': ENTITY_OU_AND_CHILDREN_ARCH,
|
||||
'ou_and_child_ns': ENTITY_OU_AND_CHILDREN_NS,
|
||||
'ou_and_child_susp': ENTITY_OU_AND_CHILDREN_SUSP,
|
||||
'ou_and_child_na_ns': ENTITY_OU_AND_CHILDREN_NA_NS,
|
||||
'ous_and_child': ENTITY_OUS_AND_CHILDREN,
|
||||
'ous_and_child_na': ENTITY_OUS_AND_CHILDREN_NA,
|
||||
'ous_and_child_arch': ENTITY_OUS_AND_CHILDREN_ARCH,
|
||||
'ous_and_child_ns': ENTITY_OUS_AND_CHILDREN_NS,
|
||||
'ous_and_child_susp': ENTITY_OUS_AND_CHILDREN_SUSP,
|
||||
'ous_and_child_na_ns': ENTITY_OUS_AND_CHILDREN_NA_NS,
|
||||
}
|
||||
# CL entity source selectors
|
||||
ENTITY_SELECTOR_ALL = 'all'
|
||||
@ -315,30 +391,217 @@ class GamCLArgs():
|
||||
]
|
||||
USER_ENTITY_SELECTOR_ALL_SUBTYPES = [
|
||||
ENTITY_USERS,
|
||||
ENTITY_USERS_NA,
|
||||
ENTITY_USERS_ARCH,
|
||||
ENTITY_USERS_NS,
|
||||
ENTITY_USERS_NS_SUSP,
|
||||
ENTITY_USERS_SUSP,
|
||||
ENTITY_USERS_ARCH_OR_SUSP,
|
||||
ENTITY_USERS_NA_NS,
|
||||
ENTITY_USERS_NS_SUSP,
|
||||
]
|
||||
#
|
||||
ENTITY_ALL_CROS = ENTITY_SELECTOR_ALL+' '+ENTITY_CROS
|
||||
ENTITY_ALL_USERS = ENTITY_SELECTOR_ALL+' '+ENTITY_USERS
|
||||
ENTITY_ALL_USERS_NA = ENTITY_SELECTOR_ALL+' '+ENTITY_USERS_NA
|
||||
ENTITY_ALL_USERS_ARCH = ENTITY_SELECTOR_ALL+' '+ENTITY_USERS_ARCH
|
||||
ENTITY_ALL_USERS_NS = ENTITY_SELECTOR_ALL+' '+ENTITY_USERS_NS
|
||||
ENTITY_ALL_USERS_NS_SUSP = ENTITY_SELECTOR_ALL+' '+ENTITY_USERS_NS_SUSP
|
||||
ENTITY_ALL_USERS_SUSP = ENTITY_SELECTOR_ALL+' '+ENTITY_USERS_SUSP
|
||||
ENTITY_ALL_USERS_NA_NS = ENTITY_SELECTOR_ALL+' '+ENTITY_USERS_NA_NS
|
||||
ENTITY_ALL_USERS_ARCH_OR_SUSP = ENTITY_SELECTOR_ALL+' '+ENTITY_USERS_ARCH_OR_SUSP
|
||||
ENTITY_ALL_USERS_NS_SUSP = ENTITY_SELECTOR_ALL+' '+ENTITY_USERS_NS_SUSP
|
||||
#
|
||||
ALL_USER_ENTITY_TYPES = {
|
||||
ENTITY_ALL_USERS,
|
||||
ENTITY_ALL_USERS_NA,
|
||||
ENTITY_ALL_USERS_ARCH,
|
||||
ENTITY_ALL_USERS_NS,
|
||||
ENTITY_ALL_USERS_SUSP,
|
||||
ENTITY_ALL_USERS_NA_NS,
|
||||
ENTITY_ALL_USERS_NS_SUSP,
|
||||
}
|
||||
DOMAIN_ENTITY_TYPES = {
|
||||
ENTITY_DOMAINS,
|
||||
ENTITY_DOMAINS_NA,
|
||||
ENTITY_DOMAINS_ARCH,
|
||||
ENTITY_DOMAINS_NS,
|
||||
ENTITY_DOMAINS_SUSP,
|
||||
ENTITY_DOMAINS_NA_NS,
|
||||
}
|
||||
GROUP_ENTITY_TYPES = {
|
||||
ENTITY_GROUP,
|
||||
ENTITY_GROUP_NA,
|
||||
ENTITY_GROUP_ARCH,
|
||||
ENTITY_GROUP_NS,
|
||||
ENTITY_GROUP_SUSP,
|
||||
ENTITY_GROUP_NA_NS,
|
||||
ENTITY_GROUP_INDE,
|
||||
}
|
||||
GROUPS_ENTITY_TYPES = {
|
||||
ENTITY_GROUPS,
|
||||
ENTITY_GROUPS_NA,
|
||||
ENTITY_GROUPS_ARCH,
|
||||
ENTITY_GROUPS_NS,
|
||||
ENTITY_GROUPS_SUSP,
|
||||
ENTITY_GROUPS_NA_NS,
|
||||
ENTITY_GROUPS_INDE,
|
||||
}
|
||||
GROUP_USERS_ENTITY_TYPES = {
|
||||
ENTITY_GROUP_USERS,
|
||||
ENTITY_GROUP_USERS_NA,
|
||||
ENTITY_GROUP_USERS_ARCH,
|
||||
ENTITY_GROUP_USERS_NS,
|
||||
ENTITY_GROUP_USERS_SUSP,
|
||||
ENTITY_GROUP_USERS_NA_NS,
|
||||
ENTITY_GROUP_USERS_SELECT,
|
||||
}
|
||||
OU_ENTITY_TYPES = {
|
||||
ENTITY_OU,
|
||||
ENTITY_OU_AND_CHILDREN,
|
||||
ENTITY_OU_NA,
|
||||
ENTITY_OU_AND_CHILDREN_NA,
|
||||
ENTITY_OU_ARCH,
|
||||
ENTITY_OU_AND_CHILDREN_ARCH,
|
||||
ENTITY_OU_NS,
|
||||
ENTITY_OU_AND_CHILDREN_NS,
|
||||
ENTITY_OU_SUSP,
|
||||
ENTITY_OU_AND_CHILDREN_SUSP,
|
||||
ENTITY_OU_NA_NS,
|
||||
ENTITY_OU_AND_CHILDREN_NA_NS,
|
||||
}
|
||||
OUS_ENTITY_TYPES = {
|
||||
ENTITY_OUS,
|
||||
ENTITY_OUS_AND_CHILDREN,
|
||||
ENTITY_OUS_NA,
|
||||
ENTITY_OUS_AND_CHILDREN_NA,
|
||||
ENTITY_OUS_ARCH,
|
||||
ENTITY_OUS_AND_CHILDREN_ARCH,
|
||||
ENTITY_OUS_NS,
|
||||
ENTITY_OUS_AND_CHILDREN_NS,
|
||||
ENTITY_OUS_SUSP,
|
||||
ENTITY_OUS_AND_CHILDREN_SUSP,
|
||||
ENTITY_OUS_NA_NS,
|
||||
ENTITY_OUS_AND_CHILDREN_NA_NS,
|
||||
}
|
||||
OU_DIRECT_ENTITY_TYPES = {
|
||||
ENTITY_OU,
|
||||
ENTITY_OUS,
|
||||
ENTITY_OU_NA,
|
||||
ENTITY_OUS_NA,
|
||||
ENTITY_OU_ARCH,
|
||||
ENTITY_OUS_ARCH,
|
||||
ENTITY_OU_NS,
|
||||
ENTITY_OUS_NS,
|
||||
ENTITY_OU_SUSP,
|
||||
ENTITY_OUS_SUSP,
|
||||
ENTITY_OU_NA_NS,
|
||||
ENTITY_OUS_NA_NS,
|
||||
}
|
||||
CROS_OU_ENTITY_TYPES = {
|
||||
ENTITY_CROS_OU,
|
||||
ENTITY_CROS_OU_AND_CHILDREN,
|
||||
ENTITY_CROS_OU_QUERY,
|
||||
ENTITY_CROS_OU_AND_CHILDREN_QUERY,
|
||||
ENTITY_CROS_OU_QUERIES,
|
||||
ENTITY_CROS_OU_AND_CHILDREN_QUERIES,
|
||||
}
|
||||
CROS_OUS_ENTITY_TYPES = {
|
||||
ENTITY_CROS_OUS,
|
||||
ENTITY_CROS_OUS_AND_CHILDREN,
|
||||
ENTITY_CROS_OUS_QUERY,
|
||||
ENTITY_CROS_OUS_AND_CHILDREN_QUERY,
|
||||
ENTITY_CROS_OUS_QUERIES,
|
||||
ENTITY_CROS_OUS_AND_CHILDREN_QUERIES,
|
||||
}
|
||||
CROS_OU_CHILDREN_ENTITY_TYPES = {
|
||||
ENTITY_CROS_OU_AND_CHILDREN,
|
||||
ENTITY_CROS_OU_AND_CHILDREN_QUERY,
|
||||
ENTITY_CROS_OU_AND_CHILDREN_QUERIES,
|
||||
ENTITY_CROS_OUS_AND_CHILDREN,
|
||||
ENTITY_CROS_OUS_AND_CHILDREN_QUERY,
|
||||
ENTITY_CROS_OUS_AND_CHILDREN_QUERIES,
|
||||
}
|
||||
CROS_OU_QUERY_ENTITY_TYPES = {
|
||||
ENTITY_CROS_OU_QUERY,
|
||||
ENTITY_CROS_OU_AND_CHILDREN_QUERY,
|
||||
ENTITY_CROS_OUS_QUERY,
|
||||
ENTITY_CROS_OUS_AND_CHILDREN_QUERY,
|
||||
}
|
||||
CROS_OU_QUERIES_ENTITY_TYPES = {
|
||||
ENTITY_CROS_OU_QUERIES,
|
||||
ENTITY_CROS_OU_AND_CHILDREN_QUERIES,
|
||||
ENTITY_CROS_OUS_QUERIES,
|
||||
ENTITY_CROS_OUS_AND_CHILDREN_QUERIES,
|
||||
}
|
||||
#
|
||||
ALL_USERS_QUERY_MAP = {
|
||||
ENTITY_ALL_USERS: 'isSuspended=False',
|
||||
ENTITY_ALL_USERS_NA: 'isArchived=False',
|
||||
ENTITY_ALL_USERS_ARCH: 'isArchived=True',
|
||||
ENTITY_ALL_USERS_NS: 'isSuspended=False',
|
||||
ENTITY_ALL_USERS_NS_SUSP: None,
|
||||
ENTITY_ALL_USERS_SUSP: 'isSuspended=True',
|
||||
ENTITY_ALL_USERS_NA_NS: 'isArchived=False isSuspended=False',
|
||||
ENTITY_ALL_USERS_NS_SUSP: None,
|
||||
}
|
||||
DOMAINS_QUERY_MAP = {
|
||||
ENTITY_DOMAINS: None,
|
||||
ENTITY_DOMAINS_NA: 'isArchived=False',
|
||||
ENTITY_DOMAINS_ARCH: 'isArchived=True',
|
||||
ENTITY_DOMAINS_NS: 'isSuspended=False',
|
||||
ENTITY_DOMAINS_SUSP: 'isSuspended=True',
|
||||
ENTITY_DOMAINS_NA_NS: 'isArchived=False isSuspended=False',
|
||||
}
|
||||
GROUPS_QUERY_MAP = { #(isArchived, isSuspended)
|
||||
ENTITY_GROUP_NA: (False, None),
|
||||
ENTITY_GROUPS_NA: (False, None),
|
||||
ENTITY_GROUP_ARCH: (True, None),
|
||||
ENTITY_GROUPS_ARCH: (True, None),
|
||||
ENTITY_GROUP_NS: (None, False),
|
||||
ENTITY_GROUPS_NS: (None, False),
|
||||
ENTITY_GROUP_SUSP: (None, True),
|
||||
ENTITY_GROUPS_SUSP: (None, True),
|
||||
ENTITY_GROUP_NA_NS: (False, False),
|
||||
ENTITY_GROUPS_NA_NS: (False, False),
|
||||
}
|
||||
GROUP_USERS_QUERY_MAP = { #(isArchived, isSuspended)
|
||||
ENTITY_GROUP_USERS_NA: (False, None),
|
||||
ENTITY_GROUP_USERS_ARCH: (True, None),
|
||||
ENTITY_GROUP_USERS_NS: (None, False),
|
||||
ENTITY_GROUP_USERS_SUSP: (None, True),
|
||||
ENTITY_GROUP_USERS_NA_NS: (False, False),
|
||||
}
|
||||
OU_QUERY_MAP = { #(isArchived, isSuspended)
|
||||
ENTITY_OU_NA: (False, None),
|
||||
ENTITY_OUS_NA: (False, None),
|
||||
ENTITY_OU_AND_CHILDREN_NA: (False, None),
|
||||
ENTITY_OUS_AND_CHILDREN_NA: (False, None),
|
||||
ENTITY_OU_ARCH: (True, None),
|
||||
ENTITY_OUS_ARCH: (True, None),
|
||||
ENTITY_OU_AND_CHILDREN_ARCH: (True, None),
|
||||
ENTITY_OUS_AND_CHILDREN_ARCH: (True, None),
|
||||
ENTITY_OU_NS: (None, False),
|
||||
ENTITY_OUS_NS: (None, False),
|
||||
ENTITY_OU_AND_CHILDREN_NS: (None, False),
|
||||
ENTITY_OUS_AND_CHILDREN_NS: (None, False),
|
||||
ENTITY_OU_SUSP: (None, True),
|
||||
ENTITY_OUS_SUSP: (None, True),
|
||||
ENTITY_OU_AND_CHILDREN_SUSP: (None, True),
|
||||
ENTITY_OUS_AND_CHILDREN_SUSP: (None, True),
|
||||
ENTITY_OU_NA_NS: (False, False),
|
||||
ENTITY_OUS_NA_NS: (False, False),
|
||||
ENTITY_OU_AND_CHILDREN_NA_NS: (False, False),
|
||||
ENTITY_OUS_AND_CHILDREN_NA_NS: (False, False),
|
||||
}
|
||||
#
|
||||
ENTITY_SELECTOR_ALL_SUBTYPES_MAP = {
|
||||
ENTITY_CROS: ENTITY_ALL_CROS,
|
||||
ENTITY_USERS: ENTITY_ALL_USERS,
|
||||
ENTITY_USERS_NA: ENTITY_ALL_USERS_NA,
|
||||
ENTITY_USERS_ARCH: ENTITY_ALL_USERS_ARCH,
|
||||
ENTITY_USERS_NS: ENTITY_ALL_USERS_NS,
|
||||
ENTITY_USERS_NS_SUSP: ENTITY_ALL_USERS_NS_SUSP,
|
||||
ENTITY_USERS_SUSP: ENTITY_ALL_USERS_SUSP,
|
||||
ENTITY_USERS_NA_NS: ENTITY_ALL_USERS_NA_NS,
|
||||
ENTITY_USERS_ARCH_OR_SUSP: ENTITY_ALL_USERS_ARCH_OR_SUSP,
|
||||
ENTITY_USERS_NS_SUSP: ENTITY_ALL_USERS_NS_SUSP,
|
||||
}
|
||||
# Allowed values for CL source selector datafile, csvkmd
|
||||
CROS_ENTITY_SELECTOR_DATAFILE_CSVKMD_SUBTYPES = [
|
||||
@ -352,22 +615,37 @@ class GamCLArgs():
|
||||
ENTITY_CIGROUPS,
|
||||
ENTITY_CIGROUP_USERS,
|
||||
ENTITY_DOMAINS,
|
||||
ENTITY_DOMAINS_NA,
|
||||
ENTITY_DOMAINS_ARCH,
|
||||
ENTITY_DOMAINS_NS,
|
||||
ENTITY_DOMAINS_SUSP,
|
||||
ENTITY_DOMAINS_NA_NS,
|
||||
ENTITY_GROUPS,
|
||||
ENTITY_GROUPS_INDE,
|
||||
ENTITY_GROUPS_NA,
|
||||
ENTITY_GROUPS_ARCH,
|
||||
ENTITY_GROUPS_NS,
|
||||
ENTITY_GROUPS_SUSP,
|
||||
ENTITY_GROUPS_NA_NS,
|
||||
ENTITY_GROUP_USERS,
|
||||
ENTITY_GROUP_USERS_NA,
|
||||
ENTITY_GROUP_USERS_ARCH,
|
||||
ENTITY_GROUP_USERS_NS,
|
||||
ENTITY_GROUP_USERS_SUSP,
|
||||
ENTITY_GROUP_USERS_NA_NS,
|
||||
ENTITY_GROUP_USERS_SELECT,
|
||||
ENTITY_OUS,
|
||||
ENTITY_OUS_NA,
|
||||
ENTITY_OUS_ARCH,
|
||||
ENTITY_OUS_NS,
|
||||
ENTITY_OUS_SUSP,
|
||||
ENTITY_OUS_NA_NS,
|
||||
ENTITY_OUS_AND_CHILDREN,
|
||||
ENTITY_OUS_AND_CHILDREN_NA,
|
||||
ENTITY_OUS_AND_CHILDREN_ARCH,
|
||||
ENTITY_OUS_AND_CHILDREN_NS,
|
||||
ENTITY_OUS_AND_CHILDREN_SUSP,
|
||||
ENTITY_OUS_AND_CHILDREN_NA_NS,
|
||||
ENTITY_COURSEPARTICIPANTS,
|
||||
ENTITY_STUDENTS,
|
||||
ENTITY_TEACHERS,
|
||||
@ -377,6 +655,7 @@ class GamCLArgs():
|
||||
GAM_CMD = 'gam'
|
||||
COMMIT_BATCH_CMD = 'commit-batch'
|
||||
PRINT_CMD = 'print'
|
||||
DATETIME_CMD = 'datetime'
|
||||
SET_CMD = 'set'
|
||||
CLEAR_CMD = 'clear'
|
||||
SLEEP_CMD = 'sleep'
|
||||
@ -472,9 +751,14 @@ class GamCLArgs():
|
||||
ARG_CHATMEMBERS = 'chatmembers'
|
||||
ARG_CHATMESSAGE = 'chatmessage'
|
||||
ARG_CHATMESSAGES = 'chatmessages'
|
||||
ARG_CHATSECTION = 'chatsection'
|
||||
ARG_CHATSECTIONS = 'chatsections'
|
||||
ARG_CHATSECTIONITEM = 'chatsectionitem'
|
||||
ARG_CHATSECTIONITEMS = 'chatsectionitems'
|
||||
ARG_CHATSPACE = 'chatspace'
|
||||
ARG_CHATSPACES = 'chatspaces'
|
||||
ARG_CHATSPACEDM = 'chatspacedm'
|
||||
ARG_CHROMEDEVICECOUNTS = 'chromedevicecounts'
|
||||
ARG_CHROMEAPP = 'chromeapp'
|
||||
ARG_CHROMEAPPS = 'chromeapps'
|
||||
ARG_CHROMEAPPDEVICES = 'chromeappdevices'
|
||||
@ -508,6 +792,7 @@ class GamCLArgs():
|
||||
ARG_CLASSIFICATIONLABELPERMISSIONS = 'classificationlabelpermissions'
|
||||
ARG_CLASS = 'class'
|
||||
ARG_CLASSES = 'classes'
|
||||
ARG_CLASSCOUNTS = 'classcounts'
|
||||
ARG_CLASSPARTICIPANTS = 'classparticipants'
|
||||
ARG_CLASSROOMINVITATION = 'classroominvitation'
|
||||
ARG_CLASSROOMINVITATIONS = 'classroominvitations'
|
||||
@ -526,8 +811,12 @@ class GamCLArgs():
|
||||
ARG_COURSE = 'course'
|
||||
ARG_COURSES = 'courses'
|
||||
ARG_COURSEANNOUNCEMENTS = 'courseannouncements'
|
||||
ARG_COURSECOUNTS = 'coursecounts'
|
||||
ARG_COURSEMATERIALS = 'coursematerials'
|
||||
ARG_COURSEPARTICIPANTS = 'courseparticipants'
|
||||
ARG_COURSESTUDENTGROUP = 'coursestudentgroup'
|
||||
ARG_COURSESTUDENTGROUPS = 'coursestudentgroups'
|
||||
ARG_COURSESTUDENTGROUPMEMBERS = 'coursestudentgroupmembers'
|
||||
ARG_COURSESUBMISSIONS = 'coursesubmissions'
|
||||
ARG_COURSETOPICS = 'coursetopics'
|
||||
ARG_COURSEWORK = 'coursework'
|
||||
@ -639,6 +928,8 @@ class GamCLArgs():
|
||||
ARG_GUARDIANINVITE = 'guardianinvite'
|
||||
ARG_GUARDIANINVITATION = 'guardianinvitation'
|
||||
ARG_GUARDIANINVITATIONS = 'guardianinvitations'
|
||||
ARG_GUESTUSER = 'guestuser'
|
||||
ARG_GUESTUSERS = 'guestusers'
|
||||
ARG_HOLD = 'hold'
|
||||
ARG_HOLDS = 'holds'
|
||||
ARG_IMAP = 'imap'
|
||||
@ -856,6 +1147,7 @@ class GamCLArgs():
|
||||
OB_ARGUMENT = 'argument'
|
||||
OB_ASP_ID_LIST = 'ASPIDList'
|
||||
OB_ASSET_ID = 'AssetID'
|
||||
OB_ADMIN_ASSIGNEE_TYPE_LIST = 'AdminAssigneeTypeList'
|
||||
OB_BROWSER_ENROLLEMNT_TOKEN_ID = 'BrowserEnrollmentTokenID'
|
||||
OB_BROWSER_ENTITY = 'BrowserEntity'
|
||||
OB_BUILDING_ID = 'BuildingID'
|
||||
@ -871,6 +1163,7 @@ class GamCLArgs():
|
||||
OB_CHAT_MEMBER = 'ChatMember'
|
||||
OB_CHAT_MESSAGE = 'ChatMessage'
|
||||
OB_CHAT_MESSAGE_ID = 'ChatMessageID'
|
||||
OB_CHAT_SECTION = 'ChatSection'
|
||||
OB_CHAT_SPACE = 'ChatSpace'
|
||||
OB_CHAT_SPACE_LIST = 'ChatSpaceList'
|
||||
OB_CHAT_THREAD = 'ChatThread'
|
||||
@ -921,6 +1214,7 @@ class GamCLArgs():
|
||||
OB_CSE_KEYPAIR_ID = 'CSEKeyPairID'
|
||||
OB_CUSTOMER_ID = 'CustomerID'
|
||||
OB_CUSTOMER_AUTH_TOKEN = 'CustomerAuthToken'
|
||||
OB_DATETIME_FORMAT = 'DateTimeFormat'
|
||||
OB_DEVICE_FILE_ENTITY = 'DeviceFileEntity'
|
||||
OB_DEVICE_ENTITY = 'DeviceEntity'
|
||||
OB_DEVICE_ID = 'DeviceID'
|
||||
@ -933,6 +1227,7 @@ class GamCLArgs():
|
||||
OB_DOMAIN_NAME_LIST = 'DomainNameList'
|
||||
OB_DRIVE_FILE_ENTITY = 'DriveFileEntity'
|
||||
OB_DRIVE_FILE_ID = 'DriveFileID'
|
||||
OB_DRIVE_FILE_ID_LIST = 'DriveFileIDList'
|
||||
OB_DRIVE_FILE_NAME = 'DriveFileName'
|
||||
OB_DRIVE_FILE_PERMISSION_ENTITY = 'DriveFilePermissionEntity'
|
||||
OB_DRIVE_FILE_PERMISSION_ID = 'DriveFilePermissionID'
|
||||
@ -990,6 +1285,7 @@ class GamCLArgs():
|
||||
OB_MATTER_ITEM = 'MatterItem'
|
||||
OB_MATTER_ITEM_LIST = 'MatterItemList'
|
||||
OB_MEET_CONFERENCE_NAME = 'MeetConferenceName'
|
||||
OB_MEET_ID = 'MeetID'
|
||||
OB_MESSAGE_ID = 'MessageID'
|
||||
OB_MIMETYPE = 'MimeType'
|
||||
OB_MIMETYPE_LIST = 'MimeTypeList'
|
||||
@ -997,6 +1293,7 @@ class GamCLArgs():
|
||||
OB_MOBILE_ENTITY = 'MobileEntity'
|
||||
OB_NETWORK_ID = 'networkID'
|
||||
OB_NAME = 'Name'
|
||||
OB_NUMBER_RANGE_LIST = 'NumberRangeList'
|
||||
OB_ORGANIZER_TYPE_LIST = 'OrganizerTypeList'
|
||||
OB_ORGUNIT_ENTITY = 'OrgUnitEntity'
|
||||
OB_ORGUNIT_ITEM = 'OrgUnitItem'
|
||||
@ -1054,7 +1351,10 @@ class GamCLArgs():
|
||||
OB_SPREADSHEET_RANGE_LIST = 'SpreadsheetRangeList'
|
||||
OB_STATE_NAME_LIST = "StateNameList"
|
||||
OB_STRING = 'String'
|
||||
OB_STRING_ENTITY = 'StringEntity'
|
||||
OB_STRING_LIST = 'StringList'
|
||||
OB_STUDENTGROUP_ID = 'StudentGroupID'
|
||||
OB_STUDENTGROUP_ID_ENTITY = 'StudentGroupIDEntity'
|
||||
OB_STUDENT_ITEM = 'StudentItem'
|
||||
OB_TAG = 'Tag'
|
||||
OB_TAGMANAGER_PATH_LIST = 'TagManagerPathList'
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2025 Ross Scroggs All Rights Reserved.
|
||||
# Copyright (C) 2026 Ross Scroggs All Rights Reserved.
|
||||
#
|
||||
# All Rights Reserved.
|
||||
#
|
||||
@ -96,6 +96,9 @@ class GamEntity():
|
||||
CHAT_MEMBER_USER = 'chmu'
|
||||
CHAT_MESSAGE = 'chms'
|
||||
CHAT_MESSAGE_ID = 'chmi'
|
||||
CHAT_OWNER_USER = 'chou'
|
||||
CHAT_SECTION = 'chse'
|
||||
CHAT_SECTION_ITEM = 'chsi'
|
||||
CHAT_SPACE = 'chsp'
|
||||
CHAT_THREAD = 'chth'
|
||||
CHILD_ORGANIZATIONAL_UNIT = 'corg'
|
||||
@ -105,6 +108,7 @@ class GamEntity():
|
||||
CHROME_BROWSER_ENROLLMENT_TOKEN = 'cbet'
|
||||
CHROME_CHANNEL = 'chan'
|
||||
CHROME_DEVICE = 'chdv'
|
||||
CHROME_DEVICE_COUNT = 'chdc'
|
||||
CHROME_MODEL = 'chmo'
|
||||
CHROME_NETWORK_ID = 'chni'
|
||||
CHROME_NETWORK_NAME = 'chnn'
|
||||
@ -155,6 +159,8 @@ class GamEntity():
|
||||
COURSE_MATERIAL_STATE = 'cmst'
|
||||
COURSE_NAME = 'cona'
|
||||
COURSE_STATE = 'cost'
|
||||
COURSE_STUDENTGROUP = 'cosg'
|
||||
COURSE_STUDENTGROUP_MEMBER = 'csgm'
|
||||
COURSE_SUBMISSION_ID = 'csid'
|
||||
COURSE_SUBMISSION_STATE = 'csst'
|
||||
COURSE_TOPIC = 'ctop'
|
||||
@ -247,6 +253,7 @@ class GamEntity():
|
||||
GUARDIAN = 'guar'
|
||||
GUARDIAN_INVITATION = 'gari'
|
||||
GUARDIAN_AND_INVITATION = 'gani'
|
||||
GUEST_USER = 'gstu'
|
||||
IAM_POLICY = 'iamp'
|
||||
IMAP_ENABLED = 'imap'
|
||||
INBOUND_SSO_ASSIGNMENT = 'insa'
|
||||
@ -352,7 +359,7 @@ class GamEntity():
|
||||
SNIPPETS_ENABLED = 'snip'
|
||||
SSO_KEY = 'ssok'
|
||||
SSO_SETTINGS = 'ssos'
|
||||
SOURCE_USER = 'src'
|
||||
SOURCE_USER = 'srcu'
|
||||
SPREADSHEET = 'sprd'
|
||||
SPREADSHEET_RANGE = 'ssrn'
|
||||
START_TIME = 'strt'
|
||||
@ -382,11 +389,13 @@ class GamEntity():
|
||||
URL = 'url '
|
||||
USER = 'user'
|
||||
USER_ALIAS = 'uali'
|
||||
USER_NOT_ARCHIVED = 'usna'
|
||||
USER_ARCHIVED = 'usar'
|
||||
USER_EMAIL = 'uema'
|
||||
USER_INVITATION = 'uinv'
|
||||
USER_NOT_SUSPENDED = 'uns'
|
||||
USER_SCHEMA = 'usch'
|
||||
USER_NOT_SUSPENDED = 'usns'
|
||||
USER_SUSPENDED = 'usup'
|
||||
USER_SCHEMA = 'usch'
|
||||
VACATION = 'vaca'
|
||||
VACATION_ENABLED = 'vace'
|
||||
VALUE = 'val'
|
||||
@ -458,6 +467,9 @@ class GamEntity():
|
||||
CHAT_MEMBER: ['Chat Members', 'Chat Member'],
|
||||
CHAT_MEMBER_GROUP: ['Chat Group Members', 'Chat Group Member'],
|
||||
CHAT_MEMBER_USER: ['Chat User Members', 'Chat User Member'],
|
||||
CHAT_OWNER_USER: ['Chat User Owners', 'Chat User Owner'],
|
||||
CHAT_SECTION: ['Chat User Sections', 'Chat User Section'],
|
||||
CHAT_SECTION_ITEM: ['Chat User Section Items', 'Chat User Section Item'],
|
||||
CHAT_SPACE: ['Chat Spaces', 'Chat Space'],
|
||||
CHAT_THREAD: ['Chat Threads', 'Chat Thread'],
|
||||
CHILD_ORGANIZATIONAL_UNIT: ['Child Organizational Units', 'Child Organizational Unit'],
|
||||
@ -467,6 +479,7 @@ class GamEntity():
|
||||
CHROME_BROWSER_ENROLLMENT_TOKEN: ['Chrome Browser Enrollment Tokens', 'Chrome Browser Enrollment Token'],
|
||||
CHROME_CHANNEL: ['Chrome Channels', 'Chrome Channel'],
|
||||
CHROME_DEVICE: ['Chrome Devices', 'Chrome Device'],
|
||||
CHROME_DEVICE_COUNT: ['Chrome Device Counts', 'Chrome Device Count'],
|
||||
CHROME_MODEL: ['Chrome Models', 'Chrome Model'],
|
||||
CHROME_NETWORK_ID: ['Chrome Network IDs', 'Chrome Network ID'],
|
||||
CHROME_NETWORK_NAME: ['Chrome Network Names', 'Chrome Network Name'],
|
||||
@ -517,6 +530,8 @@ class GamEntity():
|
||||
COURSE_MATERIAL_STATE: ['Course Material States', 'Course Material State'],
|
||||
COURSE_NAME: ['Course Names', 'Course Name'],
|
||||
COURSE_STATE: ['Course States', 'Course State'],
|
||||
COURSE_STUDENTGROUP: ['Course Student Groups', 'Course Student Group'],
|
||||
COURSE_STUDENTGROUP_MEMBER: ['Course Student Group Members', 'Course Student Group Member'],
|
||||
COURSE_SUBMISSION_ID: ['Course Submission IDs', 'Course Submission ID'],
|
||||
COURSE_SUBMISSION_STATE: ['Course Submission States', 'Course Submission State'],
|
||||
COURSE_TOPIC: ['Course Topics', 'Course Topic'],
|
||||
@ -606,6 +621,7 @@ class GamEntity():
|
||||
GROUP_MEMBERSHIP_TREE: ['Group Membership Trees', 'Group Membership Tree'],
|
||||
GROUP_SETTINGS: ['Group Settings', 'Group Settings'],
|
||||
GROUP_TREE: ['Group Trees', 'Group Tree'],
|
||||
GUEST_USER: ['Guest Users', 'Guest User'],
|
||||
GUARDIAN: ['Guardians', 'Guardian'],
|
||||
GUARDIAN_INVITATION: ['Guardian Invitations', 'Guardian Invitation'],
|
||||
GUARDIAN_AND_INVITATION: ['Guardians and Invitations', 'Guardian and Invitation'],
|
||||
@ -744,11 +760,13 @@ class GamEntity():
|
||||
URL: ['URLs', 'URL'],
|
||||
USER: ['Users', 'User'],
|
||||
USER_ALIAS: ['User Aliases', 'User Alias'],
|
||||
USER_NOT_ARCHIVED: ['Users (Not archived)', 'User (Not archived)'],
|
||||
USER_ARCHIVED: ['Users (Archived)', 'User (Archived)'],
|
||||
USER_EMAIL: ['User Emails', 'User Email'],
|
||||
USER_INVITATION: ['User Invitations', 'User Invitation'],
|
||||
USER_NOT_SUSPENDED: ['Users (Not suspended)', 'User (Not suspended)'],
|
||||
USER_SCHEMA: ['Schemas', 'Schema'],
|
||||
USER_SUSPENDED: ['Users (Suspended)', 'User (Suspended)'],
|
||||
USER_SCHEMA: ['Schemas', 'Schema'],
|
||||
VACATION: ['Vacation', 'Vacation'],
|
||||
VACATION_ENABLED: ['Vacation Enabled', 'Vacation Enabled'],
|
||||
VALUE: ['Values', 'Value'],
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2025 Ross Scroggs All Rights Reserved.
|
||||
# Copyright (C) 2026 Ross Scroggs All Rights Reserved.
|
||||
#
|
||||
# All Rights Reserved.
|
||||
#
|
||||
@ -42,6 +42,7 @@ CANNOT_DELETE_PERMISSION = 'cannotDeletePermission'
|
||||
CANNOT_DELETE_PRIMARY_CALENDAR = 'cannotDeletePrimaryCalendar'
|
||||
CANNOT_DELETE_PRIMARY_SENDAS = 'cannotDeletePrimarySendAs'
|
||||
CANNOT_DELETE_RESOURCE_WITH_CHILDREN = 'cannotDeleteResourceWithChildren'
|
||||
CANNOT_MODIFY_ACL_OF_CALENDAR_OWNER = 'cannotModifyAclOfCalendarOwner'
|
||||
CANNOT_MODIFY_INHERITED_PERMISSION = 'cannotModifyInheritedPermission'
|
||||
CANNOT_MODIFY_INHERITED_TEAMDRIVE_PERMISSION = 'cannotModifyInheritedTeamDrivePermission'
|
||||
CANNOT_MODIFY_RESTRICTED_LABEL = 'cannotModifyRestrictedLabel'
|
||||
@ -55,6 +56,7 @@ CANNOT_SHARE_GROUPS_WITHLINK = 'cannotShareGroupsWithLink'
|
||||
CANNOT_SHARE_USERS_WITHLINK = 'cannotShareUsersWithLink'
|
||||
CANNOT_SHARE_TEAMDRIVE_TOPFOLDER_WITH_ANYONEORDOMAINS = 'cannotShareTeamDriveTopFolderWithAnyoneOrDomains'
|
||||
CANNOT_SHARE_TEAMDRIVE_WITH_NONGOOGLE_ACCOUNTS = 'cannotShareTeamDriveWithNonGoogleAccounts'
|
||||
CANNOT_UNSUBSCRIBE_FROM_OWNED_CALENDAR = 'cannotUnsubscribeFromOwnedCalendar'
|
||||
CANNOT_UPDATE_PERMISSION = 'cannotUpdatePermission'
|
||||
CONDITION_NOT_MET = 'conditionNotMet'
|
||||
CONFLICT = 'conflict'
|
||||
@ -179,6 +181,7 @@ TEAMDRIVES_SHARING_RESTRICTION_NOT_ALLOWED = 'teamDrivesSharingRestrictionNotAll
|
||||
TEAMDRIVES_SHORTCUT_FILE_NOT_SUPPORTED = 'teamDrivesShortcutFileNotSupported'
|
||||
TIME_RANGE_EMPTY = 'timeRangeEmpty'
|
||||
TRANSIENT_ERROR = 'transientError'
|
||||
UNIMPLEMENTED_ERROR = 'unimplementedError'
|
||||
UNKNOWN_ERROR = 'unknownError'
|
||||
UNSUPPORTED_LANGUAGE_CODE = 'unsupportedLanguageCode'
|
||||
UNSUPPORTED_SUPERVISED_ACCOUNT = 'unsupportedSupervisedAccount'
|
||||
@ -195,7 +198,7 @@ ACTIVITY_THROW_REASONS = [SERVICE_NOT_AVAILABLE, BAD_REQUEST]
|
||||
ALERT_THROW_REASONS = [SERVICE_NOT_AVAILABLE, AUTH_ERROR, PERMISSION_DENIED]
|
||||
CALENDAR_THROW_REASONS = [SERVICE_NOT_AVAILABLE, AUTH_ERROR, NOT_A_CALENDAR_USER]
|
||||
CIGROUP_CREATE_THROW_REASONS = [SERVICE_NOT_AVAILABLE, ALREADY_EXISTS, DOMAIN_NOT_FOUND, DOMAIN_CANNOT_USE_APIS, FORBIDDEN, INVALID, INVALID_ARGUMENT, PERMISSION_DENIED, FAILED_PRECONDITION]
|
||||
CIGROUP_GET_THROW_REASONS = [SERVICE_NOT_AVAILABLE, NOT_FOUND, GROUP_NOT_FOUND, DOMAIN_NOT_FOUND, DOMAIN_CANNOT_USE_APIS, FORBIDDEN, BAD_REQUEST, INVALID, SYSTEM_ERROR, PERMISSION_DENIED]
|
||||
CIGROUP_GET_THROW_REASONS = [SERVICE_NOT_AVAILABLE, NOT_FOUND, GROUP_NOT_FOUND, DOMAIN_NOT_FOUND, DOMAIN_CANNOT_USE_APIS, FORBIDDEN, BAD_REQUEST, INVALID, INVALID_ARGUMENT, SYSTEM_ERROR, PERMISSION_DENIED]
|
||||
CIGROUP_LIST_THROW_REASONS = [SERVICE_NOT_AVAILABLE, RESOURCE_NOT_FOUND, DOMAIN_NOT_FOUND, DOMAIN_CANNOT_USE_APIS, FORBIDDEN, BAD_REQUEST, INVALID, INVALID_ARGUMENT, SYSTEM_ERROR, PERMISSION_DENIED]
|
||||
CIGROUP_LIST_USERKEY_THROW_REASONS = CIGROUP_LIST_THROW_REASONS+[INVALID_ARGUMENT]
|
||||
CIGROUP_UPDATE_THROW_REASONS = [SERVICE_NOT_AVAILABLE, NOT_FOUND, GROUP_NOT_FOUND, DOMAIN_NOT_FOUND, DOMAIN_CANNOT_USE_APIS,
|
||||
@ -405,6 +408,8 @@ class cannotDeletePrimarySendAs(Exception):
|
||||
pass
|
||||
class cannotDeleteResourceWithChildren(Exception):
|
||||
pass
|
||||
class cannotModifyAclOfCalendarOwner(Exception):
|
||||
pass
|
||||
class cannotModifyInheritedPermission(Exception):
|
||||
pass
|
||||
class cannotModifyInheritedTeamDrivePermission(Exception):
|
||||
@ -431,6 +436,8 @@ class cannotShareTeamDriveTopFolderWithAnyoneOrDomains(Exception):
|
||||
pass
|
||||
class cannotShareTeamDriveWithNonGoogleAccounts(Exception):
|
||||
pass
|
||||
class cannotUnsubscribeFromOwnedCalendar(Exception):
|
||||
pass
|
||||
class cannotUpdatePermission(Exception):
|
||||
pass
|
||||
class conditionNotMet(Exception):
|
||||
@ -671,6 +678,8 @@ class timeRangeEmpty(Exception):
|
||||
pass
|
||||
class transientError(Exception):
|
||||
pass
|
||||
class unimplementedError(Exception):
|
||||
pass
|
||||
class unknownError(Exception):
|
||||
pass
|
||||
class unsupportedLanguageCode(Exception):
|
||||
@ -710,6 +719,7 @@ REASON_EXCEPTION_MAP = {
|
||||
CANNOT_DELETE_PRIMARY_CALENDAR: cannotDeletePrimaryCalendar,
|
||||
CANNOT_DELETE_PRIMARY_SENDAS: cannotDeletePrimarySendAs,
|
||||
CANNOT_DELETE_RESOURCE_WITH_CHILDREN: cannotDeleteResourceWithChildren,
|
||||
CANNOT_MODIFY_ACL_OF_CALENDAR_OWNER: cannotModifyAclOfCalendarOwner,
|
||||
CANNOT_MODIFY_INHERITED_PERMISSION: cannotModifyInheritedPermission,
|
||||
CANNOT_MODIFY_INHERITED_TEAMDRIVE_PERMISSION: cannotModifyInheritedTeamDrivePermission,
|
||||
CANNOT_MODIFY_RESTRICTED_LABEL: cannotModifyRestrictedLabel,
|
||||
@ -723,6 +733,7 @@ REASON_EXCEPTION_MAP = {
|
||||
CANNOT_SHARE_USERS_WITHLINK: cannotShareUsersWithLink,
|
||||
CANNOT_SHARE_TEAMDRIVE_TOPFOLDER_WITH_ANYONEORDOMAINS: cannotShareTeamDriveTopFolderWithAnyoneOrDomains,
|
||||
CANNOT_SHARE_TEAMDRIVE_WITH_NONGOOGLE_ACCOUNTS: cannotShareTeamDriveWithNonGoogleAccounts,
|
||||
CANNOT_UNSUBSCRIBE_FROM_OWNED_CALENDAR: cannotUnsubscribeFromOwnedCalendar,
|
||||
CANNOT_UPDATE_PERMISSION: cannotUpdatePermission,
|
||||
CONDITION_NOT_MET: conditionNotMet,
|
||||
CONFLICT: conflict,
|
||||
@ -843,6 +854,7 @@ REASON_EXCEPTION_MAP = {
|
||||
TEAMDRIVES_SHORTCUT_FILE_NOT_SUPPORTED: teamDrivesShortcutFileNotSupported,
|
||||
TIME_RANGE_EMPTY: timeRangeEmpty,
|
||||
TRANSIENT_ERROR: transientError,
|
||||
UNIMPLEMENTED_ERROR: unimplementedError,
|
||||
UNKNOWN_ERROR: unknownError,
|
||||
UNSUPPORTED_LANGUAGE_CODE: unsupportedLanguageCode,
|
||||
UNSUPPORTED_SUPERVISED_ACCOUNT: unsupportedSupervisedAccount,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2025 Ross Scroggs All Rights Reserved.
|
||||
# Copyright (C) 2026 Ross Scroggs All Rights Reserved.
|
||||
#
|
||||
# All Rights Reserved.
|
||||
#
|
||||
@ -31,6 +31,8 @@ API_CALLS_RETRY_DATA = 'rtry'
|
||||
CACHE_DIR = 'gacd'
|
||||
# Reset GAM cache directory after discovery
|
||||
CACHE_DISCOVERY_ONLY = 'gcdo'
|
||||
# Classroom owner service object
|
||||
CLASSROOM_OWNER_SA = 'cosa'
|
||||
# Classroom service not available
|
||||
CLASSROOM_SERVICE_NOT_AVAILABLE = 'csna'
|
||||
# Command logging
|
||||
@ -105,8 +107,12 @@ CURRENT_SVCACCT_USER = 'csa'
|
||||
DATETIME_NOW = 'dtno'
|
||||
# If debug_level > 0: extra_args['prettyPrint'] = True, httplib2.debuglevel = gam_debug_level, appsObj.debug = True
|
||||
DEBUG_LEVEL = 'dbgl'
|
||||
# Whether debug output should redact sensitive credentials
|
||||
DEBUG_REDACTION = 'dbrd'
|
||||
# Decoded ID token
|
||||
DECODED_ID_TOKEN = 'didt'
|
||||
# Developer Preview APIs
|
||||
DEVELOPER_PREVIEW_APIS = 'dapi'
|
||||
# Index of start of <UserTypeEntity> in command line
|
||||
ENTITY_CL_DELAY_START = 'ecld'
|
||||
ENTITY_CL_START = 'ecls'
|
||||
@ -221,6 +227,7 @@ Globals = {
|
||||
API_CALLS_RETRY_DATA: {},
|
||||
CACHE_DIR: None,
|
||||
CACHE_DISCOVERY_ONLY: True,
|
||||
CLASSROOM_OWNER_SA: {},
|
||||
CLASSROOM_SERVICE_NOT_AVAILABLE: False,
|
||||
CMDLOG_HANDLER: None,
|
||||
CMDLOG_LOGGER: None,
|
||||
@ -260,7 +267,9 @@ Globals = {
|
||||
CURRENT_SVCACCT_USER: None,
|
||||
DATETIME_NOW: None,
|
||||
DEBUG_LEVEL: 0,
|
||||
DEBUG_REDACTION: True,
|
||||
DECODED_ID_TOKEN: None,
|
||||
DEVELOPER_PREVIEW_APIS: set(),
|
||||
ENTITY_CL_DELAY_START: 1,
|
||||
ENTITY_CL_START: 1,
|
||||
EXTRA_ARGS_LIST: [],
|
||||
|
||||
@ -53,7 +53,7 @@ Please go to:
|
||||
5. Click "NEXT"
|
||||
6. Under "Audience", choose INTERNAL
|
||||
7. Click "NEXT"
|
||||
8. Under, "Contact Information", enter an email address in "Email addresses *"
|
||||
8. Under, "Contact Information", enter {2} or another value in "Email addresses *"
|
||||
9. Click "NEXT"
|
||||
10. Under "Finish", click "I agree to the Google API Services: User Data Policy."
|
||||
11. Click "CONTINUE"
|
||||
@ -224,6 +224,8 @@ COUNT_N_EXCEEDS_MAX_TO_PROCESS_M = 'Count {0} exceeds maximum to {1} {2}'
|
||||
CORRUPT_FILE = 'Corrupt file'
|
||||
COULD_NOT_FIND_ANY_YUBIKEY = 'Could not find any YubiKey\n'
|
||||
COULD_NOT_FIND_YUBIKEY_WITH_SERIAL = 'Could not find YubiKey with serial number {0}\n'
|
||||
CREATE_DELEGATE_NOTIFY_MESSAGE = '#user# has granted you #delegate# access to read, delete and send mail on their behalf.'
|
||||
CREATE_DELEGATE_NOTIFY_SUBJECT = '#user# mail delegation to #delegate#'
|
||||
CREATE_USER_NOTIFY_MESSAGE = 'Hello #givenname# #familyname#,\n\nYou have a new account at #domain#\nAccount details:\nUsername: #user#\nPassword: #password#\nStart using your new account by signing in at\nhttps://www.google.com/accounts/AccountChooser?Email=#user#&continue=https://workspace.google.com/dashboard\n'
|
||||
CREATE_USER_NOTIFY_SUBJECT = 'Welcome to #domain#'
|
||||
CSV_DATA_ALREADY_SAVED = 'CSV data already saved'
|
||||
@ -234,6 +236,7 @@ DATA_TRANSFER_COMPLETED = 'Data Transfer completed: {0}\n'
|
||||
DATA_UPLOADED_TO_DRIVE_FILE = 'Data uploaded to Drive File'
|
||||
DEFAULT_SMIME = 'Default S/MIME'
|
||||
DELETED = 'Deleted'
|
||||
DEVELOPER_PREVIEW_REQUIRED = 'Developer Preview is required for this command\n'
|
||||
DEVICE_LIST_BUG = 'GAM hit Google internal bug 237397223. Please file a Google Support ticket stating that you are encountering this bug.'
|
||||
DEVICE_LIST_BUG_WORKAROUND_NOT_POSSIBLE = 'GAM workaround for this issue only works if orderby argument is not used and query does not contain \'register\'.'
|
||||
DEVICE_LIST_BUG_ATTEMPTING_WORKAROUND = 'GAM is attempting to work around the bug by filtering for devices created on or after the newest we\'ve seen ({0})...\n'
|
||||
@ -353,6 +356,7 @@ LESS_THAN_1_SECOND = 'less than 1 second'
|
||||
LIST_CHROMEOS_INVALID_INPUT_PAGE_TOKEN_RETRY = 'List ChromeOSdevices Invalid Input: pageToken retry'
|
||||
LOGGING_INITIALIZATION_ERROR = 'Logging initialization error: {0}'
|
||||
LOOKING_UP_GOOGLE_UNIQUE_ID = 'Looking up Google Unique ID'
|
||||
MAP_PERMISSIONS_EMAIL_FILE_HEADERS_REQUIRED = '{0} <CSVFileInput> requires headers "sourceEmail" and "destinationEmail"'
|
||||
MARKED_AS = 'Marked as'
|
||||
MATCHED_THE_FOLLOWING = 'Matched the following'
|
||||
MATTER_NOT_OPEN = 'Matter needs to be open, current state is: {0}'
|
||||
@ -499,6 +503,7 @@ STATISTICS_MOVE_FILE = 'Total: {0}, Moved: {1}, Shortcut created {2}, Shortcut e
|
||||
STATISTICS_MOVE_FOLDER = 'Total: {0}, Moved: {1}, Shortcut created {2}, Shortcut exists {3}, Duplicate: {4}, Merged: {5}, Move Failed: {6}, Not writable: {7}'
|
||||
STATISTICS_USER_NOT_ORGANIZER = 'User not organizer: {0}'
|
||||
STRING_LENGTH = 'string length'
|
||||
STUDENT_NOT_IN_COURSE = 'Student not in course'
|
||||
SUBKEY_FIELD_MISMATCH = 'subkeyfield {0} does not match saved subkeyfield {1}'
|
||||
SUBSCRIPTION_NOT_FOUND = 'Could not find subscription'
|
||||
SUFFIX_NOT_ALLOWED_WITH_CUSTOMLANGUAGE = 'Suffix {0} not allowed with customLanguage {1}'
|
||||
@ -515,7 +520,7 @@ To set up Google Chat for your current project, please go to:
|
||||
|
||||
and follow the instructions at:
|
||||
|
||||
https://github.com/GAM-team/GAM/wiki/Chat-Bot#set-up-a-chat-bot
|
||||
https://github.com/GAM-team/GAM/wiki/Chat-Bot-Setup-Use#set-up-a-chat-bot
|
||||
|
||||
You'll use projects/{1}/topics/no-topic in Connection settings Cloud Pub/Sub Topic Name
|
||||
"""
|
||||
|
||||
@ -100,7 +100,7 @@ _SKUS = {
|
||||
'1010470003': {
|
||||
'product': '101047', 'aliases': ['geminibiz'], 'displayName': 'Gemini Business'},
|
||||
'1010470004': {
|
||||
'product': '101047', 'aliases': ['geminiedu'], 'displayName': 'Gemini Education'},
|
||||
'product': '101047', 'aliases': ['gaiproedu', 'geminiedu'], 'displayName': 'Google AI Pro for Education'},
|
||||
'1010470005': {
|
||||
'product': '101047', 'aliases': ['geminiedupremium'], 'displayName': 'Gemini Education Premium'},
|
||||
'1010470006': {
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
"""
|
||||
|
||||
GAM_VER_LIBS = [
|
||||
'arrow',
|
||||
'chardet',
|
||||
'cryptography',
|
||||
'filelock',
|
||||
@ -33,6 +34,5 @@ GAM_VER_LIBS = [
|
||||
'passlib',
|
||||
'pathvalidate',
|
||||
'pyscard',
|
||||
'python-dateutil',
|
||||
'yubikey-manager',
|
||||
]
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (C) 2023 Ross Scroggs All Rights Reserved.
|
||||
# Copyright (C) 2025 Ross Scroggs All Rights Reserved.
|
||||
#
|
||||
# All Rights Reserved.
|
||||
#
|
||||
@ -19,11 +19,19 @@
|
||||
"""YubiKey"""
|
||||
|
||||
import base64
|
||||
import datetime
|
||||
from datetime import datetime, timedelta
|
||||
from secrets import SystemRandom
|
||||
import string
|
||||
import sys
|
||||
|
||||
from gam import mplock
|
||||
|
||||
from gam import systemErrorExit
|
||||
from gam import readStdin
|
||||
from gam import writeStdout
|
||||
|
||||
from gam.gamlib import glmsgs as Msg
|
||||
|
||||
from cryptography.hazmat.primitives import hashes, serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import padding
|
||||
from smartcard.Exceptions import CardConnectionException
|
||||
@ -32,7 +40,6 @@ from ykman.piv import generate_self_signed_certificate, generate_chuid
|
||||
from yubikit.piv import DEFAULT_MANAGEMENT_KEY, \
|
||||
InvalidPinError, \
|
||||
KEY_TYPE, \
|
||||
MANAGEMENT_KEY_TYPE, \
|
||||
PIN_POLICY, \
|
||||
PivSession, \
|
||||
OBJECT_ID, \
|
||||
@ -49,14 +56,6 @@ YUBIKEY_VALUE_ERROR_RC = 85
|
||||
YUBIKEY_MULTIPLE_CONNECTED_RC = 86
|
||||
YUBIKEY_NOT_FOUND_RC = 87
|
||||
|
||||
from gam import mplock
|
||||
|
||||
from gam import systemErrorExit
|
||||
from gam import readStdin
|
||||
from gam import writeStdout
|
||||
|
||||
from gam.gamlib import glmsgs as Msg
|
||||
|
||||
PIN_PUK_CHARS = string.ascii_letters+string.digits+string.punctuation
|
||||
|
||||
class YubiKey():
|
||||
@ -148,17 +147,17 @@ class YubiKey():
|
||||
piv.change_puk('12345678', new_puk)
|
||||
piv.change_pin('123456', new_pin)
|
||||
writeStdout(Msg.YUBIKEY_PIN_SET_TO.format(new_pin))
|
||||
piv.authenticate(MANAGEMENT_KEY_TYPE.TDES, DEFAULT_MANAGEMENT_KEY)
|
||||
piv.authenticate(piv.management_key_type, DEFAULT_MANAGEMENT_KEY)
|
||||
piv.verify_pin(new_pin)
|
||||
writeStdout(Msg.YUBIKEY_GENERATING_NONEXPORTABLE_PRIVATE_KEY)
|
||||
pubkey = piv.generate_key(SLOT.AUTHENTICATION,
|
||||
KEY_TYPE.RSA2048,
|
||||
PIN_POLICY.ALWAYS,
|
||||
TOUCH_POLICY.NEVER)
|
||||
now = datetime.datetime.utcnow()
|
||||
valid_to = now + datetime.timedelta(days=36500)
|
||||
now = datetime.utcnow()
|
||||
valid_to = now + timedelta(days=3650)
|
||||
subject = 'CN=GAM Created Key'
|
||||
piv.authenticate(MANAGEMENT_KEY_TYPE.TDES, DEFAULT_MANAGEMENT_KEY)
|
||||
piv.authenticate(piv.management_key_type, DEFAULT_MANAGEMENT_KEY)
|
||||
piv.verify_pin(new_pin)
|
||||
cert = generate_self_signed_certificate(piv,
|
||||
SLOT.AUTHENTICATION,
|
||||
|
||||
@ -1,27 +0,0 @@
|
||||
# Copyright 2014 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# Set default logging handler to avoid "No handler found" warnings.
|
||||
import logging
|
||||
|
||||
try: # Python 2.7+
|
||||
from logging import NullHandler
|
||||
except ImportError:
|
||||
|
||||
class NullHandler(logging.Handler):
|
||||
def emit(self, record):
|
||||
pass
|
||||
|
||||
|
||||
logging.getLogger(__name__).addHandler(NullHandler())
|
||||
@ -1,167 +0,0 @@
|
||||
# Copyright 2016 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Helpers for authentication using oauth2client or google-auth."""
|
||||
|
||||
import httplib2
|
||||
|
||||
try:
|
||||
import google.auth
|
||||
import google.auth.credentials
|
||||
|
||||
HAS_GOOGLE_AUTH = True
|
||||
except ImportError: # pragma: NO COVER
|
||||
HAS_GOOGLE_AUTH = False
|
||||
|
||||
try:
|
||||
import google_auth_httplib2
|
||||
except ImportError: # pragma: NO COVER
|
||||
google_auth_httplib2 = None
|
||||
|
||||
try:
|
||||
import oauth2client
|
||||
import oauth2client.client
|
||||
|
||||
HAS_OAUTH2CLIENT = True
|
||||
except ImportError: # pragma: NO COVER
|
||||
HAS_OAUTH2CLIENT = False
|
||||
|
||||
|
||||
def credentials_from_file(filename, scopes=None, quota_project_id=None):
|
||||
"""Returns credentials loaded from a file."""
|
||||
if HAS_GOOGLE_AUTH:
|
||||
credentials, _ = google.auth.load_credentials_from_file(
|
||||
filename, scopes=scopes, quota_project_id=quota_project_id
|
||||
)
|
||||
return credentials
|
||||
else:
|
||||
raise EnvironmentError(
|
||||
"client_options.credentials_file is only supported in google-auth."
|
||||
)
|
||||
|
||||
|
||||
def default_credentials(scopes=None, quota_project_id=None):
|
||||
"""Returns Application Default Credentials."""
|
||||
if HAS_GOOGLE_AUTH:
|
||||
credentials, _ = google.auth.default(
|
||||
scopes=scopes, quota_project_id=quota_project_id
|
||||
)
|
||||
return credentials
|
||||
elif HAS_OAUTH2CLIENT:
|
||||
if scopes is not None or quota_project_id is not None:
|
||||
raise EnvironmentError(
|
||||
"client_options.scopes and client_options.quota_project_id are not supported in oauth2client."
|
||||
"Please install google-auth."
|
||||
)
|
||||
return oauth2client.client.GoogleCredentials.get_application_default()
|
||||
else:
|
||||
raise EnvironmentError(
|
||||
"No authentication library is available. Please install either "
|
||||
"google-auth or oauth2client."
|
||||
)
|
||||
|
||||
|
||||
def with_scopes(credentials, scopes):
|
||||
"""Scopes the credentials if necessary.
|
||||
|
||||
Args:
|
||||
credentials (Union[
|
||||
google.auth.credentials.Credentials,
|
||||
oauth2client.client.Credentials]): The credentials to scope.
|
||||
scopes (Sequence[str]): The list of scopes.
|
||||
|
||||
Returns:
|
||||
Union[google.auth.credentials.Credentials,
|
||||
oauth2client.client.Credentials]: The scoped credentials.
|
||||
"""
|
||||
if HAS_GOOGLE_AUTH and isinstance(credentials, google.auth.credentials.Credentials):
|
||||
return google.auth.credentials.with_scopes_if_required(credentials, scopes)
|
||||
else:
|
||||
try:
|
||||
if credentials.create_scoped_required():
|
||||
return credentials.create_scoped(scopes)
|
||||
else:
|
||||
return credentials
|
||||
except AttributeError:
|
||||
return credentials
|
||||
|
||||
|
||||
def authorized_http(credentials):
|
||||
"""Returns an http client that is authorized with the given credentials.
|
||||
|
||||
Args:
|
||||
credentials (Union[
|
||||
google.auth.credentials.Credentials,
|
||||
oauth2client.client.Credentials]): The credentials to use.
|
||||
|
||||
Returns:
|
||||
Union[httplib2.Http, google_auth_httplib2.AuthorizedHttp]: An
|
||||
authorized http client.
|
||||
"""
|
||||
from googleapiclient.http import build_http
|
||||
|
||||
if HAS_GOOGLE_AUTH and isinstance(credentials, google.auth.credentials.Credentials):
|
||||
if google_auth_httplib2 is None:
|
||||
raise ValueError(
|
||||
"Credentials from google.auth specified, but "
|
||||
"google-api-python-client is unable to use these credentials "
|
||||
"unless google-auth-httplib2 is installed. Please install "
|
||||
"google-auth-httplib2."
|
||||
)
|
||||
return google_auth_httplib2.AuthorizedHttp(credentials, http=build_http())
|
||||
else:
|
||||
return credentials.authorize(build_http())
|
||||
|
||||
|
||||
def refresh_credentials(credentials):
|
||||
# Refresh must use a new http instance, as the one associated with the
|
||||
# credentials could be a AuthorizedHttp or an oauth2client-decorated
|
||||
# Http instance which would cause a weird recursive loop of refreshing
|
||||
# and likely tear a hole in spacetime.
|
||||
refresh_http = httplib2.Http()
|
||||
if HAS_GOOGLE_AUTH and isinstance(credentials, google.auth.credentials.Credentials):
|
||||
request = google_auth_httplib2.Request(refresh_http)
|
||||
return credentials.refresh(request)
|
||||
else:
|
||||
return credentials.refresh(refresh_http)
|
||||
|
||||
|
||||
def apply_credentials(credentials, headers):
|
||||
# oauth2client and google-auth have the same interface for this.
|
||||
if not is_valid(credentials):
|
||||
refresh_credentials(credentials)
|
||||
return credentials.apply(headers)
|
||||
|
||||
|
||||
def is_valid(credentials):
|
||||
if HAS_GOOGLE_AUTH and isinstance(credentials, google.auth.credentials.Credentials):
|
||||
return credentials.valid
|
||||
else:
|
||||
return (
|
||||
credentials.access_token is not None
|
||||
and not credentials.access_token_expired
|
||||
)
|
||||
|
||||
|
||||
def get_credentials_from_http(http):
|
||||
if http is None:
|
||||
return None
|
||||
elif hasattr(http.request, "credentials"):
|
||||
return http.request.credentials
|
||||
elif hasattr(http, "credentials") and not isinstance(
|
||||
http.credentials, httplib2.Credentials
|
||||
):
|
||||
return http.credentials
|
||||
else:
|
||||
return None
|
||||
@ -1,207 +0,0 @@
|
||||
# Copyright 2015 Google Inc. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Helper functions for commonly used utilities."""
|
||||
|
||||
import functools
|
||||
import inspect
|
||||
import logging
|
||||
import urllib
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
POSITIONAL_WARNING = "WARNING"
|
||||
POSITIONAL_EXCEPTION = "EXCEPTION"
|
||||
POSITIONAL_IGNORE = "IGNORE"
|
||||
POSITIONAL_SET = frozenset(
|
||||
[POSITIONAL_WARNING, POSITIONAL_EXCEPTION, POSITIONAL_IGNORE]
|
||||
)
|
||||
|
||||
positional_parameters_enforcement = POSITIONAL_WARNING
|
||||
|
||||
_SYM_LINK_MESSAGE = "File: {0}: Is a symbolic link."
|
||||
_IS_DIR_MESSAGE = "{0}: Is a directory"
|
||||
_MISSING_FILE_MESSAGE = "Cannot access {0}: No such file or directory"
|
||||
|
||||
|
||||
def positional(max_positional_args):
|
||||
"""A decorator to declare that only the first N arguments may be positional.
|
||||
|
||||
This decorator makes it easy to support Python 3 style keyword-only
|
||||
parameters. For example, in Python 3 it is possible to write::
|
||||
|
||||
def fn(pos1, *, kwonly1=None, kwonly2=None):
|
||||
...
|
||||
|
||||
All named parameters after ``*`` must be a keyword::
|
||||
|
||||
fn(10, 'kw1', 'kw2') # Raises exception.
|
||||
fn(10, kwonly1='kw1') # Ok.
|
||||
|
||||
Example
|
||||
^^^^^^^
|
||||
|
||||
To define a function like above, do::
|
||||
|
||||
@positional(1)
|
||||
def fn(pos1, kwonly1=None, kwonly2=None):
|
||||
...
|
||||
|
||||
If no default value is provided to a keyword argument, it becomes a
|
||||
required keyword argument::
|
||||
|
||||
@positional(0)
|
||||
def fn(required_kw):
|
||||
...
|
||||
|
||||
This must be called with the keyword parameter::
|
||||
|
||||
fn() # Raises exception.
|
||||
fn(10) # Raises exception.
|
||||
fn(required_kw=10) # Ok.
|
||||
|
||||
When defining instance or class methods always remember to account for
|
||||
``self`` and ``cls``::
|
||||
|
||||
class MyClass(object):
|
||||
|
||||
@positional(2)
|
||||
def my_method(self, pos1, kwonly1=None):
|
||||
...
|
||||
|
||||
@classmethod
|
||||
@positional(2)
|
||||
def my_method(cls, pos1, kwonly1=None):
|
||||
...
|
||||
|
||||
The positional decorator behavior is controlled by
|
||||
``_helpers.positional_parameters_enforcement``, which may be set to
|
||||
``POSITIONAL_EXCEPTION``, ``POSITIONAL_WARNING`` or
|
||||
``POSITIONAL_IGNORE`` to raise an exception, log a warning, or do
|
||||
nothing, respectively, if a declaration is violated.
|
||||
|
||||
Args:
|
||||
max_positional_arguments: Maximum number of positional arguments. All
|
||||
parameters after this index must be
|
||||
keyword only.
|
||||
|
||||
Returns:
|
||||
A decorator that prevents using arguments after max_positional_args
|
||||
from being used as positional parameters.
|
||||
|
||||
Raises:
|
||||
TypeError: if a keyword-only argument is provided as a positional
|
||||
parameter, but only if
|
||||
_helpers.positional_parameters_enforcement is set to
|
||||
POSITIONAL_EXCEPTION.
|
||||
"""
|
||||
|
||||
def positional_decorator(wrapped):
|
||||
@functools.wraps(wrapped)
|
||||
def positional_wrapper(*args, **kwargs):
|
||||
if len(args) > max_positional_args:
|
||||
plural_s = ""
|
||||
if max_positional_args != 1:
|
||||
plural_s = "s"
|
||||
message = (
|
||||
"{function}() takes at most {args_max} positional "
|
||||
"argument{plural} ({args_given} given)".format(
|
||||
function=wrapped.__name__,
|
||||
args_max=max_positional_args,
|
||||
args_given=len(args),
|
||||
plural=plural_s,
|
||||
)
|
||||
)
|
||||
if positional_parameters_enforcement == POSITIONAL_EXCEPTION:
|
||||
raise TypeError(message)
|
||||
elif positional_parameters_enforcement == POSITIONAL_WARNING:
|
||||
logger.warning(message)
|
||||
return wrapped(*args, **kwargs)
|
||||
|
||||
return positional_wrapper
|
||||
|
||||
if isinstance(max_positional_args, int):
|
||||
return positional_decorator
|
||||
else:
|
||||
args, _, _, defaults, _, _, _ = inspect.getfullargspec(max_positional_args)
|
||||
return positional(len(args) - len(defaults))(max_positional_args)
|
||||
|
||||
|
||||
def parse_unique_urlencoded(content):
|
||||
"""Parses unique key-value parameters from urlencoded content.
|
||||
|
||||
Args:
|
||||
content: string, URL-encoded key-value pairs.
|
||||
|
||||
Returns:
|
||||
dict, The key-value pairs from ``content``.
|
||||
|
||||
Raises:
|
||||
ValueError: if one of the keys is repeated.
|
||||
"""
|
||||
urlencoded_params = urllib.parse.parse_qs(content)
|
||||
params = {}
|
||||
for key, value in urlencoded_params.items():
|
||||
if len(value) != 1:
|
||||
msg = "URL-encoded content contains a repeated value:" "%s -> %s" % (
|
||||
key,
|
||||
", ".join(value),
|
||||
)
|
||||
raise ValueError(msg)
|
||||
params[key] = value[0]
|
||||
return params
|
||||
|
||||
|
||||
def update_query_params(uri, params):
|
||||
"""Updates a URI with new query parameters.
|
||||
|
||||
If a given key from ``params`` is repeated in the ``uri``, then
|
||||
the URI will be considered invalid and an error will occur.
|
||||
|
||||
If the URI is valid, then each value from ``params`` will
|
||||
replace the corresponding value in the query parameters (if
|
||||
it exists).
|
||||
|
||||
Args:
|
||||
uri: string, A valid URI, with potential existing query parameters.
|
||||
params: dict, A dictionary of query parameters.
|
||||
|
||||
Returns:
|
||||
The same URI but with the new query parameters added.
|
||||
"""
|
||||
parts = urllib.parse.urlparse(uri)
|
||||
query_params = parse_unique_urlencoded(parts.query)
|
||||
query_params.update(params)
|
||||
new_query = urllib.parse.urlencode(query_params)
|
||||
new_parts = parts._replace(query=new_query)
|
||||
return urllib.parse.urlunparse(new_parts)
|
||||
|
||||
|
||||
def _add_query_parameter(url, name, value):
|
||||
"""Adds a query parameter to a url.
|
||||
|
||||
Replaces the current value if it already exists in the URL.
|
||||
|
||||
Args:
|
||||
url: string, url to add the query parameter to.
|
||||
name: string, query parameter name.
|
||||
value: string, query parameter value.
|
||||
|
||||
Returns:
|
||||
Updated query parameter. Does not update the url if value is None.
|
||||
"""
|
||||
if value is None:
|
||||
return url
|
||||
else:
|
||||
return update_query_params(url, {name: value})
|
||||
@ -1,315 +0,0 @@
|
||||
# Copyright 2014 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Channel notifications support.
|
||||
|
||||
Classes and functions to support channel subscriptions and notifications
|
||||
on those channels.
|
||||
|
||||
Notes:
|
||||
- This code is based on experimental APIs and is subject to change.
|
||||
- Notification does not do deduplication of notification ids, that's up to
|
||||
the receiver.
|
||||
- Storing the Channel between calls is up to the caller.
|
||||
|
||||
|
||||
Example setting up a channel:
|
||||
|
||||
# Create a new channel that gets notifications via webhook.
|
||||
channel = new_webhook_channel("https://example.com/my_web_hook")
|
||||
|
||||
# Store the channel, keyed by 'channel.id'. Store it before calling the
|
||||
# watch method because notifications may start arriving before the watch
|
||||
# method returns.
|
||||
...
|
||||
|
||||
resp = service.objects().watchAll(
|
||||
bucket="some_bucket_id", body=channel.body()).execute()
|
||||
channel.update(resp)
|
||||
|
||||
# Store the channel, keyed by 'channel.id'. Store it after being updated
|
||||
# since the resource_id value will now be correct, and that's needed to
|
||||
# stop a subscription.
|
||||
...
|
||||
|
||||
|
||||
An example Webhook implementation using webapp2. Note that webapp2 puts
|
||||
headers in a case insensitive dictionary, as headers aren't guaranteed to
|
||||
always be upper case.
|
||||
|
||||
id = self.request.headers[X_GOOG_CHANNEL_ID]
|
||||
|
||||
# Retrieve the channel by id.
|
||||
channel = ...
|
||||
|
||||
# Parse notification from the headers, including validating the id.
|
||||
n = notification_from_headers(channel, self.request.headers)
|
||||
|
||||
# Do app specific stuff with the notification here.
|
||||
if n.resource_state == 'sync':
|
||||
# Code to handle sync state.
|
||||
elif n.resource_state == 'exists':
|
||||
# Code to handle the exists state.
|
||||
elif n.resource_state == 'not_exists':
|
||||
# Code to handle the not exists state.
|
||||
|
||||
|
||||
Example of unsubscribing.
|
||||
|
||||
service.channels().stop(channel.body()).execute()
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import datetime
|
||||
import uuid
|
||||
|
||||
from googleapiclient import _helpers as util
|
||||
from googleapiclient import errors
|
||||
|
||||
# The unix time epoch starts at midnight 1970.
|
||||
EPOCH = datetime.datetime(1970, 1, 1)
|
||||
|
||||
# Map the names of the parameters in the JSON channel description to
|
||||
# the parameter names we use in the Channel class.
|
||||
CHANNEL_PARAMS = {
|
||||
"address": "address",
|
||||
"id": "id",
|
||||
"expiration": "expiration",
|
||||
"params": "params",
|
||||
"resourceId": "resource_id",
|
||||
"resourceUri": "resource_uri",
|
||||
"type": "type",
|
||||
"token": "token",
|
||||
}
|
||||
|
||||
X_GOOG_CHANNEL_ID = "X-GOOG-CHANNEL-ID"
|
||||
X_GOOG_MESSAGE_NUMBER = "X-GOOG-MESSAGE-NUMBER"
|
||||
X_GOOG_RESOURCE_STATE = "X-GOOG-RESOURCE-STATE"
|
||||
X_GOOG_RESOURCE_URI = "X-GOOG-RESOURCE-URI"
|
||||
X_GOOG_RESOURCE_ID = "X-GOOG-RESOURCE-ID"
|
||||
|
||||
|
||||
def _upper_header_keys(headers):
|
||||
new_headers = {}
|
||||
for k, v in headers.items():
|
||||
new_headers[k.upper()] = v
|
||||
return new_headers
|
||||
|
||||
|
||||
class Notification(object):
|
||||
"""A Notification from a Channel.
|
||||
|
||||
Notifications are not usually constructed directly, but are returned
|
||||
from functions like notification_from_headers().
|
||||
|
||||
Attributes:
|
||||
message_number: int, The unique id number of this notification.
|
||||
state: str, The state of the resource being monitored.
|
||||
uri: str, The address of the resource being monitored.
|
||||
resource_id: str, The unique identifier of the version of the resource at
|
||||
this event.
|
||||
"""
|
||||
|
||||
@util.positional(5)
|
||||
def __init__(self, message_number, state, resource_uri, resource_id):
|
||||
"""Notification constructor.
|
||||
|
||||
Args:
|
||||
message_number: int, The unique id number of this notification.
|
||||
state: str, The state of the resource being monitored. Can be one
|
||||
of "exists", "not_exists", or "sync".
|
||||
resource_uri: str, The address of the resource being monitored.
|
||||
resource_id: str, The identifier of the watched resource.
|
||||
"""
|
||||
self.message_number = message_number
|
||||
self.state = state
|
||||
self.resource_uri = resource_uri
|
||||
self.resource_id = resource_id
|
||||
|
||||
|
||||
class Channel(object):
|
||||
"""A Channel for notifications.
|
||||
|
||||
Usually not constructed directly, instead it is returned from helper
|
||||
functions like new_webhook_channel().
|
||||
|
||||
Attributes:
|
||||
type: str, The type of delivery mechanism used by this channel. For
|
||||
example, 'web_hook'.
|
||||
id: str, A UUID for the channel.
|
||||
token: str, An arbitrary string associated with the channel that
|
||||
is delivered to the target address with each event delivered
|
||||
over this channel.
|
||||
address: str, The address of the receiving entity where events are
|
||||
delivered. Specific to the channel type.
|
||||
expiration: int, The time, in milliseconds from the epoch, when this
|
||||
channel will expire.
|
||||
params: dict, A dictionary of string to string, with additional parameters
|
||||
controlling delivery channel behavior.
|
||||
resource_id: str, An opaque id that identifies the resource that is
|
||||
being watched. Stable across different API versions.
|
||||
resource_uri: str, The canonicalized ID of the watched resource.
|
||||
"""
|
||||
|
||||
@util.positional(5)
|
||||
def __init__(
|
||||
self,
|
||||
type,
|
||||
id,
|
||||
token,
|
||||
address,
|
||||
expiration=None,
|
||||
params=None,
|
||||
resource_id="",
|
||||
resource_uri="",
|
||||
):
|
||||
"""Create a new Channel.
|
||||
|
||||
In user code, this Channel constructor will not typically be called
|
||||
manually since there are functions for creating channels for each specific
|
||||
type with a more customized set of arguments to pass.
|
||||
|
||||
Args:
|
||||
type: str, The type of delivery mechanism used by this channel. For
|
||||
example, 'web_hook'.
|
||||
id: str, A UUID for the channel.
|
||||
token: str, An arbitrary string associated with the channel that
|
||||
is delivered to the target address with each event delivered
|
||||
over this channel.
|
||||
address: str, The address of the receiving entity where events are
|
||||
delivered. Specific to the channel type.
|
||||
expiration: int, The time, in milliseconds from the epoch, when this
|
||||
channel will expire.
|
||||
params: dict, A dictionary of string to string, with additional parameters
|
||||
controlling delivery channel behavior.
|
||||
resource_id: str, An opaque id that identifies the resource that is
|
||||
being watched. Stable across different API versions.
|
||||
resource_uri: str, The canonicalized ID of the watched resource.
|
||||
"""
|
||||
self.type = type
|
||||
self.id = id
|
||||
self.token = token
|
||||
self.address = address
|
||||
self.expiration = expiration
|
||||
self.params = params
|
||||
self.resource_id = resource_id
|
||||
self.resource_uri = resource_uri
|
||||
|
||||
def body(self):
|
||||
"""Build a body from the Channel.
|
||||
|
||||
Constructs a dictionary that's appropriate for passing into watch()
|
||||
methods as the value of body argument.
|
||||
|
||||
Returns:
|
||||
A dictionary representation of the channel.
|
||||
"""
|
||||
result = {
|
||||
"id": self.id,
|
||||
"token": self.token,
|
||||
"type": self.type,
|
||||
"address": self.address,
|
||||
}
|
||||
if self.params:
|
||||
result["params"] = self.params
|
||||
if self.resource_id:
|
||||
result["resourceId"] = self.resource_id
|
||||
if self.resource_uri:
|
||||
result["resourceUri"] = self.resource_uri
|
||||
if self.expiration:
|
||||
result["expiration"] = self.expiration
|
||||
|
||||
return result
|
||||
|
||||
def update(self, resp):
|
||||
"""Update a channel with information from the response of watch().
|
||||
|
||||
When a request is sent to watch() a resource, the response returned
|
||||
from the watch() request is a dictionary with updated channel information,
|
||||
such as the resource_id, which is needed when stopping a subscription.
|
||||
|
||||
Args:
|
||||
resp: dict, The response from a watch() method.
|
||||
"""
|
||||
for json_name, param_name in CHANNEL_PARAMS.items():
|
||||
value = resp.get(json_name)
|
||||
if value is not None:
|
||||
setattr(self, param_name, value)
|
||||
|
||||
|
||||
def notification_from_headers(channel, headers):
|
||||
"""Parse a notification from the webhook request headers, validate
|
||||
the notification, and return a Notification object.
|
||||
|
||||
Args:
|
||||
channel: Channel, The channel that the notification is associated with.
|
||||
headers: dict, A dictionary like object that contains the request headers
|
||||
from the webhook HTTP request.
|
||||
|
||||
Returns:
|
||||
A Notification object.
|
||||
|
||||
Raises:
|
||||
errors.InvalidNotificationError if the notification is invalid.
|
||||
ValueError if the X-GOOG-MESSAGE-NUMBER can't be converted to an int.
|
||||
"""
|
||||
headers = _upper_header_keys(headers)
|
||||
channel_id = headers[X_GOOG_CHANNEL_ID]
|
||||
if channel.id != channel_id:
|
||||
raise errors.InvalidNotificationError(
|
||||
"Channel id mismatch: %s != %s" % (channel.id, channel_id)
|
||||
)
|
||||
else:
|
||||
message_number = int(headers[X_GOOG_MESSAGE_NUMBER])
|
||||
state = headers[X_GOOG_RESOURCE_STATE]
|
||||
resource_uri = headers[X_GOOG_RESOURCE_URI]
|
||||
resource_id = headers[X_GOOG_RESOURCE_ID]
|
||||
return Notification(message_number, state, resource_uri, resource_id)
|
||||
|
||||
|
||||
@util.positional(2)
|
||||
def new_webhook_channel(url, token=None, expiration=None, params=None):
|
||||
"""Create a new webhook Channel.
|
||||
|
||||
Args:
|
||||
url: str, URL to post notifications to.
|
||||
token: str, An arbitrary string associated with the channel that
|
||||
is delivered to the target address with each notification delivered
|
||||
over this channel.
|
||||
expiration: datetime.datetime, A time in the future when the channel
|
||||
should expire. Can also be None if the subscription should use the
|
||||
default expiration. Note that different services may have different
|
||||
limits on how long a subscription lasts. Check the response from the
|
||||
watch() method to see the value the service has set for an expiration
|
||||
time.
|
||||
params: dict, Extra parameters to pass on channel creation. Currently
|
||||
not used for webhook channels.
|
||||
"""
|
||||
expiration_ms = 0
|
||||
if expiration:
|
||||
delta = expiration - EPOCH
|
||||
expiration_ms = (
|
||||
delta.microseconds / 1000 + (delta.seconds + delta.days * 24 * 3600) * 1000
|
||||
)
|
||||
if expiration_ms < 0:
|
||||
expiration_ms = 0
|
||||
|
||||
return Channel(
|
||||
"web_hook",
|
||||
str(uuid.uuid4()),
|
||||
token,
|
||||
url,
|
||||
expiration=expiration_ms,
|
||||
params=params,
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,78 +0,0 @@
|
||||
# Copyright 2014 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Caching utility for the discovery document."""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DISCOVERY_DOC_MAX_AGE = 60 * 60 * 24 # 1 day
|
||||
DISCOVERY_DOC_DIR = os.path.join(
|
||||
os.path.dirname(os.path.realpath(__file__)), "documents"
|
||||
)
|
||||
|
||||
|
||||
def autodetect():
|
||||
"""Detects an appropriate cache module and returns it.
|
||||
|
||||
Returns:
|
||||
googleapiclient.discovery_cache.base.Cache, a cache object which
|
||||
is auto detected, or None if no cache object is available.
|
||||
"""
|
||||
if "GAE_ENV" in os.environ:
|
||||
try:
|
||||
from . import appengine_memcache
|
||||
|
||||
return appengine_memcache.cache
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
from . import file_cache
|
||||
|
||||
return file_cache.cache
|
||||
except Exception:
|
||||
LOGGER.info(
|
||||
"file_cache is only supported with oauth2client<4.0.0", exc_info=False
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
def get_static_doc(serviceName, version):
|
||||
"""Retrieves the discovery document from the directory defined in
|
||||
DISCOVERY_DOC_DIR corresponding to the serviceName and version provided.
|
||||
|
||||
Args:
|
||||
serviceName: string, name of the service.
|
||||
version: string, the version of the service.
|
||||
|
||||
Returns:
|
||||
A string containing the contents of the JSON discovery document,
|
||||
otherwise None if the JSON discovery document was not found.
|
||||
"""
|
||||
|
||||
content = None
|
||||
doc_name = "{}.{}.json".format(serviceName, version)
|
||||
|
||||
try:
|
||||
with open(os.path.join(DISCOVERY_DOC_DIR, doc_name), "r") as f:
|
||||
content = f.read()
|
||||
except FileNotFoundError:
|
||||
# File does not exist. Nothing to do here.
|
||||
pass
|
||||
|
||||
return content
|
||||
@ -1,55 +0,0 @@
|
||||
# Copyright 2014 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""App Engine memcache based cache for the discovery document."""
|
||||
|
||||
import logging
|
||||
|
||||
# This is only an optional dependency because we only import this
|
||||
# module when google.appengine.api.memcache is available.
|
||||
from google.appengine.api import memcache
|
||||
|
||||
from . import base
|
||||
from ..discovery_cache import DISCOVERY_DOC_MAX_AGE
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
NAMESPACE = "google-api-client"
|
||||
|
||||
|
||||
class Cache(base.Cache):
|
||||
"""A cache with app engine memcache API."""
|
||||
|
||||
def __init__(self, max_age):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
max_age: Cache expiration in seconds.
|
||||
"""
|
||||
self._max_age = max_age
|
||||
|
||||
def get(self, url):
|
||||
try:
|
||||
return memcache.get(url, namespace=NAMESPACE)
|
||||
except Exception as e:
|
||||
LOGGER.warning(e, exc_info=True)
|
||||
|
||||
def set(self, url, content):
|
||||
try:
|
||||
memcache.set(url, content, time=int(self._max_age), namespace=NAMESPACE)
|
||||
except Exception as e:
|
||||
LOGGER.warning(e, exc_info=True)
|
||||
|
||||
|
||||
cache = Cache(max_age=DISCOVERY_DOC_MAX_AGE)
|
||||
@ -1,46 +0,0 @@
|
||||
# Copyright 2014 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""An abstract class for caching the discovery document."""
|
||||
|
||||
import abc
|
||||
|
||||
|
||||
class Cache(object):
|
||||
"""A base abstract cache class."""
|
||||
|
||||
__metaclass__ = abc.ABCMeta
|
||||
|
||||
@abc.abstractmethod
|
||||
def get(self, url):
|
||||
"""Gets the content from the memcache with a given key.
|
||||
|
||||
Args:
|
||||
url: string, the key for the cache.
|
||||
|
||||
Returns:
|
||||
object, the value in the cache for the given key, or None if the key is
|
||||
not in the cache.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@abc.abstractmethod
|
||||
def set(self, url, content):
|
||||
"""Sets the given key and content in the cache.
|
||||
|
||||
Args:
|
||||
url: string, the key for the cache.
|
||||
content: string, the discovery document.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
@ -1,145 +0,0 @@
|
||||
# Copyright 2014 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""File based cache for the discovery document.
|
||||
|
||||
The cache is stored in a single file so that multiple processes can
|
||||
share the same cache. It locks the file whenever accessing to the
|
||||
file. When the cache content is corrupted, it will be initialized with
|
||||
an empty cache.
|
||||
"""
|
||||
|
||||
from __future__ import division
|
||||
|
||||
import datetime
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
try:
|
||||
from oauth2client.contrib.locked_file import LockedFile
|
||||
except ImportError:
|
||||
# oauth2client < 2.0.0
|
||||
try:
|
||||
from oauth2client.locked_file import LockedFile
|
||||
except ImportError:
|
||||
# oauth2client > 4.0.0 or google-auth
|
||||
raise ImportError(
|
||||
"file_cache is unavailable when using oauth2client >= 4.0.0 or google-auth"
|
||||
)
|
||||
|
||||
from . import base
|
||||
from ..discovery_cache import DISCOVERY_DOC_MAX_AGE
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
FILENAME = "google-api-python-client-discovery-doc.cache"
|
||||
EPOCH = datetime.datetime(1970, 1, 1)
|
||||
|
||||
|
||||
def _to_timestamp(date):
|
||||
try:
|
||||
return (date - EPOCH).total_seconds()
|
||||
except AttributeError:
|
||||
# The following is the equivalent of total_seconds() in Python2.6.
|
||||
# See also: https://docs.python.org/2/library/datetime.html
|
||||
delta = date - EPOCH
|
||||
return (
|
||||
delta.microseconds + (delta.seconds + delta.days * 24 * 3600) * 10**6
|
||||
) / 10**6
|
||||
|
||||
|
||||
def _read_or_initialize_cache(f):
|
||||
f.file_handle().seek(0)
|
||||
try:
|
||||
cache = json.load(f.file_handle())
|
||||
except Exception:
|
||||
# This means it opens the file for the first time, or the cache is
|
||||
# corrupted, so initializing the file with an empty dict.
|
||||
cache = {}
|
||||
f.file_handle().truncate(0)
|
||||
f.file_handle().seek(0)
|
||||
json.dump(cache, f.file_handle())
|
||||
return cache
|
||||
|
||||
|
||||
class Cache(base.Cache):
|
||||
"""A file based cache for the discovery documents."""
|
||||
|
||||
def __init__(self, max_age):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
max_age: Cache expiration in seconds.
|
||||
"""
|
||||
self._max_age = max_age
|
||||
self._file = os.path.join(tempfile.gettempdir(), FILENAME)
|
||||
f = LockedFile(self._file, "a+", "r")
|
||||
try:
|
||||
f.open_and_lock()
|
||||
if f.is_locked():
|
||||
_read_or_initialize_cache(f)
|
||||
# If we can not obtain the lock, other process or thread must
|
||||
# have initialized the file.
|
||||
except Exception as e:
|
||||
LOGGER.warning(e, exc_info=True)
|
||||
finally:
|
||||
f.unlock_and_close()
|
||||
|
||||
def get(self, url):
|
||||
f = LockedFile(self._file, "r+", "r")
|
||||
try:
|
||||
f.open_and_lock()
|
||||
if f.is_locked():
|
||||
cache = _read_or_initialize_cache(f)
|
||||
if url in cache:
|
||||
content, t = cache.get(url, (None, 0))
|
||||
if _to_timestamp(datetime.datetime.now()) < t + self._max_age:
|
||||
return content
|
||||
return None
|
||||
else:
|
||||
LOGGER.debug("Could not obtain a lock for the cache file.")
|
||||
return None
|
||||
except Exception as e:
|
||||
LOGGER.warning(e, exc_info=True)
|
||||
finally:
|
||||
f.unlock_and_close()
|
||||
|
||||
def set(self, url, content):
|
||||
f = LockedFile(self._file, "r+", "r")
|
||||
try:
|
||||
f.open_and_lock()
|
||||
if f.is_locked():
|
||||
cache = _read_or_initialize_cache(f)
|
||||
cache[url] = (content, _to_timestamp(datetime.datetime.now()))
|
||||
# Remove stale cache.
|
||||
for k, (_, timestamp) in list(cache.items()):
|
||||
if (
|
||||
_to_timestamp(datetime.datetime.now())
|
||||
>= timestamp + self._max_age
|
||||
):
|
||||
del cache[k]
|
||||
f.file_handle().truncate(0)
|
||||
f.file_handle().seek(0)
|
||||
json.dump(cache, f.file_handle())
|
||||
else:
|
||||
LOGGER.debug("Could not obtain a lock for the cache file.")
|
||||
except Exception as e:
|
||||
LOGGER.warning(e, exc_info=True)
|
||||
finally:
|
||||
f.unlock_and_close()
|
||||
|
||||
|
||||
cache = Cache(max_age=DISCOVERY_DOC_MAX_AGE)
|
||||
@ -1,197 +0,0 @@
|
||||
# Copyright 2014 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Errors for the library.
|
||||
|
||||
All exceptions defined by the library
|
||||
should be defined in this file.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
__author__ = "jcgregorio@google.com (Joe Gregorio)"
|
||||
|
||||
import json
|
||||
|
||||
from googleapiclient import _helpers as util
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
"""Base error for this module."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class HttpError(Error):
|
||||
"""HTTP data was invalid or unexpected."""
|
||||
|
||||
@util.positional(3)
|
||||
def __init__(self, resp, content, uri=None):
|
||||
self.resp = resp
|
||||
if not isinstance(content, bytes):
|
||||
raise TypeError("HTTP content should be bytes")
|
||||
self.content = content
|
||||
self.uri = uri
|
||||
self.error_details = ""
|
||||
self.reason = self._get_reason()
|
||||
|
||||
@property
|
||||
def status_code(self):
|
||||
"""Return the HTTP status code from the response content."""
|
||||
return self.resp.status
|
||||
|
||||
def _get_reason(self):
|
||||
"""Calculate the reason for the error from the response content."""
|
||||
reason = self.resp.reason
|
||||
try:
|
||||
try:
|
||||
data = json.loads(self.content.decode("utf-8"))
|
||||
except json.JSONDecodeError:
|
||||
# In case it is not json
|
||||
data = self.content.decode("utf-8")
|
||||
if isinstance(data, dict):
|
||||
reason = data["error"]["message"]
|
||||
error_detail_keyword = next(
|
||||
(
|
||||
kw
|
||||
for kw in ["detail", "details", "errors", "message"]
|
||||
if kw in data["error"]
|
||||
),
|
||||
"",
|
||||
)
|
||||
if error_detail_keyword:
|
||||
self.error_details = data["error"][error_detail_keyword]
|
||||
elif isinstance(data, list) and len(data) > 0:
|
||||
first_error = data[0]
|
||||
reason = first_error["error"]["message"]
|
||||
if "details" in first_error["error"]:
|
||||
self.error_details = first_error["error"]["details"]
|
||||
else:
|
||||
self.error_details = data
|
||||
except (ValueError, KeyError, TypeError):
|
||||
pass
|
||||
if reason is None:
|
||||
reason = ""
|
||||
return reason.strip()
|
||||
|
||||
def __repr__(self):
|
||||
if self.error_details:
|
||||
return '<HttpError %s when requesting %s returned "%s". Details: "%s">' % (
|
||||
self.resp.status,
|
||||
self.uri,
|
||||
self.reason,
|
||||
self.error_details,
|
||||
)
|
||||
elif self.uri:
|
||||
return '<HttpError %s when requesting %s returned "%s">' % (
|
||||
self.resp.status,
|
||||
self.uri,
|
||||
self.reason,
|
||||
)
|
||||
else:
|
||||
return '<HttpError %s "%s">' % (self.resp.status, self.reason)
|
||||
|
||||
__str__ = __repr__
|
||||
|
||||
|
||||
class InvalidJsonError(Error):
|
||||
"""The JSON returned could not be parsed."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class UnknownFileType(Error):
|
||||
"""File type unknown or unexpected."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class UnknownLinkType(Error):
|
||||
"""Link type unknown or unexpected."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class UnknownApiNameOrVersion(Error):
|
||||
"""No API with that name and version exists."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class UnacceptableMimeTypeError(Error):
|
||||
"""That is an unacceptable mimetype for this operation."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class MediaUploadSizeError(Error):
|
||||
"""Media is larger than the method can accept."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class ResumableUploadError(HttpError):
|
||||
"""Error occurred during resumable upload."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class InvalidChunkSizeError(Error):
|
||||
"""The given chunksize is not valid."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class InvalidNotificationError(Error):
|
||||
"""The channel Notification is invalid."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class BatchError(HttpError):
|
||||
"""Error occurred during batch operations."""
|
||||
|
||||
@util.positional(2)
|
||||
def __init__(self, reason, resp=None, content=None):
|
||||
self.resp = resp
|
||||
self.content = content
|
||||
self.reason = reason
|
||||
|
||||
def __repr__(self):
|
||||
if getattr(self.resp, "status", None) is None:
|
||||
return '<BatchError "%s">' % (self.reason)
|
||||
else:
|
||||
return '<BatchError %s "%s">' % (self.resp.status, self.reason)
|
||||
|
||||
__str__ = __repr__
|
||||
|
||||
|
||||
class UnexpectedMethodError(Error):
|
||||
"""Exception raised by RequestMockBuilder on unexpected calls."""
|
||||
|
||||
@util.positional(1)
|
||||
def __init__(self, methodId=None):
|
||||
"""Constructor for an UnexpectedMethodError."""
|
||||
super(UnexpectedMethodError, self).__init__(
|
||||
"Received unexpected call %s" % methodId
|
||||
)
|
||||
|
||||
|
||||
class UnexpectedBodyError(Error):
|
||||
"""Exception raised by RequestMockBuilder on unexpected bodies."""
|
||||
|
||||
def __init__(self, expected, provided):
|
||||
"""Constructor for an UnexpectedMethodError."""
|
||||
super(UnexpectedBodyError, self).__init__(
|
||||
"Expected: [%s] - Provided: [%s]" % (expected, provided)
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,183 +0,0 @@
|
||||
# Copyright 2014 Joe Gregorio
|
||||
#
|
||||
# Licensed under the MIT License
|
||||
|
||||
"""MIME-Type Parser
|
||||
|
||||
This module provides basic functions for handling mime-types. It can handle
|
||||
matching mime-types against a list of media-ranges. See section 14.1 of the
|
||||
HTTP specification [RFC 2616] for a complete explanation.
|
||||
|
||||
http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
|
||||
|
||||
Contents:
|
||||
- parse_mime_type(): Parses a mime-type into its component parts.
|
||||
- parse_media_range(): Media-ranges are mime-types with wild-cards and a 'q'
|
||||
quality parameter.
|
||||
- quality(): Determines the quality ('q') of a mime-type when
|
||||
compared against a list of media-ranges.
|
||||
- quality_parsed(): Just like quality() except the second parameter must be
|
||||
pre-parsed.
|
||||
- best_match(): Choose the mime-type with the highest quality ('q')
|
||||
from a list of candidates.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
from functools import reduce
|
||||
|
||||
__version__ = "0.1.3"
|
||||
__author__ = "Joe Gregorio"
|
||||
__email__ = "joe@bitworking.org"
|
||||
__license__ = "MIT License"
|
||||
__credits__ = ""
|
||||
|
||||
|
||||
def parse_mime_type(mime_type):
|
||||
"""Parses a mime-type into its component parts.
|
||||
|
||||
Carves up a mime-type and returns a tuple of the (type, subtype, params)
|
||||
where 'params' is a dictionary of all the parameters for the media range.
|
||||
For example, the media range 'application/xhtml;q=0.5' would get parsed
|
||||
into:
|
||||
|
||||
('application', 'xhtml', {'q', '0.5'})
|
||||
"""
|
||||
parts = mime_type.split(";")
|
||||
params = dict(
|
||||
[tuple([s.strip() for s in param.split("=", 1)]) for param in parts[1:]]
|
||||
)
|
||||
full_type = parts[0].strip()
|
||||
# Java URLConnection class sends an Accept header that includes a
|
||||
# single '*'. Turn it into a legal wildcard.
|
||||
if full_type == "*":
|
||||
full_type = "*/*"
|
||||
(type, subtype) = full_type.split("/")
|
||||
|
||||
return (type.strip(), subtype.strip(), params)
|
||||
|
||||
|
||||
def parse_media_range(range):
|
||||
"""Parse a media-range into its component parts.
|
||||
|
||||
Carves up a media range and returns a tuple of the (type, subtype,
|
||||
params) where 'params' is a dictionary of all the parameters for the media
|
||||
range. For example, the media range 'application/*;q=0.5' would get parsed
|
||||
into:
|
||||
|
||||
('application', '*', {'q', '0.5'})
|
||||
|
||||
In addition this function also guarantees that there is a value for 'q'
|
||||
in the params dictionary, filling it in with a proper default if
|
||||
necessary.
|
||||
"""
|
||||
(type, subtype, params) = parse_mime_type(range)
|
||||
if (
|
||||
"q" not in params
|
||||
or not params["q"]
|
||||
or not float(params["q"])
|
||||
or float(params["q"]) > 1
|
||||
or float(params["q"]) < 0
|
||||
):
|
||||
params["q"] = "1"
|
||||
|
||||
return (type, subtype, params)
|
||||
|
||||
|
||||
def fitness_and_quality_parsed(mime_type, parsed_ranges):
|
||||
"""Find the best match for a mime-type amongst parsed media-ranges.
|
||||
|
||||
Find the best match for a given mime-type against a list of media_ranges
|
||||
that have already been parsed by parse_media_range(). Returns a tuple of
|
||||
the fitness value and the value of the 'q' quality parameter of the best
|
||||
match, or (-1, 0) if no match was found. Just as for quality_parsed(),
|
||||
'parsed_ranges' must be a list of parsed media ranges.
|
||||
"""
|
||||
best_fitness = -1
|
||||
best_fit_q = 0
|
||||
(target_type, target_subtype, target_params) = parse_media_range(mime_type)
|
||||
for (type, subtype, params) in parsed_ranges:
|
||||
type_match = type == target_type or type == "*" or target_type == "*"
|
||||
subtype_match = (
|
||||
subtype == target_subtype or subtype == "*" or target_subtype == "*"
|
||||
)
|
||||
if type_match and subtype_match:
|
||||
param_matches = reduce(
|
||||
lambda x, y: x + y,
|
||||
[
|
||||
1
|
||||
for (key, value) in target_params.items()
|
||||
if key != "q" and key in params and value == params[key]
|
||||
],
|
||||
0,
|
||||
)
|
||||
fitness = (type == target_type) and 100 or 0
|
||||
fitness += (subtype == target_subtype) and 10 or 0
|
||||
fitness += param_matches
|
||||
if fitness > best_fitness:
|
||||
best_fitness = fitness
|
||||
best_fit_q = params["q"]
|
||||
|
||||
return best_fitness, float(best_fit_q)
|
||||
|
||||
|
||||
def quality_parsed(mime_type, parsed_ranges):
|
||||
"""Find the best match for a mime-type amongst parsed media-ranges.
|
||||
|
||||
Find the best match for a given mime-type against a list of media_ranges
|
||||
that have already been parsed by parse_media_range(). Returns the 'q'
|
||||
quality parameter of the best match, 0 if no match was found. This function
|
||||
bahaves the same as quality() except that 'parsed_ranges' must be a list of
|
||||
parsed media ranges.
|
||||
"""
|
||||
|
||||
return fitness_and_quality_parsed(mime_type, parsed_ranges)[1]
|
||||
|
||||
|
||||
def quality(mime_type, ranges):
|
||||
"""Return the quality ('q') of a mime-type against a list of media-ranges.
|
||||
|
||||
Returns the quality 'q' of a mime-type when compared against the
|
||||
media-ranges in ranges. For example:
|
||||
|
||||
>>> quality('text/html','text/*;q=0.3, text/html;q=0.7,
|
||||
text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5')
|
||||
0.7
|
||||
|
||||
"""
|
||||
parsed_ranges = [parse_media_range(r) for r in ranges.split(",")]
|
||||
|
||||
return quality_parsed(mime_type, parsed_ranges)
|
||||
|
||||
|
||||
def best_match(supported, header):
|
||||
"""Return mime-type with the highest quality ('q') from list of candidates.
|
||||
|
||||
Takes a list of supported mime-types and finds the best match for all the
|
||||
media-ranges listed in header. The value of header must be a string that
|
||||
conforms to the format of the HTTP Accept: header. The value of 'supported'
|
||||
is a list of mime-types. The list of supported mime-types should be sorted
|
||||
in order of increasing desirability, in case of a situation where there is
|
||||
a tie.
|
||||
|
||||
>>> best_match(['application/xbel+xml', 'text/xml'],
|
||||
'text/*;q=0.5,*/*; q=0.1')
|
||||
'text/xml'
|
||||
"""
|
||||
split_header = _filter_blank(header.split(","))
|
||||
parsed_header = [parse_media_range(r) for r in split_header]
|
||||
weighted_matches = []
|
||||
pos = 0
|
||||
for mime_type in supported:
|
||||
weighted_matches.append(
|
||||
(fitness_and_quality_parsed(mime_type, parsed_header), pos, mime_type)
|
||||
)
|
||||
pos += 1
|
||||
weighted_matches.sort()
|
||||
|
||||
return weighted_matches[-1][0][1] and weighted_matches[-1][2] or ""
|
||||
|
||||
|
||||
def _filter_blank(i):
|
||||
for s in i:
|
||||
if s.strip():
|
||||
yield s
|
||||
@ -1,429 +0,0 @@
|
||||
# Copyright 2014 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Model objects for requests and responses.
|
||||
|
||||
Each API may support one or more serializations, such
|
||||
as JSON, Atom, etc. The model classes are responsible
|
||||
for converting between the wire format and the Python
|
||||
object representation.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
__author__ = "jcgregorio@google.com (Joe Gregorio)"
|
||||
|
||||
import json
|
||||
import logging
|
||||
import platform
|
||||
import urllib
|
||||
import warnings
|
||||
|
||||
from googleapiclient import version as googleapiclient_version
|
||||
from googleapiclient.errors import HttpError
|
||||
|
||||
try:
|
||||
from google.api_core.version_header import API_VERSION_METADATA_KEY
|
||||
|
||||
HAS_API_VERSION = True
|
||||
except ImportError:
|
||||
HAS_API_VERSION = False
|
||||
|
||||
_LIBRARY_VERSION = googleapiclient_version.__version__
|
||||
_PY_VERSION = platform.python_version()
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
dump_request_response = False
|
||||
|
||||
|
||||
def _abstract():
|
||||
raise NotImplementedError("You need to override this function")
|
||||
|
||||
|
||||
class Model(object):
|
||||
"""Model base class.
|
||||
|
||||
All Model classes should implement this interface.
|
||||
The Model serializes and de-serializes between a wire
|
||||
format such as JSON and a Python object representation.
|
||||
"""
|
||||
|
||||
def request(self, headers, path_params, query_params, body_value):
|
||||
"""Updates outgoing requests with a serialized body.
|
||||
|
||||
Args:
|
||||
headers: dict, request headers
|
||||
path_params: dict, parameters that appear in the request path
|
||||
query_params: dict, parameters that appear in the query
|
||||
body_value: object, the request body as a Python object, which must be
|
||||
serializable.
|
||||
Returns:
|
||||
A tuple of (headers, path_params, query, body)
|
||||
|
||||
headers: dict, request headers
|
||||
path_params: dict, parameters that appear in the request path
|
||||
query: string, query part of the request URI
|
||||
body: string, the body serialized in the desired wire format.
|
||||
"""
|
||||
_abstract()
|
||||
|
||||
def response(self, resp, content):
|
||||
"""Convert the response wire format into a Python object.
|
||||
|
||||
Args:
|
||||
resp: httplib2.Response, the HTTP response headers and status
|
||||
content: string, the body of the HTTP response
|
||||
|
||||
Returns:
|
||||
The body de-serialized as a Python object.
|
||||
|
||||
Raises:
|
||||
googleapiclient.errors.HttpError if a non 2xx response is received.
|
||||
"""
|
||||
_abstract()
|
||||
|
||||
|
||||
class BaseModel(Model):
|
||||
"""Base model class.
|
||||
|
||||
Subclasses should provide implementations for the "serialize" and
|
||||
"deserialize" methods, as well as values for the following class attributes.
|
||||
|
||||
Attributes:
|
||||
accept: The value to use for the HTTP Accept header.
|
||||
content_type: The value to use for the HTTP Content-type header.
|
||||
no_content_response: The value to return when deserializing a 204 "No
|
||||
Content" response.
|
||||
alt_param: The value to supply as the "alt" query parameter for requests.
|
||||
"""
|
||||
|
||||
accept = None
|
||||
content_type = None
|
||||
no_content_response = None
|
||||
alt_param = None
|
||||
|
||||
def _log_request(self, headers, path_params, query, body):
|
||||
"""Logs debugging information about the request if requested."""
|
||||
if dump_request_response:
|
||||
LOGGER.info("--request-start--")
|
||||
LOGGER.info("-headers-start-")
|
||||
for h, v in headers.items():
|
||||
LOGGER.info("%s: %s", h, v)
|
||||
LOGGER.info("-headers-end-")
|
||||
LOGGER.info("-path-parameters-start-")
|
||||
for h, v in path_params.items():
|
||||
LOGGER.info("%s: %s", h, v)
|
||||
LOGGER.info("-path-parameters-end-")
|
||||
LOGGER.info("body: %s", body)
|
||||
LOGGER.info("query: %s", query)
|
||||
LOGGER.info("--request-end--")
|
||||
|
||||
def request(self, headers, path_params, query_params, body_value, api_version=None):
|
||||
"""Updates outgoing requests with a serialized body.
|
||||
|
||||
Args:
|
||||
headers: dict, request headers
|
||||
path_params: dict, parameters that appear in the request path
|
||||
query_params: dict, parameters that appear in the query
|
||||
body_value: object, the request body as a Python object, which must be
|
||||
serializable by json.
|
||||
api_version: str, The precise API version represented by this request,
|
||||
which will result in an API Version header being sent along with the
|
||||
HTTP request.
|
||||
Returns:
|
||||
A tuple of (headers, path_params, query, body)
|
||||
|
||||
headers: dict, request headers
|
||||
path_params: dict, parameters that appear in the request path
|
||||
query: string, query part of the request URI
|
||||
body: string, the body serialized as JSON
|
||||
"""
|
||||
query = self._build_query(query_params)
|
||||
headers["accept"] = self.accept
|
||||
headers["accept-encoding"] = "gzip, deflate"
|
||||
if "user-agent" in headers:
|
||||
headers["user-agent"] += " "
|
||||
else:
|
||||
headers["user-agent"] = ""
|
||||
headers["user-agent"] += "(gzip)"
|
||||
if "x-goog-api-client" in headers:
|
||||
headers["x-goog-api-client"] += " "
|
||||
else:
|
||||
headers["x-goog-api-client"] = ""
|
||||
headers["x-goog-api-client"] += "gdcl/%s gl-python/%s" % (
|
||||
_LIBRARY_VERSION,
|
||||
_PY_VERSION,
|
||||
)
|
||||
|
||||
if api_version and HAS_API_VERSION:
|
||||
headers[API_VERSION_METADATA_KEY] = api_version
|
||||
elif api_version:
|
||||
warnings.warn(
|
||||
"The `api_version` argument is ignored as a newer version of "
|
||||
"`google-api-core` is required to use this feature."
|
||||
"Please upgrade `google-api-core` to 2.19.0 or newer."
|
||||
)
|
||||
|
||||
if body_value is not None:
|
||||
headers["content-type"] = self.content_type
|
||||
body_value = self.serialize(body_value)
|
||||
self._log_request(headers, path_params, query, body_value)
|
||||
return (headers, path_params, query, body_value)
|
||||
|
||||
def _build_query(self, params):
|
||||
"""Builds a query string.
|
||||
|
||||
Args:
|
||||
params: dict, the query parameters
|
||||
|
||||
Returns:
|
||||
The query parameters properly encoded into an HTTP URI query string.
|
||||
"""
|
||||
if self.alt_param is not None:
|
||||
params.update({"alt": self.alt_param})
|
||||
astuples = []
|
||||
for key, value in params.items():
|
||||
if type(value) == type([]):
|
||||
for x in value:
|
||||
x = x.encode("utf-8")
|
||||
astuples.append((key, x))
|
||||
else:
|
||||
if isinstance(value, str) and callable(value.encode):
|
||||
value = value.encode("utf-8")
|
||||
astuples.append((key, value))
|
||||
return "?" + urllib.parse.urlencode(astuples)
|
||||
|
||||
def _log_response(self, resp, content):
|
||||
"""Logs debugging information about the response if requested."""
|
||||
if dump_request_response:
|
||||
LOGGER.info("--response-start--")
|
||||
for h, v in resp.items():
|
||||
LOGGER.info("%s: %s", h, v)
|
||||
if content:
|
||||
LOGGER.info(content)
|
||||
LOGGER.info("--response-end--")
|
||||
|
||||
def response(self, resp, content):
|
||||
"""Convert the response wire format into a Python object.
|
||||
|
||||
Args:
|
||||
resp: httplib2.Response, the HTTP response headers and status
|
||||
content: string, the body of the HTTP response
|
||||
|
||||
Returns:
|
||||
The body de-serialized as a Python object.
|
||||
|
||||
Raises:
|
||||
googleapiclient.errors.HttpError if a non 2xx response is received.
|
||||
"""
|
||||
self._log_response(resp, content)
|
||||
# Error handling is TBD, for example, do we retry
|
||||
# for some operation/error combinations?
|
||||
if resp.status < 300:
|
||||
if resp.status == 204:
|
||||
# A 204: No Content response should be treated differently
|
||||
# to all the other success states
|
||||
return self.no_content_response
|
||||
return self.deserialize(content)
|
||||
else:
|
||||
LOGGER.debug("Content from bad request was: %r" % content)
|
||||
raise HttpError(resp, content)
|
||||
|
||||
def serialize(self, body_value):
|
||||
"""Perform the actual Python object serialization.
|
||||
|
||||
Args:
|
||||
body_value: object, the request body as a Python object.
|
||||
|
||||
Returns:
|
||||
string, the body in serialized form.
|
||||
"""
|
||||
_abstract()
|
||||
|
||||
def deserialize(self, content):
|
||||
"""Perform the actual deserialization from response string to Python
|
||||
object.
|
||||
|
||||
Args:
|
||||
content: string, the body of the HTTP response
|
||||
|
||||
Returns:
|
||||
The body de-serialized as a Python object.
|
||||
"""
|
||||
_abstract()
|
||||
|
||||
|
||||
class JsonModel(BaseModel):
|
||||
"""Model class for JSON.
|
||||
|
||||
Serializes and de-serializes between JSON and the Python
|
||||
object representation of HTTP request and response bodies.
|
||||
"""
|
||||
|
||||
accept = "application/json"
|
||||
content_type = "application/json"
|
||||
alt_param = "json"
|
||||
|
||||
def __init__(self, data_wrapper=False):
|
||||
"""Construct a JsonModel.
|
||||
|
||||
Args:
|
||||
data_wrapper: boolean, wrap requests and responses in a data wrapper
|
||||
"""
|
||||
self._data_wrapper = data_wrapper
|
||||
|
||||
def serialize(self, body_value):
|
||||
if (
|
||||
isinstance(body_value, dict)
|
||||
and "data" not in body_value
|
||||
and self._data_wrapper
|
||||
):
|
||||
body_value = {"data": body_value}
|
||||
return json.dumps(body_value)
|
||||
|
||||
def deserialize(self, content):
|
||||
try:
|
||||
content = content.decode("utf-8")
|
||||
except AttributeError:
|
||||
pass
|
||||
try:
|
||||
body = json.loads(content)
|
||||
except json.decoder.JSONDecodeError:
|
||||
body = content
|
||||
else:
|
||||
if self._data_wrapper and "data" in body:
|
||||
body = body["data"]
|
||||
return body
|
||||
|
||||
@property
|
||||
def no_content_response(self):
|
||||
return {}
|
||||
|
||||
|
||||
class RawModel(JsonModel):
|
||||
"""Model class for requests that don't return JSON.
|
||||
|
||||
Serializes and de-serializes between JSON and the Python
|
||||
object representation of HTTP request, and returns the raw bytes
|
||||
of the response body.
|
||||
"""
|
||||
|
||||
accept = "*/*"
|
||||
content_type = "application/json"
|
||||
alt_param = None
|
||||
|
||||
def deserialize(self, content):
|
||||
return content
|
||||
|
||||
@property
|
||||
def no_content_response(self):
|
||||
return ""
|
||||
|
||||
|
||||
class MediaModel(JsonModel):
|
||||
"""Model class for requests that return Media.
|
||||
|
||||
Serializes and de-serializes between JSON and the Python
|
||||
object representation of HTTP request, and returns the raw bytes
|
||||
of the response body.
|
||||
"""
|
||||
|
||||
accept = "*/*"
|
||||
content_type = "application/json"
|
||||
alt_param = "media"
|
||||
|
||||
def deserialize(self, content):
|
||||
return content
|
||||
|
||||
@property
|
||||
def no_content_response(self):
|
||||
return ""
|
||||
|
||||
|
||||
class ProtocolBufferModel(BaseModel):
|
||||
"""Model class for protocol buffers.
|
||||
|
||||
Serializes and de-serializes the binary protocol buffer sent in the HTTP
|
||||
request and response bodies.
|
||||
"""
|
||||
|
||||
accept = "application/x-protobuf"
|
||||
content_type = "application/x-protobuf"
|
||||
alt_param = "proto"
|
||||
|
||||
def __init__(self, protocol_buffer):
|
||||
"""Constructs a ProtocolBufferModel.
|
||||
|
||||
The serialized protocol buffer returned in an HTTP response will be
|
||||
de-serialized using the given protocol buffer class.
|
||||
|
||||
Args:
|
||||
protocol_buffer: The protocol buffer class used to de-serialize a
|
||||
response from the API.
|
||||
"""
|
||||
self._protocol_buffer = protocol_buffer
|
||||
|
||||
def serialize(self, body_value):
|
||||
return body_value.SerializeToString()
|
||||
|
||||
def deserialize(self, content):
|
||||
return self._protocol_buffer.FromString(content)
|
||||
|
||||
@property
|
||||
def no_content_response(self):
|
||||
return self._protocol_buffer()
|
||||
|
||||
|
||||
def makepatch(original, modified):
|
||||
"""Create a patch object.
|
||||
|
||||
Some methods support PATCH, an efficient way to send updates to a resource.
|
||||
This method allows the easy construction of patch bodies by looking at the
|
||||
differences between a resource before and after it was modified.
|
||||
|
||||
Args:
|
||||
original: object, the original deserialized resource
|
||||
modified: object, the modified deserialized resource
|
||||
Returns:
|
||||
An object that contains only the changes from original to modified, in a
|
||||
form suitable to pass to a PATCH method.
|
||||
|
||||
Example usage:
|
||||
item = service.activities().get(postid=postid, userid=userid).execute()
|
||||
original = copy.deepcopy(item)
|
||||
item['object']['content'] = 'This is updated.'
|
||||
service.activities.patch(postid=postid, userid=userid,
|
||||
body=makepatch(original, item)).execute()
|
||||
"""
|
||||
patch = {}
|
||||
for key, original_value in original.items():
|
||||
modified_value = modified.get(key, None)
|
||||
if modified_value is None:
|
||||
# Use None to signal that the element is deleted
|
||||
patch[key] = None
|
||||
elif original_value != modified_value:
|
||||
if type(original_value) == type({}):
|
||||
# Recursively descend objects
|
||||
patch[key] = makepatch(original_value, modified_value)
|
||||
else:
|
||||
# In the case of simple types or arrays we just replace
|
||||
patch[key] = modified_value
|
||||
else:
|
||||
# Don't add anything to patch if there's no change
|
||||
pass
|
||||
for key in modified:
|
||||
if key not in original:
|
||||
patch[key] = modified[key]
|
||||
|
||||
return patch
|
||||
@ -1,317 +0,0 @@
|
||||
# Copyright 2014 Google Inc. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Schema processing for discovery based APIs
|
||||
|
||||
Schemas holds an APIs discovery schemas. It can return those schema as
|
||||
deserialized JSON objects, or pretty print them as prototype objects that
|
||||
conform to the schema.
|
||||
|
||||
For example, given the schema:
|
||||
|
||||
schema = \"\"\"{
|
||||
"Foo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"etag": {
|
||||
"type": "string",
|
||||
"description": "ETag of the collection."
|
||||
},
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"description": "Type of the collection ('calendar#acl').",
|
||||
"default": "calendar#acl"
|
||||
},
|
||||
"nextPageToken": {
|
||||
"type": "string",
|
||||
"description": "Token used to access the next
|
||||
page of this result. Omitted if no further results are available."
|
||||
}
|
||||
}
|
||||
}
|
||||
}\"\"\"
|
||||
|
||||
s = Schemas(schema)
|
||||
print s.prettyPrintByName('Foo')
|
||||
|
||||
Produces the following output:
|
||||
|
||||
{
|
||||
"nextPageToken": "A String", # Token used to access the
|
||||
# next page of this result. Omitted if no further results are available.
|
||||
"kind": "A String", # Type of the collection ('calendar#acl').
|
||||
"etag": "A String", # ETag of the collection.
|
||||
},
|
||||
|
||||
The constructor takes a discovery document in which to look up named schema.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
# TODO(jcgregorio) support format, enum, minimum, maximum
|
||||
|
||||
__author__ = "jcgregorio@google.com (Joe Gregorio)"
|
||||
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
from googleapiclient import _helpers as util
|
||||
|
||||
|
||||
class Schemas(object):
|
||||
"""Schemas for an API."""
|
||||
|
||||
def __init__(self, discovery):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
discovery: object, Deserialized discovery document from which we pull
|
||||
out the named schema.
|
||||
"""
|
||||
self.schemas = discovery.get("schemas", {})
|
||||
|
||||
# Cache of pretty printed schemas.
|
||||
self.pretty = {}
|
||||
|
||||
@util.positional(2)
|
||||
def _prettyPrintByName(self, name, seen=None, dent=0):
|
||||
"""Get pretty printed object prototype from the schema name.
|
||||
|
||||
Args:
|
||||
name: string, Name of schema in the discovery document.
|
||||
seen: list of string, Names of schema already seen. Used to handle
|
||||
recursive definitions.
|
||||
|
||||
Returns:
|
||||
string, A string that contains a prototype object with
|
||||
comments that conforms to the given schema.
|
||||
"""
|
||||
if seen is None:
|
||||
seen = []
|
||||
|
||||
if name in seen:
|
||||
# Do not fall into an infinite loop over recursive definitions.
|
||||
return "# Object with schema name: %s" % name
|
||||
seen.append(name)
|
||||
|
||||
if name not in self.pretty:
|
||||
self.pretty[name] = _SchemaToStruct(
|
||||
self.schemas[name], seen, dent=dent
|
||||
).to_str(self._prettyPrintByName)
|
||||
|
||||
seen.pop()
|
||||
|
||||
return self.pretty[name]
|
||||
|
||||
def prettyPrintByName(self, name):
|
||||
"""Get pretty printed object prototype from the schema name.
|
||||
|
||||
Args:
|
||||
name: string, Name of schema in the discovery document.
|
||||
|
||||
Returns:
|
||||
string, A string that contains a prototype object with
|
||||
comments that conforms to the given schema.
|
||||
"""
|
||||
# Return with trailing comma and newline removed.
|
||||
return self._prettyPrintByName(name, seen=[], dent=0)[:-2]
|
||||
|
||||
@util.positional(2)
|
||||
def _prettyPrintSchema(self, schema, seen=None, dent=0):
|
||||
"""Get pretty printed object prototype of schema.
|
||||
|
||||
Args:
|
||||
schema: object, Parsed JSON schema.
|
||||
seen: list of string, Names of schema already seen. Used to handle
|
||||
recursive definitions.
|
||||
|
||||
Returns:
|
||||
string, A string that contains a prototype object with
|
||||
comments that conforms to the given schema.
|
||||
"""
|
||||
if seen is None:
|
||||
seen = []
|
||||
|
||||
return _SchemaToStruct(schema, seen, dent=dent).to_str(self._prettyPrintByName)
|
||||
|
||||
def prettyPrintSchema(self, schema):
|
||||
"""Get pretty printed object prototype of schema.
|
||||
|
||||
Args:
|
||||
schema: object, Parsed JSON schema.
|
||||
|
||||
Returns:
|
||||
string, A string that contains a prototype object with
|
||||
comments that conforms to the given schema.
|
||||
"""
|
||||
# Return with trailing comma and newline removed.
|
||||
return self._prettyPrintSchema(schema, dent=0)[:-2]
|
||||
|
||||
def get(self, name, default=None):
|
||||
"""Get deserialized JSON schema from the schema name.
|
||||
|
||||
Args:
|
||||
name: string, Schema name.
|
||||
default: object, return value if name not found.
|
||||
"""
|
||||
return self.schemas.get(name, default)
|
||||
|
||||
|
||||
class _SchemaToStruct(object):
|
||||
"""Convert schema to a prototype object."""
|
||||
|
||||
@util.positional(3)
|
||||
def __init__(self, schema, seen, dent=0):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
schema: object, Parsed JSON schema.
|
||||
seen: list, List of names of schema already seen while parsing. Used to
|
||||
handle recursive definitions.
|
||||
dent: int, Initial indentation depth.
|
||||
"""
|
||||
# The result of this parsing kept as list of strings.
|
||||
self.value = []
|
||||
|
||||
# The final value of the parsing.
|
||||
self.string = None
|
||||
|
||||
# The parsed JSON schema.
|
||||
self.schema = schema
|
||||
|
||||
# Indentation level.
|
||||
self.dent = dent
|
||||
|
||||
# Method that when called returns a prototype object for the schema with
|
||||
# the given name.
|
||||
self.from_cache = None
|
||||
|
||||
# List of names of schema already seen while parsing.
|
||||
self.seen = seen
|
||||
|
||||
def emit(self, text):
|
||||
"""Add text as a line to the output.
|
||||
|
||||
Args:
|
||||
text: string, Text to output.
|
||||
"""
|
||||
self.value.extend([" " * self.dent, text, "\n"])
|
||||
|
||||
def emitBegin(self, text):
|
||||
"""Add text to the output, but with no line terminator.
|
||||
|
||||
Args:
|
||||
text: string, Text to output.
|
||||
"""
|
||||
self.value.extend([" " * self.dent, text])
|
||||
|
||||
def emitEnd(self, text, comment):
|
||||
"""Add text and comment to the output with line terminator.
|
||||
|
||||
Args:
|
||||
text: string, Text to output.
|
||||
comment: string, Python comment.
|
||||
"""
|
||||
if comment:
|
||||
divider = "\n" + " " * (self.dent + 2) + "# "
|
||||
lines = comment.splitlines()
|
||||
lines = [x.rstrip() for x in lines]
|
||||
comment = divider.join(lines)
|
||||
self.value.extend([text, " # ", comment, "\n"])
|
||||
else:
|
||||
self.value.extend([text, "\n"])
|
||||
|
||||
def indent(self):
|
||||
"""Increase indentation level."""
|
||||
self.dent += 1
|
||||
|
||||
def undent(self):
|
||||
"""Decrease indentation level."""
|
||||
self.dent -= 1
|
||||
|
||||
def _to_str_impl(self, schema):
|
||||
"""Prototype object based on the schema, in Python code with comments.
|
||||
|
||||
Args:
|
||||
schema: object, Parsed JSON schema file.
|
||||
|
||||
Returns:
|
||||
Prototype object based on the schema, in Python code with comments.
|
||||
"""
|
||||
stype = schema.get("type")
|
||||
if stype == "object":
|
||||
self.emitEnd("{", schema.get("description", ""))
|
||||
self.indent()
|
||||
if "properties" in schema:
|
||||
properties = schema.get("properties", {})
|
||||
sorted_properties = OrderedDict(sorted(properties.items()))
|
||||
for pname, pschema in sorted_properties.items():
|
||||
self.emitBegin('"%s": ' % pname)
|
||||
self._to_str_impl(pschema)
|
||||
elif "additionalProperties" in schema:
|
||||
self.emitBegin('"a_key": ')
|
||||
self._to_str_impl(schema["additionalProperties"])
|
||||
self.undent()
|
||||
self.emit("},")
|
||||
elif "$ref" in schema:
|
||||
schemaName = schema["$ref"]
|
||||
description = schema.get("description", "")
|
||||
s = self.from_cache(schemaName, seen=self.seen)
|
||||
parts = s.splitlines()
|
||||
self.emitEnd(parts[0], description)
|
||||
for line in parts[1:]:
|
||||
self.emit(line.rstrip())
|
||||
elif stype == "boolean":
|
||||
value = schema.get("default", "True or False")
|
||||
self.emitEnd("%s," % str(value), schema.get("description", ""))
|
||||
elif stype == "string":
|
||||
value = schema.get("default", "A String")
|
||||
self.emitEnd('"%s",' % str(value), schema.get("description", ""))
|
||||
elif stype == "integer":
|
||||
value = schema.get("default", "42")
|
||||
self.emitEnd("%s," % str(value), schema.get("description", ""))
|
||||
elif stype == "number":
|
||||
value = schema.get("default", "3.14")
|
||||
self.emitEnd("%s," % str(value), schema.get("description", ""))
|
||||
elif stype == "null":
|
||||
self.emitEnd("None,", schema.get("description", ""))
|
||||
elif stype == "any":
|
||||
self.emitEnd('"",', schema.get("description", ""))
|
||||
elif stype == "array":
|
||||
self.emitEnd("[", schema.get("description"))
|
||||
self.indent()
|
||||
self.emitBegin("")
|
||||
self._to_str_impl(schema["items"])
|
||||
self.undent()
|
||||
self.emit("],")
|
||||
else:
|
||||
self.emit("Unknown type! %s" % stype)
|
||||
self.emitEnd("", "")
|
||||
|
||||
self.string = "".join(self.value)
|
||||
return self.string
|
||||
|
||||
def to_str(self, from_cache):
|
||||
"""Prototype object based on the schema, in Python code with comments.
|
||||
|
||||
Args:
|
||||
from_cache: callable(name, seen), Callable that retrieves an object
|
||||
prototype for a schema with the given name. Seen is a list of schema
|
||||
names already seen as we recursively descend the schema definition.
|
||||
|
||||
Returns:
|
||||
Prototype object based on the schema, in Python code with comments.
|
||||
The lines of the code will all be properly indented.
|
||||
"""
|
||||
self.from_cache = from_cache
|
||||
return self._to_str_impl(self.schema)
|
||||
@ -1,15 +0,0 @@
|
||||
# Copyright 2021 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
__version__ = "2.164.0"
|
||||
@ -1,28 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2007 - 2015 Michael Twomey
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||
# copy of this software and associated documentation files (the
|
||||
# "Software"), to deal in the Software without restriction, including
|
||||
# without limitation the rights to use, copy, modify, merge, publish,
|
||||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||||
# permit persons to whom the Software is furnished to do so, subject to
|
||||
# the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included
|
||||
# in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
"""ISO 8601 date time string parsing
|
||||
|
||||
"""
|
||||
|
||||
__all__ = ["parse_date", "ParseError", "UTC"]
|
||||
@ -1,160 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""ISO 8601 date time string parsing
|
||||
|
||||
"""
|
||||
|
||||
from datetime import (datetime, timedelta, tzinfo)
|
||||
import time as _time
|
||||
import re
|
||||
|
||||
ISO8601_REGEX = re.compile(r'^(?P<year>[0-9]{4})-(?P<month>[0-9]{2})-(?P<day>[0-9]{2})(?P<separator>[ T])(?P<hour>[0-9]{2}):(?P<minute>[0-9]{2}):(?P<second>[0-9]{2})([.,](?P<second_fraction>[0-9]+)){0,1}(?P<timezone>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9]{2}):(?P<tz_minute>[0-9]{2}))$')
|
||||
ISO8601_TZ_REGEX = re.compile(r'^(?P<timezone>Z|(?P<tz_sign>[-+])(?P<tz_hour>[0-9]{2}):(?P<tz_minute>[0-9]{2}))$')
|
||||
|
||||
class ParseError(Exception):
|
||||
"""Raised when there is a problem parsing a date string"""
|
||||
|
||||
# Yoinked from python docs
|
||||
ZERO = timedelta(0)
|
||||
class Utc(tzinfo):
|
||||
"""UTC Timezone
|
||||
|
||||
"""
|
||||
def utcoffset(self, dt):
|
||||
return ZERO
|
||||
|
||||
def tzname(self, dt):
|
||||
return "UTC"
|
||||
|
||||
def dst(self, dt):
|
||||
return ZERO
|
||||
|
||||
def __repr__(self):
|
||||
return "<iso8601.Utc>"
|
||||
|
||||
UTC = Utc()
|
||||
|
||||
class FixedOffset(tzinfo):
|
||||
"""Fixed offset in hours and minutes from UTC
|
||||
|
||||
"""
|
||||
def __init__(self, offset_hours, offset_minutes, name):
|
||||
self.__offset_hours = offset_hours # Keep for later __getinitargs__
|
||||
self.__offset_minutes = offset_minutes # Keep for later __getinitargs__
|
||||
self.__offset = timedelta(hours=offset_hours, minutes=offset_minutes)
|
||||
self.__name = name
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, FixedOffset):
|
||||
return (other.__offset == self.__offset) and (other.__name == self.__name)
|
||||
if isinstance(other, tzinfo):
|
||||
return other == self
|
||||
return False
|
||||
|
||||
def __getinitargs__(self):
|
||||
return (self.__offset_hours, self.__offset_minutes, self.__name)
|
||||
|
||||
def utcoffset(self, dt):
|
||||
return self.__offset
|
||||
|
||||
def tzname(self, dt):
|
||||
return self.__name
|
||||
|
||||
def dst(self, dt):
|
||||
return ZERO
|
||||
|
||||
def __repr__(self):
|
||||
return "<FixedOffset %r %r>" % (self.__name, self.__offset)
|
||||
|
||||
# A class capturing the platform's idea of local time.
|
||||
|
||||
STDOFFSET = timedelta(seconds = -_time.timezone)
|
||||
if _time.daylight:
|
||||
DSTOFFSET = timedelta(seconds = -_time.altzone)
|
||||
else:
|
||||
DSTOFFSET = STDOFFSET
|
||||
|
||||
DSTDIFF = DSTOFFSET - STDOFFSET
|
||||
|
||||
class LocalTimezone(tzinfo):
|
||||
"""Local time zone
|
||||
|
||||
"""
|
||||
|
||||
def utcoffset(self, dt):
|
||||
if self._isdst(dt):
|
||||
return DSTOFFSET
|
||||
else:
|
||||
return STDOFFSET
|
||||
|
||||
def dst(self, dt):
|
||||
if self._isdst(dt):
|
||||
return DSTDIFF
|
||||
else:
|
||||
return ZERO
|
||||
|
||||
def tzname(self, dt):
|
||||
return _time.tzname[self._isdst(dt)]
|
||||
|
||||
def _isdst(self, dt):
|
||||
tt = (dt.year, dt.month, dt.day,
|
||||
dt.hour, dt.minute, dt.second,
|
||||
dt.weekday(), 0, 0)
|
||||
stamp = _time.mktime(tt)
|
||||
tt = _time.localtime(stamp)
|
||||
return tt.tm_isdst > 0
|
||||
|
||||
Local = LocalTimezone()
|
||||
|
||||
def parse_timezone(matches):
|
||||
"""Parses ISO 8601 time zone specs into tzinfo offsets
|
||||
|
||||
"""
|
||||
|
||||
if matches["timezone"] == "Z":
|
||||
return UTC
|
||||
sign = matches["tz_sign"]
|
||||
hours = int(matches['tz_hour'])
|
||||
minutes = int(matches['tz_minute'])
|
||||
description = "%s%02d:%02d" % (sign, hours, minutes)
|
||||
if sign == "-":
|
||||
hours = -hours
|
||||
minutes = -minutes
|
||||
return FixedOffset(hours, minutes, description)
|
||||
|
||||
def parse_timezone_str(tzstring):
|
||||
m = ISO8601_TZ_REGEX.match(tzstring)
|
||||
if not m:
|
||||
raise ParseError("Unable to parse timezone string %r" % tzstring)
|
||||
groups = m.groupdict()
|
||||
return parse_timezone(groups)
|
||||
|
||||
def parse_date(datestring):
|
||||
"""Parses ISO 8601 dates into datetime objects
|
||||
|
||||
The timezone is parsed from the date string. However it is quite common to
|
||||
have dates without a timezone (not strictly correct). In this case the
|
||||
default timezone specified in default_timezone is used. This is UTC by
|
||||
default.
|
||||
|
||||
:param datestring: The date to parse as a string
|
||||
:returns: A datetime.datetime instance
|
||||
:raises: ParseError when there is a problem parsing the date or
|
||||
constructing the datetime instance.
|
||||
|
||||
"""
|
||||
m = ISO8601_REGEX.match(datestring)
|
||||
if not m:
|
||||
raise ParseError("Unable to parse date string %r" % datestring)
|
||||
groups = m.groupdict()
|
||||
tz = parse_timezone(groups)
|
||||
try:
|
||||
return (datetime(year=int(groups['year']),
|
||||
month=int(groups['month']),
|
||||
day=int(groups['day']),
|
||||
hour=int(groups['hour']),
|
||||
minute=int(groups['minute']),
|
||||
second=int(groups['second']),
|
||||
tzinfo=tz),
|
||||
tz)
|
||||
except Exception as e:
|
||||
raise ParseError(e)
|
||||
982
src/gam/six.py
982
src/gam/six.py
@ -1,982 +0,0 @@
|
||||
# Copyright (c) 2010-2020 Benjamin Peterson
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
"""Utilities for writing code that runs on Python 2 and 3"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import functools
|
||||
import itertools
|
||||
import operator
|
||||
import sys
|
||||
import types
|
||||
|
||||
__author__ = "Benjamin Peterson <benjamin@python.org>"
|
||||
__version__ = "1.15.0"
|
||||
|
||||
|
||||
# Useful for very coarse version differentiation.
|
||||
PY2 = sys.version_info[0] == 2
|
||||
PY3 = sys.version_info[0] == 3
|
||||
PY34 = sys.version_info[0:2] >= (3, 4)
|
||||
|
||||
if PY3:
|
||||
string_types = str,
|
||||
integer_types = int,
|
||||
class_types = type,
|
||||
text_type = str
|
||||
binary_type = bytes
|
||||
|
||||
MAXSIZE = sys.maxsize
|
||||
else:
|
||||
string_types = basestring,
|
||||
integer_types = (int, long)
|
||||
class_types = (type, types.ClassType)
|
||||
text_type = unicode
|
||||
binary_type = str
|
||||
|
||||
if sys.platform.startswith("java"):
|
||||
# Jython always uses 32 bits.
|
||||
MAXSIZE = int((1 << 31) - 1)
|
||||
else:
|
||||
# It's possible to have sizeof(long) != sizeof(Py_ssize_t).
|
||||
class X(object):
|
||||
|
||||
def __len__(self):
|
||||
return 1 << 31
|
||||
try:
|
||||
len(X())
|
||||
except OverflowError:
|
||||
# 32-bit
|
||||
MAXSIZE = int((1 << 31) - 1)
|
||||
else:
|
||||
# 64-bit
|
||||
MAXSIZE = int((1 << 63) - 1)
|
||||
del X
|
||||
|
||||
|
||||
def _add_doc(func, doc):
|
||||
"""Add documentation to a function."""
|
||||
func.__doc__ = doc
|
||||
|
||||
|
||||
def _import_module(name):
|
||||
"""Import module, returning the module after the last dot."""
|
||||
__import__(name)
|
||||
return sys.modules[name]
|
||||
|
||||
|
||||
class _LazyDescr(object):
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __get__(self, obj, tp):
|
||||
result = self._resolve()
|
||||
setattr(obj, self.name, result) # Invokes __set__.
|
||||
try:
|
||||
# This is a bit ugly, but it avoids running this again by
|
||||
# removing this descriptor.
|
||||
delattr(obj.__class__, self.name)
|
||||
except AttributeError:
|
||||
pass
|
||||
return result
|
||||
|
||||
|
||||
class MovedModule(_LazyDescr):
|
||||
|
||||
def __init__(self, name, old, new=None):
|
||||
super(MovedModule, self).__init__(name)
|
||||
if PY3:
|
||||
if new is None:
|
||||
new = name
|
||||
self.mod = new
|
||||
else:
|
||||
self.mod = old
|
||||
|
||||
def _resolve(self):
|
||||
return _import_module(self.mod)
|
||||
|
||||
def __getattr__(self, attr):
|
||||
_module = self._resolve()
|
||||
value = getattr(_module, attr)
|
||||
setattr(self, attr, value)
|
||||
return value
|
||||
|
||||
|
||||
class _LazyModule(types.ModuleType):
|
||||
|
||||
def __init__(self, name):
|
||||
super(_LazyModule, self).__init__(name)
|
||||
self.__doc__ = self.__class__.__doc__
|
||||
|
||||
def __dir__(self):
|
||||
attrs = ["__doc__", "__name__"]
|
||||
attrs += [attr.name for attr in self._moved_attributes]
|
||||
return attrs
|
||||
|
||||
# Subclasses should override this
|
||||
_moved_attributes = []
|
||||
|
||||
|
||||
class MovedAttribute(_LazyDescr):
|
||||
|
||||
def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
|
||||
super(MovedAttribute, self).__init__(name)
|
||||
if PY3:
|
||||
if new_mod is None:
|
||||
new_mod = name
|
||||
self.mod = new_mod
|
||||
if new_attr is None:
|
||||
if old_attr is None:
|
||||
new_attr = name
|
||||
else:
|
||||
new_attr = old_attr
|
||||
self.attr = new_attr
|
||||
else:
|
||||
self.mod = old_mod
|
||||
if old_attr is None:
|
||||
old_attr = name
|
||||
self.attr = old_attr
|
||||
|
||||
def _resolve(self):
|
||||
module = _import_module(self.mod)
|
||||
return getattr(module, self.attr)
|
||||
|
||||
|
||||
class _SixMetaPathImporter(object):
|
||||
|
||||
"""
|
||||
A meta path importer to import six.moves and its submodules.
|
||||
|
||||
This class implements a PEP302 finder and loader. It should be compatible
|
||||
with Python 2.5 and all existing versions of Python3
|
||||
"""
|
||||
|
||||
def __init__(self, six_module_name):
|
||||
self.name = six_module_name
|
||||
self.known_modules = {}
|
||||
|
||||
def _add_module(self, mod, *fullnames):
|
||||
for fullname in fullnames:
|
||||
self.known_modules[self.name + "." + fullname] = mod
|
||||
|
||||
def _get_module(self, fullname):
|
||||
return self.known_modules[self.name + "." + fullname]
|
||||
|
||||
def find_module(self, fullname, path=None):
|
||||
if fullname in self.known_modules:
|
||||
return self
|
||||
return None
|
||||
|
||||
def __get_module(self, fullname):
|
||||
try:
|
||||
return self.known_modules[fullname]
|
||||
except KeyError:
|
||||
raise ImportError("This loader does not know module " + fullname)
|
||||
|
||||
def load_module(self, fullname):
|
||||
try:
|
||||
# in case of a reload
|
||||
return sys.modules[fullname]
|
||||
except KeyError:
|
||||
pass
|
||||
mod = self.__get_module(fullname)
|
||||
if isinstance(mod, MovedModule):
|
||||
mod = mod._resolve()
|
||||
else:
|
||||
mod.__loader__ = self
|
||||
sys.modules[fullname] = mod
|
||||
return mod
|
||||
|
||||
def is_package(self, fullname):
|
||||
"""
|
||||
Return true, if the named module is a package.
|
||||
|
||||
We need this method to get correct spec objects with
|
||||
Python 3.4 (see PEP451)
|
||||
"""
|
||||
return hasattr(self.__get_module(fullname), "__path__")
|
||||
|
||||
def get_code(self, fullname):
|
||||
"""Return None
|
||||
|
||||
Required, if is_package is implemented"""
|
||||
self.__get_module(fullname) # eventually raises ImportError
|
||||
return None
|
||||
get_source = get_code # same as get_code
|
||||
|
||||
_importer = _SixMetaPathImporter(__name__)
|
||||
|
||||
|
||||
class _MovedItems(_LazyModule):
|
||||
|
||||
"""Lazy loading of moved objects"""
|
||||
__path__ = [] # mark as package
|
||||
|
||||
|
||||
_moved_attributes = [
|
||||
MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
|
||||
MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
|
||||
MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"),
|
||||
MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
|
||||
MovedAttribute("intern", "__builtin__", "sys"),
|
||||
MovedAttribute("map", "itertools", "builtins", "imap", "map"),
|
||||
MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"),
|
||||
MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"),
|
||||
MovedAttribute("getoutput", "commands", "subprocess"),
|
||||
MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
|
||||
MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"),
|
||||
MovedAttribute("reduce", "__builtin__", "functools"),
|
||||
MovedAttribute("shlex_quote", "pipes", "shlex", "quote"),
|
||||
MovedAttribute("StringIO", "StringIO", "io"),
|
||||
MovedAttribute("UserDict", "UserDict", "collections"),
|
||||
MovedAttribute("UserList", "UserList", "collections"),
|
||||
MovedAttribute("UserString", "UserString", "collections"),
|
||||
MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
|
||||
MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
|
||||
MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"),
|
||||
MovedModule("builtins", "__builtin__"),
|
||||
MovedModule("configparser", "ConfigParser"),
|
||||
MovedModule("collections_abc", "collections", "collections.abc" if sys.version_info >= (3, 3) else "collections"),
|
||||
MovedModule("copyreg", "copy_reg"),
|
||||
MovedModule("dbm_gnu", "gdbm", "dbm.gnu"),
|
||||
MovedModule("dbm_ndbm", "dbm", "dbm.ndbm"),
|
||||
MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread" if sys.version_info < (3, 9) else "_thread"),
|
||||
MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
|
||||
MovedModule("http_cookies", "Cookie", "http.cookies"),
|
||||
MovedModule("html_entities", "htmlentitydefs", "html.entities"),
|
||||
MovedModule("html_parser", "HTMLParser", "html.parser"),
|
||||
MovedModule("http_client", "httplib", "http.client"),
|
||||
MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
|
||||
MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"),
|
||||
MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
|
||||
MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"),
|
||||
MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
|
||||
MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
|
||||
MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
|
||||
MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
|
||||
MovedModule("cPickle", "cPickle", "pickle"),
|
||||
MovedModule("queue", "Queue"),
|
||||
MovedModule("reprlib", "repr"),
|
||||
MovedModule("socketserver", "SocketServer"),
|
||||
MovedModule("_thread", "thread", "_thread"),
|
||||
MovedModule("tkinter", "Tkinter"),
|
||||
MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
|
||||
MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
|
||||
MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
|
||||
MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
|
||||
MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
|
||||
MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"),
|
||||
MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
|
||||
MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
|
||||
MovedModule("tkinter_colorchooser", "tkColorChooser",
|
||||
"tkinter.colorchooser"),
|
||||
MovedModule("tkinter_commondialog", "tkCommonDialog",
|
||||
"tkinter.commondialog"),
|
||||
MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
|
||||
MovedModule("tkinter_font", "tkFont", "tkinter.font"),
|
||||
MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
|
||||
MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
|
||||
"tkinter.simpledialog"),
|
||||
MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"),
|
||||
MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"),
|
||||
MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"),
|
||||
MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
|
||||
MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"),
|
||||
MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"),
|
||||
]
|
||||
# Add windows specific modules.
|
||||
if sys.platform == "win32":
|
||||
_moved_attributes += [
|
||||
MovedModule("winreg", "_winreg"),
|
||||
]
|
||||
|
||||
for attr in _moved_attributes:
|
||||
setattr(_MovedItems, attr.name, attr)
|
||||
if isinstance(attr, MovedModule):
|
||||
_importer._add_module(attr, "moves." + attr.name)
|
||||
del attr
|
||||
|
||||
_MovedItems._moved_attributes = _moved_attributes
|
||||
|
||||
moves = _MovedItems(__name__ + ".moves")
|
||||
_importer._add_module(moves, "moves")
|
||||
|
||||
|
||||
class Module_six_moves_urllib_parse(_LazyModule):
|
||||
|
||||
"""Lazy loading of moved objects in six.moves.urllib_parse"""
|
||||
|
||||
|
||||
_urllib_parse_moved_attributes = [
|
||||
MovedAttribute("ParseResult", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("SplitResult", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("parse_qs", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("parse_qsl", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("urldefrag", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("urljoin", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("urlparse", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("urlsplit", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("urlunparse", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("urlunsplit", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("quote", "urllib", "urllib.parse"),
|
||||
MovedAttribute("quote_plus", "urllib", "urllib.parse"),
|
||||
MovedAttribute("unquote", "urllib", "urllib.parse"),
|
||||
MovedAttribute("unquote_plus", "urllib", "urllib.parse"),
|
||||
MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"),
|
||||
MovedAttribute("urlencode", "urllib", "urllib.parse"),
|
||||
MovedAttribute("splitquery", "urllib", "urllib.parse"),
|
||||
MovedAttribute("splittag", "urllib", "urllib.parse"),
|
||||
MovedAttribute("splituser", "urllib", "urllib.parse"),
|
||||
MovedAttribute("splitvalue", "urllib", "urllib.parse"),
|
||||
MovedAttribute("uses_fragment", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("uses_netloc", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("uses_params", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("uses_query", "urlparse", "urllib.parse"),
|
||||
MovedAttribute("uses_relative", "urlparse", "urllib.parse"),
|
||||
]
|
||||
for attr in _urllib_parse_moved_attributes:
|
||||
setattr(Module_six_moves_urllib_parse, attr.name, attr)
|
||||
del attr
|
||||
|
||||
Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes
|
||||
|
||||
_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"),
|
||||
"moves.urllib_parse", "moves.urllib.parse")
|
||||
|
||||
|
||||
class Module_six_moves_urllib_error(_LazyModule):
|
||||
|
||||
"""Lazy loading of moved objects in six.moves.urllib_error"""
|
||||
|
||||
|
||||
_urllib_error_moved_attributes = [
|
||||
MovedAttribute("URLError", "urllib2", "urllib.error"),
|
||||
MovedAttribute("HTTPError", "urllib2", "urllib.error"),
|
||||
MovedAttribute("ContentTooShortError", "urllib", "urllib.error"),
|
||||
]
|
||||
for attr in _urllib_error_moved_attributes:
|
||||
setattr(Module_six_moves_urllib_error, attr.name, attr)
|
||||
del attr
|
||||
|
||||
Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes
|
||||
|
||||
_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"),
|
||||
"moves.urllib_error", "moves.urllib.error")
|
||||
|
||||
|
||||
class Module_six_moves_urllib_request(_LazyModule):
|
||||
|
||||
"""Lazy loading of moved objects in six.moves.urllib_request"""
|
||||
|
||||
|
||||
_urllib_request_moved_attributes = [
|
||||
MovedAttribute("urlopen", "urllib2", "urllib.request"),
|
||||
MovedAttribute("install_opener", "urllib2", "urllib.request"),
|
||||
MovedAttribute("build_opener", "urllib2", "urllib.request"),
|
||||
MovedAttribute("pathname2url", "urllib", "urllib.request"),
|
||||
MovedAttribute("url2pathname", "urllib", "urllib.request"),
|
||||
MovedAttribute("getproxies", "urllib", "urllib.request"),
|
||||
MovedAttribute("Request", "urllib2", "urllib.request"),
|
||||
MovedAttribute("OpenerDirector", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"),
|
||||
MovedAttribute("ProxyHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("BaseHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"),
|
||||
MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("FileHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("FTPHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("UnknownHandler", "urllib2", "urllib.request"),
|
||||
MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"),
|
||||
MovedAttribute("urlretrieve", "urllib", "urllib.request"),
|
||||
MovedAttribute("urlcleanup", "urllib", "urllib.request"),
|
||||
MovedAttribute("URLopener", "urllib", "urllib.request"),
|
||||
MovedAttribute("FancyURLopener", "urllib", "urllib.request"),
|
||||
MovedAttribute("proxy_bypass", "urllib", "urllib.request"),
|
||||
MovedAttribute("parse_http_list", "urllib2", "urllib.request"),
|
||||
MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"),
|
||||
]
|
||||
for attr in _urllib_request_moved_attributes:
|
||||
setattr(Module_six_moves_urllib_request, attr.name, attr)
|
||||
del attr
|
||||
|
||||
Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes
|
||||
|
||||
_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"),
|
||||
"moves.urllib_request", "moves.urllib.request")
|
||||
|
||||
|
||||
class Module_six_moves_urllib_response(_LazyModule):
|
||||
|
||||
"""Lazy loading of moved objects in six.moves.urllib_response"""
|
||||
|
||||
|
||||
_urllib_response_moved_attributes = [
|
||||
MovedAttribute("addbase", "urllib", "urllib.response"),
|
||||
MovedAttribute("addclosehook", "urllib", "urllib.response"),
|
||||
MovedAttribute("addinfo", "urllib", "urllib.response"),
|
||||
MovedAttribute("addinfourl", "urllib", "urllib.response"),
|
||||
]
|
||||
for attr in _urllib_response_moved_attributes:
|
||||
setattr(Module_six_moves_urllib_response, attr.name, attr)
|
||||
del attr
|
||||
|
||||
Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes
|
||||
|
||||
_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"),
|
||||
"moves.urllib_response", "moves.urllib.response")
|
||||
|
||||
|
||||
class Module_six_moves_urllib_robotparser(_LazyModule):
|
||||
|
||||
"""Lazy loading of moved objects in six.moves.urllib_robotparser"""
|
||||
|
||||
|
||||
_urllib_robotparser_moved_attributes = [
|
||||
MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"),
|
||||
]
|
||||
for attr in _urllib_robotparser_moved_attributes:
|
||||
setattr(Module_six_moves_urllib_robotparser, attr.name, attr)
|
||||
del attr
|
||||
|
||||
Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes
|
||||
|
||||
_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"),
|
||||
"moves.urllib_robotparser", "moves.urllib.robotparser")
|
||||
|
||||
|
||||
class Module_six_moves_urllib(types.ModuleType):
|
||||
|
||||
"""Create a six.moves.urllib namespace that resembles the Python 3 namespace"""
|
||||
__path__ = [] # mark as package
|
||||
parse = _importer._get_module("moves.urllib_parse")
|
||||
error = _importer._get_module("moves.urllib_error")
|
||||
request = _importer._get_module("moves.urllib_request")
|
||||
response = _importer._get_module("moves.urllib_response")
|
||||
robotparser = _importer._get_module("moves.urllib_robotparser")
|
||||
|
||||
def __dir__(self):
|
||||
return ['parse', 'error', 'request', 'response', 'robotparser']
|
||||
|
||||
_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"),
|
||||
"moves.urllib")
|
||||
|
||||
|
||||
def add_move(move):
|
||||
"""Add an item to six.moves."""
|
||||
setattr(_MovedItems, move.name, move)
|
||||
|
||||
|
||||
def remove_move(name):
|
||||
"""Remove item from six.moves."""
|
||||
try:
|
||||
delattr(_MovedItems, name)
|
||||
except AttributeError:
|
||||
try:
|
||||
del moves.__dict__[name]
|
||||
except KeyError:
|
||||
raise AttributeError("no such move, %r" % (name,))
|
||||
|
||||
|
||||
if PY3:
|
||||
_meth_func = "__func__"
|
||||
_meth_self = "__self__"
|
||||
|
||||
_func_closure = "__closure__"
|
||||
_func_code = "__code__"
|
||||
_func_defaults = "__defaults__"
|
||||
_func_globals = "__globals__"
|
||||
else:
|
||||
_meth_func = "im_func"
|
||||
_meth_self = "im_self"
|
||||
|
||||
_func_closure = "func_closure"
|
||||
_func_code = "func_code"
|
||||
_func_defaults = "func_defaults"
|
||||
_func_globals = "func_globals"
|
||||
|
||||
|
||||
try:
|
||||
advance_iterator = next
|
||||
except NameError:
|
||||
def advance_iterator(it):
|
||||
return it.next()
|
||||
next = advance_iterator
|
||||
|
||||
|
||||
try:
|
||||
callable = callable
|
||||
except NameError:
|
||||
def callable(obj):
|
||||
return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
|
||||
|
||||
|
||||
if PY3:
|
||||
def get_unbound_function(unbound):
|
||||
return unbound
|
||||
|
||||
create_bound_method = types.MethodType
|
||||
|
||||
def create_unbound_method(func, cls):
|
||||
return func
|
||||
|
||||
Iterator = object
|
||||
else:
|
||||
def get_unbound_function(unbound):
|
||||
return unbound.im_func
|
||||
|
||||
def create_bound_method(func, obj):
|
||||
return types.MethodType(func, obj, obj.__class__)
|
||||
|
||||
def create_unbound_method(func, cls):
|
||||
return types.MethodType(func, None, cls)
|
||||
|
||||
class Iterator(object):
|
||||
|
||||
def next(self):
|
||||
return type(self).__next__(self)
|
||||
|
||||
callable = callable
|
||||
_add_doc(get_unbound_function,
|
||||
"""Get the function out of a possibly unbound function""")
|
||||
|
||||
|
||||
get_method_function = operator.attrgetter(_meth_func)
|
||||
get_method_self = operator.attrgetter(_meth_self)
|
||||
get_function_closure = operator.attrgetter(_func_closure)
|
||||
get_function_code = operator.attrgetter(_func_code)
|
||||
get_function_defaults = operator.attrgetter(_func_defaults)
|
||||
get_function_globals = operator.attrgetter(_func_globals)
|
||||
|
||||
|
||||
if PY3:
|
||||
def iterkeys(d, **kw):
|
||||
return iter(d.keys(**kw))
|
||||
|
||||
def itervalues(d, **kw):
|
||||
return iter(d.values(**kw))
|
||||
|
||||
def iteritems(d, **kw):
|
||||
return iter(d.items(**kw))
|
||||
|
||||
def iterlists(d, **kw):
|
||||
return iter(d.lists(**kw))
|
||||
|
||||
viewkeys = operator.methodcaller("keys")
|
||||
|
||||
viewvalues = operator.methodcaller("values")
|
||||
|
||||
viewitems = operator.methodcaller("items")
|
||||
else:
|
||||
def iterkeys(d, **kw):
|
||||
return d.iterkeys(**kw)
|
||||
|
||||
def itervalues(d, **kw):
|
||||
return d.itervalues(**kw)
|
||||
|
||||
def iteritems(d, **kw):
|
||||
return d.iteritems(**kw)
|
||||
|
||||
def iterlists(d, **kw):
|
||||
return d.iterlists(**kw)
|
||||
|
||||
viewkeys = operator.methodcaller("viewkeys")
|
||||
|
||||
viewvalues = operator.methodcaller("viewvalues")
|
||||
|
||||
viewitems = operator.methodcaller("viewitems")
|
||||
|
||||
_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.")
|
||||
_add_doc(itervalues, "Return an iterator over the values of a dictionary.")
|
||||
_add_doc(iteritems,
|
||||
"Return an iterator over the (key, value) pairs of a dictionary.")
|
||||
_add_doc(iterlists,
|
||||
"Return an iterator over the (key, [values]) pairs of a dictionary.")
|
||||
|
||||
|
||||
if PY3:
|
||||
def b(s):
|
||||
return s.encode("latin-1")
|
||||
|
||||
def u(s):
|
||||
return s
|
||||
unichr = chr
|
||||
import struct
|
||||
int2byte = struct.Struct(">B").pack
|
||||
del struct
|
||||
byte2int = operator.itemgetter(0)
|
||||
indexbytes = operator.getitem
|
||||
iterbytes = iter
|
||||
import io
|
||||
StringIO = io.StringIO
|
||||
BytesIO = io.BytesIO
|
||||
del io
|
||||
_assertCountEqual = "assertCountEqual"
|
||||
if sys.version_info[1] <= 1:
|
||||
_assertRaisesRegex = "assertRaisesRegexp"
|
||||
_assertRegex = "assertRegexpMatches"
|
||||
_assertNotRegex = "assertNotRegexpMatches"
|
||||
else:
|
||||
_assertRaisesRegex = "assertRaisesRegex"
|
||||
_assertRegex = "assertRegex"
|
||||
_assertNotRegex = "assertNotRegex"
|
||||
else:
|
||||
def b(s):
|
||||
return s
|
||||
# Workaround for standalone backslash
|
||||
|
||||
def u(s):
|
||||
return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape")
|
||||
unichr = unichr
|
||||
int2byte = chr
|
||||
|
||||
def byte2int(bs):
|
||||
return ord(bs[0])
|
||||
|
||||
def indexbytes(buf, i):
|
||||
return ord(buf[i])
|
||||
iterbytes = functools.partial(itertools.imap, ord)
|
||||
import StringIO
|
||||
StringIO = BytesIO = StringIO.StringIO
|
||||
_assertCountEqual = "assertItemsEqual"
|
||||
_assertRaisesRegex = "assertRaisesRegexp"
|
||||
_assertRegex = "assertRegexpMatches"
|
||||
_assertNotRegex = "assertNotRegexpMatches"
|
||||
_add_doc(b, """Byte literal""")
|
||||
_add_doc(u, """Text literal""")
|
||||
|
||||
|
||||
def assertCountEqual(self, *args, **kwargs):
|
||||
return getattr(self, _assertCountEqual)(*args, **kwargs)
|
||||
|
||||
|
||||
def assertRaisesRegex(self, *args, **kwargs):
|
||||
return getattr(self, _assertRaisesRegex)(*args, **kwargs)
|
||||
|
||||
|
||||
def assertRegex(self, *args, **kwargs):
|
||||
return getattr(self, _assertRegex)(*args, **kwargs)
|
||||
|
||||
|
||||
def assertNotRegex(self, *args, **kwargs):
|
||||
return getattr(self, _assertNotRegex)(*args, **kwargs)
|
||||
|
||||
|
||||
if PY3:
|
||||
exec_ = getattr(moves.builtins, "exec")
|
||||
|
||||
def reraise(tp, value, tb=None):
|
||||
try:
|
||||
if value is None:
|
||||
value = tp()
|
||||
if value.__traceback__ is not tb:
|
||||
raise value.with_traceback(tb)
|
||||
raise value
|
||||
finally:
|
||||
value = None
|
||||
tb = None
|
||||
|
||||
else:
|
||||
def exec_(_code_, _globs_=None, _locs_=None):
|
||||
"""Execute code in a namespace."""
|
||||
if _globs_ is None:
|
||||
frame = sys._getframe(1)
|
||||
_globs_ = frame.f_globals
|
||||
if _locs_ is None:
|
||||
_locs_ = frame.f_locals
|
||||
del frame
|
||||
elif _locs_ is None:
|
||||
_locs_ = _globs_
|
||||
exec("""exec _code_ in _globs_, _locs_""")
|
||||
|
||||
exec_("""def reraise(tp, value, tb=None):
|
||||
try:
|
||||
raise tp, value, tb
|
||||
finally:
|
||||
tb = None
|
||||
""")
|
||||
|
||||
|
||||
if sys.version_info[:2] > (3,):
|
||||
exec_("""def raise_from(value, from_value):
|
||||
try:
|
||||
raise value from from_value
|
||||
finally:
|
||||
value = None
|
||||
""")
|
||||
else:
|
||||
def raise_from(value, from_value):
|
||||
raise value
|
||||
|
||||
|
||||
print_ = getattr(moves.builtins, "print", None)
|
||||
if print_ is None:
|
||||
def print_(*args, **kwargs):
|
||||
"""The new-style print function for Python 2.4 and 2.5."""
|
||||
fp = kwargs.pop("file", sys.stdout)
|
||||
if fp is None:
|
||||
return
|
||||
|
||||
def write(data):
|
||||
if not isinstance(data, basestring):
|
||||
data = str(data)
|
||||
# If the file has an encoding, encode unicode with it.
|
||||
if (isinstance(fp, file) and
|
||||
isinstance(data, unicode) and
|
||||
fp.encoding is not None):
|
||||
errors = getattr(fp, "errors", None)
|
||||
if errors is None:
|
||||
errors = "strict"
|
||||
data = data.encode(fp.encoding, errors)
|
||||
fp.write(data)
|
||||
want_unicode = False
|
||||
sep = kwargs.pop("sep", None)
|
||||
if sep is not None:
|
||||
if isinstance(sep, unicode):
|
||||
want_unicode = True
|
||||
elif not isinstance(sep, str):
|
||||
raise TypeError("sep must be None or a string")
|
||||
end = kwargs.pop("end", None)
|
||||
if end is not None:
|
||||
if isinstance(end, unicode):
|
||||
want_unicode = True
|
||||
elif not isinstance(end, str):
|
||||
raise TypeError("end must be None or a string")
|
||||
if kwargs:
|
||||
raise TypeError("invalid keyword arguments to print()")
|
||||
if not want_unicode:
|
||||
for arg in args:
|
||||
if isinstance(arg, unicode):
|
||||
want_unicode = True
|
||||
break
|
||||
if want_unicode:
|
||||
newline = unicode("\n")
|
||||
space = unicode(" ")
|
||||
else:
|
||||
newline = "\n"
|
||||
space = " "
|
||||
if sep is None:
|
||||
sep = space
|
||||
if end is None:
|
||||
end = newline
|
||||
for i, arg in enumerate(args):
|
||||
if i:
|
||||
write(sep)
|
||||
write(arg)
|
||||
write(end)
|
||||
if sys.version_info[:2] < (3, 3):
|
||||
_print = print_
|
||||
|
||||
def print_(*args, **kwargs):
|
||||
fp = kwargs.get("file", sys.stdout)
|
||||
flush = kwargs.pop("flush", False)
|
||||
_print(*args, **kwargs)
|
||||
if flush and fp is not None:
|
||||
fp.flush()
|
||||
|
||||
_add_doc(reraise, """Reraise an exception.""")
|
||||
|
||||
if sys.version_info[0:2] < (3, 4):
|
||||
# This does exactly the same what the :func:`py3:functools.update_wrapper`
|
||||
# function does on Python versions after 3.2. It sets the ``__wrapped__``
|
||||
# attribute on ``wrapper`` object and it doesn't raise an error if any of
|
||||
# the attributes mentioned in ``assigned`` and ``updated`` are missing on
|
||||
# ``wrapped`` object.
|
||||
def _update_wrapper(wrapper, wrapped,
|
||||
assigned=functools.WRAPPER_ASSIGNMENTS,
|
||||
updated=functools.WRAPPER_UPDATES):
|
||||
for attr in assigned:
|
||||
try:
|
||||
value = getattr(wrapped, attr)
|
||||
except AttributeError:
|
||||
continue
|
||||
else:
|
||||
setattr(wrapper, attr, value)
|
||||
for attr in updated:
|
||||
getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
|
||||
wrapper.__wrapped__ = wrapped
|
||||
return wrapper
|
||||
_update_wrapper.__doc__ = functools.update_wrapper.__doc__
|
||||
|
||||
def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
|
||||
updated=functools.WRAPPER_UPDATES):
|
||||
return functools.partial(_update_wrapper, wrapped=wrapped,
|
||||
assigned=assigned, updated=updated)
|
||||
wraps.__doc__ = functools.wraps.__doc__
|
||||
|
||||
else:
|
||||
wraps = functools.wraps
|
||||
|
||||
|
||||
def with_metaclass(meta, *bases):
|
||||
"""Create a base class with a metaclass."""
|
||||
# This requires a bit of explanation: the basic idea is to make a dummy
|
||||
# metaclass for one level of class instantiation that replaces itself with
|
||||
# the actual metaclass.
|
||||
class metaclass(type):
|
||||
|
||||
def __new__(cls, name, this_bases, d):
|
||||
if sys.version_info[:2] >= (3, 7):
|
||||
# This version introduced PEP 560 that requires a bit
|
||||
# of extra care (we mimic what is done by __build_class__).
|
||||
resolved_bases = types.resolve_bases(bases)
|
||||
if resolved_bases is not bases:
|
||||
d['__orig_bases__'] = bases
|
||||
else:
|
||||
resolved_bases = bases
|
||||
return meta(name, resolved_bases, d)
|
||||
|
||||
@classmethod
|
||||
def __prepare__(cls, name, this_bases):
|
||||
return meta.__prepare__(name, bases)
|
||||
return type.__new__(metaclass, 'temporary_class', (), {})
|
||||
|
||||
|
||||
def add_metaclass(metaclass):
|
||||
"""Class decorator for creating a class with a metaclass."""
|
||||
def wrapper(cls):
|
||||
orig_vars = cls.__dict__.copy()
|
||||
slots = orig_vars.get('__slots__')
|
||||
if slots is not None:
|
||||
if isinstance(slots, str):
|
||||
slots = [slots]
|
||||
for slots_var in slots:
|
||||
orig_vars.pop(slots_var)
|
||||
orig_vars.pop('__dict__', None)
|
||||
orig_vars.pop('__weakref__', None)
|
||||
if hasattr(cls, '__qualname__'):
|
||||
orig_vars['__qualname__'] = cls.__qualname__
|
||||
return metaclass(cls.__name__, cls.__bases__, orig_vars)
|
||||
return wrapper
|
||||
|
||||
|
||||
def ensure_binary(s, encoding='utf-8', errors='strict'):
|
||||
"""Coerce **s** to six.binary_type.
|
||||
|
||||
For Python 2:
|
||||
- `unicode` -> encoded to `str`
|
||||
- `str` -> `str`
|
||||
|
||||
For Python 3:
|
||||
- `str` -> encoded to `bytes`
|
||||
- `bytes` -> `bytes`
|
||||
"""
|
||||
if isinstance(s, binary_type):
|
||||
return s
|
||||
if isinstance(s, text_type):
|
||||
return s.encode(encoding, errors)
|
||||
raise TypeError("not expecting type '%s'" % type(s))
|
||||
|
||||
|
||||
def ensure_str(s, encoding='utf-8', errors='strict'):
|
||||
"""Coerce *s* to `str`.
|
||||
|
||||
For Python 2:
|
||||
- `unicode` -> encoded to `str`
|
||||
- `str` -> `str`
|
||||
|
||||
For Python 3:
|
||||
- `str` -> `str`
|
||||
- `bytes` -> decoded to `str`
|
||||
"""
|
||||
# Optimization: Fast return for the common case.
|
||||
if type(s) is str:
|
||||
return s
|
||||
if PY2 and isinstance(s, text_type):
|
||||
return s.encode(encoding, errors)
|
||||
elif PY3 and isinstance(s, binary_type):
|
||||
return s.decode(encoding, errors)
|
||||
elif not isinstance(s, (text_type, binary_type)):
|
||||
raise TypeError("not expecting type '%s'" % type(s))
|
||||
return s
|
||||
|
||||
|
||||
def ensure_text(s, encoding='utf-8', errors='strict'):
|
||||
"""Coerce *s* to six.text_type.
|
||||
|
||||
For Python 2:
|
||||
- `unicode` -> `unicode`
|
||||
- `str` -> `unicode`
|
||||
|
||||
For Python 3:
|
||||
- `str` -> `str`
|
||||
- `bytes` -> decoded to `str`
|
||||
"""
|
||||
if isinstance(s, binary_type):
|
||||
return s.decode(encoding, errors)
|
||||
elif isinstance(s, text_type):
|
||||
return s
|
||||
else:
|
||||
raise TypeError("not expecting type '%s'" % type(s))
|
||||
|
||||
|
||||
def python_2_unicode_compatible(klass):
|
||||
"""
|
||||
A class decorator that defines __unicode__ and __str__ methods under Python 2.
|
||||
Under Python 3 it does nothing.
|
||||
|
||||
To support Python 2 and 3 with a single code base, define a __str__ method
|
||||
returning text and apply this decorator to the class.
|
||||
"""
|
||||
if PY2:
|
||||
if '__str__' not in klass.__dict__:
|
||||
raise ValueError("@python_2_unicode_compatible cannot be applied "
|
||||
"to %s because it doesn't define __str__()." %
|
||||
klass.__name__)
|
||||
klass.__unicode__ = klass.__str__
|
||||
klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
|
||||
return klass
|
||||
|
||||
|
||||
# Complete the moves implementation.
|
||||
# This code is at the end of this module to speed up module loading.
|
||||
# Turn this module into a package.
|
||||
__path__ = [] # required for PEP 302 and PEP 451
|
||||
__package__ = __name__ # see PEP 366 @ReservedAssignment
|
||||
if globals().get("__spec__") is not None:
|
||||
__spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable
|
||||
# Remove other six meta path importers, since they cause problems. This can
|
||||
# happen if six is removed from sys.modules and then reloaded. (Setuptools does
|
||||
# this for some reason.)
|
||||
if sys.meta_path:
|
||||
for i, importer in enumerate(sys.meta_path):
|
||||
# Here's some real nastiness: Another "instance" of the six module might
|
||||
# be floating around. Therefore, we can't use isinstance() to check for
|
||||
# the six meta path importer, since the other six instance will have
|
||||
# inserted an importer with different class.
|
||||
if (type(importer).__name__ == "_SixMetaPathImporter" and
|
||||
importer.name == __name__):
|
||||
del sys.meta_path[i]
|
||||
break
|
||||
del i, importer
|
||||
# Finally, add the importer to the meta path import hook.
|
||||
sys.meta_path.append(_importer)
|
||||
@ -1,7 +0,0 @@
|
||||
# This file contains all requirements needed for GAM development work
|
||||
|
||||
# Include all build requirements
|
||||
-r requirements.txt
|
||||
|
||||
# Dev-specific requirements
|
||||
pre-commit
|
||||
21
src/tools/hooks/hook-googleapiclient.model.py
Normal file
21
src/tools/hooks/hook-googleapiclient.model.py
Normal file
@ -0,0 +1,21 @@
|
||||
# ------------------------------------------------------------------
|
||||
# Copyright (c) 2021 PyInstaller Development Team.
|
||||
#
|
||||
# This file is distributed under the terms of the GNU General Public
|
||||
# License (version 2.0 or later).
|
||||
#
|
||||
# The full license is available in LICENSE, distributed with
|
||||
# this software.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
from PyInstaller.utils.hooks import copy_metadata
|
||||
from PyInstaller.utils.hooks import collect_data_files
|
||||
|
||||
# googleapiclient.model queries the library version via
|
||||
# pkg_resources.get_distribution("google-api-python-client").version,
|
||||
# so we need to collect that package's metadata
|
||||
datas = copy_metadata('google_api_python_client')
|
||||
# we don't want these cached discovery files and they make the binary HUUUGEEEE
|
||||
#datas += collect_data_files('googleapiclient.discovery_cache', excludes=['*.txt', '**/__pycache__'])
|
||||
19
src/tools/hooks/hook-httplib2.py
Normal file
19
src/tools/hooks/hook-httplib2.py
Normal file
@ -0,0 +1,19 @@
|
||||
# ------------------------------------------------------------------
|
||||
# Copyright (c) 2020 PyInstaller Development Team.
|
||||
#
|
||||
# This file is distributed under the terms of the GNU General Public
|
||||
# License (version 2.0 or later).
|
||||
#
|
||||
# The full license is available in LICENSE, distributed with
|
||||
# this software.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
# This is needed to bundle cacerts.txt that comes with httplib2 module
|
||||
|
||||
# WE DON'T NEED httplib2/cacerts.txt since we get our own
|
||||
|
||||
#from PyInstaller.utils.hooks import collect_data_files
|
||||
|
||||
#datas = collect_data_files('httplib2')
|
||||
@ -9,7 +9,7 @@ import { TOTP } from 'totp-generator';
|
||||
|
||||
async function screenshot(driver, filename) {
|
||||
// uncomment to save .png screenshots
|
||||
//await driver.saveScreenshot(filename);
|
||||
await driver.saveScreenshot(filename);
|
||||
return
|
||||
}
|
||||
|
||||
@ -81,11 +81,13 @@ async function runSSD() {
|
||||
await driver.sendKeys(id_arr);
|
||||
await screenshot(driver, 'login02.png');
|
||||
await driver.sendKeys([Key.Tab]);
|
||||
console.log('Our secret is ' + process.env.TOTP_SECRET.length + ' characters.');
|
||||
// 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);
|
||||
const { otp } = await TOTP.generate(process.env.TOTP_SECRET, {algorithm: 'SHA-256'});
|
||||
console.log('Our token is ' + otp.length + ' characters.');
|
||||
const otp_arr = [...otp];
|
||||
await driver.sendKeys(otp_arr);
|
||||
await screenshot(driver, 'login03.png');
|
||||
await driver.sendKeys([Key.Enter]);
|
||||
|
||||
@ -111,7 +113,8 @@ async function runSSD() {
|
||||
await screenshot(driver, 'login12.png');
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error during Appium run:", error.name);
|
||||
console.error(error);
|
||||
//console.error("Error during Appium run:");
|
||||
}
|
||||
|
||||
// INTENTIONAL Keep driver open so tray icon for Certum doesn't close
|
||||
|
||||
@ -19,6 +19,8 @@
|
||||
|
||||
## Definitions
|
||||
```
|
||||
<AdminAssigneeType> ::= group|user|serviceaccount|unknown
|
||||
<AdminAssigneeTypeList> ::= "<AdminAssigneeType>(,<AdminAssigneeType>)*"
|
||||
<DomainName> ::= <String>(.<String>)+
|
||||
<EmailAddress> ::= <String>@<DomainName>
|
||||
<GroupItem> ::= <EmailAddress>|<UniqueID>|<String>
|
||||
@ -1475,16 +1477,25 @@ gam delete admin <RoleAssignmentId>
|
||||
## Display administrators
|
||||
```
|
||||
gam print admins [todrive <ToDriveAttribute>*]
|
||||
[user|group <EmailAddress>|<UniqueID>] [role <RoleItem>] [condition]
|
||||
[privileges] [oneitemperrow]
|
||||
[user|group <EmailAddress>|<UniqueID>] [role <RoleItem>]
|
||||
[types <AdminAssigneeTypeList>]
|
||||
[recursive] [condition] [privileges] [oneitemperrow]
|
||||
gam show admins
|
||||
[user|group <EmailAddress>|<UniqueID>] [role <RoleItem>] [condition] [privileges]
|
||||
[user|group <EmailAddress>|<UniqueID>] [role <RoleItem>]
|
||||
[types <AdminAssigneeTypeList>]
|
||||
[recursive] [condition] [privileges]
|
||||
```
|
||||
By default, all administrators and roles are displayed; choose from the following
|
||||
options to limit the display:
|
||||
* `user <UserItem>` - Display only this administrator
|
||||
* `user|group <EmailAddress>|<UniqueID>` - Display assignments to this administrator
|
||||
* `role <RoleItem>` - Display only administrators with this role
|
||||
|
||||
By default, all admin assignee types are displayed. use `types <AdminAssigneeTypeList>` to filter
|
||||
admin assignments by the type of the assignee.
|
||||
|
||||
By default, assignments to security groups are displayed as a single item; use `recursive`
|
||||
to display assignments to the members of the security groups; the security group membershop is recursively expanded.
|
||||
|
||||
* `condition` - Display any conditions associated with a role assignment
|
||||
* `privileges` - Display privileges associated with each role assignment
|
||||
|
||||
@ -1513,9 +1524,7 @@ gam config csv_input_row_filter "scopeType:regex:ORG_UNIT" redirect stdout ./Upd
|
||||
```
|
||||
|
||||
## Copy non-system admin roles from a source workspace to a target workspace
|
||||
This requires GAM version 7.18.01 or higher.
|
||||
|
||||
In the source workspace to the following:
|
||||
In the source workspace do the following:
|
||||
```
|
||||
gam redirect csv ./SourceNonSystemRoles.csv print adminroles privileges nosystemroles formatjson quotechar "'"
|
||||
```
|
||||
|
||||
@ -103,7 +103,7 @@ gam print aliases [todrive <ToDriveAttribute>*]
|
||||
[limittoou <OrgUnitItem>])
|
||||
[user|users <EmailAddressList>] [group|groups <EmailAddressList>]
|
||||
[select <UserTypeEntity>]
|
||||
[aliasmatchpattern <REMatchPattern>]
|
||||
[issuspended <Boolean>] [isarchived <Boolean>] [aliasmatchpattern <REMatchPattern>]
|
||||
[shownoneditable] [nogroups] [nousers]
|
||||
[onerowpertarget] [delimiter <Character>]
|
||||
[suppressnoaliasrows]
|
||||
@ -117,6 +117,8 @@ By default, group and user aliases in all domains in the account are selected; t
|
||||
* `user|users <EmailAddressList>` - Print aliases for users in `<EmailAddressList`
|
||||
* `select <UserTypeEntity>` - Print aliases for users in `<UserTypeEntity>`
|
||||
* `group|groups <EmailAddressList>` - Print aliases for groups in `<EmailAddressList`
|
||||
* `issuspended <Boolean>` - Limit users based on their status
|
||||
* `isarchived <Boolean>` - Limit users based on their status
|
||||
* `aliasmatchpattern <REMatchPattern>` - Print aliases that match a pattern
|
||||
* `nogroups` - Print only user aliases
|
||||
* `nousers` - Print only group aliases
|
||||
|
||||
@ -30,8 +30,8 @@
|
||||
- [Update an existing Service Account key](#update-an-existing-service-account-key)
|
||||
- [Replace all existing Service Account keys](#replace-all-existing-service-account-keys)
|
||||
- [Delete Service Account keys](#delete-service-account-keys)
|
||||
- [Upload a Service Account key to a service account with no keys](#upload-a-service-account-key-to-a-service-account-with-no-keys)
|
||||
- [Display Service Account keys](#display-service-account-keys)
|
||||
- [Upload a Service Account key to a service account without a valid private key](#upload-a-service-account-key-to-a-service-account-without-a-valid-private-key)
|
||||
- [Manage Service Account access](#manage-service-account-access)
|
||||
- [Full Service Account access](#full-service-account-access)
|
||||
- [Selective Service Account access](#selective-service-account-access)
|
||||
@ -184,7 +184,7 @@ perform these steps and then retry the create project command.
|
||||
|
||||
## Authorize Service Account Key Uploads
|
||||
|
||||
*IMPORTANT:* Google best practice is to NOT use service account keys. Rather than overriding Google's default policy please consider [running GAM on Google Compute Engine Securely](https://github.com/GAM-team/GAM/wiki/l-Running-GAM-on-Google-Compute-Engine-(GCE)-Securely) so that service account keys are not necessary.
|
||||
*IMPORTANT:* Google best practice is to NOT use service account keys. Rather than overriding Google's default policy please consider [Running GAM7 securely on a Google Compute Engine](https://github.com/GAM-team/GAM/wiki/Running-GAM7-securely-on-a-Google-Compute-Engine) (if running in Google Cloud) or [Workload Identity Federation](https://github.com/GAM-team/GAM/wiki/Using-GAM7-with-keyless-authentication-Workload-Identity-Federation) (if running outside Google Cloud) so that service account keys are not necessary.
|
||||
|
||||
If you try to create a project and get an error saying that Constraint `constraints/iam.disableServiceAccountKeyUpload violated for service account projects/gam-project-xxxxx`,
|
||||
perform these steps and then you should be able to authorize and use your project.
|
||||
@ -326,7 +326,7 @@ Use an existing project to create and download two files: `client_secrets.json`
|
||||
Use an existing uninitialized/uncredentialed project and configure it to be a GAM project; this typically used when
|
||||
the GCP administrators have created a basic project because project creation is not available for most users.
|
||||
|
||||
See Jay's notes about how to do this: https://github.com/GAM-team/GAM/wiki/GAM-with--minimal-GCP-rights
|
||||
See Jay's notes about how to do this: https://github.com/GAM-team/GAM/wiki/GAM-with-minimal-GCP-rights
|
||||
|
||||
```
|
||||
gam use project [<EmailAddress>] [project <ProjectID>]
|
||||
@ -394,9 +394,9 @@ gam show projects [[admin] <EmailAddress>] [all|<ProjectIDEntity>]
|
||||
* `<EmailAddress>` - A Google Workspace admin/GCP project manager; if omitted, you will be prompted for the address
|
||||
|
||||
Use these options to select projects.
|
||||
* `all` - All projects accessible by the administrator; this is the default
|
||||
* `all` - All projects accessible by the administrator
|
||||
* `current` - The project referenced in `client_secrets.json`
|
||||
* `gam` - Projects accessible by the administrator that were created by Gam, i.e, their project ID begins with `gam-project-`
|
||||
* `gam` - Projects accessible by the administrator that were created by Gam, i.e, their project ID begins with `gam-project-`; this is the default
|
||||
* `<ProjectID>` - A Google API project ID
|
||||
* `filter <String>` - A filter to select projects accessible by the administrator; see the API documentation
|
||||
* `states all|active|deleterequested` - Limit display to projects based on state; the default is `active`
|
||||
@ -412,9 +412,9 @@ gam print projects [[admin] <EmailAddress>] [all|<ProjectIDEntity>] [todrive <To
|
||||
* `<EmailAddress>` - A Google Workspace admin/GCP project manager; if omitted, you will be prompted for the address
|
||||
|
||||
Use these options to select projects.
|
||||
* `all` - All projects accessible by the administrator; this is the default
|
||||
* `all` - All projects accessible by the administrator
|
||||
* `current` - The project referenced in `client_secrets.json`
|
||||
* `gam` - Projects accessible by the administrator that were created by Gam, i.e, their project ID begins with `gam-project-`
|
||||
* `gam` - Projects accessible by the administrator that were created by Gam, i.e, their project ID begins with `gam-project-`; this is the default
|
||||
* `<ProjectID>` - A Google API project ID
|
||||
* `filter <String>` - A filter to select projects accessible by the administrator; see the API documentation
|
||||
* `states all|active|deleterequested` - Limit display to projects based on state; the default is `active`
|
||||
@ -781,6 +781,14 @@ Here are some sample values:
|
||||
Create a new Service Account private key; all existing private keys remain valid.
|
||||
The `oauth2service.json` file is updated with the new private key.
|
||||
|
||||
This command requires that the current Service Account private key is valid, if you get the following error:
|
||||
```
|
||||
ERROR: 401: authError - Request had invalid authentication credentials.
|
||||
Expected OAuth 2 access token, login cookie or other valid authentication credential.
|
||||
See https://developers.google.com/identity/sign-in/web/devconsole-project.
|
||||
```
|
||||
see: [Upload a Service Account key to a service account without a valid private key](#upload-a-service-account-key-to-a-service-account-without-a-valid-private-key)
|
||||
|
||||
Keep a good record of where each Service Account key is used as the keys themselves do not record this information.
|
||||
|
||||
The two forms of the command are equivalent; the second form is used by Legacy GAM.
|
||||
@ -809,6 +817,14 @@ The `oauth2service.json` file is updated with the new private key. If you had pr
|
||||
this `oauth2service.json` file to other users, you must redistribute the updated file as the private key
|
||||
in the distributed copies has been revoked.
|
||||
|
||||
This command requires that the current Service Account private key is valid, if you get the following error:
|
||||
```
|
||||
ERROR: 401: authError - Request had invalid authentication credentials.
|
||||
Expected OAuth 2 access token, login cookie or other valid authentication credential.
|
||||
See https://developers.google.com/identity/sign-in/web/devconsole-project.
|
||||
```
|
||||
see: [Upload a Service Account key to a service account without a valid private key](#upload-a-service-account-key-to-a-service-account-without-a-valid-private-key)
|
||||
|
||||
The two forms of the command are equivalent; the second form is used by Legacy GAM.
|
||||
```
|
||||
gam update sakey
|
||||
@ -828,6 +844,14 @@ in the distributed copies has been revoked.
|
||||
|
||||
This command can be used if your Service Account keys have been compromised; all existing private keys are revoked.
|
||||
|
||||
This command requires that the current Service Account private key is valid, if you get the following error:
|
||||
```
|
||||
ERROR: 401: authError - Request had invalid authentication credentials.
|
||||
Expected OAuth 2 access token, login cookie or other valid authentication credential.
|
||||
See https://developers.google.com/identity/sign-in/web/devconsole-project.
|
||||
```
|
||||
see: [Upload a Service Account key to a service account without a valid private key](#upload-a-service-account-key-to-a-service-account-without-a-valid-private-key)
|
||||
|
||||
The two forms of the command are equivalent; the second form is used by Legacy GAM.
|
||||
```
|
||||
gam replace sakeys
|
||||
@ -844,25 +868,20 @@ You can delete Service Accounts keys thus revoking access for that key. Generall
|
||||
delete a service account key for a distributed copy of an `oauth2service.json` file to disable
|
||||
that user's service account access.
|
||||
|
||||
This command requires that the current Service Account private key is valid, if you get the following error:
|
||||
```
|
||||
ERROR: 401: authError - Request had invalid authentication credentials.
|
||||
Expected OAuth 2 access token, login cookie or other valid authentication credential.
|
||||
See https://developers.google.com/identity/sign-in/web/devconsole-project.
|
||||
```
|
||||
see: [Upload a Service Account key to a service account without a valid private key](#upload-a-service-account-key-to-a-service-account-without-a-valid-private-key)
|
||||
|
||||
You can disable your current Service Account key if you specify the `doit` argument. This is your
|
||||
acknowledgement that you will have to manually create a new Service Account key in the Developer's Console
|
||||
or upload a new key with the `gam upload sakey` command.
|
||||
```
|
||||
gam delete sakeys <ServiceAccountKeyList>+ [doit]
|
||||
```
|
||||
## Upload a Service Account key to a service account with no keys
|
||||
There are two cases where you will use this command:
|
||||
* Your workspace is configured to disable service account private key uploads and you are creating a project.
|
||||
* All of your service account keys have been deleted, either manually or with the `gam delete sakeys` command.
|
||||
|
||||
The `oauth2service.json` file is updated with the new private key. If you had previously distributed
|
||||
any `oauth2service.json` file to other users, you must redistribute the updated file with the new key.
|
||||
```
|
||||
gam upload sakey [admin <EmailAddress>]
|
||||
(algorithm KEY_ALG_RSA_1024|KEY_ALG_RSA_2048)|
|
||||
(localkeysize 1024|2048|4096 [validityhours <Number>])|
|
||||
(yubikey yubikey_pin yubikey_slot AUTHENTICATION|SIGNATURE yubikey_serialnumber <Number>)
|
||||
```
|
||||
## Display Service Account keys
|
||||
There are system keys and user keys; user keys are what Gam uses; GCP uses system keys.
|
||||
|
||||
@ -876,6 +895,19 @@ gam show sakeys [all|system|user]
|
||||
|
||||
The private key currently being used in `oauth2service.json` will be marked as `usedToAuthenticateThisRequest: True`.
|
||||
|
||||
## Upload a Service Account key to a service account without a valid private key
|
||||
There are two cases where you will use this command:
|
||||
* Your workspace is configured to disable service account private key uploads and you are creating a project.
|
||||
* All of your service account keys have been deleted, either manually or with the `gam delete sakeys` command.
|
||||
|
||||
The `oauth2service.json` file is updated with the new private key. If you had previously distributed
|
||||
any `oauth2service.json` file to other users, you must redistribute the updated file with the new key.
|
||||
```
|
||||
gam upload sakey [admin <EmailAddress>]
|
||||
(algorithm KEY_ALG_RSA_1024|KEY_ALG_RSA_2048)|
|
||||
(localkeysize 1024|2048|4096 [validityhours <Number>])|
|
||||
(yubikey yubikey_pin yubikey_slot AUTHENTICATION|SIGNATURE yubikey_serialnumber <Number>)
|
||||
```
|
||||
## Manage Service Account access
|
||||
|
||||
## Full Service Account access
|
||||
|
||||
@ -265,6 +265,7 @@
|
||||
## Named items
|
||||
```
|
||||
<AccessToken> ::= <String>
|
||||
<AdminAssigneeType> ::= group|user|serviceaccount|unknown
|
||||
<AlertID> ::= <String>
|
||||
<APIScopeURL> ::= <String>
|
||||
<APPID> ::= <String>
|
||||
@ -282,6 +283,8 @@
|
||||
<ChatEmoji> ::= emojiname <ChatEmojiName> | customemojis/<String>
|
||||
<ChatMember> ::= spaces/<String>/members/<String>
|
||||
<ChatMessage> ::= spaces/<String>/messages/<String>
|
||||
<ChatSection> ::= users/<String>/sections/<String> | sections/<String> | section <String>
|
||||
<ChatSectionItem> ::= users/<String>/sections/<String>/items/<String> | sections/<String>/items/<String>
|
||||
<ChatSpace> ::= spaces/<String> | space <String> | space spaces/<String>
|
||||
<ChatThread> ::= spaces/<String>/threads/<String>
|
||||
<ChromeProfilePermanentID> ::= <String>
|
||||
@ -325,6 +328,8 @@
|
||||
<CourseWorkState> ::= draft|published|deleted
|
||||
<CrOSID> ::= <String>
|
||||
<CustomerID> ::= <String>
|
||||
<DateTimeFormat> ::= <String>
|
||||
See: https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes
|
||||
<DeliverySetting> ::=
|
||||
allmail|
|
||||
abridged|daily|
|
||||
@ -406,6 +411,8 @@
|
||||
<MatterItem> ::= <UniqueID>|<String>
|
||||
<MatterState> ::= open|closed|deleted
|
||||
<MeetConferenceName> ::= conferenceRecords/<String>
|
||||
<MeetID> ::= <String>
|
||||
Must match this Python Regular Expression: [a-z]{3}-[a-z]{4}-[a-z]{3}
|
||||
<MeetSpaceName> ::= spaces/<String> | <String>
|
||||
<MessageContent> ::=
|
||||
(message|textmessage|htmlmessage <String>)|
|
||||
@ -421,6 +428,7 @@
|
||||
(gdoc|ghtml <UserGoogleDoc>)|
|
||||
(gcsdoc|gcshtml <StorageBucketObjectName>)
|
||||
<NumberOfSeats> ::= <Number>
|
||||
<NumberRange> ::= <Number>|(<Number>/<Number>)
|
||||
<OrgUnitID> ::= id:<String>
|
||||
<OrgUnitPath> ::= /|(/<String>)+
|
||||
<OrgUnitItem> ::= <OrgUnitID>|<OrgUnitPath>
|
||||
@ -460,6 +468,7 @@
|
||||
See: https://support.google.com/mail/answer/7190
|
||||
<QueryGroup> ::= <String>
|
||||
See: https://developers.google.com/admin-sdk/directory/v1/guides/search-groups
|
||||
<QueryItem> ::= <UniqueID>|<String>
|
||||
<QueryMemberRestrictions> ::= <String>
|
||||
See: https://cloud.google.com/identity/docs/reference/rest/v1beta1/SecuritySettings#MemberRestriction
|
||||
<QueryMobile> ::= <String>
|
||||
@ -503,6 +512,7 @@
|
||||
<SiteItem> ::= [<DomainName>/]<SiteName>
|
||||
<S/MIMEID> ::= <String>
|
||||
<SMTPHostName> ::= <String>
|
||||
<StudentGroupID> ::= <Number>
|
||||
<StudentItem> ::= <EmailAddress>|<UniqueID>|<String>
|
||||
<SharedDriveACLRole> ::=
|
||||
commenter|
|
||||
@ -559,11 +569,11 @@
|
||||
(tdreturnidonly [<Boolean>])|
|
||||
(tdshare <EmailAddress> commenter|reader|writer)*|
|
||||
(tdsheet (id:<Number>)|<String>)|
|
||||
(tdsheettimestamp [<Boolean>] [tdsheettimeformat <String>])
|
||||
(tdsheettimestamp [<Boolean>] [tdsheettimeformat <DateTimeFormat>])
|
||||
(tdsheettitle <String>)|
|
||||
(tdsubject <String>)|
|
||||
([tdsheetdaysoffset <Number>] [tdsheethoursoffset <Number>])|
|
||||
(tdtimestamp [<Boolean>] [tdtimeformat <String>]
|
||||
(tdtimestamp [<Boolean>] [tdtimeformat <DateTimeFormat>]
|
||||
[tddaysoffset <Number>] [tdhoursoffset <Number>])|
|
||||
(tdtimezone <TimeZone>)|
|
||||
(tdtitle <String>)|
|
||||
|
||||
@ -18,6 +18,9 @@ The variables `num_threads`, `num_tbatch_threads` and `auto_batch_min` in `gam.c
|
||||
* [Command data from Google Docs/Sheets/Storage](Command-Data-From-Google-Docs-Sheets-Storage)
|
||||
`gdoc <UserGoogleDoc>` and `gsheet <UserGoogleSheet>`
|
||||
|
||||
<DateTimeFormat> ::= <String>
|
||||
See: https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes
|
||||
|
||||
## Batch files
|
||||
There are two types of batch processing, one that uses processes and one that uses threads. Using processes is higher performance but `gam csv` commands are not supported.
|
||||
* `gam batch` - gam commands are run as processes, gam csv commands are not allowed in the batch file
|
||||
@ -35,6 +38,7 @@ Batch files can contain the following types of lines:
|
||||
* Blank lines - Ignored
|
||||
* \# Comment line - Ignored
|
||||
* gam \<GAMArgumentList\> - Execute a GAM command
|
||||
* File path arguments in \<GAMArgumentList\> should be enclosed in \"
|
||||
* commit-batch
|
||||
* GAM waits for all running GAM commands to complete
|
||||
* GAM continues
|
||||
@ -45,6 +49,9 @@ Batch files can contain the following types of lines:
|
||||
* sleep \<Integer\> - Batch processing will suspend for \<Integer\> seconds before the next command line is processed
|
||||
* To be effective, this should immediately follow commit-batch
|
||||
* print \<String\> - Print \<String\> on stderr
|
||||
* datetime \<DateTimeFormat\>
|
||||
* The current time is formatted with \<DateTimeFormat\> and subsequent lines will have `%datetime%` replaced with the formatted time value.
|
||||
* See: https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes
|
||||
* set \<KeywordString\> \<ValueString\>
|
||||
* Subsequent lines will have %\<KeywordString\>% replaced with \<ValueString\>
|
||||
* clear \<KeywordString\>
|
||||
|
||||
@ -9,8 +9,6 @@
|
||||
|
||||
|
||||
## Introduction
|
||||
These features were added in version 7.18.00.
|
||||
|
||||
To use these commands you add the 'Business Account Management API' to your project and update client authorization.
|
||||
```
|
||||
gam update project
|
||||
|
||||
@ -78,6 +78,8 @@ Client access works when accessing Resource calendars.
|
||||
<CSVkmdSelector> | <CSVDataSelector>
|
||||
See: https://github.com/GAM-team/GAM/wiki/Collections-of-Items
|
||||
<iCalUID> ::= <String>
|
||||
<MeetID> ::= <String>
|
||||
Must match this Python Regular Expression: [a-z]{3}-[a-z]{4}-[a-z]{3}
|
||||
|
||||
<EventAttachmentsSubfieldName> ::=
|
||||
attachments.fileid|
|
||||
@ -150,7 +152,8 @@ Client access works when accessing Resource calendars.
|
||||
endtimeunspecified|
|
||||
extendedproperties|
|
||||
eventtype|
|
||||
<EventFocusTimePropertiesSubfieldName>
|
||||
focustimeproperties|
|
||||
<EventFocusTimePropertiesSubfieldName>|
|
||||
gadget|
|
||||
guestscaninviteothers|
|
||||
guestscanmodify|
|
||||
@ -164,7 +167,8 @@ Client access works when accessing Resource calendars.
|
||||
organizer|
|
||||
<EventOrganizerSubfieldName>|
|
||||
originalstart|originalstarttime|
|
||||
<EventOutOfOfficePropertiesSubfieldName>
|
||||
outofofficeproperties|
|
||||
<EventOutOfOfficePropertiesSubfieldName>|
|
||||
privatecopy|
|
||||
recurrence|
|
||||
recurringeventid|
|
||||
@ -254,6 +258,7 @@ Client access works when accessing Resource calendars.
|
||||
(birthday <Date>)|
|
||||
(color <EventColorName>)|
|
||||
(colorindex|colorid <EventColorIndex>)|
|
||||
(conferencedata meet <MeetID>)|
|
||||
(description <String>)|
|
||||
(end|endtime (allday <Date>)|<Time>)|
|
||||
(guestscaninviteothers <Boolean>)|
|
||||
@ -261,7 +266,7 @@ Client access works when accessing Resource calendars.
|
||||
(guestscanmodify <Boolean>)|
|
||||
(guestscanseeotherguests <Boolean>)|
|
||||
guestscantseeotherguests|
|
||||
hangoutsmeet|
|
||||
googlemeet|hangoutsmeet|
|
||||
<JSONData>|
|
||||
(jsonattendees [charset <Charset>] <String>)|
|
||||
(jsonattendees file <FileName> [charset <Charset>])|
|
||||
@ -302,7 +307,7 @@ The following attributes are equivalent:
|
||||
<EventAttribute>|
|
||||
clearattachments|
|
||||
clearattendees|
|
||||
clearhangoutsmeet|
|
||||
cleargooglemeet|clearhangoutsmeet|
|
||||
(clearprivateproperty <PropertyKey>)|
|
||||
clearresources|
|
||||
(clearsharedproperty <PropertyKey>)|
|
||||
@ -511,11 +516,16 @@ Use `jsonattendees file ./attendees.json` in `create/update event`.
|
||||
|
||||
## Delete selected calendar events
|
||||
```
|
||||
gam calendar <CalendarEntity> delete events [<EventEntity>] [doit] [<EventNotificationAttribute>]
|
||||
gam calendar <CalendarEntity> purge events [<EventEntity>] [doit] [<EventNotificationAttribute>]
|
||||
gam calendar <CalendarEntity> delete events [<EventEntity>]
|
||||
[batchsize <Integer>] [doit] [<EventNotificationAttribute>]
|
||||
gam calendar <CalendarEntity> purge events [<EventEntity>]
|
||||
[batchsize <Integer>] [doit] [<EventNotificationAttribute>]
|
||||
```
|
||||
If `<EventEntity>` is not specified, all events in `<CalendarEntity>` are selected. This is not typically used.
|
||||
|
||||
By default, each event is deleted in a separate API call, use `batchsize` with an integer between 1 and 1000
|
||||
to delete the events in batches.
|
||||
|
||||
No events are deleted unless you specify the `doit` option; omit `doit` to verify that you properly selected the events to delete.
|
||||
|
||||
When events are deleted from a calendar, they are moved to the calendar's trash and are only permanently deleted (purged) after 30 days.
|
||||
@ -567,7 +577,7 @@ By default, Gam displays the information as an indented list of keys and values.
|
||||
```
|
||||
gam calendar <CalendarEntity> show events [<EventEntity>] <EventDisplayProperty>*
|
||||
[fields <EventFieldNameList>] [showdayofweek]
|
||||
[countsly] [formatjson]
|
||||
[countsly|formatjson]
|
||||
```
|
||||
In `<EventEntity>`, any `<EventSelectProperty>` options must precede all other options.
|
||||
|
||||
@ -585,9 +595,10 @@ By default, Gam displays event details, use `countsonly` to display only the num
|
||||
|
||||
```
|
||||
gam calendar <CalendarEntity> print events [<EventEntity>] <EventDisplayProperty>*
|
||||
[fields <EventFieldNameList>] [showdayofweek]
|
||||
[countsonly [eventrowfilter]]
|
||||
[formatjson [quotechar <Character>]] [todrive <ToDriveAttribute>*]
|
||||
[fields <EventFieldNameList>] [showdayofweek] [attendeeslist]
|
||||
(addcsvdata <FieldName> <String>)*
|
||||
[eventrowfilter]
|
||||
[countsonly|(formatjson [quotechar <Character>])] [todrive <ToDriveAttribute>*]
|
||||
```
|
||||
In `<EventEntity>`, any `<EventSelectProperty>` options must precede all other options.
|
||||
|
||||
@ -598,6 +609,13 @@ option `singleevents` to display all instances of a recurring event.
|
||||
|
||||
`showdayofweek` displays columns `start.dayOfWeek` and `end.dayOfWeek` when event start and end times are displayed.
|
||||
|
||||
By default, each attendee is displayed in a separate column; `attendeeslist` causes GAM to display
|
||||
the attendee email addresses in a single column `attendeesList`; no attendee details are displayed.
|
||||
The email addresses are separated by `csv_output_field_delimiter` from `gam.cfg`.
|
||||
|
||||
Add additional columns of data from the command line to the output after the calendarId.
|
||||
* `addcsvdata <FieldName> <String>`
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@ -27,13 +27,16 @@ Client access works when accessing Resource calendars.
|
||||
See: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
||||
|
||||
<CalendarSettings> ::=
|
||||
(autoacceptinvitations [<Boolean>])|
|
||||
(description <String>)|
|
||||
(location <String>)|
|
||||
(summary <String>)|
|
||||
(timezone <TimeZone>)
|
||||
|
||||
<CalendarSettingsField> ::=
|
||||
autoacceptinvitations|3
|
||||
conferenceproperties|
|
||||
dataowner|
|
||||
description|
|
||||
id|
|
||||
location|
|
||||
|
||||
@ -43,7 +43,7 @@ Even if you're not going to use GAM as a Chat Bot, you have to configure a Chat
|
||||
* Uncheck "Build this Chat app as a Workspace add-on."
|
||||
* Enter an App name and Description of your choosing.
|
||||
* For the Avatar URL you can use `https://dummyimage.com/384x256/4d4d4d/0011ff.png&text=+GAM` or a public URL to an image of your own choosing.
|
||||
* In Functionality, uncheck both "Receive 1:1 messages" and "Join spaces and group conversations"
|
||||
* Inƒ Functionality, uncheck both "Receive 1:1 messages" and "Join spaces and group conversations" if present
|
||||
* In Connection settings, choose "Cloud Pub/Sub" and enter `projects/<ProjectID>/topics/no-topic` for the Topic Name. Replace `<ProjectID>` with your GAM project ID. GAM doesn't yet listen to pub/sub so this option is not used.
|
||||
* In Visibility, uncheck "Make this Chat app available to specific people and groups in Domain Workspace".
|
||||
* Click Save.
|
||||
@ -288,7 +288,7 @@ gam create chatmessage spaces spaces/AAAADi-pvqc gdoc announcements@domain.com n
|
||||
Updates and rewrites an existing Chat message. Message will show as edited and no notification will be sent to members.
|
||||
```
|
||||
gam update chatmessage name <ChatMessage>
|
||||
<ChatContent>
|
||||
[<ChatContent>] [clearattachments <String>]
|
||||
```
|
||||
Specify the source of the message:
|
||||
* `text <String>` - The message is `<String>`
|
||||
@ -296,12 +296,22 @@ Specify the source of the message:
|
||||
* `gdoc <UserGoogleDoc>` - The message is read from a Google Doc.
|
||||
* `gcsdoc <StorageBucketObjectName>` - The message is read from a Google Cloud Storage file.
|
||||
|
||||
The option `clearattachments <String>` can be used to clear all attachments from a Chat message.
|
||||
If `<ChatContent>` is not specified, the current message text is retained and `<String>` is appended;
|
||||
`<String>` must be specified but can be empty in which case the current message test is preserved as-is.
|
||||
|
||||
### Example
|
||||
|
||||
This example updates an existing chat message with new text.
|
||||
```
|
||||
gam update chatmessage name spaces/AAAADi-pvqc/messages/PKJrx90ooIU.PKJrx90ooIU text "HELLO CHAT?"
|
||||
```
|
||||
|
||||
This example clears attachments from a chat message and appends ` - Attachments cleared`
|
||||
to the current message text.
|
||||
```
|
||||
gam update chatmessage name spaces/AAAADi-pvqc/messages/PKJrx90ooIU.PKJrx90ooIU clearattachments " - Attachments cleared"
|
||||
```
|
||||
----
|
||||
|
||||
## Delete a Chat Message
|
||||
|
||||
65
wiki/Chrome-Device-Counts.md
Normal file
65
wiki/Chrome-Device-Counts.md
Normal file
@ -0,0 +1,65 @@
|
||||
# Chrome Device Counts
|
||||
- [API documentation](#api-documentation)
|
||||
- [Notes](#notes)
|
||||
- [Definitions](#definitions)
|
||||
- [Count titles](#count-titles)
|
||||
- [Display Chrome device counts](#display-chrome-device-counts)
|
||||
|
||||
## API documentation
|
||||
* [Chrome Management API - Count Active Devices](https://developers.google.com/chrome/management/reference/rest/v1/customers.reports/countActiveDevices)
|
||||
* [Chrome Management API - Count Devices per Boot Type](https://developers.google.com/chrome/management/reference/rest/v1/customers.reports/countDevicesPerBootType)
|
||||
* [Chrome Management API - Count Devices per Release Channel](https://developers.google.com/chrome/management/reference/rest/v1/customers.reports/countDevicesPerReleaseChannel)
|
||||
|
||||
## Notes
|
||||
To use these features you must add the `Chrome Management API` to your project and authorize
|
||||
the appropriate scope: `Chrome Management API - read only`.
|
||||
```
|
||||
gam update project
|
||||
gam oauth create
|
||||
```
|
||||
|
||||
## Definitions
|
||||
```
|
||||
<Date> ::=
|
||||
<Year>-<Month>-<Day> |
|
||||
(+|-)<Number>(d|w|y) |
|
||||
today
|
||||
```
|
||||
|
||||
## Count titles
|
||||
`active` - `sevenDaysCount,thirtyDaysCount`
|
||||
`perboottype` - `devBootTypeCount,unreportedBootTypeCount,verifiedBootTypeCount`
|
||||
`perreleasechanneel` - `betaChannelCount,canaryChannelCount,devChannelCount,ltcChannelCount,ltsChannelCount,stableChannelCount,unreportedChannelCount,unsupportedChannelCount`
|
||||
|
||||
## Display Chrome device counts
|
||||
```
|
||||
gam show chromedevicecounts
|
||||
(mode all|active|perboottype|perreleasechannel)*
|
||||
[date <Date>]
|
||||
[formatjson]
|
||||
```
|
||||
By default, `mode all` is selected
|
||||
|
||||
By default, `date today` is selected.
|
||||
|
||||
By default, Gam displays the information as an indented list of keys and values.
|
||||
* `formatjson` - Display the fields in JSON format.
|
||||
|
||||
```
|
||||
gam print chromedevicecounts [todrive <ToDriveAttribute>*]
|
||||
(mode all|active|perboottype|perreleasechannel)*
|
||||
[date <Date>]
|
||||
[formatjson [quotechar <Character>]]
|
||||
```
|
||||
By default, `mode all` is selected
|
||||
|
||||
By default, `date today` is selected.
|
||||
|
||||
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.
|
||||
|
||||
By default, when writing CSV files, Gam uses a quote character of double quote `"`. The quote character is used to enclose columns that contain
|
||||
the quote character itself, the column delimiter (comma by default) and new-line characters. Any quote characters within the column are doubled.
|
||||
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.
|
||||
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.
|
||||
@ -1,9 +1,9 @@
|
||||
# Chrome Installed Apps Counts
|
||||
# Chrome Installed Apps
|
||||
- [API documentation](#api-documentation)
|
||||
- [Definitions](#definitions)
|
||||
- [Quoting rules](#quoting-rules)
|
||||
- [Display Chrome installed app details](#display-chrome-installed-app-details)
|
||||
- [Display Chrome installed apps counts](#display-chrome-installed-apps-counts)
|
||||
- [Display Chrome installed apps](#display-chrome-installed-apps)
|
||||
- [Display Chrome devices with a specific installed application](#display-chrome-devices-with-a-specific-installed-application)
|
||||
|
||||
## API documentation
|
||||
@ -54,7 +54,7 @@ gam info chromeapp android|chrome|web <AppID>
|
||||
By default, Gam displays the information as an indented list of keys and values.
|
||||
* `formatjson` - Display the fields in JSON format.
|
||||
|
||||
## Display Chrome installed apps counts
|
||||
## Display Chrome installed apps
|
||||
```
|
||||
gam show chromeapps
|
||||
[(ou <OrgUnitItem>)|(ou_and_children <OrgUnitItem>)|
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
- [Create a Chrome policy image](#create-a-chrome-policy-image)
|
||||
- [Update Chrome policy](#update-chrome-policy)
|
||||
- [Delete Chrome policy](#delete-chrome-policy)
|
||||
- [Delete Chrome app](#delete-chrome-app)
|
||||
- [Display Chrome policies](#display-chrome-policies)
|
||||
- [Copy simple policies set directly in one OU to another OU](#copy-simple-policies-set-directly-in-one-ou-to-another-ou)
|
||||
- [Copy simple and complex policies set directly in one OU to another OU](#copy-simple-and-complex-policies-set-directly-in-one-ou-to-another-ou)
|
||||
@ -228,6 +229,17 @@ Restrict students from accessing Blocked URLs.
|
||||
```
|
||||
gam update chromepolicy chrome.users.UrlBlocking urlBlocklist "https://socialmedia.com,https://videowebsite.com" orgunit "/Students"
|
||||
```
|
||||
The Policy API and GAM have no ability to edit lists, you have to supply the complete list.
|
||||
```
|
||||
# Get the current policy
|
||||
gam redirect stdout ./urlBlockList.json show chromepolicies filter chrome.users.UrlBlocking orgunit "/Students" formatjson
|
||||
|
||||
# Edit urlBlockList.json to add the new URL(s)
|
||||
{"additionalTargetKeys": [], "direct": true, "fields": [{"name": "urlBlocklist", "value": "https://socialmedia.com,https://videowebsite.com,https://nogo.com"}, {"name": "chromeInternalUrlsBlocked", "value": false}], "name": "chrome.users.UrlBlocking", "orgUnitPath": "/Students", "parentOrgUnitPath": "/"}
|
||||
|
||||
# Update the policy
|
||||
gam update chromepolicies chrome.users.UrlBlocking json file urlBlockList.json orgunit "/Students"
|
||||
```
|
||||
For managed browsers, specify that users can only sign into managed accounts belonging to company/school domains.
|
||||
```
|
||||
gam update chromepolicy chrome.users.SecondaryGoogleAccountSignin allowedDomainsForApps company.com,company.net orgunit "/Managed Browsers"
|
||||
@ -243,7 +255,6 @@ 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
|
||||
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.
|
||||
|
||||
@ -255,6 +266,13 @@ gam delete chromepolicy
|
||||
((ou|orgunit <OrgUnitItem>)|(group <GroupItem>))
|
||||
[(printerid <PrinterID>)|(appid <AppID>)]
|
||||
```
|
||||
|
||||
## Delete Chrome app
|
||||
You can delete an app, i.e., explicitly remove it from management. `<OrgUnitItem>` must specify where the app was added for management.
|
||||
```
|
||||
gam delete chromepolicy chrome.users.apps.InstallType ou|orgunit <OrgUnitItem> appid <AppID>
|
||||
```
|
||||
|
||||
## Display Chrome policies
|
||||
You can display policies for all devices/users within an OU, users with a group or for a specific printer or application within an OU.
|
||||
|
||||
|
||||
@ -10,8 +10,6 @@
|
||||
- [Display Chrome Profile commands](#display-chrome-profile-commands)
|
||||
|
||||
## Introduction
|
||||
These features were added in version 7.01.00.
|
||||
|
||||
To use these commands you must update your client authorization.
|
||||
```
|
||||
gam oauth create
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
- [Action ChromeOS devices](#action-chromeos-devices)
|
||||
- [Send remote commands to ChromeOS devices](#send-remote-commands-to-chromeos-devices)
|
||||
- [Action Examples](#action-examples)
|
||||
- [Bulk Action Example](#bulk-action-example)
|
||||
- [ChromeOS device lists](#chromeos-device-lists)
|
||||
- [Display information about ChromeOS devices](#display-information-about-chromeos-devices)
|
||||
- [Print ChromeOS devices](#print-chromeos-devices)
|
||||
@ -410,7 +411,7 @@ gam update ou csvkmd cros.csv keyfield OU datafield deviceId add croscsvdata dev
|
||||
gam <CrOSTypeEntity> update 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.
|
||||
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.
|
||||
|
||||
As deprovisioning ChromeOS devices is not reversible, you must enter `acknowledge_device_touch_requirement`
|
||||
@ -445,12 +446,19 @@ is configurable from 0 to some large number. If the status reaches `EXPIRED`, `C
|
||||
wipe_users|
|
||||
take_a_screenshot
|
||||
|
||||
gam <CrOSTypeEntity> issuecommand command <CrOSCommand> [times_to_check_status <Integer>] [doit]
|
||||
gam <CrOSTypeEntity> issuecommand command <CrOSCommand>
|
||||
[times_to_check_status <Integer>] [csv] [doit]
|
||||
```
|
||||
By default, when a Chrome command is issued, GAM outputs details of the command status as indented keywords and values.
|
||||
* `csv` - Output the details in CSV format.
|
||||
|
||||
If the final status is not reached before GAM exits, you can issue the following commands to continue checking the status.
|
||||
```
|
||||
gam <CrOSTypeEntity> getcommand commandid <CommandID> [times_to_check_status <Integer>]
|
||||
gam <CrOSTypeEntity> getcommand commandid <CommandID>
|
||||
[times_to_check_status <Integer>] [csv]
|
||||
```
|
||||
By default, when a Chrome command status is read, GAM outputs details of the command status as indented keywords and values.
|
||||
* `csv` - Output the details in CSV format.
|
||||
|
||||
### Action Examples
|
||||
Remove user profile data from the device; the device will remain enrolled and connected.
|
||||
@ -476,6 +484,40 @@ Use `wipe_users` if that's going to create too much work for you.
|
||||
```
|
||||
gam cros_ou /StudentCarts issuecommand command remote_powerwash times_to_check_status 0 doit
|
||||
```
|
||||
|
||||
### Bulk Action example
|
||||
You want to issue commands to many ChromeOS devices and monitor the results.
|
||||
|
||||
Assume a Google Sheet with two tabs: Commands and Results
|
||||
The Commands tab has at least two columns: serialNumber and command
|
||||
```
|
||||
serialNumber,command
|
||||
abc123def123,reboot
|
||||
abc123def456,remote_powerwash
|
||||
abc123def789,wipe_users
|
||||
```
|
||||
Here is the URL for the Commands tab
|
||||
```
|
||||
https://docs.google.com/spreadsheets/d/12349lNWvZwzJhwxyz/edit?gid=1588227640#gid=1588227640
|
||||
<SheetFileID> = 12349lNWvZwzJhwxyz
|
||||
<CommandTabID> = 1588227640
|
||||
Here is the URL for the Commands tab
|
||||
https://docs.google.com/spreadsheets/d/12349lNWvZwzJhwxyz/edit?gid=2102420937#gid=2102420937
|
||||
<SheetFileID> = 12349lNWvZwzJhwxyz
|
||||
<ResultsTabID> = 2102420937
|
||||
```
|
||||
Replace `user@domain.com` with the email address of the Google Sheet owner.
|
||||
|
||||
Issue the commands from the Commands tab and write the results to the Results tab; copy the `serialNumber` to the Results tab.
|
||||
```
|
||||
gam config num_threads 20 redirect csv - multiprocess sortheaders serialNumber todrive tduser user@domain.com tdfileID <SheetFileID> tdsheet id:<ResultsTabID> tdupdatesheet tdretaintitle redirect stderr - multiprocess csv gsheet user@domain.com <SheetFileID> id:<CommandsTabID> gam cros_sn "~serialNumber" issuecommand command "~command" doit csv addcsvdata serialNumber "~serialNumber"
|
||||
```
|
||||
Monitor the results by reading and updating the results from/to the Results tab; copy the `serialNumber` to the Results tab.
|
||||
```
|
||||
gam config num_threads 20 redirect csv - multiprocess sortheaders serialNumber todrive tduser user@domain.com tdfileID <SheetFileID> tdsheet id:<ResultsTabID> tdupdatesheet tdretaintitle redirect stderr - multiprocess csv gsheet user@domain.com <SheetFileID> id:<ResultsTabID> gam cros "~deviceId" getcommand commandid "~commandId" csv addcsvdata serialNumber "~serialNumber"
|
||||
```
|
||||
You can use additional `addcsvdata` options to copy device identifying information from the source to the destination.
|
||||
|
||||
## ChromeOS device lists
|
||||
ChromeOS devices have lists of data: `<CrOSListFieldName>`, `<CrOSActivityListFieldName>`, `<CrOSTelemetryListFieldName>`.
|
||||
All lists except `recentusers` are in ascending order (oldest to newest). As these lists can contain many entries,
|
||||
@ -544,7 +586,7 @@ gam print cros [todrive <ToDriveAttribute>*]
|
||||
[start <Date>] [end <Date>] [listlimit <Number>]
|
||||
[reverselists <CrOSListFieldNameList>]
|
||||
[timerangeorder ascending|descending] [showdvrsfp]
|
||||
(addcsvdata <FieldName> <String>)*
|
||||
(addcsvdata <FieldName> <String>)* [includecsvdatainjson [<Boolean>]]
|
||||
[sortheaders]
|
||||
[formatjson [quotechar <Character>]]
|
||||
```
|
||||
@ -591,9 +633,11 @@ Add additional columns of data from the command line to the output
|
||||
* `addcsvdata <FieldName> <String>`
|
||||
|
||||
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.
|
||||
|
||||
If `formatjson` and `addcsvdata` are specified, the option `includecsvdatainjson` causes GAM to add the
|
||||
additional field values to the JSON data.
|
||||
|
||||
By default, when writing CSV files, Gam uses a quote character of double quote `"`. The quote character is used to enclose columns that contain
|
||||
the quote character itself, the column delimiter (comma by default) and new-line characters. Any quote characters within the column are doubled.
|
||||
When using the `formatjson` option, double quotes are used extensively in the data resulting in hard to read/process output.
|
||||
@ -612,7 +656,7 @@ gam <CrOSTypeEntity> print cros [todrive <ToDriveAttribute>*]
|
||||
[start <Date>] [end <Date>] [listlimit <Number>]
|
||||
[reverselists <CrOSListFieldNameList>]
|
||||
[timerangeorder ascending|descending] [showdvrsfp]
|
||||
(addcsvdata <FieldName> <String>)*
|
||||
(addcsvdata <FieldName> <String>)* [includecsvdatainjson [<Boolean>]]
|
||||
[sortheaders]
|
||||
[formatjson [quotechar <Character>]]
|
||||
|
||||
@ -623,6 +667,7 @@ gam print cros [todrive <ToDriveAttribute>*] select <CrOSTypeEntity>
|
||||
[start <Date>] [end <Date>] [listlimit <Number>]
|
||||
[reverselists <CrOSListFieldNameList>]
|
||||
[timerangeorder ascending|descending] [showdvrsfp]
|
||||
(addcsvdata <FieldName> <String>)* [includecsvdatainjson [<Boolean>]]
|
||||
[sortheaders]
|
||||
[formatjson [quotechar <Character>]]
|
||||
```
|
||||
@ -645,9 +690,11 @@ Add additional columns of data from the command line to the output
|
||||
* `addcsvdata <FieldName> <String>`
|
||||
|
||||
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.
|
||||
|
||||
If `formatjson` and `addcsvdata` are specified, the option `includecsvdatainjson` causes GAM to add the
|
||||
additional field values to the JSON data.
|
||||
|
||||
By default, when writing CSV files, Gam uses a quote character of double quote `"`. The quote character is used to enclose columns that contain
|
||||
the quote character itself, the column delimiter (comma by default) and new-line characters. Any quote characters within the column are doubled.
|
||||
When using the `formatjson` option, double quotes are used extensively in the data resulting in hard to read/process output.
|
||||
@ -711,7 +758,9 @@ To retrieve the count with `showitemcountonly`:
|
||||
Linux/MacOS
|
||||
count=$(gam print cros query "sync:..2020-01-01" showitemcountonly)
|
||||
Windows PowerShell
|
||||
count = & gam print cros query "sync:..2020-01-01" showitemcountonly
|
||||
$count = & gam print cros query "sync:..2020-01-01" showitemcountonly
|
||||
Windows Command Prompt
|
||||
for /f "delims=" %a in ('gam print cros query "sync:..2020-01-01" showitemcountonly') do set count=%a
|
||||
```
|
||||
|
||||
## Print ChromeOS device activity
|
||||
|
||||
@ -379,7 +379,7 @@ Drive files with `shareMode` `Each student will get a copy` don't seem to be abl
|
||||
|
||||
## Delete courses
|
||||
Classes can only be deleted when they are in the ARCHIVED state; to delete a class, you can update its state to ARCHIVED
|
||||
and then delete it or you can specify that it be archived as parot of the delete command.
|
||||
and then delete it or you can specify that it be archived as part of the delete command.
|
||||
```
|
||||
gam delete course <CourseID> [archived]
|
||||
gam delete courses <CourseEntity> [archived]
|
||||
@ -441,8 +441,10 @@ gam print courses [todrive <ToDriveAttribute>*]
|
||||
[owneremail] [owneremailmatchpattern <REMatchPattern>]
|
||||
[alias|aliases|aliasesincolumns [delimiter <Character>]]
|
||||
[show all|students|teachers] [countsonly]
|
||||
[fields <CourseFieldNameList>] [skipfields <CourseFieldNameList>] [formatjson [quotechar <Character>]]
|
||||
[timefilter creationtime|updatetime] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>]
|
||||
[fields <CourseFieldNameList>] [skipfields <CourseFieldNameList>] [formatjson [quotechar <Character>]]
|
||||
(addcsvdata <FieldName> <String>)*
|
||||
[formatjson [quotechar <Character>]]
|
||||
```
|
||||
By default, the `print courses` command displays information about all courses.
|
||||
|
||||
@ -477,6 +479,7 @@ By default, all basic course fields are displayed; use the following options to
|
||||
* `countsonly` - Eliminates the student/teacher profile information and outputs only the student/teacher counts.
|
||||
* `fields <CourseFieldNameList>` - Select specific basic fields to display.
|
||||
* `skipfields <CourseFieldNameList>` - Select specific basic fields to eliminate from display; typically used with `coursematerialsets`.
|
||||
* `addcsvdata <FieldName> <String>` - Add additional columns of data from the command line to the output
|
||||
|
||||
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.
|
||||
@ -511,7 +514,9 @@ To retrieve the count with `showitemcountonly`:
|
||||
Linux/MacOS
|
||||
count=$(gam print courses states active showitemcountonly)
|
||||
Windows PowerShell
|
||||
count = & gam print courses states active showitemcountonly
|
||||
$count = & gam print courses states active showitemcountonly
|
||||
Windows Command Prompt
|
||||
for /f "delims=" %a in ('gam print courses states active showitemcountonly') do set count=%a
|
||||
```
|
||||
|
||||
## Display course announcements
|
||||
@ -570,6 +575,7 @@ gam print course-materials [todrive <ToDriveAttribute>*]
|
||||
(orderby <CourseMaterialOrderByFieldName> [ascending|descending])*)
|
||||
[showcreatoremails|creatoremail] [showtopicnames] [fields <CourseMaterialFieldNameList>]
|
||||
[timefilter creationtime|updatetime|scheduledtime] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>]
|
||||
[oneitemperrow]
|
||||
[countsonly] [formatjson [quotechar <Character>]]
|
||||
```
|
||||
By default, the `print course-materials` command displays course materials information for all courses.
|
||||
@ -600,6 +606,10 @@ By default, all course materials fields are displayed; use the following options
|
||||
* `showtopicnames` - Display topic names; requires and additional API call per course.
|
||||
* `fields <CourseMaterialsFieldNameList>` - Select specific fields to display.
|
||||
|
||||
With `print course-materials`, the materials selected for display are all output on one row/line as a repeating item with the other course fields.
|
||||
When `oneitemperrow` is specified, each material is output on a separate row/line with the other course fields.
|
||||
This simplifies processing the materials in the CSV file with subsequent Gam commands.
|
||||
|
||||
Use the `countsonly` option to display the number of course materials in a course but not their details.
|
||||
|
||||
By default, Gam displays the information as columns of fields; the following option causes the output to be in JSON format,
|
||||
@ -662,6 +672,7 @@ gam print course-work [todrive <ToDriveAttribute>*]
|
||||
[showcreatoremails] [showtopicnames] [fields <CourseWorkFieldNameList>]
|
||||
[showstudentsaslist [<Boolean>]] [delimiter <Character>]
|
||||
[timefilter creationtime|updatetime|scheduledtime] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>]
|
||||
[oneitemperrow]
|
||||
[countsonly] [formatjson [quotechar <Character>]]
|
||||
```
|
||||
By default, the `print course-work` command displays course work information for all courses.
|
||||
@ -695,6 +706,10 @@ By default, all course work fields are displayed; use the following options to m
|
||||
By default, when course work is assigned to individual students, the student IDs are displayed in multiple indexed columns.
|
||||
Use options `showstudentsaslist [<Boolean>]` and `delimiter <Character>` to display the student IDs is a single column as a delimited list.
|
||||
|
||||
With `print course-work`, any materials are all output on one row/line as a repeating item with the other course fields.
|
||||
When `oneitemperrow` is specified, each material is output on a separate row/line with the other course fields.
|
||||
This simplifies processing the materials in the CSV file with subsequent Gam commands.
|
||||
|
||||
Use the `countsonly` option to display the number of course works in a course but not their details.
|
||||
|
||||
By default, Gam displays the information as columns of fields; the following option causes the output to be in JSON format,
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
- [Bulk membership changes](#bulk-membership-changes)
|
||||
- [Display course membership](#display-course-membership)
|
||||
- [Display course membership counts](#display-course-membership-counts)
|
||||
- [Display course counts for teachers-students](#display-course-counts-for-teachers-students)
|
||||
|
||||
## API documentation
|
||||
* [Google Classroom API](https://developers.google.com/classroom/reference/rest)
|
||||
@ -163,5 +164,50 @@ To retrieve the count with `showitemcountonly`:
|
||||
Linux/MacOS
|
||||
count=$(gam print course-participants teacher asmith states active show students showitemcountonly)
|
||||
Windows PowerShell
|
||||
count = & gam print course-participants teacher asmith states active show students showitemcountonly
|
||||
$count = & gam print course-participants teacher asmith states active show students showitemcountonly
|
||||
Windows Command Prompt
|
||||
for /f "delims=" %a in ('gam print course-participants teacher asmith states active show students showitemcountonly') do set count=%a
|
||||
```
|
||||
|
||||
## Display course counts for teachers-students
|
||||
You can get a count of the number of courses in which a teacher or student is a participant.
|
||||
```
|
||||
gam print course-counts students|teachers [todrive <ToDriveAttribute>*]
|
||||
(course|class <CourseEntity>)*|([teacher <UserItem>] [student <UserItem>] [states <CourseStateList>])
|
||||
[mincount <Integer>]
|
||||
[formatjson [quotechar <Character>]]
|
||||
```
|
||||
By default, the `print course-counts` command displays participant counts about all courses.
|
||||
|
||||
To get participant counts for a specific set of courses, use the following option; it can be repeated to select multiple courses.
|
||||
* `(course|class <CourseID>)*` - Display courses with the specified `<CourseID>`.
|
||||
|
||||
To get participant counts for courses based on their having a particular participant, use the following options. Both options can be specified.
|
||||
* `teacher <UserItem>` - Display courses with the specified teacher.
|
||||
* `student <UserItem>` - Display courses with the specified student.
|
||||
|
||||
To get participant counts for courses based on their state, use the following option. This option can be combined with the `teacher` and `student` options.
|
||||
By default, all course states are selected.
|
||||
* `states <CourseStateList>` - Display courses with any of the specified states.
|
||||
|
||||
By default, all count values are displayed, use `mincount <Integer>` to limit the display to those counts
|
||||
greater that or equal to the specified `<Integer>`.
|
||||
|
||||
By default, Gam displays the counts as columns of fields; the following option causes the output to be in JSON format,
|
||||
* `formatjson` - Display the fields in JSON format.
|
||||
|
||||
By default, when writing CSV files, Gam uses a quote character of double quote `"`. The quote character is used to enclose columns that contain
|
||||
the quote character itself, the column delimiter (comma by default) and new-line characters. Any quote characters within the column are doubled.
|
||||
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.
|
||||
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.
|
||||
|
||||
### Example
|
||||
For teachers in all active courses, print the number of classes for which they are a participant.
|
||||
```
|
||||
gam print coursecounts teachers states active
|
||||
```
|
||||
For teachers in all active courses, print the number of classes (if it is >= 5) for which they are a participant.
|
||||
```
|
||||
gam print coursecounts teachers states active mincount 5
|
||||
```
|
||||
|
||||
250
wiki/Classroom-StudentGroups.md
Normal file
250
wiki/Classroom-StudentGroups.md
Normal file
@ -0,0 +1,250 @@
|
||||
# Classroom - Student Groups
|
||||
- [Notes](#notes)
|
||||
- [API documentation](#api-documentation)
|
||||
- [Definitions](#definitions)
|
||||
- [Special quoting for course aliases](#special-quoting-for-course-aliases)
|
||||
- [Special quoting for lists of titles](#special-quoting-for-lists-of-titles)
|
||||
- [Manage student groups](#manage-student-groups)
|
||||
- [Display student groups](#display-student-groups)
|
||||
- [Display student group counts](#display-student-group-counts)
|
||||
- [Manage student group membership](#manage-student-group-membership)
|
||||
- [Display student group membership](#display-student-group-membership)
|
||||
- [Display student group membership counts](#display-student-group-membership-counts)
|
||||
|
||||
## Notes
|
||||
These commands were added in version 7.20.00 and required enrollment in the Developer Preview program.
|
||||
As of 7.32.04 Developer Preview enrollment is no longer required.
|
||||
|
||||
## API documentation
|
||||
* [Google Classroom API](https://developers.google.com/classroom/reference/rest)
|
||||
* [Google Classroom Student Groups](https://developers.google.com/workspace/classroom/reference/rest/v1/courses.studentGroups)
|
||||
* [Google Classroom Student Group Members](https://developers.google.com/workspace/classroom/reference/rest/v1/courses.studentGroups.studentGroupMembers)
|
||||
|
||||
## Definitions
|
||||
```
|
||||
<DomainName> ::= <String>(.<String>)+
|
||||
<EmailAddress> ::= <String>@<DomainName>
|
||||
<UniqueID> ::= id:<String>
|
||||
<UserItem> ::= <EmailAddress>|<UniqueID>|<String>
|
||||
|
||||
<CourseAlias> ::= <String>
|
||||
<CourseID> ::= <Number>|d:<CourseAlias>
|
||||
<CourseIDList> ::= "<CourseID>(,<CourseID>)*"
|
||||
<CourseEntity> ::=
|
||||
<CourseIDList> | <FileSelector> | <CSVFileSelector | <CSVkmdSelector>
|
||||
See: https://github.com/GAM-team/GAM/wiki/Collections-of-Items
|
||||
<CourseState> ::= active|archived|provisioned|declined|suspended
|
||||
<CourseStateList> ::= all|"<CourseState>(,<CourseState>)*"
|
||||
|
||||
<StringList> ::= "<String>(,<String>)*"
|
||||
<StringEntity> ::=
|
||||
<StringList> | <FileSelector> | <CSVFileSelector>
|
||||
See: https://github.com/GAM-team/GAM/wiki/Collections-of-Items
|
||||
|
||||
<StudentGroupID> ::= <Number>
|
||||
<StudentGroupIDList> ::= "<StudentGroupID>(,<StudentGroupID>)*"
|
||||
<StudentGroupEntity> ::=
|
||||
<StudentGroupIDList> | <FileSelector> | <CSVFileSelector> | <CSVkmdSelector>
|
||||
```
|
||||
## Special quoting for course aliases
|
||||
As course aliases can contain spaces, some care must be used when entering `<CourseAliasList>`, `<CourseID>`, `<CourseIDList>` and `<CourseEntity>`.
|
||||
|
||||
Suppose you have a course with the alias `Math Class`. To get information about it you enter the command: `gam info course "d:Math Class"`
|
||||
|
||||
The shell strips the `"` leaving a single argument `d:Math Class`; gam correctly processes the argument as it is expecting a single course.
|
||||
|
||||
Suppose you enter the command: `gam info courses "d:Math Class"`
|
||||
|
||||
The shell strips the `"` leaving a single argument `d:Math Class`; as gam is expecting a list, it splits the argument on space leaving two items and then tries to process `d:Math` and `Class`, not what you want.
|
||||
|
||||
You must enter: `gam info courses "'d:Math Class'"`
|
||||
|
||||
The shell strips the `"` leaving a single argument `'d:Math Class'`; as gam is expecting a list, it splits the argument on space while honoring the `'` leaving one item `d:Math Class` and correctly processes the item.
|
||||
|
||||
For multiple aliases you must enter: `gam info courses "'d:Math Class','d:Science Class'"`
|
||||
|
||||
## Special quoting for lists of titles
|
||||
As student group titles can contain spaces, some care must be used when entering `selevct <StringList>`.
|
||||
|
||||
Suppose you want to create a student group `Advanced Students`. `gam create course-studentgroups course <CourseID> title "Advanced Students"`
|
||||
|
||||
The shell strips the `"` leaving a single argument `Advanced Math`; gam correctly processes the argument as it is expecting a single title.
|
||||
|
||||
Suppose you enter the command: `gam create course-studentgroups course <CourseID> select "Advanced Students"`
|
||||
|
||||
The shell strips the `"` leaving a single argument `Advanced Students`; as gam is expecting a list, it splits the argument on space leaving two items and then tries to process `Advanced` and `Students`, not what you want.
|
||||
|
||||
You must enter: `gam create course-studentgroups course <CourseID> select "'Advanced Students'"`
|
||||
|
||||
The shell strips the `"` leaving a single argument `'Advanced Students'`; as gam is expecting a list, it splits the argument on space while honoring the `'` leaving one item `Advanced Students` and correctly processes the item.
|
||||
|
||||
For multiple titles you can enter either of the following:
|
||||
* `gam create course-studentgroups course <CourseID> select "'Advanced Students','Beginner Students'"`
|
||||
* `gam create course-studentgroups course <CourseID> title"Advanced Students" title "Beginner Students"`
|
||||
|
||||
See: [Lists and Collections](Lists-and-Collections)
|
||||
|
||||
## Manage student groups
|
||||
|
||||
```
|
||||
gam create course-studentgroups
|
||||
(course|class <CourseEntity>)*|([teacher <UserItem>] [student <UserItem>] [states <CourseStateList>])
|
||||
((title <String>)|(select <StringEntity))+
|
||||
[csv [todrive <ToDriveAttribute>*] [formatjson [quotechar <Character>]]]
|
||||
gam update course-studentgroups <CourseID> <StudentGroupID> title <String>
|
||||
gam delete course-studentgroups <CourseID> <StudentGroupIDEntity>
|
||||
gam clear course-studentgroups
|
||||
(course|class <CourseEntity>)*|([teacher <UserItem>] [student <UserItem>] [states <CourseStateList>])
|
||||
```
|
||||
|
||||
When creating student groups, the API does not check for duplicate titles; you can have multiple student groups
|
||||
with the same title; they will have unique `<StudentGroupID>s`.
|
||||
|
||||
The `update|delete course-studentgroups` commands manage a specific course.
|
||||
|
||||
By default, the `create|clear course-studentgroups` commands manage student group information about all courses.
|
||||
|
||||
To manage student group information for a specific set of courses, use the following option; it can be repeated to select multiple courses.
|
||||
* `(course|class <CourseID>)*` - Display courses with the specified `<CourseID>`.
|
||||
|
||||
To manage student group information for courses based on their having a particular participant, use the following options. Both options can be specified.
|
||||
* `teacher <UserItem>` - Display courses with the specified teacher.
|
||||
* `student <UserItem>` - Display courses with the specified student.
|
||||
|
||||
To manage student group information for courses based on their state, use the following option. This option can be combined with the `teacher` and `student` options.
|
||||
By default, all course states are selected.
|
||||
* `states <CourseStateList>` - Display courses with any of the specified states.
|
||||
|
||||
By default, when a student group is created, you will get output like this:
|
||||
```
|
||||
Course: <CourseID>, Course Student Group: <Title>(<StudentGroupId>), Added
|
||||
```
|
||||
If you use the `csv` option, you will get output like this:
|
||||
```
|
||||
courseId,courseName,studentGroupId,studentGroupTitle
|
||||
<CourseID>,<CourseName>,<StudentGroupID>,<Title>
|
||||
```
|
||||
This gives you a record the the student group IDs.
|
||||
|
||||
### Example
|
||||
```
|
||||
$ more titles.csv
|
||||
title
|
||||
Advanced Students
|
||||
Middle Students
|
||||
Beginner Students
|
||||
|
||||
$ gam redirect csv ./StudentGroups.csv add coursestudentgroups course <CourseID> select csvfile titles.csv:title csv
|
||||
Course: <CourseID>, Add 3 Course Student Groups
|
||||
|
||||
$ more StudentGroups.csv
|
||||
courseId,courseName,studentGroupId,studentGroupTitle
|
||||
<CourseID>,<CourseName>,796177544247,Advanced Students
|
||||
<CourseID>,<CourseName>,796177718666,Middle Students
|
||||
<CourseID>,<CourseName>,796177727901,Beginner Students
|
||||
```
|
||||
|
||||
## Display student groups
|
||||
```
|
||||
gam print course-studentgroups [todrive <ToDriveAttribute>*]
|
||||
(course|class <CourseEntity>)*|([teacher <UserItem>] [student <UserItem>] [states <CourseStateList>])
|
||||
[formatjson [quotechar <Character>]]
|
||||
```
|
||||
By default, the `print course-studentgroups` command displays student group information about all courses.
|
||||
|
||||
To display student group information for a specific set of courses, use the following option; it can be repeated to select multiple courses.
|
||||
* `(course|class <CourseID>)*` - Display courses with the specified `<CourseID>`.
|
||||
|
||||
To display student group information for courses based on their having a particular participant, use the following options. Both options can be specified.
|
||||
* `teacher <UserItem>` - Display courses with the specified teacher.
|
||||
* `student <UserItem>` - Display courses with the specified student.
|
||||
|
||||
To display student group information for courses based on their state, use the following option. This option can be combined with the `teacher` and `student` options.
|
||||
By default, all course states are selected.
|
||||
* `states <CourseStateList>` - Display courses with any of the specified states.
|
||||
|
||||
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.
|
||||
|
||||
By default, when writing CSV files, Gam uses a quote character of double quote `"`. The quote character is used to enclose columns that contain
|
||||
the quote character itself, the column delimiter (comma by default) and new-line characters. Any quote characters within the column are doubled.
|
||||
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.
|
||||
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.
|
||||
|
||||
## Display student group counts
|
||||
Display the number of student groups
|
||||
```
|
||||
gam print course-studentgroup [todrive <ToDriveAttribute>*]
|
||||
(course|class <CourseEntity>)*|([teacher <UserItem>] [student <UserItem>] [states <CourseStateList>])
|
||||
showitemcountonly
|
||||
```
|
||||
|
||||
## Manage student group membership
|
||||
```
|
||||
gam create course-studentgroup-members <CourseID> <StudentGroupID> <UserTypeEntity>
|
||||
gam delete course-studentgroup-members <CourseID> <StudentGroupID> <UserTypeEntity>
|
||||
gam sync course-studentgroup-members <CourseID> <StudentGroupID> <UserTypeEntity>
|
||||
gam clear course-studentgroupmembers <CourseID> <StudentGroupID>
|
||||
```
|
||||
|
||||
# Display student group membership
|
||||
```
|
||||
gam print course-studentgroup-members [todrive <ToDriveAttribute>*]
|
||||
(course|class <CourseEntity>)*|([teacher <UserItem>] [student <UserItem>] [states <CourseStateList>])
|
||||
[formatjson [quotechar <Character>]]
|
||||
```
|
||||
By default, the `print course-studentgroup-members` command displays student group member information about all courses.
|
||||
|
||||
To display student group member information for a specific set of courses, use the following option; it can be repeated to select multiple courses.
|
||||
* `(course|class <CourseID>)*` - Display courses with the specified `<CourseID>`.
|
||||
|
||||
To display student group member information for courses based on their having a particular participant, use the following options. Both options can be specified.
|
||||
* `teacher <UserItem>` - Display courses with the specified teacher.
|
||||
* `student <UserItem>` - Display courses with the specified student.
|
||||
|
||||
To display student group member information for courses based on their state, use the following option. This option can be combined with the `teacher` and `student` options.
|
||||
By default, all course states are selected.
|
||||
* `states <CourseStateList>` - Display courses with any of the specified states.
|
||||
|
||||
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.
|
||||
|
||||
By default, when writing CSV files, Gam uses a quote character of double quote `"`. The quote character is used to enclose columns that contain
|
||||
the quote character itself, the column delimiter (comma by default) and new-line characters. Any quote characters within the column are doubled.
|
||||
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.
|
||||
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.
|
||||
|
||||
## Display student group membership counts
|
||||
Display the number of student group members
|
||||
```
|
||||
gam print course-studentgroup-members [todrive <ToDriveAttribute>*]
|
||||
(course|class <CourseEntity>)*|([teacher <UserItem>] [student <UserItem>] [states <CourseStateList>])
|
||||
showitemcountonly
|
||||
```
|
||||
Example
|
||||
```
|
||||
$ gam print course-participants teacher asmith states active show students showitemcountonly
|
||||
Getting all Courses that match query (Teacher: asmith@domain.com, Course State: ACTIVE), may take some time on a large Google Workspace Account...
|
||||
Got 3 Courses...
|
||||
Getting Students for Course: 636981507234 (1/3)
|
||||
Got 30 Students...
|
||||
Got 43 Students...
|
||||
Getting Students for Course: 589346784341 (2/3)
|
||||
Got 22 Students...
|
||||
Getting Students for Course: 589345535881 (3/3)
|
||||
Got 23 Students...
|
||||
88
|
||||
```
|
||||
The `Getting` and `Got` messages are written to stderr, the count is writtem to stdout.
|
||||
|
||||
To retrieve the count with `showitemcountonly`:
|
||||
```
|
||||
Linux/MacOS
|
||||
count=$(gam print course-participants teacher asmith states active show students showitemcountonly)
|
||||
Windows PowerShell
|
||||
$count = & gam print course-participants teacher asmith states active show students showitemcountonly
|
||||
Windows Command Prompt
|
||||
for /f "delims=" %a in ('gam print course-participants teacher asmith states active show students showitemcountonly') do set count=%a
|
||||
```
|
||||
@ -40,6 +40,18 @@ Use `gam user user@domain.com update serviceaccount` and make sure that the foll
|
||||
* [Filters](https://support.google.com/a/answer/7549103)
|
||||
* [Device Search Fields](https://developers.google.com/admin-sdk/directory/v1/search-operators)
|
||||
|
||||
Use this table to filter/query for specific device types:
|
||||
| Device Type | Filter/Query |
|
||||
|-------------|--------------|
|
||||
| ANDROID | type:android |
|
||||
| CHROME_OS | type:chromeos |
|
||||
| GOOGLE_SYNC | type:googlesync |
|
||||
| IOS | type:ios |
|
||||
| LINUX | type:linux |
|
||||
| MAC_OS | type:mac |
|
||||
| WINDOWS | type:windows |
|
||||
|
||||
|
||||
## Definitions
|
||||
```
|
||||
<AssetTag> ::= <String>
|
||||
@ -272,7 +284,9 @@ To retrieve the count with `showitemcountonly`:
|
||||
Linux/MacOS
|
||||
count=$(gam print devices queries "'model:Mac'" showitemcountonly)
|
||||
Windows PowerShell
|
||||
count = & gam print devices queries "'model:Mac'" showitemcountonly
|
||||
$count = & gam print devices queries "'model:Mac'" showitemcountonly
|
||||
Windows Command Prompt
|
||||
for /f "delims=" %a in ('gam print devices queries "'model:Mac'" showitemcountonly') do set count=%a
|
||||
```
|
||||
|
||||
## Approve or block device users
|
||||
@ -363,10 +377,11 @@ To retrieve the count with `showitemcountonly`:
|
||||
Linux/MacOS
|
||||
count=$(gam print deviceusers queries "'model:Mac'" showitemcountonly)
|
||||
Windows PowerShell
|
||||
count = & gam print deviceusers queries "'model:Mac'" showitemcountonly
|
||||
$count = & gam print deviceusers queries "'model:Mac'" showitemcountonly
|
||||
Windows Command Prompt
|
||||
for /f "delims=" %a in ('gam print deviceusers queries "'model:Mac'" showitemcountonly') do set count=%a
|
||||
```
|
||||
|
||||
|
||||
## Display device user client state
|
||||
```
|
||||
gam info deviceuserstate <DeviceUserEntity> [clientid <String>]
|
||||
@ -375,11 +390,11 @@ gam info deviceuserstate <DeviceUserEntity> [clientid <String>]
|
||||
## Update device user client state
|
||||
The API that supports this command is in beta mode. In particular, setting `assettags` and `customvalues`
|
||||
works if you set the values once; each additional time you set values they are added to the existing values
|
||||
and they is no way at the moment to clear values.
|
||||
and the `clear` option does not work to clear values.
|
||||
```
|
||||
gam update deviceuserstate <DeviceUserEntity> [clientid <String>]
|
||||
[customid <String>] [assettags clear|<AssetTagList>]
|
||||
[compliantstate|compliancestate compliant|noncompliant] [managedstate clear|managed|unmanaged]
|
||||
[healthscore very_poor|poor|neutral|good|very_good] [scorereason clear|<String>]
|
||||
(customvalue (bool|boolean <Boolean>)|(number <Integer>)|(string <String>))*
|
||||
(customvalue clear|(bool|boolean <String> <Boolean>)|(number <String> <Integer>)|(string <String> <String>))*
|
||||
```
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
- [Display user group member options](#display-user-group-member-options)
|
||||
- [Display group membership in CSV format](#display-group-membership-in-csv-format)
|
||||
- [Display group membership in hierarchical format](#display-group-membership-in-hierarchical-format)
|
||||
|
||||
- [Manage external users in groups with allowExternalMembers False](#manage-external-users-in-groups-with-allowexternalmembers-false)
|
||||
## API documentation
|
||||
* [Cloud Identity Groups Overview](https://cloud.google.com/identity/docs/groups)
|
||||
* [Cloud Identity Groups API - Groups](https://cloud.google.com/identity/docs/reference/rest/v1/groups)
|
||||
@ -349,11 +349,15 @@ gam print cigroup-members [todrive <ToDriveAttribute>*]
|
||||
[emailmatchpattern [not] <REMatchPattern>] [namematchpattern [not] <REMatchPattern>]
|
||||
[descriptionmatchpattern [not] <REMatchPattern>]
|
||||
[roles <GroupRoleList>] [members] [managers] [owners]
|
||||
[internal] [internaldomains all|primary|<DomainNameList>] [external]
|
||||
[verifyallowexternal [<Boolean>]]
|
||||
[types <CIGroupMemberTypeList>]
|
||||
<CIGroupMembersFieldName>* [fields <CIGroupMembersFieldNameList>]
|
||||
[minimal|basic|full]
|
||||
[(recursive [noduplicates]) | |includederivedmembership] [nogroupeemail]
|
||||
[memberemaildisplaypattern|memberemailskippattern <REMatchPattern>]
|
||||
(addcsvdata <FieldName> <String>)*
|
||||
[formatjson [quotechar <Character>]]
|
||||
```
|
||||
By default, the group membership of all groups in the account are displayed, these options allow selection of subsets of groups:
|
||||
* `cimember <UserItem>` - Limit display to groups that contain `<UserItem>` as a member
|
||||
@ -378,6 +382,24 @@ By default, all members, managers and owners in the group are displayed; these o
|
||||
By default, all types of members (cbcmbrowser, chromeosdevice, customer, group, serviceaccount, user) in the group are displayed; this option modifies that behavior:
|
||||
* `types <CIGroupMemberTypeList>` - Display specified types
|
||||
|
||||
Which domains are considered internal domains:
|
||||
* `internaldomains all` - All of your workspace domains; this is the default
|
||||
* `internaldomains primary` - Your workspace primary domain
|
||||
* `internaldomains <DomainNameList>` - A list of domain names
|
||||
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal` - Display members whose domain matches a value in `internaldomains`
|
||||
* `external` - Display members whose domain does not match value in `internaldomains`
|
||||
* `internal external` - Display all members, indicate their category: `internal` or `external`
|
||||
|
||||
Members without an email address, e.g. `customer`, `chrome-os-device` and `cbcm-browser` are considered `internal`.
|
||||
|
||||
When the `internal` or `external` options are specified, GAM adds the column `allowExternalMembers`
|
||||
that shows that setting for the group and adds the column `category` that shows whether the member
|
||||
is `external` or `internal`.
|
||||
|
||||
The option `verifyallowexternal` causes GAM to only display `external` users in groups with `allowExternalMembers=False'.
|
||||
|
||||
By default, members that are groups are displayed as a single entry of type GROUP; this option recursively expands group members to display their user members.
|
||||
* `recursive` - Recursively expand group members
|
||||
|
||||
@ -414,6 +436,9 @@ has in any constituent group, it is not necessarily its role in the top group.
|
||||
The options `recursive noduplicates` and `includederivedmembership types user` return the same list of users.
|
||||
The `includederivedmembership` option makes less API calls but doesn't show level and subgroup information.
|
||||
|
||||
Add additional columns of data from the command line to the output
|
||||
* `addcsvdata <FieldName> <String>`
|
||||
|
||||
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.
|
||||
|
||||
@ -430,9 +455,10 @@ gam show cigroup-members
|
||||
[emailmatchpattern [not] <REMatchPattern>] [namematchpattern [not] <REMatchPattern>]
|
||||
[descriptionmatchpattern [not] <REMatchPattern>]
|
||||
[roles <GroupRoleList>] [members] [managers] [owners]
|
||||
[internal] [internaldomains all|primary|<DomainNameList>] [external]
|
||||
[types <CIGroupMemberTypeList>]
|
||||
[memberemaildisplaypattern|memberemailskippattern <REMatchPattern>]
|
||||
[minimal|basic|full]
|
||||
[memberemaildisplaypattern|memberemailskippattern <REMatchPattern>]
|
||||
[(depth <Number>) | includederivedmembership]
|
||||
```
|
||||
By default, the group membership of all groups in the account are displayed, these options allow selection of subsets of groups:
|
||||
@ -458,6 +484,18 @@ By default, all members, managers and owners in the group are displayed; these o
|
||||
By default, all types of members (cbcmbrowser, chromeosdevice, customer, group, serviceaccount, user) in the group are displayed; this option modifies that behavior:
|
||||
* `types <CIGroupMemberTypeList>` - Display specified types
|
||||
|
||||
Which domains are considered internal domains:
|
||||
* `internaldomains all` - All of your workspace domains; this is the default
|
||||
* `internaldomains primary` - Your workspace primary domain
|
||||
* `internaldomains <DomainNameList>` - A list of domain names
|
||||
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal` - Display members whose domain matches a value in `internaldomains`
|
||||
* `external` - Display members whose domain does not match value in `internaldomains`
|
||||
* `internal external` - Display all members, indicate their category: `internal` or `external`
|
||||
|
||||
Members without an email address, e.g. `customer`, `chrome-os-device` and `cbcm-browser` are considered `internal`.
|
||||
|
||||
Members that have met the above qualifications to be displayed can be further qualifed by their email address.
|
||||
* `memberemaildisplaypattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will be displayed; others will not be displayed
|
||||
* `memberemailskippattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will not be displayed; others will be displayed
|
||||
@ -495,3 +533,41 @@ To show the structure of all groups you can do the following; it will be time co
|
||||
```
|
||||
gam redirect stdout ./groups.txt show cigroup-members types group
|
||||
```
|
||||
|
||||
## Manage external users in groups with allowExternalMembers False
|
||||
|
||||
* See: https://support.google.com/a/answer/16778447
|
||||
|
||||
Get external members of groups with allowExternalMembers = False
|
||||
```
|
||||
gam redirect csv ./ExternalMembersGroupsWithAEMFalse.csv print cigroup-members verifyallowexternal
|
||||
```
|
||||
|
||||
Update selected groups to allowExternalMembers = True
|
||||
* Add a column labelled `update` to ExternalMembersGroupsWithAEMFalse.csv
|
||||
* For groups to be updated, add an x to the `update` column for any member of the group to be updated
|
||||
```
|
||||
gam redirect stdout ./UpdateGroupsWithAEMFalseToAEMTrue.txt redirect stderr stdout update group csvkmd ExternalMembersGroupsWithAEMFalse.csv keyfield group matchfield update x allowexternalmembers true
|
||||
```
|
||||
|
||||
Update all groups to allowExternalMembers = True
|
||||
**Caution, be sure that this is what you want**
|
||||
```
|
||||
gam redirect stdout ./UpdateGroupsWithAEMFalseToEMTrue.txt redirect stderr stdout update group csvkmd ExternalMembersGroupsWithAEMFalse.csv keyfield group allowexternalmembers true
|
||||
```
|
||||
|
||||
Delete selected external members from groups with allowExternalMembers = False
|
||||
* Add a column labelled `delete` to ExternalMembersGroupsWithAEMFalse.csv
|
||||
* For exernal members to be deleted, add an x to the `delete` column
|
||||
* The `preview` option let's you verify what external members are to be deleted, remove it to do the deletions
|
||||
```
|
||||
gam redirect csv ./DeletedMembersFromGroupsWithAEMFalse.csv redirect stdout ./DeleteMembersFromGroupsWithAEMFalse.txt redirect stderr stdout update group csvkmd ExternalMembersGroupsWithAEMFalse.csv keyfield group matchfield delete x datafield email delete preview actioncsv csvdata email
|
||||
```
|
||||
|
||||
Delete all external members from groups with allowExternalMembers = False
|
||||
**Caution, be sure that this is what you want**
|
||||
* The `preview` option let's you verify what external members are to be deleted, remove it to do the deletions
|
||||
```
|
||||
gam redirect csv ./DeletedMembersFromGroupsWithAEMFalse.csv redirect stdout ./DeleteMembersFromGroupsWithAEMFalse.txt redirect stderr stdout update group csvkmd ExternalMembersGroupsWithAEMFalse.csv keyfield group datafield email delete preview actioncsv csvdata email
|
||||
```
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
|
||||
## API documentation
|
||||
* [Cloud Identity Groups Overview](https://cloud.google.com/identity/docs/groups)
|
||||
* [Create and Manage Groups uning API](https://support.google.com/a/answer/10427204)
|
||||
* [Create and Manage Groups using API](https://support.google.com/a/answer/10427204)
|
||||
* [Cloud Identity Groups API - Groups](https://cloud.google.com/identity/docs/reference/rest/v1/groups)
|
||||
* [Restrict Group Membership](https://support.google.com/a/answer/11192679)
|
||||
* [Lock Groups Beta](https://workspaceupdates.googleblog.com/2024/12/locked-groups-open-beta.html)
|
||||
@ -26,15 +26,6 @@
|
||||
|
||||
## Notes
|
||||
|
||||
In version 7.02.01 options `locked` and `unlocked` wre added to `gam update cigroups` that allow locking groups.
|
||||
|
||||
* See: https://workspaceupdates.googleblog.com/2024/12/locked-groups-open-beta.html
|
||||
|
||||
You'll have to do a `gam oauth create` and enable the following scope to use these options:
|
||||
```
|
||||
[*] 22) Cloud Identity Groups API Beta (Enables group locking/unlocking)
|
||||
```
|
||||
|
||||
In the Admin Directory API a group has the following characteristics:
|
||||
* `id` - The unique ID of a group
|
||||
* `email` - The group's email address
|
||||
@ -245,7 +236,7 @@ to set `<GroupAttribute>`.
|
||||
gam create cigroup <EmailAddress>
|
||||
[copyfrom <GroupItem>] <GroupAttribute>*
|
||||
[makeowner] [alias|aliases <CIGroupAliasList>]
|
||||
[security|makesecuritygroup]
|
||||
[security|makesecuritygroup] [locked]
|
||||
[dynamic <QueryDynamicGroup>]
|
||||
gam update cigroup <GroupEntity> [copyfrom <GroupItem>] <GroupAttribute>
|
||||
[security|makesecuritygroup|
|
||||
@ -276,7 +267,7 @@ gam info cigroups <GroupEntity>
|
||||
[nosecurity|nosecuritysettings]
|
||||
[allfields|<CIGroupFieldName>*|(fields <CIGroupFieldNameList>)]
|
||||
[roles <GroupRoleList>] [members] [managers] [owners]
|
||||
[internal] [internaldomains <DomainNameList>] [external]
|
||||
[internal] [internaldomains all|primary|<DomainNameList>] [external]
|
||||
[types <CIGroupMemberTypeList>]
|
||||
[memberemaildisplaypattern|memberemailskippattern <REMatchPattern>]
|
||||
[formatjson]
|
||||
@ -292,13 +283,17 @@ By default, all direct members, managers and owners in the group are displayed;
|
||||
By default, when displaying members from a group, all types of members (customer, group, serviceaccount, user) in the group are displayed; this option modifies that behavior:
|
||||
* `types <CIGroupMemberTypeList>` - Display specified types
|
||||
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal internaldomains <DomainNameList>` - Display members whose domain is in `<DomainNameList>`
|
||||
* `external internaldomains <DomainNameList>` - Display members whose domain is not in `<DomainNameList>`
|
||||
* `internal external internaldomains <DomainNameList>` - Display all members, indicate their category: internal or external
|
||||
* `internaldomains <DomainNameList>` - Defaults to value of `domain` in `gam.cfg`
|
||||
Which domains are considered internal domains:
|
||||
* `internaldomains all` - All of your workspace domains; this is the default
|
||||
* `internaldomains primary` - Your workspace primary domain
|
||||
* `internaldomains <DomainNameList>` - A list of domain names
|
||||
|
||||
Members without an email address, e.g. `customer`, `chrome-os-device` and `cbcm-browser` are considered internal.
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal` - Display members whose domain matches a value in `internaldomains`
|
||||
* `external` - Display members whose domain does not match value in `internaldomains`
|
||||
* `internal external` - Display all members, indicate their category: `internal` or `external`
|
||||
|
||||
Members without an email address, e.g. `customer`, `chrome-os-device` and `cbcm-browser` are considered `internal`.
|
||||
|
||||
Members that have met the above qualifications to be displayed can be further qualifed by their email address.
|
||||
* `memberemaildisplaypattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will be displayed; others will not be displayed
|
||||
@ -326,11 +321,12 @@ gam print cigroups [todrive <ToDriveAttribute>*]
|
||||
[descriptionmatchpattern [not] <REMatchPattern>]
|
||||
[basic|allfields|(<CIGroupFieldName>* [fields <CIGroupFieldNameList>])]
|
||||
[roles <GroupRoleList>] [memberrestrictions]
|
||||
[internal] [internaldomains <DomainNameList>] [external]
|
||||
[internal] [internaldomains all|primary|<DomainNameList>] [external]
|
||||
[members|memberscount] [managers|managerscount] [owners|ownerscount] [totalcount] [countsonly]
|
||||
[types <CIGroupMemberTypeList>]
|
||||
[memberemaildisplaypattern|memberemailskippattern <REMatchPattern>]
|
||||
[convertcrnl] [delimiter <Character>]
|
||||
(addcsvdata <FieldName> <String>)* [includecsvdatainjson [<Boolean>]]
|
||||
[formatjson [quotechar <Character>]]
|
||||
```
|
||||
By default, all groups in the account are displayed, these options allow selection of subsets of groups:
|
||||
@ -382,21 +378,34 @@ By default, no members, managers or owners in the group are displayed; these opt
|
||||
By default, when displaying members from a group, all types of members (customer, group, serviceaccount, user) in the group are displayed; this option modifies that behavior:
|
||||
* `types <CIGroupMemberTypeList>` - Display specified types
|
||||
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal internaldomains <DomainNameList>` - Display members whose domain is in `<DomainNameList>`
|
||||
* `external internaldomains <DomainNameList>` - Display members whose domain is not in `<DomainNameList>`
|
||||
* `internal external internaldomains <DomainNameList>` - Display all members, indicate their category: internal or external
|
||||
* `internaldomains <DomainNameList>` - Defaults to value of `domain` in `gam.cfg`
|
||||
Which domains are considered internal domains:
|
||||
* `internaldomains all` - All of your workspace domains; this is the default
|
||||
* `internaldomains primary` - Your workspace primary domain
|
||||
* `internaldomains <DomainNameList>` - A list of domain names
|
||||
|
||||
Members without an email address, e.g. `customer`, `chrome-os-device` and `cbcm-browser` are considered internal.
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal` - Display members whose domain matches a value in `internaldomains`
|
||||
* `external` - Display members whose domain does not match value in `internaldomains`
|
||||
* `internal external` - Display all members, indicate their category: `internal` or `external`
|
||||
|
||||
When the `internal` or `external` options are specified, GAM adds the column `allowExternalMembers`
|
||||
that shows that setting for the group.
|
||||
|
||||
Members without an email address, e.g. `customer`, `chrome-os-device` and `cbcm-browser` are considered `internal`.
|
||||
|
||||
Members that have met the above qualifications to be displayed can be further qualifed by their email address.
|
||||
* `memberemaildisplaypattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will be displayed; others will not be displayed
|
||||
* `memberemailskippattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will not be displayed; others will be displayed
|
||||
|
||||
Add additional columns of data from the command line to the output
|
||||
* `addcsvdata <FieldName> <String>`
|
||||
|
||||
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.
|
||||
|
||||
If `formatjson` and `addcsvdata` are specified, the option `includecsvdatainjson` causes GAM to add the
|
||||
additional field values to the JSON data.
|
||||
|
||||
By default, when writing CSV files, Gam uses a quote character of double quote `"`. The quote character is used to enclose columns that contain
|
||||
the quote character itself, the column delimiter (comma by default) and new-line characters. Any quote characters within the column are doubled.
|
||||
When using the `formatjson` option, double quotes are used extensively in the data resulting in hard to read/process output.
|
||||
@ -467,5 +476,7 @@ To retrieve the count with `showitemcountonly`:
|
||||
Linux/MacOS
|
||||
count=$(gam print cigroups showitemcountonly)
|
||||
Windows PowerShell
|
||||
count = & gam print cidgroups showitemcountonly
|
||||
$count = & gam print cigroups showitemcountonly
|
||||
Windows Command Prompt
|
||||
for /f "delims=" %a in ('gam print cigroups showitemcountonly') do set count=%a
|
||||
```
|
||||
|
||||
@ -53,286 +53,7 @@ You must enable access to policies in the GCP cloud console.
|
||||
These are the supported policies GAM can show today.
|
||||
|
||||
See: https://cloud.google.com/identity/docs/concepts/supported-policy-api-settings
|
||||
```
|
||||
user_takeout_status (is takeout enabled for service)
|
||||
blogger.user_takeout
|
||||
books.user_takeout
|
||||
location_history.user_takeout
|
||||
maps.user_takeout
|
||||
pay.user_takeout
|
||||
photos.user_takeout
|
||||
play.user_takeout
|
||||
play_console.user_takeout
|
||||
youtube.user_takeout
|
||||
service_status (is service enabled)
|
||||
ad_manager
|
||||
ads
|
||||
adsense
|
||||
alerts
|
||||
analytics
|
||||
applied_digital_skills
|
||||
appsheet
|
||||
arts_and_culture
|
||||
beyondcorp_enterprise
|
||||
blogger
|
||||
bookmarks
|
||||
books
|
||||
calendar
|
||||
campaign_manager
|
||||
chat
|
||||
chrome_canvas
|
||||
chrome_remote_desktop
|
||||
chrome_sync
|
||||
chrome_web_store
|
||||
classroom
|
||||
cloud
|
||||
cloud_search
|
||||
colab
|
||||
cs_first
|
||||
data_studio
|
||||
developers
|
||||
domains
|
||||
drive_and_docs
|
||||
earth
|
||||
enterprise_service_restrictions
|
||||
experimental_apps
|
||||
feedburner
|
||||
fi
|
||||
gmail
|
||||
groups
|
||||
groups_for_business
|
||||
jamboard
|
||||
keep
|
||||
location_history
|
||||
managed_play
|
||||
maps
|
||||
material_gallery
|
||||
meet
|
||||
merchant_center
|
||||
messages
|
||||
migrate
|
||||
my_business
|
||||
my_maps
|
||||
news
|
||||
partner_dash
|
||||
pay
|
||||
pay_for_business
|
||||
photos
|
||||
pinpoint
|
||||
play
|
||||
play_books_partner_center
|
||||
play_console
|
||||
public_data
|
||||
question_hub
|
||||
scholar_profiles
|
||||
search_ads_360
|
||||
search_and_assistant
|
||||
search_console
|
||||
sites
|
||||
socratic
|
||||
takeout
|
||||
tasks
|
||||
third_party_app_backups
|
||||
translate
|
||||
trips
|
||||
vault
|
||||
voice
|
||||
work_insights
|
||||
youtube
|
||||
calendar.appointment_schedules
|
||||
enablePayments
|
||||
chat.chat_apps_access
|
||||
enableApps
|
||||
enableWebhooks
|
||||
chat.chat_file_sharing
|
||||
externalFileSharing
|
||||
internalFileSharing
|
||||
chat.chat_history
|
||||
enableChatHistory
|
||||
historyOnByDefault
|
||||
allowUserModification
|
||||
chat.external_chat_restriction
|
||||
allowExternalChat
|
||||
chat.space_history
|
||||
historyState
|
||||
classroom.api_data_access
|
||||
enableApiAccess
|
||||
classroom.class_membership
|
||||
whoCanJoinClasses
|
||||
whichClassesCanUsersJoin
|
||||
classroom.guardian_access
|
||||
allowAccess
|
||||
whoCanManageGuardianAccess
|
||||
classroom.originality_reports
|
||||
enableOriginalityReportsSchoolMatches
|
||||
classroom.roster_import
|
||||
rosterImportOption
|
||||
classroom.student_unenrollment
|
||||
whoCanUnenrollStudents
|
||||
classroom.teacher_permissions
|
||||
whoCanCreateClasses
|
||||
cloud_sharing_options.cloud_data_sharing
|
||||
sharingOptions
|
||||
detector.regular_expression
|
||||
displayName
|
||||
regularExpression
|
||||
createTime
|
||||
updateTime
|
||||
detector.word_list
|
||||
displayName
|
||||
wordList
|
||||
createTime
|
||||
updateTime
|
||||
description
|
||||
drive_and_docs.drive_for_desktop
|
||||
allowDriveForDesktop
|
||||
restrictToAuthorizedDevices
|
||||
showDownloadLink
|
||||
allowRealTimePresence
|
||||
drive_and_docs.external_sharing
|
||||
externalSharingMode
|
||||
allowReceivingExternalFiles
|
||||
warnForSharingOutsideAllowlistedDomains
|
||||
allowReceivingFilesOutsideAllowlistedDomains
|
||||
allowNonGoogleInvitesInAllowlistedDomains
|
||||
warnForExternalSharing
|
||||
allowNonGoogleInvites
|
||||
allowPublishingFiles
|
||||
accessCheckerSuggestions
|
||||
allowedPartiesForDistributingContent
|
||||
drive_and_docs.file_security_update
|
||||
securityUpdate
|
||||
allowUsersToManageUpdate
|
||||
drive_and_docs.shared_drive_creation
|
||||
allowSharedDriveCreation
|
||||
orgUnitForNewSharedDrives
|
||||
customOrgUnit
|
||||
allowManagersToOverrideSettings
|
||||
allowExternalUserAccess
|
||||
allowNonMemberAccess
|
||||
allowedPartiesForDownloadPrintCopy
|
||||
allowContentManagersToShareFolders
|
||||
gmail.auto_forwarding
|
||||
enableAutoForwarding
|
||||
gmail.confidential_mode
|
||||
enableConfidentialMode
|
||||
gmail.email_attachment_safety
|
||||
enableEncryptedAttachmentProtection
|
||||
encryptedAttachmentProtectionConsequence
|
||||
enableAttachmentWithScriptsProtection
|
||||
attachmentWithScriptsProtectionConsequence
|
||||
enableAnomalousAttachmentProtection
|
||||
anomalousAttachmentProtectionConsequence
|
||||
allowedAnomalousAttachmentFiletypes
|
||||
applyFutureRecommendedSettingsAutomatically
|
||||
encryptedAttachmentProtectionQuarantineId
|
||||
attachmentWithScriptsProtectionQuarantineId
|
||||
anomalousAttachmentProtectionQuarantineId
|
||||
gmail.email_image_proxy_bypass
|
||||
imageProxyBypassPattern
|
||||
enableImageProxy
|
||||
gmail.enhanced_pre_delivery_message_scanning
|
||||
enableImprovedSuspiciousContentDetection
|
||||
gmail.enhanced_smime_encryption
|
||||
enableSmimeEncryption
|
||||
allowUserToUploadCertificates
|
||||
gmail.gmail_name_format
|
||||
allowCustomDisplayNames
|
||||
defaultDisplayNameFormat
|
||||
gmail.imap_access
|
||||
enableImapAccess
|
||||
gmail.links_and_external_images
|
||||
enableShortenerScanning
|
||||
enableExternalImageScanning
|
||||
enableAggressiveWarningsOnUntrustedLinks
|
||||
applyFutureSettingsAutomatically
|
||||
gmail.per_user_outbound_gateway
|
||||
allowUsersToUseExternalSmtpServers
|
||||
gmail.pop_access
|
||||
enablePopAccess
|
||||
gmail.spoofing_and_authentication
|
||||
detectDomainNameSpoofing
|
||||
detectEmployeeNameSpoofing
|
||||
detectDomainSpoofingFromUnauthenticatedSenders
|
||||
detectUnauthenticatedEmails
|
||||
domainNameSpoofingConsequence
|
||||
employeeNameSpoofingConsequence
|
||||
domainSpoofingConsequence
|
||||
unauthenticatedEmailConsequence
|
||||
detectGroupsSpoofing
|
||||
groupsSpoofingVisibilityType
|
||||
groupsSpoofingConsequence
|
||||
applyFutureSettingsAutomatically
|
||||
domainNameSpoofingQuarantineId
|
||||
employeeNameSpoofingQuarantineId
|
||||
domainSpoofingQuarantineId
|
||||
unauthenticatedEmailQuarantineId
|
||||
groupsSpoofingQuarantineId
|
||||
gmail.user_email_uploads
|
||||
enableMailAndContactsImport
|
||||
gmail.workspace_sync_for_outlook
|
||||
enableGoogleWorkspaceSyncForMicrosoftOutlook
|
||||
groups_for_business.groups_sharing
|
||||
ownersCanAllowIncomingMailFromPublic
|
||||
collaborationCapability
|
||||
createGroupsAccessLevel
|
||||
ownersCanAllowExternalMembers
|
||||
ownersCanHideGroups
|
||||
newGroupsAreHidden
|
||||
viewTopicsDefaultAccessLevel
|
||||
meet.safety_access
|
||||
meetingsAllowedToJoin
|
||||
meet.safety_domain
|
||||
usersAllowedToJoin
|
||||
meet.safety_external_participants
|
||||
enableExternalLabel
|
||||
meet.safety_host_management
|
||||
enableHostManagement
|
||||
meet.video_recording
|
||||
enableRecording
|
||||
rule.dlp
|
||||
displayName
|
||||
description
|
||||
triggers
|
||||
condition
|
||||
action
|
||||
state
|
||||
createTime
|
||||
updateTime
|
||||
ruleTypeMetadata
|
||||
rule.system_defined_alerts
|
||||
displayName
|
||||
description
|
||||
action
|
||||
state
|
||||
createTime
|
||||
updateTime
|
||||
security.advanced_protection_program
|
||||
enableAdvancedProtectionSelfEnrollment
|
||||
securityCodeOption
|
||||
security.less_secure_apps
|
||||
allowLessSecureApps
|
||||
security.login_challenges
|
||||
enableEmployeeIdChallenge
|
||||
security.password
|
||||
allowedStrength
|
||||
minimumLength
|
||||
maximumLength
|
||||
enforceRequirementsAtLogin
|
||||
allowReuse
|
||||
expirationDuration
|
||||
security.session_controls
|
||||
webSessionDuration
|
||||
security.super_admin_account_recovery
|
||||
enableAccountRecovery
|
||||
security.user_account_recovery
|
||||
enableAccountRecovery
|
||||
sites.sites_creation_and_modification
|
||||
allowSitesCreation
|
||||
allowSitesModification
|
||||
workspace_marketplace.apps_allowlist
|
||||
apps
|
||||
```
|
||||
|
||||
## Display Cloud Identity Policies
|
||||
Display selected policies.
|
||||
```
|
||||
|
||||
@ -351,6 +351,10 @@ Data fields identified in a `csvkmd` argument.
|
||||
<SiteACLScopeList> | <FileSelector> | <CSVFileSelector> | <CSVkmdSelector> | <CSVDataSelector>
|
||||
<SiteEntity> ::=
|
||||
<SiteList> | <FileSelector> | <CSVFileSelector> | <CSVkmdSelector> | <CSVDataSelector>
|
||||
<StringEntity> ::=
|
||||
<StringList> | <FileSelector> | <CSVFileSelector>
|
||||
<StudentGroupEntity> ::=
|
||||
<StudentGroupIDList> | <FileSelector> | <CSVFileSelector> | <CSVkmdSelector>
|
||||
<TagManagerAccountPathEntity> ::=
|
||||
<TagManagerAccountPathList> |
|
||||
(select <FileSelector>|<CSVFileSelector>) |
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
# Collections of Users
|
||||
- [Python Regular Expressions](Python-Regular-Expressions) Search function
|
||||
- [Notes](#notes)
|
||||
- [Definitions](#definitions)
|
||||
- [User Type Entity](#user-type-entity)
|
||||
- [All non-suspended Users](#all-non-suspended-users)
|
||||
@ -37,6 +38,37 @@
|
||||
- [Examples using CSV files to print users from groups](#examples-using-CSV-files-to-print-users-from-groups)
|
||||
- [Examples using multiple queries](#examples-using-multiple-queries)
|
||||
|
||||
## Notes
|
||||
|
||||
The followig items referencing non-archived/archived users were added to `<UserTypeEntity>` in version 7.22.00.
|
||||
```
|
||||
all users_na
|
||||
all users_arch
|
||||
all users_na_ns
|
||||
all users_arch_or_susp
|
||||
domains_na
|
||||
domains_arch
|
||||
domains_na_ns
|
||||
groups_na
|
||||
groups_arch
|
||||
groups_na_ns
|
||||
group_users_na
|
||||
group_users_arch
|
||||
group_users_na_ns
|
||||
ou_na
|
||||
ou_arch
|
||||
ou_na_ns
|
||||
ou_and_children_na
|
||||
ou_and_children_arch
|
||||
ou_and_children_na_ns
|
||||
ous_na
|
||||
ous_arch
|
||||
ous_na_ns
|
||||
ous_and_children_na
|
||||
ous_and_children_arch
|
||||
ous_and_children_na_ns
|
||||
```
|
||||
|
||||
## Definitions
|
||||
* [Basic Items](Basic-Items)
|
||||
|
||||
@ -90,25 +122,25 @@
|
||||
<SharedDriveNameEntity>
|
||||
|
||||
<UserTypeEntity> ::=
|
||||
(all users|users_ns|users_susp|users_ns_susp)|
|
||||
(all users|users_na|users_arch|users_ns|users_susp|users_ns_susp|users_arch_or_susp|users_na_ns)|
|
||||
(user <UserItem>)|
|
||||
(users <UserList>)|
|
||||
(oauthuser)
|
||||
(domains|domains_ns|domains_susp <DomainNameList>)|
|
||||
(group|group_ns|group_susp|group_inde <GroupItem>)|
|
||||
(groups|groups_ns|groups_susp|groups_inde <GroupList>)|
|
||||
(domains|domains_na|domains_arch|domains_ns|domains_susp|domains_na_ns <DomainNameListList>)|
|
||||
(group|group_na|group_arch|group_ns|group_susp|group_na_ns|group_inde <GroupItem>)|
|
||||
(groups|groups_na|groups_arch|groups_ns|groups_susp|groups_na_ns|groups_inde <GroupList>)|
|
||||
(group_inde <GroupItem>)|(groups_inde <GroupList>)|
|
||||
(group_users|group_users_ns|group_users_susp <GroupList>
|
||||
(group_users|group_users_na|group_users_arch|group_users_ns|group_users_susp|group_users_na_ns <GroupList>
|
||||
[members] [managers] [owners]
|
||||
[primarydomain] [domains <DomainNameList>] [recursive|includederivedmembership] end)|
|
||||
(group_users_select <GroupList>
|
||||
[members] [managers] [owners]
|
||||
[notsuspended|suspended] [notarchived|archived]
|
||||
[primarydomain] [domains <DomainNameList>] [recursive|includederivedmembership] end)|
|
||||
(ou|ou_ns|ou_susp <OrgUnitItem>)|
|
||||
(ou_and_children|ou_and_children_ns|ou_and_children_susp <OrgUnitItem>)|
|
||||
(ous|ous_ns|ous_susp <OrgUnitList>)|
|
||||
(ous_and_children|ous_and_children_ns|ous_and_children_susp <OrgUnitList>)|
|
||||
(ou|ou_na|ou_arch|ou_ns|ou_susp|ou_na_ns <OrgUnitItem>)|
|
||||
(ou_and_children|ou_and_children_na|ou_and_children_arch|ou_and_children_ns|ou_and_children_susp|ou_and_children_na_ns <OrgUnitItem>)|
|
||||
(ous|ous_na|ous_arch|ous_ns|ous_susp|ous_na_ns <OrgUnitList>)|
|
||||
(ous_and_children|ous_and_children_na|ous_and_children_arch|ous_and_children_ns|ous_and_children_susp|ous_and_children_na_ns <OrgUnitList>)|
|
||||
(courseparticipants <CourseIDList>)|
|
||||
(students <CourseIDList>)|
|
||||
(teachers <CourseIDList>)|
|
||||
@ -126,41 +158,47 @@
|
||||
(gdoc(:<FieldName>)+ <UserGoogleDoc>)|
|
||||
(gcscsv(:<FieldName>)+ <StorageBucketObjectName>)|
|
||||
(gcsdoc(:<FieldName>)+ <StorageBucketObjectName>))
|
||||
[warnifnodata] [columndelimiter <Character>] [noescapechar <Boolean>][quotechar <Character>]
|
||||
[warnifnodata] [columndelimiter <Character>] [noescapechar <Boolean>] [quotechar <Character>]
|
||||
[endcsv|(fields <FieldNameList>)]
|
||||
(matchfield|skipfield <FieldName> <RESearchPattern>)*
|
||||
[delimiter <Character>])|
|
||||
(datafile
|
||||
users|groups|groups_ns|groups_susp|groups_inde|ous|ous_ns|ous_susp|
|
||||
ous_and_children|ous_and_children_ns|ous_and_children_susp|
|
||||
courseparticipants|students|teachers
|
||||
users|
|
||||
groups|groups_na|groups_arch|groups_ns|groups_susp|groups_na_ns|groups_inde|
|
||||
ous|ous_na|ous_arch|ous_ns|ous_susps|ous_na_ns|
|
||||
ous_and_children|ous_and_children_na|ous_and_children_arch|ous_and_children_ns|ous_and_children_susp|ous_and_children_na_ns|
|
||||
courseparticipants|students|teachers
|
||||
((<FileName> [charset <Charset>])|
|
||||
(gdoc <UserGoogleDoc>)|
|
||||
(gcsdoc <StorageBucketObjectName>))
|
||||
[delimiter <Character>])|
|
||||
(csvdatafile
|
||||
users|groups|groups_ns|groups_susp|groups_inde|ous|ous_ns|ous_susp|
|
||||
ous_and_children|ous_and_children_ns|ous_and_children_susp|
|
||||
courseparticipants|students|teachers
|
||||
users|
|
||||
groups|groups_na|groups_arch|groups_ns|groups_susp|groups_na_ns|groups_inde|
|
||||
ous|ous_na|ous_arch|ous_ns|ous_susps|ous_na_ns|
|
||||
ous_and_children|ous_and_children_na|ous_and_children_arch|ous_and_children_ns|ous_and_children_susp|ous_and_children_na_ns|
|
||||
courseparticipants|students|teachers
|
||||
((<FileName>(:<FieldName>)+ [charset <Charset>] )|
|
||||
(gsheet(:<FieldName>)+ <UserGoogleSheet>)|
|
||||
(gdoc(:<FieldName>)+ <UserGoogleDoc>)|
|
||||
(gcscsv(:<FieldName>)+ <StorageBucketObjectName>)|
|
||||
(gcsdoc(:<FieldName>)+ <StorageBucketObjectName>))
|
||||
[warnifnodata] [columndelimiter <Character>] [noescapechar <Boolean>][quotechar <Character>]
|
||||
[warnifnodata] [columndelimiter <Character>] [noescapechar <Boolean>] [quotechar <Character>]
|
||||
[endcsv|(fields <FieldNameList>)]
|
||||
(matchfield|skipfield <FieldName> <RESearchPattern>)*
|
||||
[delimiter <Character>])|
|
||||
(csvkmd
|
||||
users|groups|groups_ns|groups_susp|groups_inde|ous|ous_ns|ous_susp|
|
||||
ous_and_children|ous_and_children_ns|ous_and_children_susp|
|
||||
courseparticipants|students|teachers
|
||||
users|
|
||||
groups|groups_na|groups_arch|groups_ns|groups_susp|groups_na_ns|groups_inde|
|
||||
ous|ous_na|ous_arch|ous_ns|ous_susps|ous_na_ns|
|
||||
ous_and_children|ous_and_children_na|ous_and_children_arch|ous_and_children_ns|ous_and_children_susp|ous_and_children_na_ns|
|
||||
courseparticipants|students|teachers
|
||||
((<FileName>|
|
||||
(gsheet <UserGoogleSheet>)|
|
||||
(gdoc <UserGoogleDoc>)|
|
||||
(gcscsv <StorageBucketObjectName>)|
|
||||
(gcsdoc <StorageBucketObjectName>))
|
||||
[charset <Charset>] [columndelimiter <Character>] [noescapechar <Boolean>][quotechar <Character>] [fields <FieldNameList>])
|
||||
[charset <Charset>] [columndelimiter <Character>] [noescapechar <Boolean>] [quotechar <Character>] [fields <FieldNameList>])
|
||||
keyfield <FieldName> [keypattern <RESearchPattern>] [keyvalue <RESubstitution>] [delimiter <Character>]
|
||||
subkeyfield <FieldName> [keypattern <RESearchPattern>] [keyvalue <RESubstitution>] [delimiter <Character>]
|
||||
(matchfield|skipfield <FieldName> <RESearchPattern>)*
|
||||
@ -172,6 +210,12 @@
|
||||
|
||||
Use these options to select users for GAM commands.
|
||||
|
||||
## All non-archived Users
|
||||
* `all users_na`
|
||||
|
||||
## All archived Users
|
||||
* `all users_arch`
|
||||
|
||||
## All non-suspended Users
|
||||
* `all users`
|
||||
* `all users_ns`
|
||||
@ -179,6 +223,12 @@ Use these options to select users for GAM commands.
|
||||
## All suspended Users
|
||||
* `all users_susp`
|
||||
|
||||
## All archived or suspended Users
|
||||
* `all users_arch_or_susp`
|
||||
|
||||
## All non-archived and non-suspended Users
|
||||
* `all users_na_ns`
|
||||
|
||||
## All non-suspended and suspended Users
|
||||
* `all users_ns_susp`
|
||||
|
||||
@ -192,22 +242,31 @@ Use these options to select users for GAM commands.
|
||||
* `oauthuser`
|
||||
|
||||
## Users in the domains `<DomainNameList>`
|
||||
* `domains|domains_ns|domains_susp <DomainNameList>`
|
||||
* `domains|domains_na|domains_arch|domains_ns|domains_susp|domains_na_ns <DomainNameList>`
|
||||
* `domains` - All users
|
||||
* `domains_na` - Non-archived users
|
||||
* `domains_arch` - Archived users
|
||||
* `domains_ns` - Non-suspended users
|
||||
* `domains_susp` - Suspended users
|
||||
* `domains_na_ns` - Non-archived and non-suspended users
|
||||
|
||||
## Users directly in the group `<GroupItem>`
|
||||
* `group|group_ns|group_susp <GroupItem>`
|
||||
* `group|group_na|group_arch|group_ns|group_susp|group_na_ns <GroupItem>`
|
||||
* `group` - All user members
|
||||
* `group_na` - Non-archived user members
|
||||
* `group_arch` - Archived user members
|
||||
* `group_ns` - Non-suspended user members
|
||||
* `group_susp` - Suspended user members
|
||||
* `group_na_ns` - Non-archived and non-suspended user members
|
||||
|
||||
## Users directly in the groups `<GroupList>`
|
||||
* `groups|groups_ns|groups_susp <GroupList>`
|
||||
* `groups|groups_na|groups_arch|groups_ns|groups_susp|groups_na_ns <GroupList>`
|
||||
* `groups` - All user members
|
||||
* `groups_na` - Non-archived user members
|
||||
* `groups_arch` - Archived user members
|
||||
* `groups_ns` - Non-suspended user members
|
||||
* `groups_susp` - Suspended user members
|
||||
* `groups_na_ns` - Non-archived and non-suspended user members
|
||||
|
||||
## Users directly and indirectly in the group `<GroupItem>`
|
||||
* `group_inde` - All user members including those from all subgroups
|
||||
@ -216,10 +275,13 @@ Use these options to select users for GAM commands.
|
||||
* `groups_inde` - All user members including those from all subgroups
|
||||
|
||||
## Selected Users from groups
|
||||
* `group_users|group_users_ns|group_users_susp <GroupList> [members] [managers] [owners] [primarydomain] [domains <DomainNameList>] [recursive|includederivedmembership] end`
|
||||
* `group_users|group_users_na|group_users_arch|group_users_ns|group_users_susp|group_users_na_ns <GroupList> [members] [managers] [owners] [primarydomain] [domains <DomainNameList>] [recursive|includederivedmembership] end`
|
||||
* `group_users` - All user members
|
||||
* `group_users_na` - Non-archived user members
|
||||
* `group_users_arch` - Archived user members
|
||||
* `group_users_ns` - Non-suspended user members
|
||||
* `group_users_susp` - Suspended user members
|
||||
* `group_users_na_ns` - Non-archived and non-suspended user members
|
||||
* `[members] [managers] [owners]` - The desired roles; if roles are not specified, all roles are included
|
||||
* `primarydomain` - Select Users from the primary domain
|
||||
* `domains <DomainNameList>` - Select Users from the list of domains
|
||||
@ -259,30 +321,41 @@ Use these options to select users for GAM commands.
|
||||
* `end` - Terminate the selection
|
||||
|
||||
## Users directly in the Organization Unit `<OrgUnitItem>`
|
||||
* `ou|ou_ns|ou_susp <OrgUnitItem>`
|
||||
* `ou|ou_na|ou_arch|ou_ns|ou_susp|ou_na_ns <OrgUnitItem>`
|
||||
* `ou` - All users
|
||||
* `ou_ns` - Non-Suspended users
|
||||
* `ou_na` - Non-archived users
|
||||
* `ou_arch` - Archived users
|
||||
* `ou_ns` - Non-suspended users
|
||||
* `ou_susp` - Suspended users
|
||||
* `ou_na_ns` - Non-archived and nn-suspended users
|
||||
|
||||
## Users in the Organization Unit `<OrgUnitItem>` and all of its sub Organization Units
|
||||
* `ou_and_children|ou_and_children_ns|ou_and_children_susp <OrgUnitItem>`
|
||||
* `ou_and_children|ou_and_children_na|ou_and_children_arch|ou_and_children_ns|ou_and_children_susp|ou_and_children_na_ns <OrgUnitItem>`
|
||||
* `ou_and_children` - All users
|
||||
* `ou_and_children_na` - Non-archived users
|
||||
* `ou_and_children_arch` - Archived users
|
||||
* `ou_and_children_ns` - Non-suspended users
|
||||
* `ou_and_children_susp` - Suspended users
|
||||
* `ou_and_children_na_ns` - Non-archived and nn-suspended users
|
||||
|
||||
## Users directly in the Organization Units `<OrgUnitList>`
|
||||
* `ous|ous_ns|ous_susp <OrgUnitList>` - Users directly in the Organization Units `<OrgUnitList>`
|
||||
* `ous|ous_na|ous_arch|ous_ns|ous_susp|ous_na_ns <OrgUnitList>` - Users directly in the Organization Units `<OrgUnitList>`
|
||||
* `ous` - All users
|
||||
* `ous_na` - Non-archived users
|
||||
* `ous_arch` - Archived users
|
||||
* `ous_ns` - Non-suspended users
|
||||
* `ous_susp` - Suspended users
|
||||
* `ous_na_ns` - Non-archived and nn-suspended users
|
||||
|
||||
`<OrgUnitList>` may require special quoting based on whether the OUs contain spaces, commas or single quotes.
|
||||
|
||||
For quoting rules, see: [List Quoting Rules](Command-Line-Parsing)
|
||||
|
||||
## Users in the Organization Units `<OrgUnitList>` and all of their sub Organization Units
|
||||
* `ous_and_children|ous_and_children_ns|ous_and_children_susp <OrgUnitList>` - Users in the Organization Units `<OrgUnitList>` and all of their sub Organization Units
|
||||
* `ous_and_children|ous_and_children_na|ous_and_children_arch|ous_and_children_ns|ous_and_children_susp|ous_and_children_na_ns <OrgUnitList>` - Users in the Organization Units `<OrgUnitList>` and all of their sub Organization Units
|
||||
* `ous_and_children` - All users
|
||||
* `ous_and_children_na` - Non-archived users
|
||||
* `ous_and_children_arch` - Archived users
|
||||
* `ous_and_children_ns` - Non-suspended users
|
||||
* `ous_and_children_susp` - Suspended users
|
||||
|
||||
@ -363,15 +436,21 @@ csvfile
|
||||
## Users from groups/OUs/courses in a flat file/Google Doc/Google Cloud Storage Object
|
||||
```
|
||||
datafile
|
||||
users|groups|groups_ns|groups_susp|groups_inde|ous|ous_ns|ous_susp|
|
||||
ous_and_children|ous_and_children_ns|ous_and_children_susp|
|
||||
courseparticipants|students|teachers
|
||||
users|
|
||||
groups|groups_na|groups_arch|groups_ns|groups_susp|groups_na_ns|groups_inde|
|
||||
ous|ous_na|ous_arch|ous_ns|ous_susp|ous_na_ns|
|
||||
ous_and_children|ous_and_children_na|ous_and_children_arch|ous_and_children_ns|ous_and_children_susp|ous_and_children_na_ns|
|
||||
courseparticipants|students|teachers
|
||||
((<FileName> [charset <Charset>])|
|
||||
(gdoc <UserGoogleDoc>)|
|
||||
(gcsdoc <StorageBucketObjectName>))
|
||||
[delimiter <Character>]
|
||||
```
|
||||
* `users|groups|groups_ns|groups_susp|groups_inde|ous|ous_ns|ous_susp|ous_and_children|ous_and_children_ns|ous_and_children_susp|courseparticipants|students|teachers` - The type of item in the file
|
||||
* `users|`
|
||||
* `groups|groups_na|groups_arch|groups_ns_|groups_susp|groups_na_ns|groups_inde|`
|
||||
* `ous|ous_na|ous_arch|ous_ns|ous_susp|ous_na_ns|`
|
||||
* `ous_and_children|ous_and_children_na|ous_and_children_arch|ous_and_children_ns|ous_and_children_susp|ous_and_children_na_ns|`
|
||||
* `courseparticipants|students|teachers` - The type of item in the file
|
||||
* `<FileName>` - A flat file containing rows of the type of item specified
|
||||
* `charset <Charset>` - The character aset of the file if it isn't UTF-8
|
||||
* `gdoc <UserGoogleDoc>` - A Google Doc containing rows of the type of item specified
|
||||
@ -381,9 +460,11 @@ datafile
|
||||
## Users from groups/OUs/courses in a CSV file/Google Sheet/Google Doc/Google Cloud Storage Object
|
||||
```
|
||||
csvdatafile
|
||||
users|groups|groups_ns|groups_susp|groups_inde|ous|ous_ns|ous_susp|
|
||||
ous_and_children|ous_and_children_ns|ous_and_children_susp|
|
||||
courseparticipants|students|teachers
|
||||
users|
|
||||
groups|groups_na|groups_arch|groups_ns|groups_susp|groups_na_ns|groups_inde|
|
||||
ous|ous_na|ous_arch|ous_ns|ous_susp|ous_na_ns|
|
||||
ous_and_children|ous_and_children_na|ous_and_children_arch|ous_and_children_ns|ous_and_children_susp|ous_and_children_na_ns|
|
||||
courseparticipants|students|teachers
|
||||
((<FileName>(:<FieldName>)+ [charset <Charset>] )|
|
||||
(gsheet(:<FieldName>)+ <UserGoogleSheet>)|
|
||||
(gdoc(:<FieldName>)+ <UserGoogleDoc>)|
|
||||
@ -394,9 +475,13 @@ csvdatafile
|
||||
(matchfield|skipfield <FieldName> <RESearchPattern>)*
|
||||
[delimiter <Character>]
|
||||
```
|
||||
* `users|groups|groups_ns|groups_susp|groups_inde|ous|ous_ns|ous_susp|ous_and_children|ous_and_children_ns|ous_and_children_susp|courseparticipants|students|teachers` - The type of item in the file
|
||||
* `users|`
|
||||
* `groups|groups_na|groups_arch|groups_ns_|groups_susp|groups_na_ns|groups_inde|`
|
||||
* `ous|ous_na|ous_arch|ous_ns|ous_susp|ous_na_ns|`
|
||||
* `ous_and_children|ous_and_children_na|ous_and_children_arch|ous_and_children_ns|ous_and_children_susp|ous_and_children_na_ns|`
|
||||
* `courseparticipants|students|teachers` - The type of item in the file
|
||||
* `<FileName>(:<FieldName>)+` - A CSV file and the one or more columns contain the type of item specified
|
||||
* `charset <Charset>` - The character aset of the file if it isn't UTF-8
|
||||
* `charset <Charset>` - The character set of the file if it isn't UTF-8
|
||||
* `gsheet(:<FieldName>)+ <UserGoogleSheet>` - A Google Sheet and the one or more columns contain the type of item specified
|
||||
* `gdoc(:<FieldName>)+ <UserGoogleDoc>` - A Google Doc and the one or more columns contain the type of item specified
|
||||
* `gcscsv(:<FieldName>)+ <StorageBucketObjectName>` - A Google Cloud Storage Bucket Object and the one or more columns contain the type of item specified
|
||||
@ -413,9 +498,11 @@ csvdatafile
|
||||
## Users directly in or from groups/OUs/courses in a CSV file/Google Sheet/Google Doc/Google Cloud Storage Object
|
||||
```
|
||||
csvkmd
|
||||
users|groups|groups_ns|groups_susp|groups_inde|ous|ous_ns|ous_susp|
|
||||
ous_and_children|ous_and_children_ns|ous_and_children_susp|
|
||||
courseparticipants|students|teachers
|
||||
users|
|
||||
groups|groups_na|groups_arch|groups_ns|groups_susp|groups_na_ns|groups_inde|
|
||||
ous|ous_na|ous_arch|ous_ns|ous_susp|ous_na_ns|
|
||||
ous_and_children|ous_and_children_na|ous_and_children_arch|ous_and_children_ns|ous_and_children_susp|ous_and_children_na_ns|
|
||||
courseparticipants|students|teachers
|
||||
((<FileName>|
|
||||
(gsheet <UserGoogleSheet>)|
|
||||
(gdoc <UserGoogleDoc>)|
|
||||
@ -427,9 +514,13 @@ csvkmd
|
||||
(matchfield|skipfield <FieldName> <RESearchPattern>)*
|
||||
[datafield <FieldName>(:<FieldName>)* [delimiter <Character>]]
|
||||
```
|
||||
* `users|groups|groups_ns_|groups_susp|groups_inde|ous|ous_ns|ous_susp|ous_and_children|ous_and_children_ns|ous_and_children_susp|courseparticipants|students|teachers` - The type of item in the file
|
||||
* `users|`
|
||||
* `groups|groups_na|groups_arch|groups_ns_|groups_susp|groups_na_ns|groups_inde|`
|
||||
* `ous|ous_na|ous_arch|ous_ns|ous_susp|ous_na_ns|`
|
||||
* `ous_and_children|ous_and_children_na|ous_and_children_arch|ous_and_children_ns|ous_and_children_susp|ous_and_children_na_ns|`
|
||||
* `courseparticipants|students|teachers` - The type of item in the file
|
||||
* `<FileName>` - A CSV file containing rows with columns of the type of item specified
|
||||
* `charset <Charset>` - The character aset of the file if it isn't UTF-8
|
||||
* `charset <Charset>` - The character set of the file if it isn't UTF-8
|
||||
* `gsheet <UserGoogleSheet>` - A Google Sheet containing rows with columns of the type of item specified
|
||||
* `gdoc <UserGoogleDoc>` - A Google Doc containing rows with columns of the type of item specified
|
||||
* `gcscsv <StorageBucketObjectName>` - A Google Cloud Storage Bucket Object with columns of the type of item specified
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
- [Plain Text](#plain-text)
|
||||
- [HTML](#html)
|
||||
- [Read data from a Google Sheet](#read-data-from-a-google-sheet)
|
||||
- [Limited Service Account Access](#limited-service-account-access)
|
||||
- [Read data from a Google Cloud Storage File](#read-data-from-a-google-cloud-storage-file)
|
||||
- [Plain Text](#plain-text)
|
||||
- [CSV](#csv)
|
||||
@ -79,6 +80,25 @@ Example:
|
||||
```
|
||||
gam csv gsheet you@exmaple.com <DriveFileIDEntity> "Sheet 1" gam create user firstname "~FirstName" lastname "~lastName" email "~email"
|
||||
```
|
||||
|
||||
## Limited Service Account Access
|
||||
If you want to disable a user's service account access to Drive and Sheets but still allow reading command data from Google Docs and Sheets,
|
||||
issue the following commands. The admin specified in `gam oauth create` can read command data from Docs and Sheets to which it has access.
|
||||
```
|
||||
gam config commanddata_clientaccess true save
|
||||
gam oauth create
|
||||
Enable the following and proceed to authorization.
|
||||
|
||||
[*] 42) Drive API - commanddata_clientaccess
|
||||
[*] 54) Sheets API - commanddata_clientaccess
|
||||
```
|
||||
In these options, the `<EmailAddress> is not used, but for clarity you may want to specify the
|
||||
email address of the admin specified in `gam oauth create`.
|
||||
```
|
||||
gdoc <EmailAddress> <DriveFileIDEntity>|<DriveFileNameEntity>|(<SharedDriveEntity> <SharedDriveFileNameEntity>)
|
||||
gsheet <EmailAddress> <DriveFileIDEntity>|<DriveFileNameEntity>|(<SharedDriveEntity> <SharedDriveFileNameEntity>) <SheetEntity>
|
||||
```
|
||||
|
||||
## Read data from a Google Cloud Storage File
|
||||
```
|
||||
<StorageBucketName> ::= <String>
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
- [Windows PowerShell](#windows-powershell)
|
||||
- [List quoting rules](#list-quoting-rules)
|
||||
- [Queries example](#queries-example)
|
||||
- [Capture command output](#capture-command-output)
|
||||
|
||||
## Linux and MacOS
|
||||
|
||||
@ -79,3 +80,25 @@ gam print users queries "\"orgUnitPath='/Students/Lower School/2027'\",\"orgUnit
|
||||
```
|
||||
gam print users queries "`"orgUnitPath=\'/Students/Lower\ School/2027\'`",`"orgUnitPath=\'/Students/Lower\ School/2028\'`""
|
||||
```
|
||||
|
||||
## Capture command output
|
||||
|
||||
To retrieve an item count with `showitemcountonly`:
|
||||
```
|
||||
Linux/MacOS
|
||||
count=$(gam print users query "orgUnitPath='/Students/Middle School'" showitemcountonly)
|
||||
Windows PowerShell
|
||||
$count = & gam print users query "orgUnitPath='/Students/Middle School'" showitemcountonly
|
||||
Windows Command Prompt
|
||||
for /f "delims=" %a in ('gam print users query "orgUnitPath='/Students/Middle School'" showitemcountonly') do set count=%a
|
||||
```
|
||||
|
||||
To retrieve a File/Shared Drive ID with `returnidonly`:
|
||||
```
|
||||
Linux/MacOS
|
||||
itemId=$(gam user user@domain.com create shareddrive|drivefile ... returnidonly)
|
||||
Windows PowerShell
|
||||
$itemId = & gam user user@domain.com create shareddrive|drivefile ... returnidonly
|
||||
Windows Command Prompt
|
||||
for /f "delims=" %a in ('gam user user@domain.com create shareddrive|drivefile ... returnidonly') do set itemId=%a
|
||||
```
|
||||
|
||||
@ -13,10 +13,6 @@
|
||||
- [CAA Region Codes](#caa-region-codes)
|
||||
|
||||
## Notes
|
||||
This Wiki page was built directly from Jay Lee's Wiki page; my sincere thanks for his efforts.
|
||||
|
||||
GAM 6.20.00 and newer can create and manage access levels which can be assigned to Workspace services for your users.
|
||||
|
||||
To use these features you must update your project.
|
||||
```
|
||||
gam update project
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
- [Delete duplicate email addresses from contacts](#delete-duplicate-email-addresses-from-contacts)
|
||||
- [Manage domain contact photos](#manage-domain-contact-photos)
|
||||
- [Display domain shared contacts](#display-domain-shared-contacts)
|
||||
- [Display global address list](#display-global-address-list)
|
||||
- [Display global address list](Global-Address-List)
|
||||
|
||||
## API documentation
|
||||
* [Domain Shared Contacts API](https://developers.google.com/admin-sdk/domain-shared-contacts)
|
||||
@ -29,11 +29,11 @@
|
||||
<UserGoogleDoc> ::=
|
||||
<EmailAddress> <DriveFileIDEntity>|<DriveFileNameEntity>|(<SharedDriveEntity> <SharedDriveFileNameEntity>)
|
||||
|
||||
<NoteContent> ::=
|
||||
((<String>)|
|
||||
(file <FileName> [charset <Charset>])|
|
||||
(gdoc <UserGoogleDoc>)|
|
||||
(gcsdoc <StorageBucketObjectName>))
|
||||
<ContactNoteContent> ::=
|
||||
(<String>)|
|
||||
(file|textfile <FileName> [charset <Charset>])|
|
||||
(gdoc <UserGoogleDoc>)|
|
||||
(gcsdoc <StorageBucketObjectName>)
|
||||
|
||||
<Date> ::=
|
||||
<Year>-<Month>-<Day> |
|
||||
@ -79,7 +79,7 @@
|
||||
(mileage <String>)|
|
||||
(name <String>)|
|
||||
(nickname <String>)|
|
||||
(note <NoteContent>)|
|
||||
(note <ContactNoteContent>)|
|
||||
(occupation <String>)|
|
||||
(prefix <String>)|
|
||||
(priority low|normal|high)
|
||||
|
||||
@ -60,6 +60,15 @@ Display the number of domains.
|
||||
gam print|show domains
|
||||
showitemcountonly
|
||||
```
|
||||
To retrieve the count with `showitemcountonly`:
|
||||
```
|
||||
Linux/MacOS
|
||||
count=$(gam print domains showitemcountonly)
|
||||
Windows PowerShell
|
||||
$count = & gam print domains showitemcountonly
|
||||
Windows Command Prompt
|
||||
for /f "delims=" %a in ('gam print domains showitemcountonly') do set count=%a
|
||||
```
|
||||
|
||||
## Create and delete domain aliases
|
||||
```
|
||||
@ -94,3 +103,12 @@ Display the number of domain aliases.
|
||||
gam print|show domainaliases|aliasdomains
|
||||
showitemcountonly
|
||||
```
|
||||
To retrieve the count with `showitemcountonly`:
|
||||
```
|
||||
Linux/MacOS
|
||||
count=$(gam print domainaliases showitemcountonly)
|
||||
Windows PowerShell
|
||||
$count = & gam print domainaliases showitemcountonly
|
||||
Windows Command Prompt
|
||||
for /f "delims=" %a in ('gam print domainaliases showitemcountonly') do set count=%a
|
||||
```
|
||||
|
||||
@ -25,14 +25,14 @@ start a new terminal session and reissue the command from above.
|
||||
## Executable, Manual
|
||||
|
||||
* Executable Archive, Manual, Linux/Google Cloud Shell
|
||||
- `gam-7.wx.yz-linux-x86_64-glibc2.35.tar.xz`
|
||||
- `gam-7.wx.yz-linux-x86_64-glibc2.36.tar.xz`
|
||||
- `gam-7.wx.yz-linux-x86_64-glibc2.39.tar.xz`
|
||||
- `gam-7.wx.yz-linux-x86_64-legacy.tar.xz`
|
||||
- Download the archive, extract the contents into some directory.
|
||||
- Start a terminal session.
|
||||
|
||||
* Executable Archive, Manual, Raspberry Pi/ChromeOS ARM devices
|
||||
- `gam-7.wx.yz-linux-arm64-glibc2.35.tar.xz`
|
||||
- `gam-7.wx.yz-linux-arm64-glibc2.36.tar.xz`
|
||||
- `gam-7.wx.yz-linux-arm64-glibc2.39.tar.xz`
|
||||
- `gam-7.wx.yz-linux-arm64-legacy.tar.xz`
|
||||
- Download the archive, extract the contents into some directory.
|
||||
@ -43,16 +43,26 @@ start a new terminal session and reissue the command from above.
|
||||
- Download the archive, extract the contents into some directory.
|
||||
- Start a terminal session.
|
||||
|
||||
* Executable Archive, Manual, Mac OS versions Sequoia - M3
|
||||
- `gam-7.wx.yz-macos15.4-arm64.tar.xz`
|
||||
* Executable Archive, Manual, Mac OS versions Sequoia - M2/M3
|
||||
- `gam-7.wx.yz-macos15.6-arm64.tar.xz`
|
||||
- Download the archive, extract the contents into some directory.
|
||||
- Start a terminal session.
|
||||
|
||||
* Executable Archive, Manual, Mac OS, versions Ventura, Sonoma, Sequoia - Intel
|
||||
* Executable Archive, Manual, Mac OS versions Tahoe - M2/M3/M4
|
||||
- `gam-7.wx.yz-macos26.0-arm64.tar.xz`
|
||||
- Download the archive, extract the contents into some directory.
|
||||
- Start a terminal session.
|
||||
|
||||
* Executable Archive, Manual, Mac OS, versions Ventura, Sonoma - Intel
|
||||
- `gam-7.wx.yz-macos13.7-x86_64.tar.xz`
|
||||
- Download the archive, extract the contents into some directory.
|
||||
- Start a terminal session.
|
||||
|
||||
* Executable Archive, Manual, Mac OS, versions Sequoia, Tahoe - Intel
|
||||
- `gam-7.wx.yz-macos15.6-x86_64.tar.xz`
|
||||
- Download the archive, extract the contents into some directory.
|
||||
- Start a terminal session.
|
||||
|
||||
* Executable Archive, Manual, Windows 64 bit
|
||||
- `gam-7.wx.yz-windows-x86_64.zip`
|
||||
- Download the archive, extract the contents into some directory.
|
||||
|
||||
21
wiki/GAM-Release-Process.md
Normal file
21
wiki/GAM-Release-Process.md
Normal file
@ -0,0 +1,21 @@
|
||||
# Steps to release a new GAM version
|
||||
1. In a final commit before release:
|
||||
- [src/gam/__init.py](https://github.com/GAM-team/GAM/blob/main/src/gam/__init__.py) `__version___` value should be updated to the new version.
|
||||
- [src/GamUpdate.txt](https://github.com/GAM-team/GAM/blob/main/src/GamUpdate.txt) should be updated with a high-level changelog.
|
||||
- [wiki/GamUpdates.md](https://github.com/GAM-team/GAM/blob/main/wiki/GamUpdates.md) should be updated with same high-level changelog.
|
||||
- [wiki/Version-and-Help.md](https://github.com/GAM-team/GAM/blob/main/wiki/Version-and-Help.md) should be updated with current version N.NN.NN
|
||||
- [wiki/How-to-Upgrade-Legacy-GAM-to-GAM7.md](https://github.com/GAM-team/GAM/blob/main/wiki/How-to-Upgrade-Legacy-GAM-to-GAM7.md) should be updated with current version N.NN.NN
|
||||
2. The [build.yaml](https://github.com/GAM-team/GAM/blob/main/.github/workflows/build.yml) Github Action for final commit should complete successfully and creating a new dated Draft release.
|
||||
- We should *NEVER* upload release files manually. Only release files automatically created and [attested](https://github.com/GAM-team/GAM/wiki/Verifying-a-GAM7-Build-is-Legitimate-and-Official#github-attestation-linuxmacoswindows) as created by the Github Action should be used.
|
||||
3. Edit the Draft release:
|
||||
- Create a new tag with the format: `vN.NN.NN` where N.NN.NN is the GAM release version.
|
||||
- name the release "GAM N.NN.NN" where N.NN.NN is the GAM release version.
|
||||
- Include the changelog details for the new version in details.
|
||||
- leave "Set as pre-release" unchecked and "Set as the latest release" checked.
|
||||
- Publish the release.
|
||||
|
||||
# TODO: Release Process Improvements
|
||||
- copying changelog between GamUpdate.txt, GamUpdates.md and release description is manual and tedious. Automate it.
|
||||
- copying version string from gam/__init__.py, changelogs and release details and tag in manual and tedious. Automate it.
|
||||
- See if we can block releases with binaries not uploaded by GitHub Actions to further secure release pipelines.
|
||||
|
||||
@ -15,7 +15,7 @@ The 27ft RV Jay drove his family to Niagara Falls this summer. They’re all sti
|
||||
some in full sentences 🙂
|
||||
|
||||
# Has something changed with Ross?
|
||||
He’s just older, 75 and counting.
|
||||
He’s just older, 76 and counting.
|
||||
|
||||
(Jay here, this is all I could get from Ross but he’s his usual awesome self helping admins in Chat and Groups forums as I write this and adding new features. Because some have asked, Ross is a real person. He is not an Advanced GenAI as rumours have claimed. 🙂)
|
||||
|
||||
@ -49,8 +49,8 @@ Both GAM7 and GAM-ADV versions use the same configuration file (gam.cfg), and cr
|
||||
# Help!!! Something went wrong!
|
||||
Well that’s not really a question but as ever, please reach out to either the GAM email support group:
|
||||
|
||||
[git.io/gam-group](http://git.io/gam-group)
|
||||
[GAM Discussion Forum](https://groups.google.com/forum/#!forum/google-apps-manager)
|
||||
|
||||
Or the Google Chat Space:
|
||||
|
||||
[git.io/gam-chat](http://git.io/gam-chat)
|
||||
[GAM Public Chat Room](GAM-Public-Chat-Room)
|
||||
@ -13,4 +13,4 @@ _Note: Chromebooks / Chrome OS devices should install GAM7 using [these instruct
|
||||
sudo apt update
|
||||
sudo apt install curl python3
|
||||
```
|
||||
7. [How to Install Advanced GAM](How-to-Install-Advanced-GAM)
|
||||
7. [How to Install GAM7](How-to-Install-GAM7)
|
||||
|
||||
@ -7,7 +7,7 @@ Chrome OS devices that [support Linux apps](https://support.google.com/chromeboo
|
||||
sudo apt update
|
||||
sudo apt install xz-utils
|
||||
```
|
||||
3. [How to Install Advanced GAM](How-to-Install-Advanced-GAM)
|
||||
3. [How to Install GAM7](How-to-Install-GAM7)
|
||||
|
||||
# Google cloud shell
|
||||
|
||||
|
||||
@ -10,6 +10,694 @@ 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
|
||||
|
||||
### 7.33.00
|
||||
|
||||
Added variable `developer_preview_apis` to `gam.cfg` that is a comma separated list of APIs requiring a Developer Preview key.
|
||||
Currently, `chat` is the only API that requires a Developer Preview key; it is required for the User Sections commands.
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Users-Chat#introduction
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Users-Chat#manage-chat-user-sections
|
||||
|
||||
### 7.32.07
|
||||
|
||||
Added option `includepermissionsforview published` to `gam <UserTypeEntity> print filelist` and
|
||||
`gam <UserTypeEntity> show fileinfo`. From the Drive API documentation:
|
||||
```
|
||||
Specifies which additional view's permissions to include in the response. Only published is supported.
|
||||
```
|
||||
|
||||
### 7.32.06
|
||||
|
||||
Added options to `gam <UserTypeEntity> copy drivefile ... copysubfiles` to limit copying
|
||||
to files whose `modifiedTime` meets specified requirements.
|
||||
* `start|starttime <Date>|<Time>` - If specified, `modifiedTime` must be >= the value
|
||||
* `end|endtime <Date>|<Time>` - If specified, `modifiedTime` must be <= the value
|
||||
* `range <Date>|<Time> <Date>|<Time>` - first value <= `modifiedTime` <= second value
|
||||
|
||||
### 7.32.05
|
||||
|
||||
Fixed bug in `gam <UserTypeEntity> print messages|threads ... headers <SMTPHeaderList>` where
|
||||
headers other than those specified in `<SMTPHeaderList>` were displayed.
|
||||
|
||||
Updated `gam info users <UserTypeEntity>` to display the following data when the Licensing API
|
||||
does not return data due to quota limits. Previously, no License data was displayed and
|
||||
there was no way to know if it was omitted due to API quota limits vs the user has no license?
|
||||
```
|
||||
Licenses: (1)
|
||||
Not available/incomplete
|
||||
```
|
||||
If a user has no licenses, this will be displayed.
|
||||
```
|
||||
Licenses: (0)
|
||||
```
|
||||
|
||||
You should use `license_skus = <SKUIDList>` in `gam.cfg` to list all of the licensing SKUs
|
||||
used in your workspace. Without this list, GAM has to make 70+ API calls to get the licenses
|
||||
for a user; this can cause quota limit errors.
|
||||
|
||||
### 7.32.04
|
||||
|
||||
Support for student groups in Google Classroom no longer requires Developer Preview membership.
|
||||
|
||||
Upgraded to OpenSSL 3.6.1.
|
||||
|
||||
### 7.32.03
|
||||
|
||||
Added option `template` as an additional formating option for `gam <UserTypeEntity> show signature`
|
||||
that displays just the HTML data; this simplifies capturing the data for use as input to GAM.
|
||||
```
|
||||
$ gam redirect stdout ./SigTemplate.html user user@domain.com show signature template
|
||||
$ more SigTemplate.html
|
||||
<div dir="ltr"><div dir="ltr"><div dir="ltr">
|
||||
<p style="margin:0px;font-size-adjust:none;font-stretch:normal;font-size:12px;line-height:normal;font-family:Monaco;color:rgb(0,0,0)">
|
||||
<span style="background-color:rgb(82,208,206)">{RT}Email: {Email}{/RT}</span></p>
|
||||
<p style="margin:0px;font-size-adjust:none;font-stretch:normal;font-size:12px;line-height:normal;font-family:Monaco;color:rgb(0,0,0)">
|
||||
<span style="background-color:rgb(82,208,206)">{RT}Phone: {Phone}{/RT}</span></p>
|
||||
<p style="margin:0px;font-size-adjust:none;font-stretch:normal;font-size:12px;line-height:normal;font-family:Monaco;color:rgb(0,0,0)">
|
||||
<span style="background-color:rgb(82,208,206)">{RT}Mobile: {Mobile}{/RT}</span></p>
|
||||
</div><br></div>\n</div>
|
||||
```
|
||||
|
||||
### 7.32.02
|
||||
|
||||
Added variable `oauth2_txt_lock_mode` to `gam.cfg`, the default is 644 and valid values are: 644, 664, 666.
|
||||
This value is used to set the file permissions on the `oauth2.txt.lock` file. In very special cases where
|
||||
daemon processes, e.g. Apache/httpd, are running GAM, the value 666 may have to be used.
|
||||
|
||||
### 7.32.01
|
||||
|
||||
Added option `(addcsvdata <FieldName> <String>)*` to `gam <CrOSTypeEntity> issuecommand command <CrOSCommand> csv`
|
||||
and `gam <CrOSTypeEntity> getcommand commandid <CommandID> csv` that adds additional columns of data to the CSV file output.
|
||||
* See: https://github.com/GAM-team/GAM/wiki/ChromeOS-Devices#bulk-action-example
|
||||
|
||||
### 7.32.00
|
||||
|
||||
Added option `verifyallowexternal` to `gam print cigroup-members|group-members` that causes
|
||||
GAM to only display external members in groups with `allowExternalMembers=False'.
|
||||
This option can be used to help verify that internal-only groups don't have external members.
|
||||
|
||||
Updated option `internaldomains` for the following commands:
|
||||
```
|
||||
gam info|print groups
|
||||
gam print|show group-members
|
||||
gam info|print cigroups
|
||||
gam print|show cigroup-members
|
||||
gam <UserTypeEntity> print|show filesharecounts
|
||||
```
|
||||
Which domains are considered internal domains:
|
||||
* `internaldomains all` - All of your workspace domains; this is the default
|
||||
* `internaldomains primary` - Your workspace primary domain
|
||||
* `internaldomains <DomainNameList>` - A list of domain names
|
||||
|
||||
Added option `csv` to `gam <CrOSTypeEntity> issuecommand command <CrOSCommand>`
|
||||
and `gam <CrOSTypeEntity> getcommand commandid <CommandID>` so that command details are displayed in CSV format.
|
||||
This can be used to log commands issued to devices and then monitor the results.
|
||||
|
||||
Added option `filemimetype category <MimeTypeNameList>` to `gam <UserTypeEntity> copy drivefile` to support
|
||||
copying of files based on their MimeType category.
|
||||
|
||||
Added option `attendeeslist` to `gam calendars <CalendarEntity> print events` and `gam <UserTypeEntity> print events`
|
||||
that causes GAM to display the attendee email addresses in a single column `attendeesList`; no attendee details
|
||||
are displayed. The email addresses are separated by `csv_output_field_delimiter` from `gam.cfg`.
|
||||
|
||||
Fixed bug in `gam sendemail ... replyto <EmailAddress>` that caused a message delivery error if
|
||||
`<EmailAddress>` did not include a domain name.
|
||||
|
||||
Added support for users's chat sections.
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Users-Chat#manage-chat-users-sections
|
||||
* This is in Deveoper Preview; you must have a `developer_preview_api_key` in `gam.cfg` to use these commands.
|
||||
|
||||
### 7.31.06
|
||||
|
||||
Added option `batchsize <Integer>` to `gam calendar <CalendarEntity> delete|purge events` and
|
||||
`gam <UserTypeEntity> delete|purge events <UserCalendarEntity>` that causes GAM to delete events
|
||||
with batch API calls rather than with individual API calls.
|
||||
|
||||
### 7.31.05
|
||||
|
||||
Added option `variables <RESearchPattern>` to `gam select section <SectionName> verify` and `gam config verify`
|
||||
that causes GAM to only display variables with names selected by `<RESearchPattern>`.
|
||||
```
|
||||
gam select School verify variables "^(customer|domain)"
|
||||
Section: School
|
||||
customer_id = C03abc123
|
||||
domain = school.edu
|
||||
|
||||
gam config verify variables 'dir'
|
||||
Section: DEFAULT
|
||||
cache_dir = ~/GamConfig/gamcache ; /Users/gamteam/GamConfig/gamcache
|
||||
config_dir = ~/GamConfig ; /Users/gamteam/GamConfig
|
||||
drive_dir = ~/GamWork ; /Users/gamteam/GamWork
|
||||
gmail_cse_incert_dir = ~/GmailCSE/Certs ; /Users/gamteam/GmailCSE/Certs
|
||||
gmail_cse_inkey_dir = ~/GmailCSE/Keys ; /Users/gamteam/GmailCSE/Keys
|
||||
input_dir = .
|
||||
```
|
||||
|
||||
### 7.31.04
|
||||
|
||||
Fixed bug in `gam report admin|chrome` that caused to events to not be displayed.
|
||||
|
||||
Updated `gam <UserTypeEntity> print|show messages|threads ... query <QueryGmail>` to display the query.
|
||||
|
||||
### 7.31.03
|
||||
|
||||
Due to the following Calendar API update, the `gam <UserTypeEntity> transfer calendars` command has been removed.
|
||||
* See: https://developers.google.com/workspace/calendar/release-notes#October_27_2025
|
||||
Data ownership can be transferred in the Google Calendar UI.
|
||||
|
||||
### 7.31.02
|
||||
|
||||
Added the following options to `gam <UserTypeEntity> copy drivefile`
|
||||
to limit copying to those files owned by selected users.
|
||||
* `copysubfilesownedby users <EmailAddressList>` - Only files owned by users in `<EmailAddressList>` are copied.
|
||||
* `copysubfilesownedby notusers <EmailAddressList>` - Only files not owned by users in `<EmailAddressList>` are copied.
|
||||
* `copysubfilesownedby regex <REMatchPattern>` - Only files owned by users whose email addresses match `<REMatchPattern>` are copied.
|
||||
* `copysubfilesownedby notregex <REMatchPattern>` - Only files owned by users whose email addresses do not match `<REMatchPattern>` are copied.
|
||||
|
||||
### 7.31.01
|
||||
|
||||
Code cleanup for `addcsvdata <FieldName> <String>`.
|
||||
|
||||
### 7.31.00
|
||||
|
||||
Fixed bug in `gam report chrome (user <UserItem>)|(select <UserTypeEntity>)` where no activities were returned.
|
||||
`report chrome` does not use the parameter `userKey=<EmailAddress>` as do other applications but requires
|
||||
parameter `filter DEVICE_USER==<EmailAddress>`.
|
||||
|
||||
Updated `gam report admin (user <UserItem>)|(select <UserTypeEntity>)` to use parameter `filter USER_EMAIL==<EmailAddress>`
|
||||
to display activiities affecting the user `<EmailAddress>`. Use option `userisactor` to use the parameter `userKey=<EmailAddress>`
|
||||
that displays activities where user `<EmailAddress>` executed the command that generated the activity.
|
||||
|
||||
Fixed bug in `gam print cros|filelist|users ... (addcsvdata <FieldName> <String>)+ formatjson` where the `addcsvdata` columns
|
||||
were not displayed but the additional field values were included in the JSON data. Now, the `addcsvdata` columns
|
||||
are displayed but the additional field values are only included in the JSON data when option `includdecsvdatainjson` is specified.
|
||||
|
||||
Added option `addcsvdata <FieldName> <String>` to `gam <UserTypeEntity> print cigroups|groups`
|
||||
that adds additional columns of data to the CSV file output.
|
||||
|
||||
Added option `addcsvdata <FieldName> <String>` to `gam <UserTypeEntity> print cigroupmembere|group-members`
|
||||
that adds additional columns of data to the CSV file output.
|
||||
|
||||
### 7.30.05
|
||||
|
||||
Added option `gmaileventtypes <NumberRangeList>` to `gam report gmail` that can be used to limit the event types displayed.
|
||||
```
|
||||
<NumberRange> ::= <Number>|(<Number>/<Number>)
|
||||
<NumberRangeList> ::= "<NumberRange>(,<NumberRange>)*"
|
||||
|
||||
gam report gmail user user@domain.com gmaileventtypes 1,10/11
|
||||
```
|
||||
* See: https://developers.google.com/workspace/admin/reports/v1/appendix/activity/gmail
|
||||
|
||||
Updated sorting of column headers in `gam report <ActivityApplicationName>`.
|
||||
|
||||
### 7.30.04
|
||||
|
||||
Updated `gam report gmail` to avoid the following error when incomplete start/end time information is provided.
|
||||
```
|
||||
ERROR: Invalid request: Start time and end time should both be provided, and the scan duration should not be greater than 30 days.
|
||||
```
|
||||
* No time information provided - GAM sets `range -30d today`
|
||||
* Only `start <Time>` provided - GAM sets `end <Time>+30d`
|
||||
* Only `end <Time>` provided - GAM sets `start <Time>-30d`
|
||||
|
||||
### 7.30.03
|
||||
|
||||
Updated `gam report <ActivityApplicationName>` to reflect the changes described here:
|
||||
* See: https://workspaceupdates.googleblog.com/2025/12/google-workspace-audit-log-api.html
|
||||
|
||||
Added option `resourcedetailsfilter <String>` to `gam report <ActivityApplicationName>` described here:
|
||||
* See: https://developers.google.com/workspace/admin/reports/reference/rest/v1/activities/list#query-parameters
|
||||
|
||||
For `gam <UserTypeEntity> print <Objects>`, expanded the list of `<Objects>` covered by `gam.cfg csv_output_users_audit = True`.
|
||||
|
||||
### 7.30.02
|
||||
|
||||
Added option `conferencedata meet <MeetID>` to `<EventAttribute>` that allows specifying
|
||||
a reference to an existing Google Meet when creating/updating a calendar event.
|
||||
|
||||
Upgraded to Python 3.14.2.
|
||||
|
||||
### 7.30.01
|
||||
|
||||
Fixed bug introduced in 7.30.00 that caused errors when reading CSV files.
|
||||
|
||||
Added the following options to `gam <UserTypeEntity> create focustime|outofoffice`:
|
||||
```
|
||||
((date yyyy-mm-dd)|
|
||||
(range yyyy-mm-dd yyyy-mm-dd)|
|
||||
(daily yyyy-mm-dd N)|
|
||||
(weekly yyyy-mm-dd N))
|
||||
```
|
||||
|
||||
Added the following options to `gam <UserTypeEntity> create focustime|outofoffice|workinglocation`:
|
||||
```
|
||||
noreminders|(reminder email|popup <Number>)+
|
||||
```
|
||||
|
||||
### 7.30.00
|
||||
|
||||
Added `input_dir` directory variable to `gam.cfg` that is used to select a directory for reading files with non-absolute file names.
|
||||
The default is an empty string that matches the current behavior where these files are read from the current working directory.
|
||||
This will be most useful in multiple domain situations where each domain will have distinct `drive_dir` and `input_dir` values.
|
||||
|
||||
Added support for the new resource calendar setting `autoAcceptInvitations`.
|
||||
|
||||
### 7.29.04
|
||||
|
||||
Updated `gam delete chromepolicy chrome.users.apps.InstallType ou <OrgUnitItem> appid <AppID>`
|
||||
to allow deleting an app, i.e., explicitly remove it from management. `<OrgUnitItem>` must specify where it was added for management.
|
||||
|
||||
### 7.29.03
|
||||
|
||||
Remove debugging message from `gam <UserTypeEntity> move drivefile <DriveFileEntity>`.
|
||||
|
||||
### 7.29.02
|
||||
|
||||
Fixed bug in `gam <UserTypeEntity> move drivefile <DriveFileEntity>` where the following options
|
||||
were only applied to top level files or folders re-created in the destination. Now, domain
|
||||
and email address mappings apply to all moved files/folders.
|
||||
```
|
||||
excludepermissionsfromdomains <DomainNameList>
|
||||
includepermissionsfromdomains <DomainNameList>
|
||||
mappermissionsdomain <DomainName> <DomainName>
|
||||
mappermissionsemail <EmailAddress> <EmailAddress>
|
||||
mappermissionsemailfile <CSVFileInput> endcsv
|
||||
```
|
||||
|
||||
Upgraded to Python 3.14.1.
|
||||
|
||||
### 7.29.01
|
||||
|
||||
Added option `oneitemperrow` to `gam <UserTypeEntity> print calendars ... permissions` to have each of a
|
||||
calendar's permissions displayed on a separate row with all of the other calendar fields.
|
||||
|
||||
Updated `gam yubikey reset_piv` to handle YubiKey firmware updates that caused an error.
|
||||
|
||||
### 7.29.00
|
||||
|
||||
Added options `mappermissionsemail <EmailAddress> <EmailAddress>` and ` mappermissionsemailfile <CSVFileInput> endcsv`
|
||||
to these commands:
|
||||
```
|
||||
gam [<UserTypeEntity>] copy shareddriveacls <SharedDriveEntity> to <SharedDriveEntity>
|
||||
gam [<UserTypeEntity>] sync shareddriveacls <SharedDriveEntity> with <SharedDriveEntity>
|
||||
gam <UserTypeEntity> copy drivefile <DriveFileEntity>
|
||||
gam <UserTypeEntity> move drivefile <DriveFileEntity>
|
||||
```
|
||||
When `mappermissionsemail <EmailAddress> <EmailAddress>` is specifed, an ACL that references the first `<EmailAddress>`
|
||||
in the source will be modified to reference the second `<EmailAddress>` in the destination.
|
||||
|
||||
Bulk permission email address mapping can be specified with `mappermissionsemailfile <CSVFileInput> endcsv`.
|
||||
`<CSVFileInput>` must include these columns: `sourceEmail` and `destinationEmail`.
|
||||
|
||||
These options will be most useful with inter-workspace Shared Drive copies and moves.
|
||||
|
||||
### 7.28.13
|
||||
|
||||
Added option `addcsvdata <FieldName> <String>` to `gam <UserTypeEntity> print messages`
|
||||
that adds additional columns of data to the CSV file output.
|
||||
|
||||
### 7.28.12
|
||||
|
||||
Updated `gam delete project` to handle the following error:
|
||||
```
|
||||
ERROR: 400: failedPrecondition - Project not active
|
||||
```
|
||||
|
||||
### 7.28.11
|
||||
|
||||
Removed all options/fields referencing inheritance from `gam create|update|info|print org` as this option/field is deprecated.
|
||||
|
||||
### 7.28.10
|
||||
|
||||
Added a command `gam print course-counts` that dsplays the count of the number of courses in which a teacher or student is a participant.
|
||||
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Classroom-Membership#display-course-counts-for-teachers-students
|
||||
|
||||
### 7.28.09
|
||||
|
||||
Fixed bug in `gam print cigroups ... descriptionmatchpattern [not] <REMatchPattern>` that caused a trap.
|
||||
|
||||
### 7.28.08
|
||||
|
||||
Updated `gam <UserTypeEntity> print|show chatmessages` to cache the sender UID to email address
|
||||
map so that each sender UID only has to be looked up once; this improves performance.
|
||||
|
||||
### 7.28.07
|
||||
|
||||
Fixed bug in `gam report users ... aggregatebydate|aggregatebyuser` where `accounts:used_quota_in_percentage` was incorrectly displayed.
|
||||
|
||||
### 7.28.06
|
||||
|
||||
Updated `gam <UserTypeEntity> info|print|show calendars` and
|
||||
`gam calendars <CalendarEntity> print|show settings` to display the
|
||||
new `dataOwner` field as described under `Additional details` below.
|
||||
|
||||
* See: https://workspaceupdates.googleblog.com/2025/11/secondary-calendar-management-with-dedicated-owners.html
|
||||
|
||||
### 7.28.04
|
||||
|
||||
Updated commands that display Chrome device counts to display the date in the output.
|
||||
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Chrome-Device-Counts
|
||||
|
||||
### 7.28.03
|
||||
|
||||
Improved commands to display Chrome device counts.
|
||||
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Chrome-Device-Counts
|
||||
|
||||
### 7.28.02
|
||||
|
||||
Added commands to display Chrome device counts.
|
||||
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Chrome-Device-Counts
|
||||
|
||||
### 7.28.01
|
||||
|
||||
Updated `gam <UserTypeEntity> show fileinfo <DriveFileEntity>` to display `displayName` as the key field
|
||||
of a `permission` not `deleted`.
|
||||
|
||||
### 7.28.00
|
||||
|
||||
Added option `addcsvdata <FieldName> <String>` to `gam report [usage] customers|users`
|
||||
that adds additional columns of data to the CSV file output. This will be most useful
|
||||
when reading a CSV of user information and you want to include some of the user information,
|
||||
e.g., orgUnitPath, in the output.
|
||||
```
|
||||
gam redirect csv ./Users.csv print users fields ou
|
||||
gam redirect csv ./UserStorageInfo.csv multiprocess csv Users.csv gam report users user "~primaryEmail" parameters accounts:drive_used_quota_in_mb,accounts:gmail_used_quota_in_mb,accounts:gplus_photos_used_quota_in_mb,accounts:total_quota_in_mb,accounts:used_quota_in_mb,accounts:used_quota_in_percentage addcsvdata orgUnitPath "~orgUnitPath"
|
||||
```
|
||||
|
||||
### 7.27.05
|
||||
|
||||
Added option `addcsvdata <FieldName> <String>` to `gam print courses`
|
||||
that adds additional columns of data to the CSV file output.
|
||||
|
||||
The following scope is no longer necessary: `Cloud Identity API - Groups Beta (Enables group locking/unlocking)`
|
||||
as this scope `Cloud Identity API - Groups` now provides group locking/unlocking.
|
||||
|
||||
### 7.27.04
|
||||
|
||||
Added options to `gam <UserTypeEntity> create delegate` that support
|
||||
sending email notifications when a user adds a delegate.
|
||||
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Users-Gmail-Delegates#delegation-notification
|
||||
|
||||
### 7.27.03
|
||||
|
||||
Updated `gam <UserTypeEntity> create|update|sync chatmember` role specification to `role member|manager|owner`.
|
||||
This is the mapping between the Chat UI and Chat API; GAM uses the Chat UI role names.
|
||||
```
|
||||
UI: Member, API: ROLE_MEMBER
|
||||
UI: Manager, API: ROLE_ASSISTANT_MANAGER
|
||||
UI: Owner, API: ROLE_MANAGER
|
||||
```
|
||||
|
||||
Updated `gam <UserTypeEntity> update chatspace` options for permission settings.
|
||||
```
|
||||
[managemembersandgroups owners|managers|members]
|
||||
[modifyspacedetails owners|managers|members]
|
||||
[togglehistory owners|managers|members]
|
||||
[useatmentionall owners|managers|members]
|
||||
[manageapps owners|managers|members]
|
||||
[managewebhooks owners|managers|members]
|
||||
[replymessages owners|managers|members]
|
||||
```
|
||||
|
||||
### 7.27.02
|
||||
|
||||
Added option `clearattachments <String>` to `gam [<UserTypeMessage>] update chatmessage`
|
||||
that clears all attachments from a Chat message. If `<ChatContent>` is not specified,
|
||||
the current message text is retained and `<String>` is appended; `<String>` must be specified
|
||||
but can be empty in which case the current message test is preserved as-is.
|
||||
|
||||
### 7.27.01
|
||||
|
||||
Fixed bug in `gam <UserTypeEntity> claim ownership <DriveFileEntity> ... onlyUsers|skipusers <UserTypeEntity>`
|
||||
where the email addresses in `onlyUsers|skipusers <UserTypeEntity>` were not normalized.
|
||||
|
||||
### 7.27.00
|
||||
|
||||
Added `debug_redaction` Boolean variable to `gam.cfg`. When True, the default,
|
||||
sensitive data like access/refresh tokens, client secret and authorization codes
|
||||
are redacted from debug output. This allows you to post debug output without
|
||||
compromising your account information. Even with debug redaction,
|
||||
anything shared publicly should be double-checked for sensitive content.
|
||||
|
||||
### 7.25.01
|
||||
|
||||
Fixed bug in `gam config timezone <String>` to handle timezone abbreviations correctly;
|
||||
they were incorrectly shifted to lowercase.
|
||||
|
||||
### 7.25.00
|
||||
|
||||
Removed a capabilty added in 7.24.00 that allowed reading command data from Google Docs and Sheets
|
||||
when a user's service account access to Drive and Sheets had been disabled. Jay was concerned
|
||||
that this change could be exploited to give access to all user's files.
|
||||
|
||||
This capability has been replaced by issuing the following commands. The admin specified in `gam oauth create`
|
||||
can read command data from Docs and Sheets to which it has access.
|
||||
```
|
||||
gam config commanddata_clientaccess true save
|
||||
gam oauth create
|
||||
Enable the following and proceed to authorization.
|
||||
|
||||
[*] 42) Drive API - commanddata_clientaccess
|
||||
[*] 54) Sheets API - commanddata_clientaccess
|
||||
```
|
||||
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Command-Data-From-Google-Docs-Sheets-Storage#limited-service-account-access
|
||||
|
||||
Fixed in bug in `gam report` that caused a trap with either of the `thismonth` or `previousmonths` options were used.
|
||||
|
||||
Upgraded to Python 3.14.0.
|
||||
|
||||
### 7.24.01
|
||||
|
||||
Updated GAM to handle the following error that occurs when GAM tries to authenticate
|
||||
as a user that has been disabled by Google.
|
||||
```
|
||||
ERROR: Authentication Token Error - invalid_account: Forbidden
|
||||
```
|
||||
|
||||
### 7.24.00
|
||||
|
||||
If you want to disable a user's service account access to Drive and Sheets but still allow reading command data from Google Docs and Sheets,
|
||||
issue the following command and make these settings:
|
||||
```
|
||||
gam user user@domain.com update serviceaccount
|
||||
|
||||
[ ] 20) Drive API (supports readonly)
|
||||
[*] 21) Drive API - read command data
|
||||
[ ] 42) Sheets API (supports readonly)
|
||||
[*] 43) Sheets API - read command data
|
||||
```
|
||||
|
||||
### 7.23.07
|
||||
|
||||
Fixed bug in `gam print|show admins` where all admin assignments were not displayed when
|
||||
`types <AdminAssigneeTypeList>` was not specified, i.e., all assignments should be displayed.
|
||||
|
||||
### 7.23.06
|
||||
|
||||
Added option `types <AdminAssigneeTypeList>` to `gam print|show admins` that allows filtering
|
||||
of admin assignments by the type of the assignee; by default, all assignee types are displayed.
|
||||
```
|
||||
<AdminAssigneeType> ::= group|user|serviceaccount|unknown
|
||||
<AdminAssigneeTypeList> ::= "<AdminAssigneeType>(,<AdminAssigneeType>)*"
|
||||
```
|
||||
|
||||
### 7.23.05
|
||||
|
||||
Added option `recursive` to `gam print|show admins` that will display assignments to the members
|
||||
of security groups assigned to roles; the security group membership is recursively expanded.
|
||||
|
||||
### 7.23.04
|
||||
|
||||
Added option `addcsvdata <FieldName> <String>` to `gam <UserTypeEntity> print events`
|
||||
and `gam calendars <CalendarEntity> print events` that adds additional columns of data to the CSV file output.
|
||||
An example would be to get the calendar name in addition to the calendar ID when printing events.
|
||||
```
|
||||
gam redirect csv ./Resources.csv print resources fields email,name
|
||||
gam redirect csv ./ResourceEventCounts.csv multiprocess redirect stderr - multiprocess csv Resources.csv gam calendar "~resourceEmail" print events starttime -1y countsonly addcsvdata calendarName "~resourceName"
|
||||
```
|
||||
|
||||
Upgraded to OpenSSL 3.6.0.
|
||||
|
||||
### 7.23.03
|
||||
|
||||
Upgraded to OpenSSL 3.5.4.
|
||||
|
||||
### 7.23.02
|
||||
|
||||
Added option `oneitemperrow` to 'gam print course-materials|course-work` to have each of a
|
||||
course's materials displayed on a separate row with all of the other course fields.
|
||||
This produces a CSV file that can be used in subsequent commands to process the materials without further script processing.
|
||||
|
||||
### 7.23.00
|
||||
|
||||
Added `chat_max_results` variable to `gam.cfg`.
|
||||
```
|
||||
chat_max_results
|
||||
When retrieving lists of Chat items from API,
|
||||
how many should be retrieved in each API call
|
||||
Default: 100
|
||||
Range: 1 - 1000
|
||||
```
|
||||
Previously, this vaule was always set to 1000 which could cause errors.
|
||||
|
||||
### 7.22.07
|
||||
|
||||
Added options `showdetails` and `returnidonly` to `gam create|copy vaultquery`.
|
||||
|
||||
Added option `<JSONData>` to `gam create vaultexport|vaultquery and `gam print vaultcounts``.
|
||||
|
||||
### 7.22.06
|
||||
|
||||
Added commands to create, copy and delete Vault saved queries.
|
||||
```
|
||||
gam create vaultquery <MatterItem> [name <String>]
|
||||
corpus calendar|drive|gemini|groups|hangouts_chat|mail|voice
|
||||
[scope all_data|held_data|unprocessed_data]
|
||||
(accounts <EmailAddressEntity>) | (orgunit|org|ou <OrgUnitPath>) | everyone
|
||||
(documentids (<DriveFileIDList>|(select <FileSelector>|<CSVFileSelector>))) |
|
||||
(shareddrives|teamdrives (<SharedDriveIDList>|(select <FileSelector>|<CSVFileSelector>))) |
|
||||
[(includeshareddrives <Boolean>)|(shareddrivesoption included|included_if_account_is_not_a_member|not_included)]
|
||||
(sitesurl (<URLList>||(select <FileSelector>|<CSVFileSelector>)))
|
||||
[driveversiondate <Date>|<Time>]
|
||||
[includerooms <Boolean>]
|
||||
(rooms (<ChatSpaceList>|(select <FileSelector>|<CSVFileSelector>))) |
|
||||
[terms <String>] [start|starttime <Date>|<Time>] [end|endtime <Date>|<Time>] [timezone <TimeZone>]
|
||||
[locationquery <StringList>] [peoplequery <StringList>] [minuswords <StringList>]
|
||||
[responsestatuses <AttendeeStatus>(,<AttendeeStatus>)*] [calendarversiondate <Date>|<Time>]
|
||||
(covereddata calllogs|textmessages|voicemails)*
|
||||
[shownames] [formatjson]
|
||||
|
||||
gam copy vaultquery <MatterItem> <QueryItem> [targetmatter <MatterItem"] [name <String>]
|
||||
[shownames] [formatjson]
|
||||
|
||||
gam delete vaultquery <QueryItem> matter <MatterItem>
|
||||
gam delete vaultquery <MatterItem> <QueryItem>
|
||||
```
|
||||
|
||||
Added a variant of `gam print vaultcounts` that gets its query parameters from a saved Vault query.
|
||||
```
|
||||
gam print vaultcounts [todrive <ToDriveAttributes>*]
|
||||
matter <MatterItem> <QueryItem>
|
||||
[wait <Integer>]
|
||||
```
|
||||
|
||||
### 7.22.05
|
||||
|
||||
Added a variant of `gam create vaultexport` that gets its query parameters from a saved Vault query.
|
||||
|
||||
```
|
||||
gam create vaultexport|export matter <MatterItem> [name <String>]
|
||||
vaultquery <QueryItem>
|
||||
[driveclientsideencryption any|encrypted|unencrypted]
|
||||
[includeaccessinfo <Boolean>]
|
||||
[excludedrafts <Boolean>] [mailclientsideencryption any|encrypted|unencrypted]
|
||||
[showconfidentialmodecontent <Boolean>] [usenewexport <Boolean>] [exportlinkeddrivefiles <Boolean>]
|
||||
[format ics|mbox|pst|xml]
|
||||
[region any|europe|us] [showdetails|returnidonly]
|
||||
```
|
||||
|
||||
### 7.22.04
|
||||
|
||||
Added a variant of `gam create vaulthold` that gets its parameters from a saved Vault query.
|
||||
```
|
||||
gam create vaulthold matter <MatterItem> [name <String>]
|
||||
vaultquery <QueryItem>
|
||||
[showdetails|returnidonly]
|
||||
```
|
||||
|
||||
### 7.22.03
|
||||
|
||||
Fix backwards compatability bug introduced in 7.22.00 for `gam print users` that changed `suspended`
|
||||
from a field name to a query option; it is now correctly interpreted as a field name.
|
||||
|
||||
### 7.22.02
|
||||
|
||||
An update to the httplib2 library caused GAM proxy connections to fail; this has been fixed
|
||||
by including the pysocks library needed by the latest httplib2 library.
|
||||
|
||||
### 7.22.00
|
||||
|
||||
Expanded `<UserTypeEntity>` to allow specification of non-archived/archived users.
|
||||
* See [Collections of Users](Collections-of-Users)
|
||||
|
||||
These commands have also been updated to deal with archived users:
|
||||
* `gam print aliases`
|
||||
* `gam update groups`
|
||||
* `gam info orgs`
|
||||
* `gam print orgs`
|
||||
* `gam print users`
|
||||
|
||||
Added `datetime <DateTimeFormat>` command that can be embedded in Gam batch files.
|
||||
The current time is formatted with `<DateTimeFormat>` and subsequent lines in `<BatchContent>`
|
||||
will have `%datetime%` replaced with the formatted time value.
|
||||
|
||||
See: https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes
|
||||
|
||||
### 7.21.03
|
||||
|
||||
Added option `notifyrecoveryemail` to `gam create user` and `gam <UserTypeEntity> update user password <String>`
|
||||
that sends the passsword notification email to the user's recovery email address (if defined).
|
||||
|
||||
### 7.21.02
|
||||
|
||||
GAM now builds on macOS 26 Tahoe and properly identifies the OS.
|
||||
|
||||
A custom build of the cryptography library is no longer needed for Windows arm64 builds as the project now releases their own build for the OS.
|
||||
|
||||
Upgrade to OpenSSL 3.5.3 latest
|
||||
|
||||
### 7.21.01
|
||||
|
||||
Replaced datetime, dateutil, calendar and iso8601 Python libraries with arrow library.
|
||||
This should have no performance impact; report any problems.
|
||||
|
||||
You can now use timezone names when setting `timezone` in `gam.cfg`.
|
||||
* See: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
||||
```
|
||||
gam config timezone America/Los_Angeles save
|
||||
```
|
||||
|
||||
### 7.20.04
|
||||
|
||||
Cleaned up Python library imports: googleapiclient, iso8601
|
||||
|
||||
### 7.20.03
|
||||
|
||||
Rebranded license SKU `1010470004` from `Gemini Education` to `Google AI Pro for Education`.
|
||||
|
||||
Additional updates to student groups in Google Classroom.
|
||||
|
||||
### 7.20.02
|
||||
|
||||
Upgraded `gam create course-studentgroups` to allow specification of multiple student group titles;
|
||||
multiple student groups can be created in a single command.
|
||||
* `((title <String>)|(select <StringEntity))+`
|
||||
|
||||
### 7.20.01
|
||||
|
||||
Added option `showaccesssettings` to `gam [<UserTypeEntity>] print|show chatspaces`. When listing
|
||||
Chat Spaces, the Chat API does not return the `accessSettings` field; if you need to see this field,
|
||||
add `showaccesssettings` to the command. This requires an additional Chat API call per chat space of type `SPACE`
|
||||
to get the `accessSettings` field.
|
||||
|
||||
### 7.20.00
|
||||
|
||||
Added initial support for student groups in Google Classroom. This is a work in progress and requires Developer Preview membership.
|
||||
* See: https://github.com/GAM-team/GAM/wiki/Classroom-StudentGroups#notes
|
||||
|
||||
### 7.19.03
|
||||
|
||||
Fixed bug where specifying a `<UserItem>` as `id:1234567890` could lead to errors like this:
|
||||
```
|
||||
ERROR: 400: invalidArgument - Resource name has invalid email.
|
||||
```
|
||||
|
||||
### 7.19.02
|
||||
|
||||
Update `gam info user <UserItem>` to eliminate 5 second delay when getting license info.
|
||||
@ -1858,7 +2546,7 @@ number of domain aliasess on stdout; no CSV file is written.
|
||||
|
||||
Added option `showitemcountonly` to `gam print domains` that causes GAM to display the
|
||||
number of domains on stdout; no CSV file is written.
|
||||
|
||||
|
||||
### 6.77.16
|
||||
|
||||
Fixed bug in `gam <UserTypeEntity> print filelist` that caused a trap.
|
||||
@ -2353,7 +3041,7 @@ Added option `showmimetype category <MimeTypeNameList>` to `gam <UserTypeEntity>
|
||||
<MimeTypeName> ::= application|audio|font|image|message|model|multipart|text|video
|
||||
<MimeTypeNameList> ::= "<MimeTypeName>(,<MimeTypeName>)*"
|
||||
|
||||
gam user user@domain.com print filelist fields id,name,mimetype showmimetype prefixes audio,video
|
||||
gam user user@domain.com print filelist fields id,name,mimetype showmimetype category audio,video
|
||||
```
|
||||
|
||||
### 6.71.11
|
||||
@ -2622,7 +3310,7 @@ Batch processing will suspend for `<Integer>` seconds before the next command li
|
||||
|
||||
Added the following options to `<PermissionMatch>` that allow more powerful matching.
|
||||
```
|
||||
nottype <DriveFileACLType>
|
||||
nottype <DriveFileACLType>
|
||||
typelist <DriveFileACLTypeList>
|
||||
nottypelist <DriveFileACLTypeList>
|
||||
rolelist <DriveFileACLRoleList>
|
||||
@ -3274,7 +3962,7 @@ Added support for Google Workspace Labs license.
|
||||
|
||||
### 6.64.10
|
||||
|
||||
Fixed bug introduced in 6.64.09 that caused a trap when `gam redirect csv <FileName> multiprocess` was used.
|
||||
Fixed bug introduced in 6.64.09 that caused a trap when `gam redirect csv <FileName> multiprocess` was used.
|
||||
|
||||
### 6.64.09
|
||||
|
||||
|
||||
@ -49,7 +49,7 @@ For calendars, there is an option to indicate whether to release resources for f
|
||||
A `<TransferID>` is returned which can be used to monitor the progress of the transfer.
|
||||
|
||||
NOTE: For calendars, the behaviour is not sufficiently defined in the API documentation.
|
||||
As of 2020-06-10, background transfers only transfer future non-private events with at least one guest/resource.
|
||||
Background transfers only transfer future non-private events with at least one guest/resource.
|
||||
|
||||
The option `<ParameterKey> <ParameterValue>` is for future expansion.
|
||||
|
||||
|
||||
@ -92,6 +92,38 @@ See [Collections of Items](Collections-of-Items)
|
||||
Group membership commands involve specifying collections of users;
|
||||
for `<UserTypeEntity>`, see: [Collections of Users](Collections-of-Users)
|
||||
|
||||
### Select users based on archived state
|
||||
When adding, deleting or synchronizing group members, to select only archived or non-archived users, use the following`<UserTypeEntity>`:
|
||||
```
|
||||
(all users_na|users_arch)|
|
||||
(domains_na|domains_arch <DomainNameList>)|
|
||||
(group_na|group_arch <GroupItem>)|
|
||||
(groups_na|groups_arch <GroupList>)|
|
||||
(group_users_na|group_users_arch <GroupList>
|
||||
[members] [managers] [owners]
|
||||
[primarydomain] [domains <DomainNameList>] [recursive|includederivedmembership] end)|
|
||||
(ou_na|ou_arch <OrgUnitItem>)|
|
||||
(ou_and_children_na|ou_and_children_arch <OrgUnitItem>)|
|
||||
(ous_na|ous_arch <OrgUnitList>)|
|
||||
(ous_and_children_na|ous_and_children_arch <OrgUnitList>)
|
||||
```
|
||||
|
||||
When adding, deleting or synchronizing group members, the `notarchived|archived` option can be used to select
|
||||
users in a particular archived state. This option can be used with the following `<UserTypeEntity>`:
|
||||
```
|
||||
(all users)|
|
||||
(domains <DomainNameList>)|
|
||||
(group <GroupItem>)|
|
||||
(groups <GroupList>)|
|
||||
(group_users <GroupList>
|
||||
[members] [managers] [owners]
|
||||
[primarydomain] [domains <DomainNameList>] [recursive|includederivedmembership] end)|
|
||||
(ou <OrgUnitItem>)|
|
||||
(ou_and_children <OrgUnitItem>)|
|
||||
(ous <OrgUnitList>)|
|
||||
(ous_and_children <OrgUnitList>)
|
||||
```
|
||||
|
||||
### Select users based on suspension state
|
||||
When adding, deleting or synchronizing group members, to select only suspended or non-suspended users, use the following`<UserTypeEntity>`:
|
||||
```
|
||||
@ -124,25 +156,6 @@ users in a particular suspension state. This option can be used with the followi
|
||||
(ous_and_children <OrgUnitList>)
|
||||
```
|
||||
|
||||
### Select users based on archived state
|
||||
When adding, deleting or synchronizing group members, the `notarchived|archived` option can be used to select
|
||||
users in a particular archived state. This option can be used with the following `<UserTypeEntity>`:
|
||||
```
|
||||
(all users|users_ns|users_susp|users_ns_susp)|
|
||||
(domains|domains_ns|domains_susp <DomainNameList>)|
|
||||
(group|group_ns|group_susp <GroupItem>)|
|
||||
(groups|groups_ns|groups_susp <GroupList>)|
|
||||
(group_users|group_users_ns|group_users_susp <GroupList>
|
||||
[members] [managers] [owners]
|
||||
[primarydomain] [domains <DomainNameList>] [recursive|includederivedmembership] end)|
|
||||
(ou|ou_ns|ou_susp <OrgUnitItem>)|
|
||||
(ou_and_children|ou_and_children_ns|ou_and_children_susp <OrgUnitItem>)|
|
||||
(ous|ous_ns|ous_susp <OrgUnitList>)|
|
||||
(ous_and_children|ous_and_children_ns|ous_and_children_susp <OrgUnitList>)|
|
||||
(query <QueryUser>)|
|
||||
(queries <QueryUserList>)
|
||||
```
|
||||
|
||||
## Add members to a group
|
||||
```
|
||||
gam update group|groups <GroupEntity> create|add [<GroupRole>]
|
||||
@ -275,6 +288,11 @@ For `notarchived|archived`, see: [Select users based on archived state](#select-
|
||||
|
||||
The `notsuspended|suspended` and `notarchived|archived` not only control what users are selected from `<UserTypeEntity>`
|
||||
but they also control what users are selected from `<GroupEntity>`.
|
||||
* `notsuspended` - Select only non-suspended members
|
||||
* `suspended` - Select only suspended members
|
||||
* `notarchived` - Select only non-archived members
|
||||
* `archived` - Select only archived users
|
||||
* `notsuspended notarchived` - Select non-suspended and non-archived members
|
||||
|
||||
The `remove_domain_nostatus_members` option is used to remove members from the group that are in your domain but have no status.
|
||||
These members were added to the group before the user or group that they represent was created.
|
||||
@ -373,10 +391,7 @@ By default, when clearing members from a group, all members, whether suspended/a
|
||||
* `suspended` - Clear only suspended members
|
||||
* `notarchived` - Clear only non-archived members
|
||||
* `archived` - Clear only archived users
|
||||
* `notsuspended notarchived` - Do not clear suspended and archived members
|
||||
* `suspended archived` - Clear suspended and archived members
|
||||
* `notsuspended archived` - Do not clear archived members
|
||||
* `suspended notarchived` - Do not clear suspended members
|
||||
* `notsuspended notarchived` - Clear non-suspended and non-archived members
|
||||
|
||||
Members that have met the above qualifications to be cleared can be further qualifed by their email address.
|
||||
* `emailclearpattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will be cleared; others will be retained
|
||||
@ -417,19 +432,12 @@ When `<UserTypeEntity>` specifies a group or groups:
|
||||
* `usersonly` - Only the user members from the specified groups are added
|
||||
* `groupsonly` - Only the group members from the specified groups are added
|
||||
|
||||
By default, when updating members from organization units, all users, whether suspended or not, are included.
|
||||
* `notsuspended` - Do not include suspended users
|
||||
* `suspended` - Only include suspended users
|
||||
|
||||
By default, when updating members from groups, all users, whether suspended/archived or not, are included.
|
||||
* `notsuspended` - Do not include suspended users
|
||||
* `suspended` - Only include suspended users
|
||||
* `notarchived` - Do not include archived users
|
||||
* `archived` - Only include archived users
|
||||
* `notsuspended notarchived` - Do not include suspended and archived users
|
||||
* `suspended archived` - Include only suspended or archived users
|
||||
* `notsuspended archived` - Only include archived users
|
||||
* `suspended notarchived` - Only include suspended users
|
||||
By default, when updating members from groups/organization units, all users, whether suspended/archived or not, are included.
|
||||
* `notsuspended` - Update only non-suspended members
|
||||
* `suspended` - Update only suspended members
|
||||
* `notarchived` - Update only non-archived members
|
||||
* `archived` - Update only archived users
|
||||
* `notsuspended notarchived` - Update non-suspended and non-archived members
|
||||
|
||||
You can set the `delivery` option for the updated members:
|
||||
* `allmail` - All messages, delivered as soon as they arrive
|
||||
@ -607,7 +615,7 @@ gam print group-members [todrive <ToDriveAttribute>*]
|
||||
[descriptionmatchpattern [not] <REMatchPattern>]
|
||||
[admincreatedmatch <Boolean>]
|
||||
[roles <GroupRoleList>] [members] [managers] [owners]
|
||||
[internal] [internaldomains <DomainNameList>] [external]
|
||||
[internal] [internaldomains all|primary|<DomainNameList>] [external]
|
||||
[membernames] [showdeliverysettings]
|
||||
<MembersFieldName>* [fields <MembersFieldNameList>]
|
||||
[notsuspended|suspended] [notarchived|archived]
|
||||
@ -618,6 +626,7 @@ gam print group-members [todrive <ToDriveAttribute>*]
|
||||
[(recursive [noduplicates])|includederivedmembership] [nogroupemail]
|
||||
[peoplelookup|(peoplelookupuser <EmailAddress>)]
|
||||
[unknownname <String>] [cachememberinfo [Boolean]]
|
||||
(addcsvdata <FieldName> <String>)*
|
||||
[formatjson [quotechar <Character>]]
|
||||
```
|
||||
By default, the group membership of all groups in the account are displayed, these options allow selection of subsets of groups:
|
||||
@ -673,13 +682,21 @@ By default, when displaying members from a group, all members, whether suspended
|
||||
* `notsuspended archived` - Only include archived members, this is not common but allows creating groups that allow easy identification of archived users
|
||||
* `suspended notarchived` - Only include suspended members, this is not common but allows creating groups that allow easy identification of suspended users
|
||||
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal internaldomains <DomainNameList>` - Display members whose domain is in `<DomainNameList>`
|
||||
* `external internaldomains <DomainNameList>` - Display members whose domain is not in `<DomainNameList>`
|
||||
* `internal external internaldomains <DomainNameList>` - Display all members, indicate their category: internal or external
|
||||
* `internaldomains <DomainNameList>` - Defaults to value of `domain` in `gam.cfg`
|
||||
Which domains are considered internal domains:
|
||||
* `internaldomains all` - All of your workspace domains; this is the default
|
||||
* `internaldomains primary` - Your workspace primary domain
|
||||
* `internaldomains <DomainNameList>` - A list of domain names
|
||||
|
||||
Members without an email address, e.g. `customer`, are considered internal.
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal` - Display members whose domain matches a value in `internaldomains`
|
||||
* `external` - Display members whose domain does not match value in `internaldomains`
|
||||
* `internal external` - Display all members, indicate their category: `internal` or `external`
|
||||
|
||||
Members without an email address, e.g. `customer`, are considered `internal`.
|
||||
|
||||
When the `internal` or `external` options are specified, GAM adds the column `allowExternalMembers`
|
||||
that shows that setting for the group and adds the column `category` that shows whether the member
|
||||
is `external` or `internal`.
|
||||
|
||||
Members that have met the above qualifications to be displayed can be further qualifed by their email address.
|
||||
* `memberemaildisplaypattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will be displayed; others will not be displayed
|
||||
@ -725,6 +742,9 @@ The options `recursive noduplicates` and `includederivedmembership types user no
|
||||
The `includederivedmembership` option makes less API calls but doesn't show level and subgroup information.
|
||||
Expanding a member of type CUSTOMER may produce a large volume of data as it will display all users in your domain.
|
||||
|
||||
Add additional columns of data from the command line to the output
|
||||
* `addcsvdata <FieldName> <String>`
|
||||
|
||||
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.
|
||||
|
||||
@ -744,7 +764,7 @@ gam show group-members
|
||||
[descriptionmatchpattern [not] <REMatchPattern>]
|
||||
[admincreatedmatch <Boolean>]
|
||||
[roles <GroupRoleList>] [members] [managers] [owners] [depth <Number>]
|
||||
[internal] [internaldomains <DomainNameList>] [external]
|
||||
[internal] [internaldomains all|primary|<DomainNameList>] [external]
|
||||
[notsuspended|suspended] [notarchived|archived]
|
||||
[types <GroupMemberTypeList>]
|
||||
[memberemaildisplaypattern|memberemailskippattern <REMatchPattern>]
|
||||
@ -797,232 +817,17 @@ By default, when displaying members from a group, all members, whether suspended
|
||||
By default, all types of members (customer, group, user) in the group are displayed; this option modifies that behavior:
|
||||
* `types <GroupMemberTypeList>` - Display specified types
|
||||
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal internaldomains <DomainNameList>` - Display members whose domain is in `<DomainNameList>`
|
||||
* `external internaldomains <DomainNameList>` - Display members whose domain is not in `<DomainNameList>`
|
||||
* `internal external internaldomains <DomainNameList>` - Display all members, indicate their category: internal or external
|
||||
* `internaldomains <DomainNameList>` - Defaults to value of `domain` in `gam.cfg`
|
||||
Which domains are considered internal domains:
|
||||
* `internaldomains all` - All of your workspace domains; this is the default
|
||||
* `internaldomains primary` - Your workspace primary domain
|
||||
* `internaldomains <DomainNameList>` - A list of domain names
|
||||
|
||||
Members without an email address, e.g. `customer`, are considered internal.
|
||||
|
||||
Members that have met the above qualifications to be displayed can be further qualifed by their email address.
|
||||
* `memberemaildisplaypattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will be displayed; others will not be displayed
|
||||
* `memberemailskippattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will not be displayed; others will be displayed
|
||||
|
||||
By default, members of type GROUP are recursively expanded to show their constituent members. (Members of
|
||||
type CUSTOMER are not expanded.) The `depth <Number>` argument controls the depth to which nested groups are displayed.
|
||||
* `depth -1` - all groups in the selected group and below are displayed; this is the default.
|
||||
* `depth 0` - the groups within a selected group are displayed, no descendants are displayed.
|
||||
* `depth N` - the groups within the selected group and those groups N levels below the selected group are displayed.
|
||||
|
||||
The `includederivedmembership` option causes the API to expand type GROUP and type CUSTOMER
|
||||
members to display their constituent members while still displaying the original member.
|
||||
|
||||
The options `types user` and `includederivedmembership types user` return the same list of users.
|
||||
The `includederivedmembership` option makes less API calls but doesn't show hierarchy.
|
||||
Expanding a member of type CUSTOMER may produce a large volume of data as it will display all users in your domain.
|
||||
|
||||
### Display group structure
|
||||
To see a group's structure of nested groups use the `type group` option.
|
||||
```
|
||||
$ gam show group-members group testgroup5 types group
|
||||
Group: testgroup5@domain.com
|
||||
MEMBER, GROUP, testgroup1@domain.com, ACTIVE
|
||||
MEMBER, GROUP, testgroup2@domain.com, ACTIVE
|
||||
MEMBER, GROUP, testgroup3@domain.com, ACTIVE
|
||||
MEMBER, GROUP, testgroup2@domain.com, ACTIVE
|
||||
MEMBER, GROUP, testgroup4@domain.com, ACTIVE
|
||||
```
|
||||
To show the structure of all groups you can do the following; it will be time consuming for a large number of groups.
|
||||
```
|
||||
gam redirect stdout ./groups.txt show group-members types group
|
||||
```
|
||||
|
||||
### Examples
|
||||
#### Print a CSV of all members of a group regardless of role, all fields
|
||||
```
|
||||
gam print group-members <GroupEntity>
|
||||
```
|
||||
#### Print a CSV containing all managers emails
|
||||
```
|
||||
gam print group-members <GroupEntity> role manager fields email
|
||||
```
|
||||
#### Print a CSV output of all members and their emails only
|
||||
```
|
||||
gam print group-members <GroupEntity> role member fields email
|
||||
```
|
||||
#### Display group owners in your domain, but excluding groups where the email starts with a 4 digit code
|
||||
```
|
||||
gam print group-members domain <Your Domain> emailmatchpattern not '^1234.*' roles owners
|
||||
```
|
||||
|
||||
|
||||
These options further limit the list of groups selected above:
|
||||
* `emailmatchpattern <REMatchPattern>` - Limit display to groups whose email address matches `<REMatchPattern>`
|
||||
* `emailmatchpattern not <REMatchPattern>` - Limit display to groups whose email address does not match `<REMatchPattern>`
|
||||
* `namematchpattern <REMatchPattern>` - Limit display to groups whose name matches `<REMatchPattern>`
|
||||
* `namematchpattern not <REMatchPattern>` - Limit display to groups whose name does not match `<REMatchPattern>`
|
||||
* `descriptionmatchpattern <REMatchPattern>` - Limit display to groups whose description matches `<REMatchPattern>`
|
||||
* `descriptionmatchpattern not <REMatchPattern>` - Limit display to groups whose description does not match `<REMatchPattern>`
|
||||
* `admincreatedmatch True` - Limit display to groups created by administrators
|
||||
* `admincreatedmatch False` - Limit display to groups created by users
|
||||
|
||||
By default, all members, managers and owners in the group are displayed; these options modify that behavior:
|
||||
* `roles <GroupRoleList>` - Display specified roles
|
||||
* `members` - Display members
|
||||
* `managers` - Display managers
|
||||
* `owners` - Display owners
|
||||
|
||||
By default, all types of members (customer, group, user) in the group are displayed; this option modifies that behavior:
|
||||
* `types <GroupMemberTypeList>` - Display specified types
|
||||
|
||||
By default, members that are groups are displayed as a single entry of type GROUP; this option recursively expands group members to display their user members.
|
||||
* `recursive` - Recursively expand group members
|
||||
|
||||
When `recursive` is specified, the default is to only display type user members; this option modifies those behaviors:
|
||||
* `types <GroupMemberTypeList>` - Display specified types
|
||||
|
||||
By default, when displaying members from a group, all members, whether suspended/archived or not, are included.
|
||||
* `notsuspended` - Display only non-suspended members
|
||||
* `suspended` - Display only suspended members
|
||||
* `notarchived` - Do not include archived members
|
||||
* `archived` - Only include archived members, this is not common but allows creating groups that allow easy identification of archived users
|
||||
* `notsuspended notarchived` - Do not include suspended and archived members
|
||||
* `suspended archived` - Include only suspended or archived members
|
||||
* `notsuspended archived` - Only include archived members, this is not common but allows creating groups that allow easy identification of archived users
|
||||
* `suspended notarchived` - Only include suspended members, this is not common but allows creating groups that allow easy identification of suspended users
|
||||
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal internaldomains <DomainNameList>` - Display members whose domain is in `<DomainNameList>`
|
||||
* `external internaldomains <DomainNameList>` - Display members whose domain is not in `<DomainNameList>`
|
||||
* `internal external internaldomains <DomainNameList>` - Display all members, indicate their category: internal or external
|
||||
* `internaldomains <DomainNameList>` - Defaults to value of `domain` in `gam.cfg`
|
||||
|
||||
Members without an email address, e.g. `customer`, are considered internal.
|
||||
|
||||
Members that have met the above qualifications to be displayed can be further qualifed by their email address.
|
||||
* `memberemaildisplaypattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will be displayed; others will not be displayed
|
||||
* `memberemailskippattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will not be displayed; others will be displayed
|
||||
|
||||
By default, the ID, role, email address, type and status of each member are displayed along with the group email address;
|
||||
these options specify which fields to display:
|
||||
* `membernames` - Display members full name; an additional API call per member is required
|
||||
* `showdeliverysettings` - Display delivery settings; an additional API call per member is required
|
||||
* `<MembersFieldName>*` - Individual field names
|
||||
* `fields <MembersFieldNameList>` - A comma separated list of field names
|
||||
* `delivery|deliverysettings` - Specify this field to get delivery information; an additional API call per member is required
|
||||
|
||||
For members that are users, you can specify additional information to display; an additional API call per member is required
|
||||
* `userfields <UserFieldNameList>` - Display specific user fields
|
||||
* `allschemas|(schemas|custom|customschemas <SchemaNameList>)` - Display all or specific custom schema values
|
||||
|
||||
The additional API calls can be reduced with the `cachememberinfo` option; a single API call is made for each user/group
|
||||
and the data is cached to eliminate to need to repeat the API call; this consumes more memory but dramatically reduces the number of API calls.
|
||||
|
||||
If member names are requested, names are not available for users not in the domain; you can request that GAM use the People API to retrieve
|
||||
names for these users. Names are not retrieved in all cases and success is dependent on what user is used to perform the retrievals.
|
||||
* `peoplelookup` - Use the administrator named in oauth2.txt to perform the retrievals
|
||||
* `peoplelookupuser <EmailAddress>` - Use `<EmailAddress>` to perform the retrievals
|
||||
|
||||
By default, when `membernames` is specified, GAM displays `Unknown` for members whose names can not be determined.
|
||||
Use `unknownname <String>` to specify an alternative value.
|
||||
|
||||
By default, the group email address is always shown, you can suppress it with the `nogroupemail` option.
|
||||
|
||||
The `recursive` option adds two columns, level and subgroup, to the output:
|
||||
* `level` - At what level of the expansion does the user appear; level 0 is the top level
|
||||
* `subgroup` - The group that contained the user
|
||||
|
||||
Displaying membership of multiple groups or recursive expansion may result in multiple instances of the same user being displayed; these multiple instances can be reduced to one entry.
|
||||
* `noduplicates` - Reduce multiple instances of the same user to the first instance
|
||||
|
||||
The `includederivedmembership` option is an alternative to `recursive`; it causes the API to expand type GROUP and type CUSTOMER
|
||||
members to display their constituent members while still displaying the original member.
|
||||
The API produces inconsistent results, use with caution.
|
||||
|
||||
The options `recursive noduplicates` and `includederivedmembership types user noduplicates` return the same list of users.
|
||||
The `includederivedmembership` option makes less API calls but doesn't show level and subgroup information.
|
||||
Expanding a member of type CUSTOMER may produce a large volume of data as it will display all users in your domain.
|
||||
|
||||
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.
|
||||
|
||||
By default, when writing CSV files, Gam uses a quote character of double quote `"`. The quote character is used to enclose columns that contain
|
||||
the quote character itself, the column delimiter (comma by default) and new-line characters. Any quote characters within the column are doubled.
|
||||
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.
|
||||
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.
|
||||
|
||||
## Display group membership in hierarchical format
|
||||
```
|
||||
gam show group-members
|
||||
[([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryGroupList>)]))|
|
||||
(group|group_ns|group_susp <GroupItem>)|
|
||||
(select <GroupEntity>)]
|
||||
[emailmatchpattern [not] <REMatchPattern>] [namematchpattern [not] <REMatchPattern>]
|
||||
[descriptionmatchpattern [not] <REMatchPattern>]
|
||||
[admincreatedmatch <Boolean>]
|
||||
[roles <GroupRoleList>] [members] [managers] [owners] [depth <Number>]
|
||||
[internal] [internaldomains <DomainNameList>] [external]
|
||||
[notsuspended|suspended] [notarchived|archived]
|
||||
[types <GroupMemberTypeList>]
|
||||
[memberemaildisplaypattern|memberemailskippattern <REMatchPattern>]
|
||||
[includederivedmembership]
|
||||
```
|
||||
By default, the group membership of all groups in the account are displayed, these options allow selection of subsets of groups:
|
||||
* `domain|domains <DomainNameEntity>` - Limit display to groups in the domains specified by `<DomainNameEntity>`
|
||||
* You can predefine this list with the `print_agu_domains` variable in `gam.cfg`.
|
||||
* `member <EmailItem>` - Limit display to groups that contain `<EmailItem>` as a member; mutually exclusive with `query <QueryGroup>`
|
||||
* `showownedby <EmailItem>` - Limit display to groups that contain `<EmailItem>` as an owner; mutually exclusive with `query <QueryGroup>`
|
||||
* `(query <QueryGroup>)|(queries <QueryGroupList>)` - Limit groups to those that match a query; each query is run against each domain
|
||||
* `group <GroupItem>` - Limit display to the single group `<GroupItem>`
|
||||
* `group_ns <GroupItem>` - Limit display to the single group `<GroupItem>`, display non-suspended members
|
||||
* `group_susp <GroupItem>` - Limit display to the single group `<GroupItem>`, display suspended members
|
||||
* `select <GroupEntity>` - Limit display to the groups specified in `<GroupEntity>`
|
||||
* `showownedby <UserItem>` - Limit display to groups owned by `<UserItem>`
|
||||
|
||||
When using `query <QueryGroup>` with the `name:{PREFIX}*` query, `PREFIX` must contain at least three characters.
|
||||
|
||||
You can identify groups with the `All users in the organization` member with:
|
||||
* `query "memberKey=<CustomerID>"`
|
||||
* `member id:<CustomerID>`
|
||||
|
||||
These options further limit the list of groups selected above:
|
||||
* `emailmatchpattern <REMatchPattern>` - Limit display to groups whose email address matches `<REMatchPattern>`
|
||||
* `emailmatchpattern not <REMatchPattern>` - Limit display to groups whose email address does not match `<REMatchPattern>`
|
||||
* `namematchpattern <REMatchPattern>` - Limit display to groups whose name matches `<REMatchPattern>`
|
||||
* `namematchpattern not <REMatchPattern>` - Limit display to groups whose name does not match `<REMatchPattern>`
|
||||
* `descriptionmatchpattern <REMatchPattern>` - Limit display to groups whose description matches `<REMatchPattern>`
|
||||
* `descriptionmatchpattern not <REMatchPattern>` - Limit display to groups whose description does not match `<REMatchPattern>`
|
||||
* `admincreatedmatch True` - Limit display to groups created by administrators
|
||||
* `admincreatedmatch False` - Limit display to groups created by users
|
||||
|
||||
By default, all members, managers and owners in the group are displayed; these options modify that behavior:
|
||||
* `roles <GroupRoleList>` - Display specified roles
|
||||
* `members` - Display members
|
||||
* `managers` - Display managers
|
||||
* `owners` - Display owners
|
||||
|
||||
By default, when displaying members from a group, all members, whether suspended/archived or not, are included.
|
||||
* `notsuspended` - Display only non-suspended members
|
||||
* `suspended` - Display only suspended members
|
||||
* `notarchived` - Do not include archived members
|
||||
* `archived` - Only include archived members, this is not common but allows creating groups that allow easy identification of archived users
|
||||
* `notsuspended notarchived` - Do not include suspended and archived members
|
||||
* `suspended archived` - Include only suspended or archived members
|
||||
* `notsuspended archived` - Only include archived members, this is not common but allows creating groups that allow easy identification of archived users
|
||||
* `suspended notarchived` - Only include suspended members, this is not common but allows creating groups that allow easy identification of suspended users
|
||||
|
||||
By default, all types of members (customer, group, user) in the group are displayed; this option modifies that behavior:
|
||||
* `types <GroupMemberTypeList>` - Display specified types
|
||||
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal internaldomains <DomainNameList>` - Display members whose domain is in `<DomainNameList>`
|
||||
* `external internaldomains <DomainNameList>` - Display members whose domain is not in `<DomainNameList>`
|
||||
* `internal external internaldomains <DomainNameList>` - Display all members, indicate their category: internal or external
|
||||
* `internaldomains <DomainNameList>` - Defaults to value of `domain` in `gam.cfg`
|
||||
|
||||
Members without an email address, e.g. `customer`, are considered internal.
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal` - Display members whose domain matches a value in `internaldomains`
|
||||
* `external` - Display members whose domain does not match value in `internaldomains`
|
||||
* `internal external` - Display all members, indicate their category: `internal` or `external`
|
||||
|
||||
Members without an email address, e.g. `customer`, are considered `internal`.
|
||||
|
||||
Members that have met the above qualifications to be displayed can be further qualifed by their email address.
|
||||
* `memberemaildisplaypattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will be displayed; others will not be displayed
|
||||
|
||||
@ -396,7 +396,7 @@ gam info group|groups <GroupEntity>
|
||||
[basic] <GroupFieldName>* [fields <GroupFieldNameList>] [nodeprecated]
|
||||
[ciallfields|(cifields <CIGroupFieldNameList>)]
|
||||
[roles <GroupRoleList>] [members] [managers] [owners]
|
||||
[internal] [internaldomains <DomainNameList>] [external]
|
||||
[internal] [internaldomains All|<DomainNameList>] [external]
|
||||
[notsuspended|suspended] [notarchived|archived]
|
||||
[types <GroupMemberTypeList>]
|
||||
[memberemaildisplaypattern|memberemailskippattern <REMatchPattern>]
|
||||
@ -423,13 +423,17 @@ By default, when displaying members from a group, all members, whether suspended
|
||||
By default, when displaying members from a group, all types of members (customer, group, user) in the group are displayed; this option modifies that behavior:
|
||||
* `types <GroupMemberTypeList>` - Display specified types
|
||||
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal internaldomains <DomainNameList>` - Display members whose domain is in `<DomainNameList>`
|
||||
* `external internaldomains <DomainNameList>` - Display members whose domain is not in `<DomainNameList>`
|
||||
* `internal external internaldomains <DomainNameList>` - Display all members, indicate their category: internal or external
|
||||
* `internaldomains <DomainNameList>` - Defaults to value of `domain` in `gam.cfg`
|
||||
Which domains are considered internal domains:
|
||||
* `internaldomains all` - All of your workspace domains; this is the default
|
||||
* `internaldomains primary` - Your primary workspace domain
|
||||
* `internaldomains <DomainNameList>` - A list of domain names
|
||||
|
||||
Members without an email address, e.g. `customer`, are considered internal.
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal` - Display members whose domain matches a value in `internaldomains`
|
||||
* `external` - Display members whose domain does not match value in `internaldomains`
|
||||
* `internal external` - Display all members, indicate their category: `internal` or `external`
|
||||
|
||||
Members without an email address, e.g. `customer`, are considered `internal`.
|
||||
|
||||
Members that have met the above qualifications to be displayed can be further qualifed by their email address.
|
||||
* `memberemaildisplaypattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will be displayed; others will not be displayed
|
||||
@ -467,13 +471,14 @@ gam print groups [todrive <ToDriveAttribute>*]
|
||||
[ciallfields|(cifields <CIGroupFieldNameList>)]
|
||||
[nodeprecated]
|
||||
[roles <GroupRoleList>]
|
||||
[internal] [internaldomains <DomainNameList>] [external]
|
||||
[internal] [internaldomains all|primary|<DomainNameList>] [external]
|
||||
[members|memberscount] [managers|managerscount] [owners|ownerscount] [totalcount] [countsonly]
|
||||
[includederivedmembership]
|
||||
[notsuspended|suspended] [notarchived|archived]
|
||||
[types <GroupMemberTypeList>]
|
||||
[memberemaildisplaypattern|memberemailskippattern <REMatchPattern>]
|
||||
[convertcrnl] [delimiter <Character>] [sortheaders]
|
||||
(addcsvdata <FieldName> <String>)* [includecsvdatainjson [<Boolean>]]
|
||||
[formatjson [quotechar <Character>]]
|
||||
```
|
||||
By default, all groups in the account are displayed, these options allow selection of subsets of groups:
|
||||
@ -557,21 +562,34 @@ By default, when displaying members from a group, all members, whether suspended
|
||||
By default, when displaying members from a group, all types of members (customer, group, user) in the group are displayed; this option modifies that behavior:
|
||||
* `types <GroupMemberTypeList>` - Display specified types
|
||||
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal internaldomains <DomainNameList>` - Display members whose domain is in `<DomainNameList>`
|
||||
* `external internaldomains <DomainNameList>` - Display members whose domain is not in `<DomainNameList>`
|
||||
* `internal external internaldomains <DomainNameList>` - Display all members, indicate their category: internal or external
|
||||
* `internaldomains <DomainNameList>` - Defaults to value of `domain` in `gam.cfg`
|
||||
Which domains are considered internal domains:
|
||||
* `internaldomains all` - All of your workspace domains; this is the default
|
||||
* `internaldomains primary` - Your workspace primary domain
|
||||
* `internaldomains <DomainNameList>` - A list of domain names
|
||||
|
||||
Members without an email address, e.g. `customer`, are considered internal.
|
||||
By default, when listing group members, GAM does not take the domain of the member into account.
|
||||
* `internal` - Display members whose domain matches a value in `internaldomains`
|
||||
* `external` - Display members whose domain does not match value in `internaldomains`
|
||||
* `internal external` - Display all members, indicate their category: `internal` or `external`
|
||||
|
||||
Members without an email address, e.g. `customer`, are considered `internal`.
|
||||
|
||||
When the `internal` or `external` options are specified, GAM adds the column `allowExternalMembers`
|
||||
that shows that setting for the group.
|
||||
|
||||
Members that have met the above qualifications to be displayed can be further qualifed by their email address.
|
||||
* `memberemaildisplaypattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will be displayed; others will not be displayed
|
||||
* `memberemailskippattern <REMatchPattern>` - Members with email addresses that match `<REMatchPattern>` will not be displayed; others will be displayed
|
||||
|
||||
Add additional columns of data from the command line to the output
|
||||
* `addcsvdata <FieldName> <String>`
|
||||
|
||||
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.
|
||||
|
||||
If `formatjson` and `addcsvdata` are specified, the option `includecsvdatainjson` causes GAM to add the
|
||||
additional field values to the JSON data.
|
||||
|
||||
By default, when writing CSV files, Gam uses a quote character of double quote `"`. The quote character is used to enclose columns that contain
|
||||
the quote character itself, the column delimiter (comma by default) and new-line characters. Any quote characters within the column are doubled.
|
||||
When using the `formatjson` option, double quotes are used extensively in the data resulting in hard to read/process output.
|
||||
@ -680,5 +698,7 @@ To retrieve the count with `showitemcountonly`:
|
||||
Linux/MacOS
|
||||
count=$(gam print groups showitemcountonly)
|
||||
Windows PowerShell
|
||||
count = & gam print groups showitemcountonly
|
||||
```
|
||||
$count = & gam print groups showitemcountonly
|
||||
Windows Command Prompt
|
||||
for /f "delims=" %a in ('gam print groups showitemcountonly') do set count=%a
|
||||
|
||||
|
||||
31
wiki/Home.md
31
wiki/Home.md
@ -10,7 +10,36 @@ To run all commands properly, GAM7 requires three things:
|
||||
* A special service account that is authorized to act on behalf of your users in order to modify user-specific settings and data such as Drive files, Calendars and Gmail messages and settings like signatures.
|
||||
|
||||
# Documentation
|
||||
See the sections in the right-hand column for documentation.
|
||||
|
||||
## Update History
|
||||
A log of updates to GAM7.
|
||||
|
||||
## Installation
|
||||
Instructions detailing ways of installing GAM7 and alternate installation issues.
|
||||
|
||||
## Configuration
|
||||
Instructions detailing configuration of GAM7 and alternate authorization methods.
|
||||
|
||||
## Notes and Information
|
||||
References to resources that enhance your use of GAM7.
|
||||
|
||||
## Definitions
|
||||
BNF definitions of common items in the GAM7 command syntax.
|
||||
|
||||
## Command Processing
|
||||
Information regarding use of command line options to control how GAM7 operates.
|
||||
|
||||
## Collections
|
||||
BNF Syntax definitions of ways to specify multiple Googlw Workspace opjects.
|
||||
|
||||
## Client Access
|
||||
Syntax, descriptions and examples of commands that are executed by your Google Workspace administrator.
|
||||
|
||||
## Special Service Account Access
|
||||
How to set up a GAM7 Chat Bot; this is required to use the Chat API to manage Chat Spaces in your Google Workspace.
|
||||
|
||||
## Service Account Access
|
||||
Syntax, descriptions and examples of commands that are executed on behalf of your Google Workspace users.
|
||||
|
||||
# Installation
|
||||
* [How to Install GAM7](How-to-Install-GAM7)
|
||||
|
||||
@ -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$ gam version
|
||||
WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/admin/GAMConfig/oauth2.txt, Not Found
|
||||
GAM 7.19.02 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM 7.33.00 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.13.7 64-bit final
|
||||
MacOS Sequoia 15.6.1 x86_64
|
||||
Python 3.14.2 64-bit final
|
||||
macOS Tahoe 26.2 x86_64
|
||||
Path: /Users/admin/bin/gam7
|
||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
|
||||
@ -550,6 +550,7 @@ Section: DEFAULT
|
||||
cache_discovery_only = true
|
||||
channel_customer_id = ''
|
||||
charset = utf-8
|
||||
chat_max_results = 100
|
||||
classroom_max_results = 0
|
||||
client_secrets_json = client_secrets.json ; /Users/admin/GAMConfig/client_secrets.json
|
||||
clock_skew_in_seconds = 10
|
||||
@ -559,6 +560,7 @@ Section: DEFAULT
|
||||
config_dir = /Users/admin/GAMConfig
|
||||
contact_max_results = 100
|
||||
csv_input_column_delimiter = ,
|
||||
csv_input_no_escape_char = true
|
||||
csv_input_quote_char = '"'
|
||||
csv_input_row_drop_filter = ''
|
||||
csv_input_row_drop_filter_mode = anymatch
|
||||
@ -571,32 +573,47 @@ Section: DEFAULT
|
||||
csv_output_header_drop_filter = ''
|
||||
csv_output_header_filter = ''
|
||||
csv_output_header_force = ''
|
||||
csv_output_header_order = ''
|
||||
csv_output_line_terminator = lf
|
||||
csv_output_no_escape_char = false
|
||||
csv_output_quote_char = '"'
|
||||
csv_output_row_drop_filter = ''
|
||||
csv_output_row_drop_filter_mode = anymatch
|
||||
csv_output_row_filter = ''
|
||||
csv_output_row_filter_mode = allmatch
|
||||
csv_output_row_limit = 0
|
||||
csv_output_sort_headers = ''
|
||||
csv_output_subfield_delimiter = '.'
|
||||
csv_output_timestamp_column = ''
|
||||
csv_output_users_audit = false
|
||||
customer_id = C01234567
|
||||
debug_level = 0
|
||||
debug_redaction = true
|
||||
developer_preview_api_key = ''
|
||||
developer_preview_apis = ''
|
||||
device_max_results = 200
|
||||
domain = domain.com
|
||||
drive_dir = /Users/admin/GAMWork
|
||||
drive_max_results = 1000
|
||||
drive_v3_native_names = true
|
||||
email_batch_size = 50
|
||||
enable_dasa = false
|
||||
enable_gcloud_reauth = false
|
||||
enforce_expansive_access = true
|
||||
event_max_results = 250
|
||||
extra_args = ''
|
||||
gmail_cse_incert_dir = ''
|
||||
gmail_cse_inkey_dir = ''
|
||||
input_dir = .
|
||||
inter_batch_wait = 0
|
||||
license_max_results = 100
|
||||
license_skus = ''
|
||||
member_max_results = 200
|
||||
member_max_results_ci_basic = 1000
|
||||
member_max_results_ci_full = 500
|
||||
message_batch_size = 50
|
||||
message_max_results = 500
|
||||
mobile_max_results = 100
|
||||
multiprocess_pool_limit = 0
|
||||
never_time = Never
|
||||
no_browser = false
|
||||
no_cache = false
|
||||
@ -606,13 +623,15 @@ Section: DEFAULT
|
||||
num_threads = 5
|
||||
oauth2_txt = oauth2.txt ; /Users/admin/GAMConfig/oauth2.txt
|
||||
oauth2service_json = oauth2service.json ; /Users/admin/GAMConfig/oauth2service.json
|
||||
output_dateformat = ''
|
||||
output_timeformat = ''
|
||||
people_max_results = 100
|
||||
print_agu_domains = ''
|
||||
print_cros_ous = ''
|
||||
print_cros_ous_and_children = ''
|
||||
process_wait_limit = 0
|
||||
quick_cros_move = false
|
||||
quick_info_user = False
|
||||
quick_info_user = false
|
||||
reseller_id = ''
|
||||
retry_api_service_not_available = false
|
||||
section = ''
|
||||
@ -629,12 +648,13 @@ Section: DEFAULT
|
||||
smtp_username = ''
|
||||
timezone = local
|
||||
tls_max_version = ''
|
||||
tls_min_version = 'TLSv1_2'
|
||||
tls_min_version = 'TLSv1_3'
|
||||
todrive_clearfilter = false
|
||||
todrive_clientaccess = false
|
||||
todrive_conversion = true
|
||||
todrive_localcopy = false
|
||||
todrive_locale = ''
|
||||
todrive_no_escape_char = true
|
||||
todrive_nobrowser = false
|
||||
todrive_noemail = true
|
||||
todrive_parent = root
|
||||
@ -647,6 +667,8 @@ Section: DEFAULT
|
||||
todrive_user = ''
|
||||
truncate_client_id = false
|
||||
update_cros_ou_with_id = false
|
||||
use_chat_admin_access = false
|
||||
use_course_owner_access = false
|
||||
use_projectid_as_name = false
|
||||
user_max_results = 500
|
||||
user_service_account_access_only = false
|
||||
@ -751,6 +773,7 @@ Section: DEFAULT
|
||||
cache_discovery_only = true
|
||||
channel_customer_id = ''
|
||||
charset = utf-8
|
||||
chat_max_results = 100
|
||||
classroom_max_results = 0
|
||||
client_secrets_json = client_secrets.json ; C:\GAMConfig\client_secrets.json
|
||||
clock_skew_in_seconds = 10
|
||||
@ -760,6 +783,7 @@ Section: DEFAULT
|
||||
config_dir = C:\GAMConfig
|
||||
contact_max_results = 100
|
||||
csv_input_column_delimiter = ,
|
||||
csv_input_no_escape_char = true
|
||||
csv_input_quote_char = '"'
|
||||
csv_input_row_drop_filter = ''
|
||||
csv_input_row_drop_filter_mode = anymatch
|
||||
@ -772,32 +796,47 @@ Section: DEFAULT
|
||||
csv_output_header_drop_filter = ''
|
||||
csv_output_header_filter = ''
|
||||
csv_output_header_force = ''
|
||||
csv_output_header_order = ''
|
||||
csv_output_line_terminator = lf
|
||||
csv_output_no_escape_char = false
|
||||
csv_output_quote_char = '"'
|
||||
csv_output_row_drop_filter = ''
|
||||
csv_output_row_drop_filter_mode = anymatch
|
||||
csv_output_row_filter = ''
|
||||
csv_output_row_filter_mode = allmatch
|
||||
csv_output_row_limit = 0
|
||||
csv_output_sort_headers = ''
|
||||
csv_output_subfield_delimiter = '.'
|
||||
csv_output_timestamp_column = ''
|
||||
csv_output_users_audit = false
|
||||
customer_id = my_customer
|
||||
debug_level = 0
|
||||
debug_redaction = true
|
||||
developer_preview_api_key = ''
|
||||
developer_preview_apis = ''
|
||||
device_max_results = 200
|
||||
domain = ''
|
||||
drive_dir = C:\GAMWork
|
||||
drive_max_results = 1000
|
||||
drive_v3_native_names = true
|
||||
email_batch_size = 50
|
||||
enable_dasa = false
|
||||
enable_gcloud_reauth = false
|
||||
enforce_expansive_access = true
|
||||
event_max_results = 250
|
||||
extra_args = ''
|
||||
gmail_cse_incert_dir = ''
|
||||
gmail_cse_inkey_dir = ''
|
||||
input_dir = .
|
||||
inter_batch_wait = 0
|
||||
license_max_results = 100
|
||||
license_skus = ''
|
||||
member_max_results = 200
|
||||
member_max_results_ci_basic = 1000
|
||||
member_max_results_ci_full = 500
|
||||
message_batch_size = 50
|
||||
message_max_results = 500
|
||||
mobile_max_results = 100
|
||||
multiprocess_pool_limit = 0
|
||||
never_time = Never
|
||||
no_browser = false
|
||||
no_cache = false
|
||||
@ -807,13 +846,15 @@ Section: DEFAULT
|
||||
num_threads = 5
|
||||
oauth2_txt = oauth2.txt ; C:\GAMConfig\oauth2.txt
|
||||
oauth2service_json = oauth2service.json ; C:\GAMConfig\oauth2service.json
|
||||
output_dateformat = ''
|
||||
output_timeformat = ''
|
||||
people_max_results = 100
|
||||
print_agu_domains = ''
|
||||
print_cros_ous = ''
|
||||
print_cros_ous_and_children = ''
|
||||
process_wait_limit = 0
|
||||
quick_cros_move = false
|
||||
quick_info_user = False
|
||||
quick_info_user = false
|
||||
reseller_id = ''
|
||||
retry_api_service_not_available = false
|
||||
section = ''
|
||||
@ -830,12 +871,13 @@ Section: DEFAULT
|
||||
smtp_username = ''
|
||||
timezone = utc
|
||||
tls_max_version = ''
|
||||
tls_min_version = 'TLSv1_2'
|
||||
tls_min_version = 'TLSv1_3'
|
||||
todrive_clearfilter = false
|
||||
todrive_clientaccess = false
|
||||
todrive_conversion = true
|
||||
todrive_localcopy = false
|
||||
todrive_locale = ''
|
||||
todrive_no_escape_char = true
|
||||
todrive_nobrowser = false
|
||||
todrive_noemail = true
|
||||
todrive_parent = root
|
||||
@ -848,6 +890,8 @@ Section: DEFAULT
|
||||
todrive_user = ''
|
||||
truncate_client_id = false
|
||||
update_cros_ou_with_id = false
|
||||
use_chat_admin_access = false
|
||||
use_course_owner_access = false
|
||||
use_projectid_as_name = false
|
||||
user_max_results = 500
|
||||
user_service_account_access_only = false
|
||||
@ -990,10 +1034,10 @@ writes the credentials into the file oauth2.txt.
|
||||
C:\>del C:\GAMConfig\oauth2.txt
|
||||
C:\>gam version
|
||||
WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found
|
||||
GAM 7.19.02 - https://github.com/GAM-team/GAM - pythonsource
|
||||
GAM 7.33.00 - https://github.com/GAM-team/GAM - pythonsource
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.13.7 64-bit final
|
||||
Windows-10-10.0.17134 AMD64
|
||||
Python 3.14.2 64-bit final
|
||||
Windows 11 10.0.26200 AMD64
|
||||
Path: C:\GAM7
|
||||
Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
|
||||
@ -1289,6 +1333,7 @@ Section: DEFAULT
|
||||
cache_discovery_only = true
|
||||
channel_customer_id = ''
|
||||
charset = utf-8
|
||||
chat_max_results = 100
|
||||
classroom_max_results = 0
|
||||
client_secrets_json = client_secrets.json ; C:\GAMConfig\client_secrets.json
|
||||
clock_skew_in_seconds = 10
|
||||
@ -1298,6 +1343,7 @@ Section: DEFAULT
|
||||
config_dir = C:\GAMConfig
|
||||
contact_max_results = 100
|
||||
csv_input_column_delimiter = ,
|
||||
csv_input_no_escape_char = true
|
||||
csv_input_quote_char = '"'
|
||||
csv_input_row_drop_filter = ''
|
||||
csv_input_row_drop_filter_mode = anymatch
|
||||
@ -1310,32 +1356,47 @@ Section: DEFAULT
|
||||
csv_output_header_drop_filter = ''
|
||||
csv_output_header_filter = ''
|
||||
csv_output_header_force = ''
|
||||
csv_output_header_order = ''
|
||||
csv_output_line_terminator = lf
|
||||
csv_output_no_escape_char = false
|
||||
csv_output_quote_char = '"'
|
||||
csv_output_row_drop_filter = ''
|
||||
csv_output_row_drop_filter_mode = anymatch
|
||||
csv_output_row_filter = ''
|
||||
csv_output_row_filter_mode = allmatch
|
||||
csv_output_row_limit = 0
|
||||
csv_output_sort_headers = ''
|
||||
csv_output_subfield_delimiter = '.'
|
||||
csv_output_timestamp_column = ''
|
||||
csv_output_users_audit = false
|
||||
customer_id = C01234567
|
||||
debug_level = 0
|
||||
debug_redaction = true
|
||||
developer_preview_api_key = ''
|
||||
developer_preview_apis = ''
|
||||
device_max_results = 200
|
||||
domain = domain.com
|
||||
drive_dir = C:\GAMWork
|
||||
drive_max_results = 1000
|
||||
drive_v3_native_names = true
|
||||
email_batch_size = 50
|
||||
enable_dasa = false
|
||||
enable_gcloud_reauth = false
|
||||
enforce_expansive_access = true
|
||||
event_max_results = 250
|
||||
extra_args = ''
|
||||
gmail_cse_incert_dir = ''
|
||||
gmail_cse_inkey_dir = ''
|
||||
input_dir = .
|
||||
inter_batch_wait = 0
|
||||
license_max_results = 100
|
||||
license_skus = ''
|
||||
member_max_results = 200
|
||||
member_max_results_ci_basic = 1000
|
||||
member_max_results_ci_full = 500
|
||||
message_batch_size = 50
|
||||
message_max_results = 500
|
||||
mobile_max_results = 100
|
||||
multiprocess_pool_limit = 0
|
||||
never_time = Never
|
||||
no_browser = false
|
||||
no_cache = false
|
||||
@ -1353,7 +1414,7 @@ Section: DEFAULT
|
||||
print_cros_ous_and_children = ''
|
||||
process_wait_limit = 0
|
||||
quick_cros_move = false
|
||||
quick_info_user = False
|
||||
quick_info_user = false
|
||||
reseller_id = ''
|
||||
retry_api_service_not_available = false
|
||||
section = ''
|
||||
@ -1370,12 +1431,13 @@ Section: DEFAULT
|
||||
smtp_username = ''
|
||||
timezone = local
|
||||
tls_max_version = ''
|
||||
tls_min_version = 'TLSv1_2'
|
||||
tls_min_version = 'TLSv1_3'
|
||||
todrive_clearfilter = false
|
||||
todrive_clientaccess = false
|
||||
todrive_conversion = true
|
||||
todrive_localcopy = false
|
||||
todrive_locale = ''
|
||||
todrive_no_escape_char = true
|
||||
todrive_nobrowser = false
|
||||
todrive_noemail = true
|
||||
todrive_parent = root
|
||||
@ -1388,6 +1450,8 @@ Section: DEFAULT
|
||||
todrive_user = ''
|
||||
truncate_client_id = false
|
||||
update_cros_ou_with_id = false
|
||||
use_chat_admin_access = false
|
||||
use_course_owner_access = false
|
||||
use_projectid_as_name = false
|
||||
user_max_results = 500
|
||||
user_service_account_access_only = false
|
||||
|
||||
@ -60,9 +60,9 @@
|
||||
| G Suite Legacy | Google-Apps | standard |
|
||||
| G Suite Lite | Google-Apps-Lite | gsuitelite |
|
||||
| Gemini Business | 1010470003 | geminibiz
|
||||
| Gemini Education | 1010470004 | geminiedu |
|
||||
| Gemini Education Premium | 1010470005 | geminiedupremium |
|
||||
| Gemini Enterprise | 1010470001 | geminient | duetai |
|
||||
| Google AI Pro for Education | 1010470004 | gaiproedu |
|
||||
| Google AI Ultra for Business | 1010470008 | geminiultra |
|
||||
| Google Apps Message Security | Google-Apps-For-Postini | postini |
|
||||
| Google Chrome Device Management | Google-Chrome-Device-Management | cdm |
|
||||
@ -161,18 +161,17 @@
|
||||
assuredcontrolsplus | 1010390002 | Assured Controls Plus |
|
||||
bce | beyondcorp | beyondcorpenterprise | cep | chromeenterprisepremium | 1010400001 | Chrome Enterprise Premium |
|
||||
cdm | chrome | googlechromedevicemanagement | Google-Chrome-Device-Management |
|
||||
bce | beyondcorp | beyondcorpenterprise | cep | chromeenterprisepremium | 1010400001 | Chrome Enterprise Premium |
|
||||
cdm | chrome | googlechromedevicemanagement | Google-Chrome-Device-Management |
|
||||
cloudidentity | identity | 1010010001 | Cloud Identity |
|
||||
cloudidentitypremium | identitypremium | 1010050001 | Cloud Identity Premium |
|
||||
cloudsearch | 1010350001 | Cloud Search |
|
||||
colabpro | 1010500001 | Colab Pro |
|
||||
colabpro+ | colabproplus | 1010500002 | Colab Pro+ |
|
||||
eeu | 1010490001 | SKU Endpoint Education Upgrade |
|
||||
gaiproedu | geminiedu | 1010470004 | Google AI Pro for Education |
|
||||
geminibiz | 1010470003 | Gemini Business |
|
||||
geminiedu | 1010470004 | Gemini Education |
|
||||
geminiedupremium| 1010470005 | Gemini Education Premium |
|
||||
geminient| duetai | 1010470001 | Gemini Enterprise |
|
||||
geminiultra | 1010470008 | Google AI Ultra for Business |
|
||||
gsuitebasic | gafb | gafw | basic | Google-Apps-For-Business |
|
||||
gsuitebusiness | gau | gsb | unlimited | Google-Apps-Unlimited |
|
||||
gsuitebusinessarchived | gsbau | businessarchived | 1010340002 | Google Workspace Business - Archived User |
|
||||
@ -216,7 +215,8 @@
|
||||
wsess | workspaceesentials | gsuiteessentials | essentials | d4e | driveenterprise | drive4enterprise | 1010060001 | Google Workspace Essentials (formerly G Suite Essentials) |
|
||||
wsessplus | workspaceessentialsplus | 1010060005 | Google Workspace Enterprise Essentials Plus |
|
||||
wsflw | workspacefrontline | workspacefrontlineworker | 1010020030 | Google Workspace Frontline Starter |
|
||||
wsflwstan | workspacefrontlinestan | workspacefrontlineworkerstan | 1010020031 | Google Workspace Frontline Standard
|
||||
wsflwstan | workspacefrontlinestan | workspacefrontlineworkerstan | 1010020031 | Google Workspace Frontline Standard |
|
||||
wsflwplus | workspacefrontlineplus | workspacefrontlineworkerplus | 1010020034 | Google Workspace Frontline Plus
|
||||
<SKUIDList> ::= "<SKUID>(,<SKUID>)*"
|
||||
```
|
||||
## Notes
|
||||
@ -235,10 +235,6 @@ nv:<String>:<String>
|
||||
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?
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
## Lists of basic items
|
||||
```
|
||||
<AdminAssigneeTypeList> ::= "<AdminAssigneeType>(,<AdminAssigneeType>)*"
|
||||
<APIScopeURLList> ::= "<APIScopeURL>(,<APIScopeURL>)*"
|
||||
<ASPIDList> ::= "<ASPID>(,<ASPID>)*"
|
||||
<AssetTagList> ::= "<AssetTag>(,<AssetTag>)*"
|
||||
@ -43,6 +44,7 @@
|
||||
<DomainNameList> ::= "<DomainName>(,<DomainName>)*"
|
||||
<DriveFileACLRoleList> ::= "<DriveFileACLRole>(,<DriveFileACLRole>)*"
|
||||
<DriveFileACLTypeList> ::= "<DriveFileACLType>(,<DriveFileACLType>)*"
|
||||
<DriveFileIDList> ::= "<DriveFileID>(,<DriveFileID>)*"
|
||||
<DriveFileList> ::= "<DriveFileItem>(,<DriveFileItem>)*"
|
||||
<DriveFilePermissionList> ::= "<DriveFilePermission>(,<DriveFilePermission>)*"
|
||||
<DriveFilePermissionIDList> ::= "<DriveFilePermissionID>(,<DriveFilePermissionID>)*"
|
||||
@ -75,6 +77,7 @@
|
||||
<MimeTypeNameList> ::= "<MimeTypeName>(,<MimeTypeName>)*"
|
||||
<NamespaceList> ::= "<Namespace>(,<Namespace>)*"
|
||||
<NotesNameList> ::= "<NotesName>(,<NotesName>)*"
|
||||
<NumberRangeList> ::= "<NumberRange>(,<NumberRange>)*"
|
||||
<OrgUnitList> ::= "<OrgUnitItem>(,<OrgUnitItem>)*"
|
||||
<OtherContactsResourceNameList> ::= "<OtherContactsResourceName>(,<OtherContactsResourceName>)*"
|
||||
<PeopleResourceNameList> ::= "<PeopleResourceName>(,<PeopleResourceName>)*"
|
||||
@ -98,6 +101,7 @@
|
||||
<SharedDriveACLRoleList> ::= "<SharedDriveACLRole>(,<SharedDriveACLRole>)*"
|
||||
<SharedDriveIDList> ::= "<SharedDriveID>(,<SharedDriveID>)*"
|
||||
<StringList> ::= "<String>(,<String>)*"
|
||||
<StudentGroupIDList> ::= "<StudentGroupID>(,<StudentGroupID>)*"
|
||||
<TagManagerAccountPathList> ::= "<TagManagerAccountPath>(,<TagManagerAccountPath>)*"
|
||||
<TagManagerContainerPathList> ::= "<TagManagerContainerPath>(,<TagManagerContainerPath>)*"
|
||||
<TagManagerWorkspacePathList> ::= "<TagManagerWorkspacePath>(,<TagManagerWorkspacePath>)*"
|
||||
|
||||
@ -27,12 +27,13 @@ gam [<Select>] [showsections] [<SelectOutputFilter>|<SelectInputFilter>] [<Confi
|
||||
Select a section from gam.cfg and process a GAM command using values from that section.
|
||||
```
|
||||
<Select> ::=
|
||||
select <Section> [save] [verify]
|
||||
select <Section> [save] [verify [variables <RESearchPattern>]]
|
||||
```
|
||||
- `save`
|
||||
- Set `section = <Section>` in the `[DEFAULT]` section and write configuration data to gam.cfg
|
||||
- `verify`
|
||||
- Print the variable values for the selected section
|
||||
- Use `variables <RESearchPattern>` to display variables with names selected by `<RESearchPattern>`
|
||||
- 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,
|
||||
@ -80,7 +81,7 @@ Set variables in gam.cfg.
|
||||
|
||||
```
|
||||
<Config> ::=
|
||||
config (<VariableName> [=] <Value>)* [save] [verify]
|
||||
config (<VariableName> [=] <Value>)* [save] [verify [variables <RESearchPattern>]]
|
||||
```
|
||||
- `<VariableName> [=] <Value>`
|
||||
- Set `<VariableName> = <Value>` in the current section
|
||||
@ -90,6 +91,7 @@ Set variables in gam.cfg.
|
||||
- Write configuration data to gam.cfg
|
||||
- `verify`
|
||||
- Print the variable values for the current section
|
||||
- Use `variables <RESearchPattern>` to display variables with names selected by `<RESearchPattern>`
|
||||
- Values are determined in this order: Current section, DEFAULT section, Program default
|
||||
|
||||
You can prefix `<Config>` with `<Select>` to set a variable in a particular section.
|
||||
|
||||
@ -173,5 +173,7 @@ To retrieve the count with `showitemcountonly`:
|
||||
Linux/MacOS
|
||||
count=$(gam print mobile showitemcountonly)
|
||||
Windows PowerShell
|
||||
count = & gam print mobile showitemcountonly
|
||||
$count = & gam print mobile showitemcountonly
|
||||
Windows Command Prompt
|
||||
for /f "delims=" %a in ('gam print mobile showitemcountonly') do set count=%a
|
||||
```
|
||||
|
||||
@ -32,7 +32,6 @@
|
||||
<OrgUnitFieldName> ::=
|
||||
description|
|
||||
id|orgunitid|
|
||||
inherit|blockinheritance|
|
||||
name|
|
||||
parentid|parentorgunitid|
|
||||
parent|parentorgunitpath|
|
||||
@ -73,18 +72,15 @@ For quoting rules, see: [List Quoting Rules](Command-Line-Parsing)
|
||||
Create, update and delete organization units.
|
||||
```
|
||||
gam create org|ou <OrgUnitPath> [description <String>]
|
||||
[parent <OrgUnitItem>] [inherit|(blockinheritance False)]
|
||||
[parent <OrgUnitItem>]
|
||||
[buildpath]
|
||||
gam update org|ou <OrgUnitPath> [name <String>] [description <String>]
|
||||
[parent <OrgUnitItem>] [inherit|(blockinheritance False)]
|
||||
[parent <OrgUnitItem>]
|
||||
gam delete org|ou <OrgUnitPath>
|
||||
gam update orgs|ous <OrgUnitEntity> [name <String>] [description <String>]
|
||||
[parent <OrgUnitItem>] [inherit|(blockinheritance False)]
|
||||
[parent <OrgUnitItem>]
|
||||
gam delete orgs|ous <OrgUnitEntity>
|
||||
```
|
||||
Inheritance specifies whether sub-OUs of the specified OU inherit its settings.
|
||||
* `inherit|blockinheritance false` - Sub-OUs inherit settings from the specified OU; this is the default
|
||||
|
||||
## Add users to an organizational unit
|
||||
When adding users to an OU, Gam uses a batch method to speed up processing.
|
||||
|
||||
@ -187,11 +183,15 @@ given if invalid CrOS deviceIds are specified.
|
||||
## Display organizational units
|
||||
These commands display information as an indented list of keys and values.
|
||||
```
|
||||
gam info org|ou <OrgUnitPath> [nousers|notsuspended|suspended] [children|child]
|
||||
gam info orgs|ous <OrgUnitEntity> [nousers|notsuspended|suspended] [children|child]
|
||||
gam info org|ou <OrgUnitPath> [nousers|notarchived|archived|notsuspended|suspended] [children|child]
|
||||
[nousers | ([notarchived|archived] [notsuspended|suspended])] [children|child]
|
||||
gam info orgs|ous <OrgUnitEntity> [nousers|notarchived|archived|notsuspended|suspended] [children|child]
|
||||
[nousers | ([notarchived|archived] [notsuspended|suspended])] [children|child]
|
||||
```
|
||||
By default, all users of the org units are displayed:
|
||||
* `nousers` - Don't display users of the org units
|
||||
* `notarchived` - Display non-archived users of the org units
|
||||
* `archived` - Display archived users of the org units
|
||||
* `notsuspended` - Display non-suspended users of the org units
|
||||
* `suspended` - Display suspended users of the org units
|
||||
* `children|child` - Display users in any child org unit
|
||||
@ -214,7 +214,7 @@ By default, Gam prints all child org units of /.
|
||||
* `convertcrnl` - In the description field, convert carriage return to \r and new line to \n.
|
||||
|
||||
Options `parentselector <OrgUnitSelector>` and `childselector <OrgUnitSelector>` add an additional column `orgUnitSelector` to the output.
|
||||
This column value can be used in subsequent `gam csv` commands to appropriateley select members without duplication.
|
||||
This column value can be used in subsequent `gam csv` commands to appropriately select members without duplication.
|
||||
|
||||
By default, all OUs are displayed. You can limit the display of OUs to those where the number
|
||||
of ChromeOS devices/users falls within a range. Gathering this data requires additional API calls
|
||||
@ -262,7 +262,9 @@ To retrieve the count with `showitemcountonly`:
|
||||
Linux/MacOS
|
||||
count=$(gam print orgs showitemcountonly)
|
||||
Windows PowerShell
|
||||
count = & gam print orgs showitemcountonly
|
||||
$count = & gam print orgs showitemcountonly
|
||||
Windows Command Prompt
|
||||
for /f "delims=" %a in ('gam print orgs showitemcountonly') do set count=%a
|
||||
```
|
||||
|
||||
## Display indented organizational unit tree
|
||||
|
||||
@ -14,5 +14,10 @@ Thank you.
|
||||
* Korey Rideout - https://chatgpt.com/g/g-PTxxnVPMG-gam-assist-now-turbocharged-with-gam7
|
||||
* Paul Ogier (Taming.Tech) - GAM7 Course on Udemy https://taming.tech/GAMCourse
|
||||
* Paul Ogier (Taming.Tech) - GAM7 Tutorials https://www.youtube.com/watch?v=g9LDeyXQNLI&list=PL_dLiK09pJVhKJxZHNk9CHK0q5hkZ856w
|
||||
* Paul Ogier (Taming.Tech) - Installation videos
|
||||
* GAM7 Windows Install - https://www.youtube.com/watch?v=l8pTF5UWz7o&list=PL_dLiK09pJVhKJxZHNk9CHK0q5hkZ856w&index=4
|
||||
* GAM7 macOS Install - https://www.youtube.com/watch?v=eaLWpxhC91w&list=PL_dLiK09pJVhKJxZHNk9CHK0q5hkZ856w&index=5
|
||||
* GAM7 Ubuntu Linux Install - https://www.youtube.com/watch?v=zBwhNTxGlcM&list=PL_dLiK09pJVhKJxZHNk9CHK0q5hkZ856w&index=7
|
||||
* GAM7 ChromeOS Install - https://www.youtube.com/watch?v=BNWQh8GqvgY&list=PL_dLiK09pJVhKJxZHNk9CHK0q5hkZ856w&index=6
|
||||
* Paul Ogier (Taming.Tech) - https://taming.tech/taming-gam-a-practical-guide-to-gam-and-gamadv-xtd3/
|
||||
* Steve Larsen - https://docs.google.com/spreadsheets/d/1MzzA-u-cmoQcJnQOovCnZcEKMjvOyFhfkdFdf10X_GI/edit
|
||||
|
||||
295
wiki/Reports.md
295
wiki/Reports.md
@ -12,12 +12,24 @@
|
||||
- [User reports](#user-reports)
|
||||
|
||||
## API documentation
|
||||
* [Activity Data Sources](https://support.google.com/a/answer/11482175)
|
||||
|
||||
Changes starting 2025-10-29.
|
||||
* [Reports API - Admin log event changes](https://support.google.com/a/answer/16601511)
|
||||
|
||||
Changes starting 2025-12-20
|
||||
* [Reports API - Admin log enhancements](https://workspaceupdates.googleblog.com/2025/12/google-workspace-audit-log-api.html)
|
||||
|
||||
These pages show event/parameter names; scroll down in the left column to: Reports.
|
||||
|
||||
* [Reports API - Activities](https://developers.google.com/admin-sdk/reports/v1/reference/activities)
|
||||
* [Reports API - Customer Usage](https://developers.google.com/admin-sdk/reports/v1/reference/customerUsageReports)
|
||||
* [Reports API - User Usage](https://developers.google.com/admin-sdk/reports/v1/reference/userUsageReport)
|
||||
|
||||
## Definitions
|
||||
```
|
||||
<NumberRange> ::= <Number>|(<Number>/<Number>)
|
||||
<NumberRangeList> ::= "<NumberRange>(,<NumberRange>)*"
|
||||
<DayOfWeek> ::= mon|tue|wed|thu|fri|sat|sun
|
||||
<Time> ::=
|
||||
<Year>-<Month>-<Day>(<Space>|T)<Hour>:<Minute>:<Second>[.<MilliSeconds>](Z|(+|-(<Hour>:<Minute>))) |
|
||||
@ -39,53 +51,70 @@ config csv_output_row_filter "'\"accounts:used_quota_in_mb\":count>15000'"
|
||||
## Activity reports
|
||||
```
|
||||
<ActivityApplicationName> ::=
|
||||
accessevaluation|
|
||||
accesstransparency|access|
|
||||
admin|
|
||||
admindataaction|
|
||||
assignments|
|
||||
calendar|calendars|
|
||||
chat|
|
||||
chrome|
|
||||
classroom|
|
||||
cloudsearch|
|
||||
contacts|
|
||||
contextawareaccess|
|
||||
gplus|currents|google+|
|
||||
datamigration|
|
||||
datastudio|
|
||||
directorysync|
|
||||
drive|doc|docs|
|
||||
gcp|cloud|
|
||||
geminiinworkspaceapps|gemini|geminiforworkspace|
|
||||
gmail|
|
||||
gplus|currents|google+|
|
||||
graduation|
|
||||
groups|group|
|
||||
groupsenterprise|enterprisegroups|
|
||||
jamboard|
|
||||
keep|
|
||||
ldap|
|
||||
login|logins|
|
||||
meet|hangoutsmeet|
|
||||
meethardware|
|
||||
mobile|devices|
|
||||
profile|
|
||||
rules|
|
||||
saml|
|
||||
takeout|
|
||||
tasks|
|
||||
token|tokens|oauthtoken|
|
||||
useraccounts|
|
||||
vault
|
||||
|
||||
gam report <ActivityApplicationName> [todrive <ToDriveAttribute>*]
|
||||
[(user all|<UserItem>)|(orgunit|org|ou <OrgUnitPath> [showorgunit])|(select <UserTypeEntity>)]
|
||||
[userisactor]
|
||||
[([start <Time>] [end <Time>])|(range <Time> <Time>)|
|
||||
yesterday|today|thismonth|(previousmonths <Integer>)]
|
||||
[filter <String> (filtertime<String> <Time>)*]
|
||||
[event|events <EventNameList>] [ip <String>]
|
||||
[groupidfilter <String>]
|
||||
[gmaileventtypes <NumberRangeList>]
|
||||
[groupidfilter <String>] [resourcedetailsfilter <String>]
|
||||
[maxactivities <Number>] [maxevents <Number>] [maxresults <Number>]
|
||||
[countsonly [bydate|summary] [eventrowfilter]]
|
||||
(addcsvdata <FieldName> <String>)* [shownoactivities]
|
||||
```
|
||||
Select the application with `<ActivityApplicationName>`.
|
||||
|
||||
For all `<ActivityApplicationNames>` other than `admin`, select the users for whom information is desired.
|
||||
For all `<ActivityApplicationNames>`, select the users for whom information is desired.
|
||||
* `user all` - All users, the default; there is one API call
|
||||
* `user <UserItem>` - An individual user; there is one API call
|
||||
* `orgunit|org|ou <OrgUnitPath>` - All users in the specified OU; there is one API call
|
||||
* `showorgunit` - Add a column labelled `actor.orgUnitPath` to the output; an additional API call is made to get the email addresses of the users in `<OrgUnitPath>`
|
||||
* `select <UserTypeEntity>` - A selected collection of users, e.g., `select group staff@domain.com`; there is one API call per user
|
||||
|
||||
For `<ActivityApplicationName>` `admin`, the users selected are the admins that executed the command, not the targeted user.
|
||||
Use `filter "USER_EMAIL==user@domain.com"` to select the targeted user.
|
||||
For `<ActivityApplicationName>` `admin` and `chrome`, `orgunit|org|ou <OrgUnitPath>` does not work, use `select ou <OrgUnitPath>`.
|
||||
|
||||
For `<ActivityApplicationName>` `admin`, use option `userisactor` to display activities where the user executed the command that generated the activity.
|
||||
|
||||
Limit the time period.
|
||||
* `start <Time>`
|
||||
@ -96,6 +125,20 @@ Limit the time period.
|
||||
* `thismonth` - The current calendar month up to the current time
|
||||
* `previousmonths <Integer>` - A number in the range 1 to 6 indicating calendar months previous to the current month
|
||||
|
||||
For `gam report gmail`, `start <Time>` and `end <Time>` should both be provided, and the scan duration should not be greater than 30 days.
|
||||
GAM will supply missing values:
|
||||
* No time information provided - GAM sets `range -30d today`
|
||||
* Only `start <Time>` provided - GAM sets `end <Time>+30d`
|
||||
* Only `end <Time>` provided - GAM sets `start <Time>-30d`
|
||||
|
||||
For `gam report gmail`, `gmaileventtypes <NumberRangeList>` can be used to limit the event types displayed.
|
||||
* See: https://developers.google.com/workspace/admin/reports/v1/appendix/activity/gmail
|
||||
|
||||
You can use the following filter to select a specific event; replace `X` with your desired value.
|
||||
```
|
||||
filter "event_info.mail_event_type==X"
|
||||
```
|
||||
|
||||
Apply API filters.
|
||||
* `filter|filters <String>` - `<String>` is a comma separated list of filter expressions.
|
||||
|
||||
@ -104,6 +147,12 @@ The `filtertime<String> <Time>` value replaces the string `#filtertime<String>#`
|
||||
The characters following `filtertime` can be any combination of lowercase letters and numbers. This is most useful in scripts
|
||||
where you can specify a relative date without having to change the script.
|
||||
|
||||
Limit to those users that are a member of at least one of a list of groups.
|
||||
* `groupidfilter <String>` - Format: "id:abc123,id:xyz456"
|
||||
|
||||
Limit based on resource details.
|
||||
* `resourcedetailsfilter <String>` - See: https://developers.google.com/workspace/admin/reports/reference/rest/v1/activities/list#query-parameters
|
||||
|
||||
You can use `config csv_output_row_filter` to filter the events if the API filter can't produce the results you want.
|
||||
|
||||
Limit to a list of specific events.
|
||||
@ -112,9 +161,6 @@ Limit to a list of specific events.
|
||||
Limit to a specific IP address.
|
||||
* `ip <String>`
|
||||
|
||||
Limit to those users that are a member of at least one of a list of groups.
|
||||
* `groupidfilter <String>` - Format: "id:abc123,id:xyz456"
|
||||
|
||||
Limit the total number of activites.
|
||||
* `maxactivities <Number>`
|
||||
|
||||
@ -177,8 +223,8 @@ Example output from SharedDrivesActivity.csv:
|
||||
|
||||
name,id.time,shared_drive_id,shared_drive_name
|
||||
NoActivities,,0AERPpMc23znvUkPXYZ,Shared Drive 1
|
||||
view,2023-10-18T21:27:51-07:00,0AMhgLk82dhsuUkPXYZ,Shared Drive 2
|
||||
edit,2023-09-05T15:27:01-07:00,0AM8lpdkkJaKYUkPXYZ,Shared Drive 3
|
||||
view,2025-10-18T21:27:51-07:00,0AMhgLk82dhsuUkPXYZ,Shared Drive 2
|
||||
edit,2025-09-05T15:27:01-07:00,0AM8lpdkkJaKYUkPXYZ,Shared Drive 3
|
||||
```
|
||||
|
||||
Get activities with full activty data.
|
||||
@ -189,8 +235,8 @@ Example output from SharedDrivesActivity.csv:
|
||||
|
||||
name,actor.callerType,actor.email,actor.key,actor.profileId,actor_is_collaborator_account,added_role,billable,destination_folder_id,destination_folder_title,doc_id,doc_title,doc_type,id.applicationName,id.customerId,id.time,id.uniqueQualifier,ipAddress,is_encrypted,membership_change_type,new_settings_state,old_settings_state,originating_app_id,owner,owner_is_shared_drive,owner_is_team_drive,owner_team_drive_id,primary_event,removed_role,shared_drive_id,shared_drive_name,shared_drive_settings_change_type,target,team_drive_id,team_drive_settings_change_type,type,visibility
|
||||
NoActivities,,,,,,,,,,,,,,,,,,,,,,,,,,,,,0AERPpMc23znvUkPXYZ,Shared Drive 1,,,,,,
|
||||
view,,user1@domain.com,,100016760394505151666,False,,True,,,1SDNu-yzDapqjdJq4y4xKDUATJlOPRIBodpGGeGt1n4I,Digital Poetry Journal,document,drive,C03kt1z99,2023-10-18T21:27:51-07:00,-2856812962461786835,2600:1700:9580:f4b0:2127:3b2:dd21:3806,False,,,,263492796725,Shared Drive 2,True,True,0AMhgLk82dhsuUkPXYZ,True,,0AMhgLk82dhsuUkPXYZ,Shared Drive 2,,,0AMhgLk82dhsuUkPXYZ,,access,people_with_link
|
||||
edit,,user2@domain.com,,104066776037911136666,False,,True,,,1ZwHi_v-JVXH8W6zwgb7QYoUHrZD6NzIshJEqoTCaDD0,High School Scavenger Hunt,form,drive,C03kt1z99,2023-09-05T15:27:01-07:00,-1272095408714453395,50.204.178.246,False,,,,,Shared Drive 3,True,True,0AM8lpdkkJaKYUkPXYZ,True,,0AM8lpdkkJaKYUkPXYZ,Shared Drive 3,,,0AM8lpdkkJaKYUkPXYZ,,access,shared_internally
|
||||
view,,user1@domain.com,,100016760394505151666,False,,True,,,1SDNu-yzDapqjdJq4y4xKDUATJlOPRIBodpGGeGt1n4I,Digital Poetry Journal,document,drive,C03kt1z99,2025-10-18T21:27:51-07:00,-2856812962461786835,2600:1700:9580:f4b0:2127:3b2:dd21:3806,False,,,,263492796725,Shared Drive 2,True,True,0AMhgLk82dhsuUkPXYZ,True,,0AMhgLk82dhsuUkPXYZ,Shared Drive 2,,,0AMhgLk82dhsuUkPXYZ,,access,people_with_link
|
||||
edit,,user2@domain.com,,104066776037911136666,False,,True,,,1ZwHi_v-JVXH8W6zwgb7QYoUHrZD6NzIshJEqoTCaDD0,High School Scavenger Hunt,form,drive,C03kt1z99,2025-09-05T15:27:01-07:00,-1272095408714453395,50.204.178.246,False,,,,,Shared Drive 3,True,True,0AM8lpdkkJaKYUkPXYZ,True,,0AM8lpdkkJaKYUkPXYZ,Shared Drive 3,,,0AM8lpdkkJaKYUkPXYZ,,access,shared_internally
|
||||
```
|
||||
|
||||
## Customer and user reports parameters
|
||||
@ -207,6 +253,7 @@ gam report usage customer [todrive <ToDriveAttribute>*]
|
||||
[skipdates <Date>[:<Date>](,<Date>[:<Date>])*] [skipdaysofweek <DayOfWeek>(,<DayOfWeek>)*]
|
||||
[fields|parameters <String>]
|
||||
[convertmbtogb]
|
||||
(addcsvdata <FieldName> <String>)*
|
||||
```
|
||||
Limit the time period.
|
||||
* `start <Date>` - Default value is 30 days prior to `end <Date>`
|
||||
@ -218,8 +265,10 @@ Limit the time period.
|
||||
Option `convertmbtogb` causes GAM to convert parameters expressed in megabytes
|
||||
(name ends with _in_mb) to gigabytes (name converted to _in_gb) with two decimal places.
|
||||
|
||||
Add additional columns of data from the command line to the output.
|
||||
* `addcsvdata <FieldName> <String>`
|
||||
|
||||
### Example
|
||||
Jay provided this example.
|
||||
```
|
||||
gam report usage customer parameters meet:total_call_minutes,meet:total_meeting_minutes start_date 2020-03-01 skipdaysofweek sat,sun todrive
|
||||
```
|
||||
@ -261,6 +310,7 @@ gam report customers|customer|domain [todrive <ToDriveAttribute>*]
|
||||
[(fields|parameters <String>)|(services <CustomerServiceNameList>)]
|
||||
[noauthorizedapps]
|
||||
[convertmbtogb]
|
||||
(addcsvdata <FieldName> <String>)*
|
||||
```
|
||||
Specify the report date; the default is today's date.
|
||||
* `date <Date>` - A single date; there is one API call
|
||||
@ -273,6 +323,9 @@ Specify the report date; the default is today's date.
|
||||
Option `convertmbtogb` causes GAM to convert parameters expressed in megabytes
|
||||
(name ends with _in_mb) to gigabytes (name converted to _in_gb) with two decimal places.
|
||||
|
||||
Add additional columns of data from the command line to the output.
|
||||
* `addcsvdata <FieldName> <String>`
|
||||
|
||||
If no report is available for the specified date, can an earlier date be used?
|
||||
* `limitdatechanges -1' - Back up to earlier dates to find report data; this is the default.
|
||||
* `limitdatechanges 0 | nodatechange' - Do not report on an earlier date if no report data is available for the specified date.
|
||||
@ -326,6 +379,7 @@ gam report usage user [todrive]
|
||||
[skipdates <Date>[:<Date>](,<Date>[:<Date>])*] [skipdaysofweek <DayOfWeek>(,<DayOfWeek>)*]
|
||||
[fields|parameters <String>]
|
||||
[convertmbtogb]
|
||||
(addcsvdata <FieldName> <String>)*
|
||||
```
|
||||
Select the users for whom information is desired.
|
||||
* `user all` - All users, the default; there is one API call
|
||||
@ -344,6 +398,9 @@ Limit the time period.
|
||||
Option `convertmbtogb` causes GAM to convert parameters expressed in megabytes
|
||||
(name ends with _in_mb) to gigabytes (name converted to _in_gb) with two decimal places.
|
||||
|
||||
Add additional columns of data from the command line to the output.
|
||||
* `addcsvdata <FieldName> <String>`
|
||||
|
||||
## User reports
|
||||
User reports are generally available up to four days before the current date.
|
||||
```
|
||||
@ -368,6 +425,7 @@ gam report users|user [todrive <ToDriveAttribute>*]
|
||||
[aggregatebydate|aggregatebyuser [Boolean]]
|
||||
[maxresults <Number>]
|
||||
[convertmbtogb]
|
||||
(addcsvdata <FieldName> <String>)*
|
||||
```
|
||||
Select the users for whom information is desired.
|
||||
* `user all` - All users, the default; there is one API call
|
||||
@ -376,7 +434,7 @@ Select the users for whom information is desired.
|
||||
* `showorgunit` - Add a column labelled `orgUnitPath` to the output; an additional API call is made to get the email addresses of the users in `<OrgUnitPath>`
|
||||
* `select <UserTypeEntity>` - A selected collection of users, e.g., `select group staff@domain.com`; there is one API call per user
|
||||
|
||||
By default, when `user all` is specified (or no user specification in supplied), GAM backs up looking for data with a (basically) random user. If the randaom
|
||||
By default, when `user all` is specified (or no user specification in supplied), GAM backs up looking for data with a (basically) random user. If the random
|
||||
doesn't have any data, the command reports that no data was found. Use `allverifyuser <UserItem>` to specify a specific user to use to search for data.
|
||||
|
||||
Specify the report date; the default is today's date.
|
||||
@ -390,6 +448,16 @@ Specify the report date; the default is today's date.
|
||||
Option `convertmbtogb` causes GAM to convert parameters expressed in megabytes
|
||||
(name ends with _in_mb) to gigabytes (name converted to _in_gb) with two decimal places.
|
||||
|
||||
Add additional columns of data from the command line to the output.
|
||||
* `addcsvdata <FieldName> <String>`
|
||||
|
||||
This will be most useful when reading a CSV of user information and you want to include some of the user information,
|
||||
e.g., orgUnitPath, in the output.
|
||||
```
|
||||
gam redirect csv ./Users.csv print users fields ou
|
||||
gam redirect csv ./UserStorageInfo.csv multiprocess csv Users.csv gam report users user "~primaryEmail" parameters accounts:drive_used_quota_in_mb,accounts:gmail_used_quota_in_mb,accounts:gplus_photos_used_quota_in_mb,accounts:total_quota_in_mb,accounts:used_quota_in_mb,accounts:used_quota_in_percentage addcsvdata orgUnitPath "~orgUnitPath"
|
||||
```
|
||||
|
||||
If no report is available for the specified date, can an earlier date be used?
|
||||
* `limitdatechanges -1' - Back up to earlier dates to find report data; this is the default.
|
||||
* `limitdatechanges 0 | nodatechange' - Do not report on an earlier date if no report data is available for the specified date.
|
||||
@ -446,119 +514,124 @@ Report on users total storage usage.
|
||||
```
|
||||
gam report users parameters accounts:drive_used_quota_in_mb,accounts:gmail_used_quota_in_mb,accounts:gplus_photos_used_quota_in_mb,accounts:total_quota_in_mb,accounts:used_quota_in_mb,accounts:used_quota_in_percentage
|
||||
```
|
||||
Report on users total storage usage, include OrgUnitPath
|
||||
```
|
||||
gam redirect csv ./Users.csv print users fields ou
|
||||
gam redirect csv ./UserStorageInfo.csv multiprocess csv Users.csv gam report users user "~primaryEmail" parameters accounts:drive_used_quota_in_mb,accounts:gmail_used_quota_in_mb,accounts:gplus_photos_used_quota_in_mb,accounts:total_quota_in_mb,accounts:used_quota_in_mb,accounts:used_quota_in_percentage addcsvdata orgUnitPath "~orgUnitPath"
|
||||
```
|
||||
Report on email activity for individual users.
|
||||
```
|
||||
$ gam report users select users testuser1,testuser2,testuser3 fields gmail:num_emails_received,gmail:num_emails_sent range 2023-07-01 2023-07-07
|
||||
$ gam report users select users testuser1,testuser2,testuser3 fields gmail:num_emails_received,gmail:num_emails_sent range 2025-07-01 2025-07-07
|
||||
Getting Reports for testuser1@rdschool.org (1/3)
|
||||
Got 1 Report for testuser1@domain.com on 2023-07-01...
|
||||
Got 1 Report for testuser1@domain.com on 2023-07-02...
|
||||
Got 1 Report for testuser1@domain.com on 2023-07-03...
|
||||
Got 1 Report for testuser1@domain.com on 2023-07-04...
|
||||
Got 1 Report for testuser1@domain.com on 2023-07-05...
|
||||
Got 1 Report for testuser1@domain.com on 2023-07-06...
|
||||
Got 1 Report for testuser1@domain.com on 2023-07-07...
|
||||
Got 1 Report for testuser1@domain.com on 2025-07-01...
|
||||
Got 1 Report for testuser1@domain.com on 2025-07-02...
|
||||
Got 1 Report for testuser1@domain.com on 2025-07-03...
|
||||
Got 1 Report for testuser1@domain.com on 2025-07-04...
|
||||
Got 1 Report for testuser1@domain.com on 2025-07-05...
|
||||
Got 1 Report for testuser1@domain.com on 2025-07-06...
|
||||
Got 1 Report for testuser1@domain.com on 2025-07-07...
|
||||
Getting Reports for testuser2@domain.com (2/3)
|
||||
Got 1 Report for testuser2@domain.com on 2023-07-01...
|
||||
Got 1 Report for testuser2@domain.com on 2023-07-02...
|
||||
Got 1 Report for testuser2@domain.com on 2023-07-03...
|
||||
Got 1 Report for testuser2@domain.com on 2023-07-04...
|
||||
Got 1 Report for testuser2@domain.com on 2023-07-05...
|
||||
Got 1 Report for testuser2@domain.com on 2023-07-06...
|
||||
Got 1 Report for testuser2@domain.com on 2023-07-07...
|
||||
Got 1 Report for testuser2@domain.com on 2025-07-01...
|
||||
Got 1 Report for testuser2@domain.com on 2025-07-02...
|
||||
Got 1 Report for testuser2@domain.com on 2025-07-03...
|
||||
Got 1 Report for testuser2@domain.com on 2025-07-04...
|
||||
Got 1 Report for testuser2@domain.com on 2025-07-05...
|
||||
Got 1 Report for testuser2@domain.com on 2025-07-06...
|
||||
Got 1 Report for testuser2@domain.com on 2025-07-07...
|
||||
Getting Reports for testuser3@domain.com (3/3)
|
||||
Got 1 Report for testuser3@domain.com on 2023-07-01...
|
||||
Got 1 Report for testuser3@domain.com on 2023-07-02...
|
||||
Got 1 Report for testuser3@domain.com on 2023-07-03...
|
||||
Got 1 Report for testuser3@domain.com on 2023-07-04...
|
||||
Got 1 Report for testuser3@domain.com on 2023-07-05...
|
||||
Got 1 Report for testuser3@domain.com on 2023-07-06...
|
||||
Got 1 Report for testuser3@domain.com on 2023-07-07...
|
||||
Got 1 Report for testuser3@domain.com on 2025-07-01...
|
||||
Got 1 Report for testuser3@domain.com on 2025-07-02...
|
||||
Got 1 Report for testuser3@domain.com on 2025-07-03...
|
||||
Got 1 Report for testuser3@domain.com on 2025-07-04...
|
||||
Got 1 Report for testuser3@domain.com on 2025-07-05...
|
||||
Got 1 Report for testuser3@domain.com on 2025-07-06...
|
||||
Got 1 Report for testuser3@domain.com on 2025-07-07...
|
||||
email,date,gmail:num_emails_received,gmail:num_emails_sent
|
||||
testuser1@domain.com,2023-07-01,10,1
|
||||
testuser1@domain.com,2023-07-02,5,1
|
||||
testuser1@domain.com,2023-07-03,14,3
|
||||
testuser1@domain.com,2023-07-04,3,0
|
||||
testuser1@domain.com,2023-07-05,35,4
|
||||
testuser1@domain.com,2023-07-06,30,2
|
||||
testuser1@domain.com,2023-07-07,20,0
|
||||
testuser2@domain.com,2023-07-01,3,1
|
||||
testuser2@domain.com,2023-07-02,1,0
|
||||
testuser2@domain.com,2023-07-03,4,0
|
||||
testuser2@domain.com,2023-07-04,1,0
|
||||
testuser2@domain.com,2023-07-05,15,0
|
||||
testuser2@domain.com,2023-07-06,14,0
|
||||
testuser2@domain.com,2023-07-07,9,1
|
||||
testuser3@domain.com,2023-07-01,14,0
|
||||
testuser3@domain.com,2023-07-02,14,0
|
||||
testuser3@domain.com,2023-07-03,20,0
|
||||
testuser3@domain.com,2023-07-04,12,0
|
||||
testuser3@domain.com,2023-07-05,37,2
|
||||
testuser3@domain.com,2023-07-06,42,0
|
||||
testuser3@domain.com,2023-07-07,20,0
|
||||
testuser1@domain.com,2025-07-01,10,1
|
||||
testuser1@domain.com,2025-07-02,5,1
|
||||
testuser1@domain.com,2025-07-03,14,3
|
||||
testuser1@domain.com,2025-07-04,3,0
|
||||
testuser1@domain.com,2025-07-05,35,4
|
||||
testuser1@domain.com,2025-07-06,30,2
|
||||
testuser1@domain.com,2025-07-07,20,0
|
||||
testuser2@domain.com,2025-07-01,3,1
|
||||
testuser2@domain.com,2025-07-02,1,0
|
||||
testuser2@domain.com,2025-07-03,4,0
|
||||
testuser2@domain.com,2025-07-04,1,0
|
||||
testuser2@domain.com,2025-07-05,15,0
|
||||
testuser2@domain.com,2025-07-06,14,0
|
||||
testuser2@domain.com,2025-07-07,9,1
|
||||
testuser3@domain.com,2025-07-01,14,0
|
||||
testuser3@domain.com,2025-07-02,14,0
|
||||
testuser3@domain.com,2025-07-03,20,0
|
||||
testuser3@domain.com,2025-07-04,12,0
|
||||
testuser3@domain.com,2025-07-05,37,2
|
||||
testuser3@domain.com,2025-07-06,42,0
|
||||
testuser3@domain.com,2025-07-07,20,0
|
||||
```
|
||||
Report on email activity for individual users, aggregate by date across users.
|
||||
```
|
||||
$ gam report users select users testuser1,testuser2,testuser3@domain.com fields gmail:num_emails_received,gmail:num_emails_sent range 2023-07-01 2023-07-07 aggregatebydate
|
||||
$ gam report users select users testuser1,testuser2,testuser3@domain.com fields gmail:num_emails_received,gmail:num_emails_sent range 2025-07-01 2025-07-07 aggregatebydate
|
||||
Getting Reports for testuser1@domain.com (1/3)
|
||||
Got 1 Report for testuser1@domain.com on 2023-07-01...
|
||||
Got 1 Report for testuser1@domain.com on 2023-07-02...
|
||||
Got 1 Report for testuser1@domain.com on 2023-07-03...
|
||||
Got 1 Report for testuser1@domain.com on 2023-07-04...
|
||||
Got 1 Report for testuser1@domain.com on 2023-07-05...
|
||||
Got 1 Report for testuser1@domain.com on 2023-07-06...
|
||||
Got 1 Report for testuser1@domain.com on 2023-07-07...
|
||||
Got 1 Report for testuser1@domain.com on 2025-07-01...
|
||||
Got 1 Report for testuser1@domain.com on 2025-07-02...
|
||||
Got 1 Report for testuser1@domain.com on 2025-07-03...
|
||||
Got 1 Report for testuser1@domain.com on 2025-07-04...
|
||||
Got 1 Report for testuser1@domain.com on 2025-07-05...
|
||||
Got 1 Report for testuser1@domain.com on 2025-07-06...
|
||||
Got 1 Report for testuser1@domain.com on 2025-07-07...
|
||||
Getting Reports for testuser2@domain.com (2/3)
|
||||
Got 1 Report for testuser2@domain.com on 2023-07-01...
|
||||
Got 1 Report for testuser2@domain.com on 2023-07-02...
|
||||
Got 1 Report for testuser2@domain.com on 2023-07-03...
|
||||
Got 1 Report for testuser2@domain.com on 2023-07-04...
|
||||
Got 1 Report for testuser2@domain.com on 2023-07-05...
|
||||
Got 1 Report for testuser2@domain.com on 2023-07-06...
|
||||
Got 1 Report for testuser2@domain.com on 2023-07-07...
|
||||
Got 1 Report for testuser2@domain.com on 2025-07-01...
|
||||
Got 1 Report for testuser2@domain.com on 2025-07-02...
|
||||
Got 1 Report for testuser2@domain.com on 2025-07-03...
|
||||
Got 1 Report for testuser2@domain.com on 2025-07-04...
|
||||
Got 1 Report for testuser2@domain.com on 2025-07-05...
|
||||
Got 1 Report for testuser2@domain.com on 2025-07-06...
|
||||
Got 1 Report for testuser2@domain.com on 2025-07-07...
|
||||
Getting Reports for testuser3@domain.com (3/3)
|
||||
Got 1 Report for testuser3@domain.com on 2023-07-01...
|
||||
Got 1 Report for testuser3@domain.com on 2023-07-02...
|
||||
Got 1 Report for testuser3@domain.com on 2023-07-03...
|
||||
Got 1 Report for testuser3@domain.com on 2023-07-04...
|
||||
Got 1 Report for testuser3@domain.com on 2023-07-05...
|
||||
Got 1 Report for testuser3@domain.com on 2023-07-06...
|
||||
Got 1 Report for testuser3@domain.com on 2023-07-07...
|
||||
Got 1 Report for testuser3@domain.com on 2025-07-01...
|
||||
Got 1 Report for testuser3@domain.com on 2025-07-02...
|
||||
Got 1 Report for testuser3@domain.com on 2025-07-03...
|
||||
Got 1 Report for testuser3@domain.com on 2025-07-04...
|
||||
Got 1 Report for testuser3@domain.com on 2025-07-05...
|
||||
Got 1 Report for testuser3@domain.com on 2025-07-06...
|
||||
Got 1 Report for testuser3@domain.com on 2025-07-07...
|
||||
date,gmail:num_emails_received,gmail:num_emails_sent
|
||||
2023-07-01,27,2
|
||||
2023-07-02,20,1
|
||||
2023-07-03,38,3
|
||||
2023-07-04,16,0
|
||||
2023-07-05,87,6
|
||||
2023-07-06,86,2
|
||||
2023-07-07,49,1
|
||||
2025-07-01,27,2
|
||||
2025-07-02,20,1
|
||||
2025-07-03,38,3
|
||||
2025-07-04,16,0
|
||||
2025-07-05,87,6
|
||||
2025-07-06,86,2
|
||||
2025-07-07,49,1
|
||||
```
|
||||
Report on email activity for individual users, aggregate by user across dates.
|
||||
```
|
||||
$ gam report users select users testuser1,testuser2,testuser3@domain.com fields gmail:num_emails_received,gmail:num_emails_sent range 2023-07-01 2023-07-07 aggregatebyuser
|
||||
$ gam report users select users testuser1,testuser2,testuser3@domain.com fields gmail:num_emails_received,gmail:num_emails_sent range 2025-07-01 2025-07-07 aggregatebyuser
|
||||
Getting Reports for testuser1@domain.com (1/3)
|
||||
Got 1 Report for testuser1@domain.com on 2023-07-01...
|
||||
Got 1 Report for testuser1@domain.com on 2023-07-02...
|
||||
Got 1 Report for testuser1@domain.com on 2023-07-03...
|
||||
Got 1 Report for testuser1@domain.com on 2023-07-04...
|
||||
Got 1 Report for testuser1@domain.com on 2023-07-05...
|
||||
Got 1 Report for testuser1@domain.com on 2023-07-06...
|
||||
Got 1 Report for testuser1@domain.com on 2023-07-07...
|
||||
Got 1 Report for testuser1@domain.com on 2025-07-01...
|
||||
Got 1 Report for testuser1@domain.com on 2025-07-02...
|
||||
Got 1 Report for testuser1@domain.com on 2025-07-03...
|
||||
Got 1 Report for testuser1@domain.com on 2025-07-04...
|
||||
Got 1 Report for testuser1@domain.com on 2025-07-05...
|
||||
Got 1 Report for testuser1@domain.com on 2025-07-06...
|
||||
Got 1 Report for testuser1@domain.com on 2025-07-07...
|
||||
Getting Reports for testuser2@domain.com (2/3)
|
||||
Got 1 Report for testuser2@domain.com on 2023-07-01...
|
||||
Got 1 Report for testuser2@domain.com on 2023-07-02...
|
||||
Got 1 Report for testuser2@domain.com on 2023-07-03...
|
||||
Got 1 Report for testuser2@domain.com on 2023-07-04...
|
||||
Got 1 Report for testuser2@domain.com on 2023-07-05...
|
||||
Got 1 Report for testuser2@domain.com on 2023-07-06...
|
||||
Got 1 Report for testuser2@domain.com on 2023-07-07...
|
||||
Got 1 Report for testuser2@domain.com on 2025-07-01...
|
||||
Got 1 Report for testuser2@domain.com on 2025-07-02...
|
||||
Got 1 Report for testuser2@domain.com on 2025-07-03...
|
||||
Got 1 Report for testuser2@domain.com on 2025-07-04...
|
||||
Got 1 Report for testuser2@domain.com on 2025-07-05...
|
||||
Got 1 Report for testuser2@domain.com on 2025-07-06...
|
||||
Got 1 Report for testuser2@domain.com on 2025-07-07...
|
||||
Getting Reports for testuser3@domain.com (3/3)
|
||||
Got 1 Report for testuser3@domain.com on 2023-07-01...
|
||||
Got 1 Report for testuser3@domain.com on 2023-07-02...
|
||||
Got 1 Report for testuser3@domain.com on 2023-07-03...
|
||||
Got 1 Report for testuser3@domain.com on 2023-07-04...
|
||||
Got 1 Report for testuser3@domain.com on 2023-07-05...
|
||||
Got 1 Report for testuser3@domain.com on 2023-07-06...
|
||||
Got 1 Report for testuser3@domain.com on 2023-07-07...
|
||||
Got 1 Report for testuser3@domain.com on 2025-07-01...
|
||||
Got 1 Report for testuser3@domain.com on 2025-07-02...
|
||||
Got 1 Report for testuser3@domain.com on 2025-07-03...
|
||||
Got 1 Report for testuser3@domain.com on 2025-07-04...
|
||||
Got 1 Report for testuser3@domain.com on 2025-07-05...
|
||||
Got 1 Report for testuser3@domain.com on 2025-07-06...
|
||||
Got 1 Report for testuser3@domain.com on 2025-07-07...
|
||||
email,gmail:num_emails_received,gmail:num_emails_sent
|
||||
testuser1@domain.com,117,11
|
||||
testuser2@domain.com,47,2
|
||||
@ -566,7 +639,7 @@ testuser3@domain.com,159,2
|
||||
```
|
||||
|
||||
## Monthly Report
|
||||
### An example, running this on 3rd December 2020;-
|
||||
### An example, running this on 3rd December 2025.
|
||||
If combined with a scheduled task or cron job, this will produce an ongoing report with a new tab/sheet for each month.
|
||||
```
|
||||
$ gam report usage customer parameters meet:total_call_minutes,meet:total_meeting_minutes skipdaysofweek sat,sun previousmonths 1 todrive tdfileid <File ID> tdtitle "Meet Usage" tdtimeformat %Y-%m-%d tdaddsheet tdsheet "" tdsheettimeformat "%B %Y" tdsheetdaysoffset 6
|
||||
@ -575,9 +648,9 @@ $ gam report usage customer parameters meet:total_call_minutes,meet:total_meetin
|
||||
* **gam report usage customer parameters meet:total_call_minutes,meet:total_meeting_minutes** - The GAM command
|
||||
* **skipdaysofweek sat,sun** - exclude Sat & Sun, so only working days
|
||||
* **previousmonths 1** - run against the previous months date range (regardless of how many days in the month, leap year etc)
|
||||
* **todrive tdfileid <File ID> tdtitle "Meet Usage" tdtimeformat %Y-%m-%d** - write the data to an existing Google Sheet and append with current date, so it will be called "Meet Usage - 2020-12-03"
|
||||
* **todrive tdfileid <File ID> tdtitle "Meet Usage" tdtimeformat %Y-%m-%d** - write the data to an existing Google Sheet and append with current date, so it will be called "Meet Usage - 2025-12-03"
|
||||
* **tdaddsheet tdsheet ""** - Add a new tab/sheet with no name
|
||||
* **tdsheettimeformat "%B %Y" tdsheetdaysoffset 6** - give the new tab/sheet a time stamp backdated by 6 days of 'Month Year', so for this example "November 2020", which will become the name of the new tab/sheet. The offset number must take you back in time into the previous month.
|
||||
* **tdsheettimeformat "%B %Y" tdsheetdaysoffset 6** - give the new tab/sheet a time stamp backdated by 6 days of 'Month Year', so for this example "November 2025", which will become the name of the new tab/sheet. The offset number must take you back in time into the previous month.
|
||||
|
||||
**Notes**
|
||||
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
# Reseller
|
||||
- [API documentation](#api-documentation)
|
||||
- [Notes](#notes)
|
||||
- [Manage Multiple Domains](#manage-multiple-domains)
|
||||
- [Definitions](#definitions)
|
||||
- [Manage Resold Customers](#manage-resold-customers)
|
||||
@ -12,20 +11,6 @@
|
||||
* [Reseller API - Customers](https://developers.google.com/admin-sdk/reseller/v1/reference/customers)
|
||||
* [Reseller API - Subscriptions](https://developers.google.com/admin-sdk/reseller/v1/reference/subscriptions)
|
||||
|
||||
## Notes
|
||||
|
||||
Updated handling of `seats` option in `gam create|update resoldsubscription` to properly assign
|
||||
the API fields `numberOfSeats` and `maximumNumberOfSeats`.
|
||||
Prior to version 6.50.00, this is how the `seats <NumberOfSeats> <MaximumNumberOfSeats>` option was processed:
|
||||
* Plan name `ANNUAL_MONTHLY_PAY` or `ANNUAL_YEARLY_PAY`
|
||||
* `seats <NumberOfSeats>` - `<NumberOfSeats>` was properly passed to the API
|
||||
* `seats <NumberOfSeats> <MaximumNumberOfSeats>` - `<NumberOfSeats>` was properly passed to the API; `<MaximumNumberOfSeats>` was passed to the API which ignored it
|
||||
* Plan name `FLEXIBLE` or `TRIAL`
|
||||
* `seats <NumberOfSeats>` - `<NumberOfSeats>` was improperly passed to the API; an API error was generated
|
||||
* `seats <NumberOfSeats> <MaximumNumberOfSeats>` - `<MaximumNumberOfSeats>` was properly passed to the API; `<NumberOfSeats>` was passed to the API which ignored it
|
||||
|
||||
Now, you can still use the above option which has been corrected or you can specify `seats <Number>` which will be properly passed in the correct form to the API based on plan name.
|
||||
|
||||
## Manage Multiple Domains
|
||||
Thanks to Duncan Isaksen-Loxton for a script to help manage multiple domains.
|
||||
|
||||
|
||||
@ -84,6 +84,7 @@ See [Collections of Items](Collections-of-Items)
|
||||
(zipcode|postalcode <String>)
|
||||
|
||||
<ResourceAttribute> ::=
|
||||
(autoacceptinvitations [<Boolean>])|
|
||||
(addfeatures <FeatureNameList>)|
|
||||
(buildingid <BuildingID>)|
|
||||
(capacity <Number>)|
|
||||
@ -99,6 +100,7 @@ See [Collections of Items](Collections-of-Items)
|
||||
|
||||
<ResourceFieldName> ::=
|
||||
acls|
|
||||
autoacceptinvitations|
|
||||
buildingid|
|
||||
calendar|
|
||||
capacity|
|
||||
@ -512,7 +514,9 @@ To retrieve the count with `showitemcountonly`:
|
||||
Linux/MacOS
|
||||
count=$(gam print resources showitemcountonly)
|
||||
Windows PowerShell
|
||||
count = & gam print resources showitemcountonly
|
||||
$count = & gam print resources showitemcountonly
|
||||
Windows Command Prompt
|
||||
for /f "delims=" %a in ('gam print resources showitemcountonly') do set count=%a
|
||||
```
|
||||
|
||||
## Manage resource calendar ACLs
|
||||
|
||||
@ -5,6 +5,8 @@
|
||||
## Introduction
|
||||
GAM7 can run on a Linux or Windows [Google Compute Engine (GCE)](https://cloud.google.com/products/compute) virtual machine and use the attached service account to access Google Workspace APIs. The advantage of this configuration is that no service account private key is accessible to GAM7 directly and [there is no risk of the key being stolen/lost](https://cloud.google.com/iam/docs/best-practices-for-managing-service-account-keys#alternatives).
|
||||
|
||||
**Note**: This method is recommended when running GAM **inside** Google Cloud. If you're running GAM **outside** Google Cloud (on-premises, other cloud providers, CI/CD systems), consider [Workload Identity Federation](https://github.com/GAM-team/GAM/wiki/Using-GAM7-with-keyless-authentication-Workload-Identity-Federation) instead - Google's officially recommended keyless authentication method for external environments.
|
||||
|
||||
## Setup Steps
|
||||
1. Create a [GCP project](https://cloud.google.com/resource-manager/docs/creating-managing-projects).
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Scripts
|
||||
|
||||
These scripts can be used to enhance GAM's capabilities; all are supported with Advanced GAM,
|
||||
many are supported with Legacy GAM. They require that Python 3 be installed on you computer.
|
||||
These scripts can be used to enhance GAM's capabilities; all are supported with GAM7,
|
||||
many are supported with Legacy GAM. They require that Python 3.10 or higher be installed on your computer.
|
||||
|
||||
* https://github.com/taers232c/GAM-Scripts3
|
||||
* https://www.python.org/
|
||||
|
||||
@ -3,7 +3,8 @@
|
||||
- [Query documentation](#query-documentation)
|
||||
- [Definitions](#definitions)
|
||||
- [Introduction](#introduction)
|
||||
- [GUI API permission name mapping](#gui-api-permission-name-mapping)
|
||||
- [API GUI permission name mapping](#api-gui-permission-name-mapping)
|
||||
- [API GUI restriction name mapping](#api-gui-restriction-name-mapping)
|
||||
- [Display Shared Drive themes](#display-shared-drive-themes)
|
||||
- [Manage Shared Drives](#manage-shared-drives)
|
||||
- [Create a Shared Drive](#create-a-shared-drive)
|
||||
@ -40,6 +41,7 @@
|
||||
* [Move content to Shared Drives](https://support.google.com/a/answer/7374057)
|
||||
* [Shared Drive Limits](https://support.google.com/a/users/answer/7338880)
|
||||
* [Shared Drives in Org Units](https://support.google.com/a/answer/7337635)
|
||||
* [Shared Drive Restrictions](https://developers.google.com/workspace/drive/api/guides/manage-shareddrives#backward-compatibility)
|
||||
|
||||
## Query documentation
|
||||
* [Shared Drives Search](https://developers.google.com/drive/api/guides/search-shareddrives)
|
||||
@ -79,6 +81,15 @@
|
||||
<ColorValue> ::= <ColorName>|<ColorHex>
|
||||
```
|
||||
```
|
||||
<CSVFileInput> ::=
|
||||
((<FileName> [charset <Charset>] )|
|
||||
(gsheet <UserGoogleSheet>)|
|
||||
(gdoc <UserGoogleDoc>)|
|
||||
(gcscsv <StorageBucketObjectName>)|
|
||||
(gcsdoc <StorageBucketObjectName>))
|
||||
[warnifnodata] [columndelimiter <Character>] [noescapechar <Boolean>] [quotechar <Character>]
|
||||
[endcsv|(fields <FieldNameList>)]
|
||||
|
||||
<JSONData> ::= (json [charset <Charset>] <String>) | (json file <FileName> [charset <Charset>]) |
|
||||
|
||||
<OrganizerType> ::= user|group
|
||||
@ -93,7 +104,8 @@
|
||||
<REMatchPattern> ::= <RegularExpression>
|
||||
<RESearchPattern> ::= <RegularExpression>
|
||||
<RESubstitution> ::= <String>>
|
||||
|
||||
```
|
||||
```
|
||||
<DriveFileOrderByFieldName> ::=
|
||||
createddate|createdtime|
|
||||
folder|
|
||||
@ -207,15 +219,8 @@
|
||||
downloadrestrictedforwriters|downloadrestrictions.restrictedforwriters|
|
||||
drivemembersonly|teammembersonly|
|
||||
sharingfoldersrequiresorganizerpermission
|
||||
|
||||
Each pair of restrictions below are equivalent:
|
||||
|
||||
allowcontentmanagerstosharefolders true
|
||||
sharingfoldersrequiresorganizerpermission false
|
||||
|
||||
allowcontentmanagerstosharefolders false
|
||||
sharingfoldersrequiresorganizerpermission true
|
||||
```
|
||||
|
||||
## Introduction
|
||||
A domain administrator with the Drive and Docs administrator privilege can search for Shared Drives or update permissions for Shared Drives
|
||||
owned by their organization, regardless of the admin's membership in any given Shared Drive.
|
||||
@ -225,15 +230,47 @@ Three forms of the commands are available:
|
||||
* `gam <UserTypeEntity> action ... adminaccess` - The user named in `<UserTypeEntty>` is used, adminaccess indicates that the user is a domain administrator
|
||||
* `gam <UserTypeEntity> action ...` - The user named in `<UserTypeEntty>` is used, access is limited to drives for which they are an organizer
|
||||
|
||||
## GUI API permission name mapping
|
||||
## API GUI permission name mapping
|
||||
|
||||
| GUI setting | API setting |
|
||||
|------------|------------|
|
||||
| Manager | organizer |
|
||||
| Content manager | fileOrganizer |
|
||||
| Contributor | writer |
|
||||
| Commenter | commenter |
|
||||
| Viewer | reader |
|
||||
| API setting | GUI setting |
|
||||
|-------------|-------------|
|
||||
| organizer | Manager |
|
||||
| fileOrganizer | Content manager |
|
||||
| writer | Contributor |
|
||||
| commenter | Commenter |
|
||||
| reader | Viewer |
|
||||
|
||||
## API GUI restriction name mapping
|
||||
| API Setting | Description |
|
||||
|-------------|-------------|
|
||||
| adminManagedRestrictions | Whether administrative privileges on this shared drive are required to modify restrictions. |
|
||||
| domainUsersOnly | Whether access to this shared drive and items inside this shared drive is restricted to users of the domain to which this shared drive belongs. |
|
||||
| driveMembersOnly | Whether access to items inside this shared drive is restricted to its members. |
|
||||
| allowContentManagersToShareFolders (GAM defined) | If true, users with either the organizer role or the file organizer role can share folders. If false, only users with the organizer role can share folders. |
|
||||
| sharingFoldersRequiresOrganizerPermission | If true, only users with the organizer role can share folders. If false, users with either the organizer role or the file organizer role can share folders. |
|
||||
| copyRequiresWriterPermission | Whether the options to copy, print, or download files inside this shared drive, should be disabled for readers and commenters. |
|
||||
| downloadRestrictions.restrictedForWriters | Whether download and copy is restricted for writers. If true, download is also restricted for readers. |
|
||||
| downloadRestrictions.restrictedForReaders | Whether download and copy is restricted for readers. |
|
||||
|
||||
| API Setting | False | True | GUI Setting | Checked | Unchecked |
|
||||
|-------------|-------|------|-------------|---------|-----------|
|
||||
| adminManagedRestrictions | X | | Shared drive settings can be modified | | |
|
||||
| adminManagedRestrictions | | X | Shared drive settings can **not** be modified | | |
|
||||
| | | | **Access** |
|
||||
| domainUsersOnly | X | | Allow people outside of Domain name to access files | X | |
|
||||
| domainUsersOnly | | X | Allow people outside of Domain name to access files | | X |
|
||||
| driveMembersOnly | X | | Allow people who aren't shared drive members to access files | X | |
|
||||
| driveMembersOnly | | X | Allow people who aren't shared drive members to access files | | X |
|
||||
| | | | **Role permissions** |
|
||||
| allowContentManagersToShareFolders | X | | Allow content managers to share folders | | X |
|
||||
| allowContentManagersToShareFolders | | X | Allow content managers to share folders | X | |
|
||||
| sharingFoldersRequiresOrganizerPermission | X | | Allow content managers to share folders | X | |
|
||||
| sharingFoldersRequiresOrganizerPermission | | X | Allow content managers to share folders | | X |
|
||||
| | | | **People who can download, copy, and print** |
|
||||
| downloadRestrictions.restrictedForWriters | X | | Contributors and content managers | X | |
|
||||
| downloadRestrictions.restrictedForWriters | | X | Contributors and content managers | | X |
|
||||
| downloadRestrictions.restrictedForReaders | X | | Commenters and viewers | X | |
|
||||
| downloadRestrictions.restrictedForReaders | | X | Commenters and viewers | | X |
|
||||
|
||||
## Display Shared Drive themes
|
||||
```
|
||||
@ -262,6 +299,10 @@ gam [<UserTypeEntity>] create shareddrive <Name>
|
||||
* `[restrictions.]<SharedDriveRestrictionsSubfieldName> <Boolean>` - Set Shared Drive Restrictions
|
||||
* `hide <Boolean>` - Set Shared Drive visibility
|
||||
|
||||
`<SharedDriveRestrictionsSubfieldName>` `copyrequireswriterpermission` can not be used with
|
||||
`downloadrestrictedforreaders|downloadrestrictions.restrictedforreaders` or
|
||||
`downloadrestrictedforreadersdownloadrestrictions.restrictedforwriters`.
|
||||
|
||||
If any attributes other than `themeid` are specified, GAM must create the Drive and then update the Drive attributes.
|
||||
Even though the Create API returns success, the Update API fails and reports that the Drive does not exist.
|
||||
* `errorretries <Integer>` - Number of create/update error retries; default value 5, range 0-10
|
||||
@ -289,6 +330,8 @@ Linux/MacOS
|
||||
teamDriveId=$(gam create shareddrive ... returnidonly)
|
||||
Windows PowerShell
|
||||
$teamDriveId = & gam create shareddrive ... returnidonly
|
||||
Windows Command Prompt
|
||||
for /f "delims=" %a in ('gam create shareddrive ... returnidonly') do set teamDriveId=%a
|
||||
```
|
||||
|
||||
## Bulk Create Shared Drives
|
||||
@ -342,6 +385,10 @@ gam [<UserTypeEntity>] update shareddrive <SharedDriveEntity> [name <Name>]
|
||||
* `[restrictions.]<SharedDriveRestrictionsSubfieldName> <Boolean>` - Set Shared Drive Restrictions
|
||||
* `hidden <Boolean>` - Set Shared Drive visibility
|
||||
|
||||
`<SharedDriveRestrictionsSubfieldName>` `copyrequireswriterpermission` can not be used with
|
||||
`downloadrestrictedforreaders|downloadrestrictions.restrictedforreaders` or
|
||||
`downloadrestrictedforreadersdownloadrestrictions.restrictedforwriters`.
|
||||
|
||||
* `ou|org|orgunit <OrgUnitItem>` - See: https://workspaceupdates.googleblog.com/2022/05/shared-drives-in-organizational-units-open-beta.html
|
||||
|
||||
This option is only available when the command is run as an administrator.
|
||||
@ -511,7 +558,9 @@ To retrieve the count with `showitemcountonly`:
|
||||
Linux/MacOS
|
||||
count=$(gam print shareddrives showitemcountonly)
|
||||
Windows PowerShell
|
||||
count = & gam print shareddrives showitemcountonly
|
||||
$count = & gam print shareddrives showitemcountonly
|
||||
Windows Command Prompt
|
||||
for /f "delims=" %a in ('gam print shareddrives showitemcountonly') do set count=%a
|
||||
```
|
||||
|
||||
## Display all Shared Drives with a specific organizer
|
||||
@ -564,7 +613,9 @@ To retrieve the count with `showitemcountonly`:
|
||||
Linux/MacOS
|
||||
count=$(gam print oushareddrives showitemcountonly)
|
||||
Windows PowerShell
|
||||
count = & gam print oushareddrives showitemcountonly
|
||||
$count = & gam print oushareddrives showitemcountonly
|
||||
Windows Command Prompt
|
||||
for /f "delims=" %a in ('gam print oushareddrives showitemcountonly') do set count=%a
|
||||
```
|
||||
|
||||
## Manage Shared Drive access
|
||||
@ -617,15 +668,23 @@ When deleting permissions from JSON data, permissions with role `owner` true are
|
||||
These commands are used to transfer ACLs from one Shared Drive to another.
|
||||
* `copy` - Copy all ACLs from the source Shared Drive to the target Shared Drive. The role of an existing ACL in the target Shared Drive will never be reduced.
|
||||
* `sync` - Add/delete/update ACLs in the target Shared Drive to match those in the source Shared Drive.
|
||||
|
||||
* `<UserTypeEntity>` - Not Specified
|
||||
* All Shared Drives must be in the same workspace as the admin in `oauth2.txt`.
|
||||
* `<UserTypeEntity>` - Specified
|
||||
* `adminaccess|asadmin` specified - All Shared Drives must be in the same workspace as `<UserTypeEntity>`.
|
||||
* `adminaccess|asadmin` not specified - Shared Drives can be in separate workspaces if `<UserTypeEntity>` in a manager of all of them.
|
||||
```
|
||||
gam [<UserTypeEntity>] copy shareddriveacls <SharedDriveEntity> to <SharedDriveEntity>
|
||||
[showpermissionsmessages [<Boolean>]]
|
||||
[excludepermissionsfromdomains|includepermissionsfromdomains <DomainNameList>]
|
||||
(mappermissionsemail <EmailAddress> <EmailAddress)* [mappermissionsemailfile <CSVFileInput> endcsv]
|
||||
(mappermissionsdomain <DomainName> <DomainName>)*
|
||||
[adminaccess|asadmin]
|
||||
gam [<UserTypeEntity>] sync shareddriveacls <SharedDriveEntity> with <SharedDriveEntity>
|
||||
[showpermissionsmessages [<Boolean>]]
|
||||
[excludepermissionsfromdomains|includepermissionsfromdomains <DomainNameList>]
|
||||
(mappermissionsemail <EmailAddress> <EmailAddress)* [mappermissionsemailfile <CSVFileInput> endcsv]
|
||||
(mappermissionsdomain <DomainName> <DomainName>)*
|
||||
[adminaccess|asadmin]
|
||||
```
|
||||
@ -633,8 +692,14 @@ When `excludepermissionsfromdomains <DomainNameList>` is specified, any ACL that
|
||||
|
||||
When `includepermissionsfromdomains <DomainNameList>` is specified, only ACLs that reference a domain in `<DomainNameList>` will be copied.
|
||||
|
||||
When `mappermissionsemail <EmailAddress> <EmailAddress>` is specifed, an ACL that references the first `<EmailAddress>` will be modified
|
||||
to reference the second `<EmailAddress>` when copied; the original ACL is not modified. The option can be repeated if multiple email addresses are to be mapped.
|
||||
|
||||
Bulk permission email address mapping can be specified with `mappermissionsemailfile <CSVFileInput> endcsv`.
|
||||
`<CSVFileInput>` must include these columns: `sourceEmail` and `destinationEmail`.
|
||||
|
||||
When `mappermissionsdomain <DomainName> <DomainName>` is specifed, any ACL that references the first `<DomainName>` will be modified
|
||||
to reference the second `<DonainName>` when copied; the original ACL is not modified. The option can be repeated if multiple domain names are to me mapped.
|
||||
to reference the second `<DomainName>` when copied; the original ACL is not modified. The option can be repeated if multiple domain names are to be mapped.
|
||||
|
||||
## Display Shared Drive access
|
||||
|
||||
|
||||
@ -27,6 +27,8 @@ You can modify the default todrive behavior with options in `gam.cfg` or on the
|
||||
|
||||
## Definitions
|
||||
```
|
||||
<DateTimeFormat> ::= <String>
|
||||
See: https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes
|
||||
<DriveFileID> ::= <String>
|
||||
<DriveFolderID> ::= <String>
|
||||
<TimeZone> ::= <String>
|
||||
@ -194,11 +196,11 @@ direct the uploaded file to a particular user and location and add a timestamp t
|
||||
(tdreturnidonly [<Boolean>])|
|
||||
(tdshare <EmailAddress> commenter|reader|writer)*|
|
||||
(tdsheet (id:<Number>)|<String>)|
|
||||
(tdsheettimestamp [<Boolean>] [tdsheettimeformat <String>])
|
||||
(tdsheettimestamp [<Boolean>] [tdsheettimeformat <DateTimeFormat>])
|
||||
(tdsheettitle <String>)|
|
||||
(tdsubject <String>)|
|
||||
([tdsheetdaysoffset <Number>] [tdsheethoursoffset <Number>])|
|
||||
(tdtimestamp [<Boolean>] [tdtimeformat <String>]
|
||||
(tdtimestamp [<Boolean>] [tdtimeformat <DateTimeFormat>]
|
||||
([tddaysoffset <Number>] [tdhoursoffset <Number>])|
|
||||
(tdtimezone <TimeZone>)|
|
||||
(tdtitle <String>)|
|
||||
@ -328,16 +330,16 @@ you want the updated data copied to `Latest` so you don't have to remember what
|
||||
gam redirect csv - todrive tdfileid <DriveFileID> tdupdatesheet tdsheet Tuesday tdbackupsheet "Backup Tuesday" tdcopysheet "Latest" ...
|
||||
```
|
||||
## Limited Service Account Access
|
||||
If you want to limit a user's service account access but still allow `todrive',
|
||||
issue the following command and authorize the additional service account APIs:
|
||||
If you want to limit a user's service account access to Drive, Gmail and Sheets but still allow `todrive`,
|
||||
issue the following command and make these settings:
|
||||
```
|
||||
gam user user@domain.com update serviceaccount`
|
||||
gam user user@domain.com update serviceaccount
|
||||
|
||||
Authorize these APIs:
|
||||
|
||||
Drive API - todrive
|
||||
Gmail API - Send Messages - including todrive
|
||||
Sheets API - todrive
|
||||
[ ] 20) Drive API (supports readonly)
|
||||
[*] 22) Drive API - write todrive data - has access to all Drive
|
||||
[*] 31) Gmail API - Send Messages - including todrive
|
||||
[ ] 42) Sheets API (supports readonly)
|
||||
[*] 44) Sheets API - write todrive data - has access to all Sheets
|
||||
```
|
||||
|
||||
## No Service Account Access
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user