Compare commits

...

28 Commits
v4.86 ... v4.88

Author SHA1 Message Date
Jay Lee
a06776bbbd Update var.py 2019-06-24 10:12:32 -04:00
Jay Lee
aecd725a71 Update .travis.yml 2019-06-21 21:11:59 -04:00
Jay Lee
ad8e9364e1 Update .travis.yml 2019-06-21 20:49:33 -04:00
Ross Scroggs
b812fef1c3 Update draft/import/insert/sendemail (#959)
* Update draft/import/insert/sendemail

If possible, use same option names as already exist in my Gam for same commands.

Include charset with file.

* Code cleanup
2019-06-21 20:22:44 -04:00
Ross Scroggs
56371214b0 Document gam <UserTypeEntity> sendemail (#958) 2019-06-20 18:32:28 -04:00
Jay Lee
3669a86f41 use gam.py, send to admin 2019-06-20 16:40:27 -04:00
Jay Lee
4095bf63ef Email send/import/insert/draft 2019-06-20 16:29:46 -04:00
Jay Lee
d75321ca8a put on the breaks :-) 2019-06-20 11:52:07 -04:00
Jay Lee
c931e1cdd7 retry on version extended also 2019-06-20 11:43:45 -04:00
Jay Lee
ea8cda72c7 full email on send 2019-06-19 16:46:14 -04:00
Jay Lee
f0bcd7888a don't use runCmdforUsers() 2019-06-19 15:53:48 -04:00
Jay Lee
6a08a66221 fi 2019-06-19 15:45:58 -04:00
Jay Lee
5e58edf598 command to send (spartan) test messages 2019-06-19 15:41:01 -04:00
Jay Lee
1e79772ec1 catch/retry DNS errors if we need to refresh token on startup 2019-06-19 15:09:58 -04:00
Jay Lee
3461ce053f cleanup print jobs 2019-06-19 12:49:50 -04:00
Jay Lee
9c84ce30e8 retry on ServerNotFound DNS issues 2019-06-19 12:15:23 -04:00
Jay Lee
93ca63e133 Merge branch 'master' of https://github.com/jay0lee/GAM 2019-06-18 14:54:49 -04:00
Jay Lee
e367c86fce fix issue with over-escaping aue model 2019-06-18 14:54:33 -04:00
Ross Scroggs
34dc12994a Three updates (#956)
Add notregex to GAM_CSV_ROW_FILTER to allow selection rows that don't have a particular value

Standarize formatting timestamps

Display mobile.patchSecurityLevel as a date/time
2019-06-18 14:03:19 -04:00
Jay Lee
df4de5ce4b move CrOS AUE dates to dynamic file 2019-06-18 11:20:24 -04:00
Jay Lee
ea630480b2 create cros-aue-dates.json 2019-06-18 11:14:43 -04:00
Jay Lee
db1159cd0d remove custom code verifier patch and force google-auth-oauthlib==0.4.0.
Next release may break code_verifier auto-gen based on
https://github.com/googleapis/google-auth-library-python-oauthlib/pull/48 so we lock at 0.4.0 for now.
2019-06-18 10:23:50 -04:00
Ross Scroggs
ccf1dc0585 Update calendar ACL commands (#953)
* Update calendar ACL commands

Your change was not backwards compatible;  sys.argv[4] was previously ignored

* Code cleanup
2019-06-17 19:52:08 -04:00
Jay Lee
1cb96cf057 Print Calendars ACLs, delete by ACL id 2019-06-12 14:25:10 -04:00
Jay Lee
2bc8a114c1 GAM 4.87 2019-06-12 13:46:14 -04:00
Ross Scroggs
7f183b9edc Prevent traps when filtering CSV rows (#951) 2019-06-12 13:44:01 -04:00
Jay Lee
f86be17834 Merge branch 'master' of https://github.com/jay0lee/GAM 2019-06-12 13:42:48 -04:00
Jay Lee
6854e3729a prefer id_token_jwt if present in oauth2.txt. Fixes #952 2019-06-12 13:41:16 -04:00
8 changed files with 498 additions and 390 deletions

View File

@@ -160,12 +160,14 @@ script:
do echo $newbase-bulkuser-$i >> sample.csv;
done; fi
- if [ "$e2e" = true ]; then $gam create user $newuser firstname Travis lastname $jid password random travis.jid $jid; fi
- if [ "$e2e" = true ]; then $gam user $gam_user sendemail recipient $newuser subject "test message $newbase" message "Travis test message"; fi
- if [ "$e2e" = true ]; then $gam create group $newgroup name "Travis $jid group" description "This is a description" isarchived true; fi
- if [ "$e2e" = true ]; then $gam user $newuser add license gsuitebusiness; fi
- if [ "$e2e" = true ]; then $gam update group $newgroup add owner $gam_user; fi
- if [ "$e2e" = true ]; then $gam update group $newgroup add member $newuser; fi
- if [ "$e2e" = true ]; then $gam csv sample.csv gam create user ~~email~~ firstname "Travis Bulk" lastname ~~email~~ travis.jid $jid; fi
- if [ "$e2e" = true ]; then $gam csv sample.csv gam user ~email add license gsuitebusiness; fi
- if [ "$e2e" = true ]; then $gam csv sample.csv gam user $gam_user sendemail recipient ~~email~~@pdl.jaylee.us subject "test message $newbase" message "Travis test message"; fi
- if [ "$e2e" = true ]; then $gam csv sample.csv gam update group $newgroup add member ~email; fi
- if [ "$e2e" = true ]; then $gam info group $newgroup; fi
- if [ "$e2e" = true ]; then $gam user $gam_user check serviceaccount; fi
@@ -173,7 +175,11 @@ script:
- if [ "$e2e" = true ]; then $gam user $newuser show imap; fi
- if [ "$e2e" = true ]; then $gam csv sample.csv gam user $newuser delegate to ~email; fi
- if [ "$e2e" = true ]; then $gam user $newuser show delegates; fi
- if [ "$e2e" = true ]; then $gam users "$newbase-bulkuser-01 $newbase-bulkuser-02 $newbase-bulkuser-03" delete messages query in:anywhere maxtodelete 99999 doit; fi
- if [ "$e2e" = true ]; then $gam user $gam_user importemail subject "Travis import $newbase" message "This is a test import" labels IMPORTANT,UNREAD,INBOX,STARRED; fi
- if [ "$e2e" = true ]; then $gam user $gam_user insertemail subject "Travis insert $newbase" file gam.py labels INBOX,UNREAD; fi # yep body is gam code
- if [ "$e2e" = true ]; then $gam user $gam_user sendemail subject "Travis send $gam_user $newbase" file gam.py recipient admin@pdl.jaylee.us; fi
- if [ "$e2e" = true ]; then $gam user $gam_user draftemail subject "Travis draft $newbase" message "Draft message test"; fi
- if [ "$e2e" = true ]; then $gam users "$gam_user $newbase-bulkuser-01 $newbase-bulkuser-02 $newbase-bulkuser-03" delete messages query in:anywhere maxtodelete 99999 doit; fi
- if [ "$e2e" = true ]; then $gam users "$newbase-bulkuser-04 $newbase-bulkuser-05 $newbase-bulkuser-06" trash messages query in:anywhere maxtotrash 99999 doit; fi
- if [ "$e2e" = true ]; then $gam users "$newbase-bulkuser-07 $newbase-bulkuser-08 $newbase-bulkuser-09" modify messages query in:anywhere maxtomodify 99999 addlabel IMPORTANT addlabel STARRED doit; fi
- if [ "$e2e" = true ]; then $gam create feature name Whiteboard-$newbase; fi
@@ -182,9 +188,12 @@ script:
- if [ "$e2e" = true ]; then $gam create resource $newresource "Resource Calendar $tstamp" capacity 25 features Whiteboard-$newbase,VC-$newbase building $newbuilding floor 15 type Room; fi
- if [ "$e2e" = true ]; then $gam info resource $newresource; fi
- if [ "$e2e" = true ]; then $gam user $newuser show filelist; fi
- if [ "$e2e" = true ]; then $gam calendar $newuser update read domain; fi
- if [ "$e2e" = true ]; then $gam calendar $newuser add editor $gam_user; fi
- if [ "$e2e" = true ]; then $gam calendar $newuser showacl; fi
- if [ "$e2e" = true ]; then $gam calendar $gam_user printacl | $gam csv - gam calendar $gam_user delete id ~id; fi # clear ACLs
- if [ "$e2e" = true ]; then $gam calendar $gam_user update read domain; fi
- if [ "$e2e" = true ]; then $gam calendar $gam_user update freebusy default; fi
- if [ "$e2e" = true ]; then $gam calendar $gam_user add editor $newuser; fi
- if [ "$e2e" = true ]; then $gam calendar $gam_user showacl; fi
- if [ "$e2e" = true ]; then $gam calendar $gam_user printacl | $gam csv - gam calendar $gam_user delete id ~id; fi
- if [ "$e2e" = true ]; then $gam printer register; fi
- if [ "$e2e" = true ]; then source travis/set_printer_csv_filter.sh; fi
- if [ "$e2e" = true ]; then $gam print printers > printers.csv; fi
@@ -194,10 +203,8 @@ script:
- if [ "$e2e" = true ]; then $gam csv printers.csv gam info printer ~id; fi
- if [ "$e2e" = true ]; then $gam print printjobs; fi
- if [ "$e2e" = true ]; then $gam csv printers.csv gam printjob ~id fetch; fi
- if [ "$e2e" = true ]; then $gam print printjobs | $gam csv - gam printjob ~id delete; fi
- if [ "$e2e" = true ]; then $gam csv printers.csv gam delete printer ~id; fi
#- if [ "$e2e" = true ]; then $gam calendar id:$newresource add editor $newuser; fi
#- if [ "$e2e" = true ]; then $gam calendar id:$newresource update read domain; fi
#- if [ "$e2e" = true ]; then $gam calendar id:$newresource showacl; fi
- if [ "$e2e" = true ]; then $gam csv sample.csv gam user ~email add calendar id:$newresource; fi
- if [ "$e2e" = true ]; then $gam delete resource $newresource; fi
- if [ "$e2e" = true ]; then $gam delete feature Whiteboard-$newbase; fi

View File

@@ -143,6 +143,7 @@ If an item contains spaces, it should be surrounded by ".
<ASPID> ::= <String>
<BuildingID> ::= <String>|id:<String>
<CalendarACLRole> ::= editor|freebusy|freebusyreader|owner|reader|writer
<CalendarACLRuleID> ::= user:<EmailAddress>|group:<EmailAddress>|domain:<DomainName>|default
<CalendarColorIndex> ::= <Number in range 1-24>
<CalendarItem> ::= <EmailAddress>|<String>
<ChatRoom> ::= <String>
@@ -798,7 +799,7 @@ Specify a collection of Users by directly specifying them or by specifiying item
<UserBasicAttribute>|
<UserMultiAttribute>
gam version [check] [simple]
gam version [check|checkrc|simple|extended] [location <HostName>]
gam help
gam batch <FileName>|- [charset <Charset>]
@@ -939,7 +940,9 @@ gam print aliases|nicknames [todrive] [shownoneditable] [nogroups] [nousers] [(q
gam calendar <CalendarItem> add <CalendarACLRole> ([user] <EmailAddress>)|(group <EmailAddress>)|(domain [<DomainName>])|default [sendnotifications <Boolean>]
gam calendar <CalendarItem> update <CalendarACLRole> ([user] <EmailAddress>)|(group <EmailAddress>)|(domain [<DomainName>])|default [sendnotifications <Boolean>]
gam calendar <CalendarItem> del|delete <CalendarACLRole> <EmailAddress>|(domain [<DomainName>])|default
gam calendar <CalendarItem> del|delete id <CalendarACLRuleID>
gam calendar <CalendarItem> showacl
gam calendar <CalendarItem> printacl [todrive]
<EventNotificationAttribute> ::=
notifyattendees|(sendnotifications <Boolean>)|(sendupdates all|enternalonly|none)
@@ -1283,6 +1286,11 @@ gam <UserTypeEntity> untrash messages query <QueryGmail> [doit] [max_to_untrash|
gam <UserTypeEntity> show gmailprofile [todrive]
gam <UserTypeEntity> draftemail [recipient <EmailAddress>] [subject <String>] [(message <String>)|(file <FileName> [charset <Charset>])]
gam <UserTypeEntity> importemail [recipient <EmailAddress>] [subject <String> [(message <String>)|(file <FileName> [charset <Charset>])]]
gam <UserTypeEntity> insertemail [recipient <EmailAddress>] [subject <String>] [(message <String>)|(file <FileName> [charset <Charset>])]
gam <UserTypeEntity> sendemail [recipient <EmailAddress>] [subject <String>] [(message <String>)|(file <FileName> [charset <Charset>])]
gam <UserTypeEntity> create|add delegate|delegates <EmailAddress>
gam <UserTypeEntity> delegate|delegates to <EmailAddress>
gam <UserTypeEntity> delete|del delegate|delegates <EmailAddress>

254
src/cros-aue-dates.json Normal file
View File

@@ -0,0 +1,254 @@
{
"acer ac700": "2016-08-01T00:00:00.000Z",
"acer c7 chromebook": "2017-10-01T00:00:00.000Z",
"acer c7 chromebook (c710)": "2017-10-01T00:00:00.000Z",
"acer c720 chromebook": "2019-06-01T00:00:00.000Z",
"acer c740 chromebook": "2019-06-01T00:00:00.000Z",
"acer chromebase": "2020-08-01T00:00:00.000Z",
"acer chromebase 24": "2021-06-01T00:00:00.000Z",
"acer chromebook 11 (c720, c720p)": "2019-06-01T00:00:00.000Z",
"acer chromebook 11 (c732, c732t, c732l, c732lt)": "2023-11-01T00:00:00.000Z",
"acer chromebook 11 (c740)": "2020-06-01T00:00:00.000Z",
"acer chromebook 11 (c771, c771t)": "2022-11-01T00:00:00.000Z",
"acer chromebook 11 (cb3-111, c730, c730e)": "2019-08-01T00:00:00.000Z",
"acer chromebook 11 (cb3-131, c735)": "2021-01-01T00:00:00.000Z",
"acer chromebook 11 (cb311-8h, cb311-8ht)": "2023-11-01T00:00:00.000Z",
"acer chromebook 11 n7 (c731, c731t)": "2022-01-01T00:00:00.000Z",
"acer chromebook 13 (cb5-311)": "2019-09-01T00:00:00.000Z",
"acer chromebook 13 (cb713-1w)": "2024-06-01T00:00:00.000Z",
"acer chromebook 13(cb5-311, c810)": "2019-09-01T00:00:00.000Z",
"acer chromebook 14 (cb3-431)": "2021-06-01T00:00:00.000Z",
"acer chromebook 14 for work (cp5-471)": "2022-11-01T00:00:00.000Z",
"acer chromebook 15 (c910 / cb5-571)": "2020-06-01T00:00:00.000Z",
"acer chromebook 15 (cb3-531)": "2020-06-01T00:00:00.000Z",
"acer chromebook 15 (cb3-532)": "2021-08-01T00:00:00.000Z",
"acer chromebook 15 (cb315-1h,cb315-1ht)": "2023-11-01T00:00:00.000Z",
"acer chromebook 15 (cb5-571, c910)": "2020-06-01T00:00:00.000Z",
"acer chromebook 15 (cb515-1h,cb515-1ht)": "2023-11-01T00:00:00.000Z",
"acer chromebook 311": "2025-06-01T00:00:00.000Z",
"acer chromebook 311 (c721, c733, c733u, c733t)": "2025-06-01T00:00:00.000Z",
"acer chromebook 315": "2025-06-01T00:00:00.000Z",
"acer chromebook 315 (cb315-2h)": "2025-06-01T00:00:00.000Z",
"acer chromebook 512 (c851, c851t)": "2025-06-01T00:00:00.000Z",
"acer chromebook 514": "2023-11-01T00:00:00.000Z",
"acer chromebook 714 (cb714-1w / cb714-1wt)": "2024-06-01T00:00:00.000Z",
"acer chromebook 715 (cb715-1w / cb715-1wt)": "2024-06-01T00:00:00.000Z",
"acer chromebook r11 (cb5-132t, c738t)": "2021-06-01T00:00:00.000Z",
"acer chromebook r13 (cb5-312t)": "2021-09-01T00:00:00.000Z",
"acer chromebook spin 11 (cp311-h1, cp311-1hn)": "2023-11-01T00:00:00.000Z",
"acer chromebook spin 11 (r751t)": "2023-11-01T00:00:00.000Z",
"acer chromebook spin 13 (cp713-1wn)": "2024-06-01T00:00:00.000Z",
"acer chromebook spin 15 (cp315)": "2023-11-01T00:00:00.000Z",
"acer chromebook spin 311 (r721t)": "2025-06-01T00:00:00.000Z",
"acer chromebook spin 511": "2025-06-01T00:00:00.000Z",
"acer chromebook spin 511 (r752t, r752tn)": "2025-06-01T00:00:00.000Z",
"acer chromebook spin 512 (r851tn)": "2025-06-01T00:00:00.000Z",
"acer chromebook tab 10": "2023-08-01T00:00:00.000Z",
"acer chromebox": "2019-09-01T00:00:00.000Z",
"acer chromebox cxi2": "2020-06-01T00:00:00.000Z",
"acer chromebox cxi2 / cxv2": "2020-06-01T00:00:00.000Z",
"acer chromebox cxi3": "2024-06-01T00:00:00.000Z",
"aopen chromebase commercial": "2020-09-01T00:00:00.000Z",
"aopen chromebase mini": "2022-02-01T00:00:00.000Z",
"aopen chromebox commercial": "2020-09-01T00:00:00.000Z",
"aopen chromebox commercial 2": "2024-06-01T00:00:00.000Z",
"aopen chromebox mini": "2022-02-01T00:00:00.000Z",
"asi chromebook": "2020-06-01T00:00:00.000Z",
"asus chromebit cs10": "2020-11-01T00:00:00.000Z",
"asus chromebook c200": "2019-06-01T00:00:00.000Z",
"asus chromebook c200ma": "2019-06-01T00:00:00.000Z",
"asus chromebook c201pa": "2020-06-01T00:00:00.000Z",
"asus chromebook c202sa": "2021-06-01T00:00:00.000Z",
"asus chromebook c204": "2025-06-01T00:00:00.000Z",
"asus chromebook c213na": "2023-11-01T00:00:00.000Z",
"asus chromebook c223": "2023-11-01T00:00:00.000Z",
"asus chromebook c300": "2019-08-01T00:00:00.000Z",
"asus chromebook c300ma": "2019-08-01T00:00:00.000Z",
"asus chromebook c300sa / c301sa": "2021-06-01T00:00:00.000Z",
"asus chromebook c403": "2023-11-01T00:00:00.000Z",
"asus chromebook c423": "2023-11-01T00:00:00.000Z",
"asus chromebook c523": "2023-11-01T00:00:00.000Z",
"asus chromebook flip c100pa": "2020-07-01T00:00:00.000Z",
"asus chromebook flip c101pa": "2023-08-01T00:00:00.000Z",
"asus chromebook flip c213": "2023-11-01T00:00:00.000Z",
"asus chromebook flip c214": "2025-06-01T00:00:00.000Z",
"asus chromebook flip c302": "2022-11-01T00:00:00.000Z",
"asus chromebook flip c434": "2024-06-01T00:00:00.000Z",
"asus chromebook tablet ct100": "2023-08-01T00:00:00.000Z",
"asus chromebox (cn60)": "2019-09-01T00:00:00.000Z",
"asus chromebox 2 (cn62)": "2021-06-01T00:00:00.000Z",
"asus chromebox 3": "2024-06-01T00:00:00.000Z",
"asus chromebox 3 (cn65)": "2024-06-01T00:00:00.000Z",
"asus chromebox cn60": "2019-09-01T00:00:00.000Z",
"asus chromebox cn62": "2021-06-01T00:00:00.000Z",
"bobicus chromebook 11": "2020-06-01T00:00:00.000Z",
"chromebook 11 (c730 / cb3-111)": "2019-08-01T00:00:00.000Z",
"chromebook 11 (c735)": "2021-01-01T00:00:00.000Z",
"chromebook 15 (cb515 - 1ht / 1h)": "2023-11-01T00:00:00.000Z",
"chromebook 311 (c721)": "2025-06-01T00:00:00.000Z",
"chromebook pcm-116e": "2020-06-01T00:00:00.000Z",
"consumer chromebook": "2020-06-01T00:00:00.000Z",
"cr-48": "2015-12-01T00:00:00.000Z",
"crambo chromebook": "2020-06-01T00:00:00.000Z",
"ctl chromebook j41 / j41t": "2023-11-01T00:00:00.000Z",
"ctl chromebook nl7": "2023-11-01T00:00:00.000Z",
"ctl chromebook nl7 / nl7t-360 / nl7tw-360": "2023-11-01T00:00:00.000Z",
"ctl chromebook tab tx1": "2023-08-01T00:00:00.000Z",
"ctl chromebook tablet tx1 for education": "2023-08-01T00:00:00.000Z",
"ctl chromebox cbx1": "2024-06-01T00:00:00.000Z",
"ctl j2 / j4 chromebook": "2020-06-01T00:00:00.000Z",
"ctl j5 chromebook": "2021-08-01T00:00:00.000Z",
"ctl n6 education chromebook": "2020-06-01T00:00:00.000Z",
"ctl nl61 chromebook": "2021-08-01T00:00:00.000Z",
"dell chromebook 11": "2019-06-01T00:00:00.000Z",
"dell chromebook 11 (3120)": "2020-06-01T00:00:00.000Z",
"dell chromebook 11 (3180)": "2022-05-01T00:00:00.000Z",
"dell chromebook 11 (5190)": "2023-11-01T00:00:00.000Z",
"dell chromebook 11 2-in-1 (3189)": "2022-05-01T00:00:00.000Z",
"dell chromebook 11 2-in-1 (5190)": "2023-11-01T00:00:00.000Z",
"dell chromebook 13 (3380)": "2022-11-01T00:00:00.000Z",
"dell chromebook 13 (7310)": "2020-09-01T00:00:00.000Z",
"dell chromebook 3100": "2025-06-01T00:00:00.000Z",
"dell chromebook 3100 2-in-1": "2025-06-01T00:00:00.000Z",
"dell chromebook 3400": "2025-06-01T00:00:00.000Z",
"dell chromebox": "2019-09-01T00:00:00.000Z",
"dell inspiron chromebook 14 2-in-1 (7486)": "2024-06-01T00:00:00.000Z",
"edugear chromebook k": "2020-06-01T00:00:00.000Z",
"edugear chromebook m": "2020-06-01T00:00:00.000Z",
"edugear chromebook r": "2020-06-01T00:00:00.000Z",
"edugear cmt chromebook": "2021-08-01T00:00:00.000Z",
"edxis chromebook": "2020-06-01T00:00:00.000Z",
"edxis education chromebook": "2020-06-01T00:00:00.000Z",
"epik 11.6\" chromebook elb1101": "2020-06-01T00:00:00.000Z",
"google chromebook pixel": "2018-06-01T00:00:00.000Z",
"google chromebook pixel (2015)": "2020-06-01T00:00:00.000Z",
"google cr-48": "2015-12-01T00:00:00.000Z",
"google pixel slate": "2024-06-01T00:00:00.000Z",
"google pixelbook": "2024-06-01T00:00:00.000Z",
"haier chromebook 11": "2020-06-01T00:00:00.000Z",
"haier chromebook 11 c": "2021-08-01T00:00:00.000Z",
"haier chromebook 11 g2": "2020-09-01T00:00:00.000Z",
"haier chromebook 11e": "2020-06-01T00:00:00.000Z",
"hexa chromebook pi": "2020-06-01T00:00:00.000Z",
"hisense chromebook 11": "2020-06-01T00:00:00.000Z",
"hp chromebook 11 1100-1199 / hp chromebook 11 g1": "2018-10-01T00:00:00.000Z",
"hp chromebook 11 2000-2099 / hp chromebook 11 g2": "2019-06-01T00:00:00.000Z",
"hp chromebook 11 2100-2199 / hp chromebook 11 g3": "2020-06-01T00:00:00.000Z",
"hp chromebook 11 2200-2299 / hp chromebook 11 g4/g4 ee": "2020-06-01T00:00:00.000Z",
"hp chromebook 11 g1": "2018-10-01T00:00:00.000Z",
"hp chromebook 11 g2": "2019-06-01T00:00:00.000Z",
"hp chromebook 11 g3": "2020-06-01T00:00:00.000Z",
"hp chromebook 11 g4/g4 ee": "2020-06-01T00:00:00.000Z",
"hp chromebook 11 g5": "2021-07-01T00:00:00.000Z",
"hp chromebook 11 g5 / hp chromebook 11-vxxx": "2021-07-01T00:00:00.000Z",
"hp chromebook 11 g5 ee": "2022-01-01T00:00:00.000Z",
"hp chromebook 11 g6 ee": "2023-11-01T00:00:00.000Z",
"hp chromebook 11 g7 ee": "2025-06-01T00:00:00.000Z",
"hp chromebook 11a g6 ee": "2025-06-01T00:00:00.000Z",
"hp chromebook 13 g1": "2022-11-01T00:00:00.000Z",
"hp chromebook 14": "2019-06-01T00:00:00.000Z",
"hp chromebook 14 / hp chromebook 14 g5": "2023-11-01T00:00:00.000Z",
"hp chromebook 14 ak000-099 / hp chromebook 14 g4": "2021-09-01T00:00:00.000Z",
"hp chromebook 14 db0000-db0999": "2025-06-01T00:00:00.000Z",
"hp chromebook 14 g3": "2019-10-01T00:00:00.000Z",
"hp chromebook 14 g4": "2021-09-01T00:00:00.000Z",
"hp chromebook 14 g5": "2023-11-01T00:00:00.000Z",
"hp chromebook 14 x000-x999 / hp chromebook 14 g3": "2019-10-01T00:00:00.000Z",
"hp chromebook 14a g5": "2025-06-01T00:00:00.000Z",
"hp chromebook 15 g1": "2024-06-01T00:00:00.000Z",
"hp chromebook x2 ": "2024-06-01T00:00:00.000Z",
"hp chromebook x360 11 g1 ee": "2023-11-01T00:00:00.000Z",
"hp chromebook x360 11 g2 ee": "2025-06-01T00:00:00.000Z",
"hp chromebook x360 14": "2024-06-01T00:00:00.000Z",
"hp chromebook x360 14 g1": "2024-06-01T00:00:00.000Z",
"hp chromebox cb1-(000-099) / hp chromebox g1/ hp chromebox for meetings": "2019-09-01T00:00:00.000Z",
"hp chromebox g1": "2019-09-01T00:00:00.000Z",
"hp chromebox g2": "2024-06-01T00:00:00.000Z",
"hp pavilion chromebook 14": "2018-02-01T00:00:00.000Z",
"jp sa couto chromebook": "2020-06-01T00:00:00.000Z",
"lava xolo chromebook": "2020-06-01T00:00:00.000Z",
"lenovo 100e chromebook": "2023-11-01T00:00:00.000Z",
"lenovo 100e chromebook 2nd gen": "2025-06-01T00:00:00.000Z",
"lenovo 100e chromebook 2nd gen mtk": "2025-06-01T00:00:00.000Z",
"lenovo 100s chromebook": "2020-09-01T00:00:00.000Z",
"lenovo 14e chromebook": "2025-06-01T00:00:00.000Z",
"lenovo 300e chromebook": "2025-06-01T00:00:00.000Z",
"lenovo 300e chromebook 2nd gen": "2025-06-01T00:00:00.000Z",
"lenovo 300e chromebook 2nd gen mtk": "2025-06-01T00:00:00.000Z",
"lenovo 500e chromebook": "2023-11-01T00:00:00.000Z",
"lenovo 500e chromebook 2nd gen": "2025-06-01T00:00:00.000Z",
"lenovo chromebook c330": "2022-06-01T00:00:00.000Z",
"lenovo chromebook s330": "2022-06-01T00:00:00.000Z",
"lenovo flex 11 chromebook": "2022-06-01T00:00:00.000Z",
"lenovo ideapad c330 chromebook": "2022-06-01T00:00:00.000Z",
"lenovo ideapad s330 chromebook": "2022-06-01T00:00:00.000Z",
"lenovo n20 chromebook": "2019-06-01T00:00:00.000Z",
"lenovo n21 chromebook": "2020-06-01T00:00:00.000Z",
"lenovo n22 chromebook": "2021-06-01T00:00:00.000Z",
"lenovo n23 chromebook": "2021-06-01T00:00:00.000Z",
"lenovo n23 yoga chromebook": "2022-06-01T00:00:00.000Z",
"lenovo n42 chromebook": "2021-06-01T00:00:00.000Z",
"lenovo thinkcentre chromebox": "2020-06-01T00:00:00.000Z",
"lenovo thinkpad 11e 3rd gen chromebook": "2021-06-01T00:00:00.000Z",
"lenovo thinkpad 11e 4th gen chromebook": "2023-11-01T00:00:00.000Z",
"lenovo thinkpad 11e chromebook": "2019-06-01T00:00:00.000Z",
"lenovo thinkpad 11e chromebook (4th gen)/lenovo thinkpad yoga 11e chromebook (4th gen)": "2023-11-01T00:00:00.000Z",
"lenovo thinkpad 13": "2022-11-01T00:00:00.000Z",
"lenovo thinkpad x131e chromebook": "2018-06-01T00:00:00.000Z",
"lenovo yoga c630 chromebook": "2024-06-01T00:00:00.000Z",
"lg chromebase (22cb25s)": "2020-06-01T00:00:00.000Z",
"lg chromebase (22cv241)": "2019-06-01T00:00:00.000Z",
"lumos education chromebook": "2020-06-01T00:00:00.000Z",
"m&a chromebook": "2020-06-01T00:00:00.000Z",
"mecer chromebook": "2020-06-01T00:00:00.000Z",
"mecer v2 chromebook": "2021-08-01T00:00:00.000Z",
"medion chromebook akoya s2013 ": "2020-06-01T00:00:00.000Z",
"medion chromebook s2015": "2020-06-01T00:00:00.000Z",
"multilaser chromebook m11c": "2021-08-01T00:00:00.000Z",
"ncomputing chromebook cx100": "2020-06-01T00:00:00.000Z",
"ncomputing chromebook cx110": "2020-06-01T00:00:00.000Z",
"nexian chromebook 11.6\"": "2020-06-01T00:00:00.000Z",
"pcmerge chromebook al116": "2023-11-01T00:00:00.000Z",
"pcmerge chromebookpcm-116e/pcm-116eb": "2020-06-01T00:00:00.000Z",
"pcmerge chromebookpcm-116t-432b": "2021-08-01T00:00:00.000Z",
"poin2 chromebook 11": "2020-06-01T00:00:00.000Z",
"poin2 chromebook 11c": "2022-03-01T00:00:00.000Z",
"poin2 chromebook 14": "2022-03-01T00:00:00.000Z",
"positivo chromebook c216b": "2021-08-01T00:00:00.000Z",
"positivo chromebook ch1190": "2020-06-01T00:00:00.000Z",
"prowise 11.6\" entry line chromebook": "2020-06-01T00:00:00.000Z",
"prowise chromebook eduline": "2023-11-01T00:00:00.000Z",
"prowise chromebook entryline": "2020-06-01T00:00:00.000Z",
"prowise chromebook proline": "2021-08-01T00:00:00.000Z",
"prowise proline chromebook": "2021-08-01T00:00:00.000Z",
"rgs education chromebook": "2020-06-01T00:00:00.000Z",
"samsung chromebook": "2018-07-01T00:00:00.000Z",
"samsung chromebook - xe303": "2018-07-01T00:00:00.000Z",
"samsung chromebook 2 11\"": "2019-06-01T00:00:00.000Z",
"samsung chromebook 2 11\" - xe500c12": "2020-06-01T00:00:00.000Z",
"samsung chromebook 2 13\"": "2019-06-01T00:00:00.000Z",
"samsung chromebook 3": "2021-06-01T00:00:00.000Z",
"samsung chromebook plus": "2023-08-01T00:00:00.000Z",
"samsung chromebook plus (v2)": "2024-06-01T00:00:00.000Z",
"samsung chromebook pro": "2022-11-01T00:00:00.000Z",
"samsung chromebook series 5": "2016-06-01T00:00:00.000Z",
"samsung chromebook series 5 550": "2017-05-01T00:00:00.000Z",
"samsung chromebox series 3": "2018-03-01T00:00:00.000Z",
"sector 5 e1 rugged chromebook": "2020-06-01T00:00:00.000Z",
"sector 5 e3 chromebook": "2023-11-01T00:00:00.000Z",
"senkatel c1101 chromebook": "2020-06-01T00:00:00.000Z",
"thinkpad 11e chromebook 3rd gen (yoga/clamshell)": "2021-06-01T00:00:00.000Z",
"thinkpad 13 chromebook": "2022-11-01T00:00:00.000Z",
"toshiba chromebook": "2019-06-01T00:00:00.000Z",
"toshiba chromebook 2": "2020-06-01T00:00:00.000Z",
"toshiba chromebook 2 (2015 edition)": "2020-09-01T00:00:00.000Z",
"true idc chromebook": "2020-06-01T00:00:00.000Z",
"true idc chromebook 11": "2020-06-01T00:00:00.000Z",
"videonet chromebook": "2020-06-01T00:00:00.000Z",
"videonet chromebook bl10": "2020-06-01T00:00:00.000Z",
"viewsonic nmp660 chromebox": "2024-06-01T00:00:00.000Z",
"viglen chromebook 11": "2020-06-01T00:00:00.000Z",
"viglen chromebook 11c": "2023-11-01T00:00:00.000Z",
"viglen chromebook 360": "2021-08-01T00:00:00.000Z",
"xolo chromebook": "2020-06-01T00:00:00.000Z"
}

View File

@@ -27,6 +27,7 @@ import configparser
import csv
import datetime
import difflib
from email import message_from_string
import hashlib
import io
import json
@@ -50,7 +51,6 @@ import uuid
import webbrowser
import zipfile
import http.client as http_client
from email.mime.text import MIMEText
from multiprocessing import Pool
from multiprocessing import freeze_support
from urllib.parse import urlencode, urlparse
@@ -113,32 +113,6 @@ def _createHttpObj(cache=None, override_min_tls=None, override_max_tls=None):
return httplib2.Http(tls_maximum_version=tls_maximum_version, tls_minimum_version=tls_minimum_version,
cache=cache)
# Override google_auth_oauthlib classes so we use PKCE
# Remove once https://github.com/googleapis/google-auth-library-python-oauthlib/pull/42
# is landed and released.
def _authorization_url(self, **kwargs):
kwargs.setdefault('access_type', 'offline')
rnd = SystemRandom()
random_verifier = [rnd.choice(URL_SAFE_CHARS) for _ in range(128)]
self.code_verifier = ''.join(random_verifier)
code_hash = hashlib.sha256()
code_hash.update(str.encode(self.code_verifier))
unencoded_challenge = code_hash.digest()
b64_challenge = base64.urlsafe_b64encode(unencoded_challenge)
code_challenge = b64_challenge.decode().split('=')[0]
kwargs.setdefault('code_challenge', code_challenge)
kwargs.setdefault('code_challenge_method', 'S256')
url, state = self.oauth2session.authorization_url(self.client_config['auth_uri'], **kwargs)
return url, state
def _fetch_token(self, **kwargs):
kwargs.setdefault('client_secret', self.client_config['client_secret'])
kwargs.setdefault('code_verifier', self.code_verifier)
return self.oauth2session.fetch_token(self.client_config['token_uri'], **kwargs)
google_auth_oauthlib.flow.Flow.authorization_url = _authorization_url
google_auth_oauthlib.flow.Flow.fetch_token = _fetch_token
def showUsage():
doGAMVersion(checkForArgs=False)
print('''
@@ -630,7 +604,7 @@ def SetGlobalVariables():
ROW_FILTER_COMP_PATTERN = re.compile(r'^(date|time|count)\s*([<>]=?|=|!=)\s*(.+)$', re.IGNORECASE)
ROW_FILTER_BOOL_PATTERN = re.compile(r'^(boolean):(.+)$', re.IGNORECASE)
ROW_FILTER_RE_PATTERN = re.compile(r'^(regex):(.+)$', re.IGNORECASE)
ROW_FILTER_RE_PATTERN = re.compile(r'^(regex|notregex):(.+)$', re.IGNORECASE)
def _getCfgRowFilter(itemName):
value = GC_Defaults[itemName]
@@ -673,7 +647,7 @@ def SetGlobalVariables():
continue
except re.error as e:
systemErrorExit(3, 'Item: {0}, Value: "{1}": "{2}", Invalid RE: {3}'.format(itemName, column, filterStr, e))
systemErrorExit(3, 'Item: {0}, Value: "{1}": {2}, Expected: (date|time|count<Operator><Value>) or (boolean:true|false) or (regex:<RegularExpression>)'.format(itemName, column, filterStr))
systemErrorExit(3, 'Item: {0}, Value: "{1}": {2}, Expected: (date|time|count<Operator><Value>) or (boolean:true|false) or (regex|notregex:<RegularExpression>)'.format(itemName, column, filterStr))
return rowFilters
except (TypeError, ValueError) as e:
systemErrorExit(3, 'Item: {0}, Value: "{1}", Failed to parse as JSON: {2}'.format(itemName, value, str(e)))
@@ -848,10 +822,17 @@ def _getServerTLSUsed(location):
conn = 'https:%s' % netloc
httpc = _createHttpObj()
headers = {'user-agent': GAM_INFO}
try:
httpc.request(url, headers=headers)
except (httplib2.ServerNotFoundError, RuntimeError) as e:
systemErrorExit(4, e)
retries = 5
for n in range(1, retries+1):
try:
httpc.request(url, headers=headers)
break
except (httplib2.ServerNotFoundError, RuntimeError) as e:
if n != retries:
httpc.connections = {}
waitOnFailure(n, retries, str(e))
continue
systemErrorExit(4, str(e))
cipher_name, tls_ver, _ = httpc.connections[conn].sock.cipher()
return tls_ver, cipher_name
@@ -1092,7 +1073,13 @@ def callGAPI(service, function,
service._http.cache = None
continue
systemErrorExit(4, str(e))
except (TypeError, httplib2.ServerNotFoundError, RuntimeError) as e:
except (httplib2.ServerNotFoundError, RuntimeError) as e:
if n != retries:
service._http.connections = {}
waitOnFailure(n, retries, str(e))
continue
systemErrorExit(4, str(e))
except TypeError as e:
systemErrorExit(4, str(e))
def callGAPIpages(service, function, items='items',
@@ -1242,7 +1229,7 @@ def getOauth2TxtStorageCredentials():
oauth_data = json.loads(oauth_string)
creds = google.oauth2.credentials.Credentials.from_authorized_user_file(GC_Values[GC_OAUTH2_TXT])
creds.token = oauth_data.get('token', oauth_data.get('auth_token', ''))
creds._id_token = oauth_data.get('id_token', None)
creds._id_token = oauth_data.get('id_token_jwt', oauth_data.get('id_token', None))
token_expiry = oauth_data.get('token_expiry', '1970-01-01T00:00:01Z')
creds.expiry = datetime.datetime.strptime(token_expiry, '%Y-%m-%dT%H:%M:%SZ')
GC_Values[GC_DECODED_ID_TOKEN] = oauth_data.get('decoded_id_token', '')
@@ -1252,11 +1239,19 @@ def getValidOauth2TxtCredentials(force_refresh=False):
"""Gets OAuth2 credentials which are guaranteed to be fresh and valid."""
credentials = getOauth2TxtStorageCredentials()
if (credentials and credentials.expired) or force_refresh:
try:
credentials.refresh(google_auth_httplib2.Request(_createHttpObj()))
writeCredentials(credentials)
except google.auth.exceptions.RefreshError as e:
systemErrorExit(18, str(e))
retries = 3
for n in range(1, retries+1):
try:
credentials.refresh(google_auth_httplib2.Request(_createHttpObj()))
writeCredentials(credentials)
break
except google.auth.exceptions.RefreshError as e:
systemErrorExit(18, str(e))
except (google.auth.exceptions.TransportError, httplib2.ServerNotFoundError, RuntimeError) as e:
if n != retries:
waitOnFailure(n, retries, str(e))
continue
systemErrorExit(4, str(e))
elif credentials is None or not credentials.valid:
doRequestOAuth()
credentials = getOauth2TxtStorageCredentials()
@@ -1283,6 +1278,10 @@ def getService(api, http):
http.cache = None
return service
except (httplib2.ServerNotFoundError, RuntimeError) as e:
if n != retries:
http.connections = {}
waitOnFailure(n, retries, str(e))
continue
systemErrorExit(4, str(e))
except (googleapiclient.errors.InvalidJsonError, KeyError, ValueError) as e:
http.cache = None
@@ -1410,17 +1409,24 @@ def buildGAPIServiceObject(api, act_as, showAuthError=True):
GM_Globals[GM_CURRENT_API_SCOPES] = API_SCOPE_MAPPING[api]
credentials = getSvcAcctCredentials(GM_Globals[GM_CURRENT_API_SCOPES], act_as)
request = google_auth_httplib2.Request(http)
try:
credentials.refresh(request)
service._http = google_auth_httplib2.AuthorizedHttp(credentials, http=http)
except (httplib2.ServerNotFoundError, RuntimeError) as e:
systemErrorExit(4, e)
except google.auth.exceptions.RefreshError as e:
if isinstance(e.args, tuple):
e = e.args[0]
if showAuthError:
stderrErrorMsg('User {0}: {1}'.format(GM_Globals[GM_CURRENT_API_USER], str(e)))
return handleOAuthTokenError(str(e), True)
retries = 3
for n in range(1, retries+1):
try:
credentials.refresh(request)
service._http = google_auth_httplib2.AuthorizedHttp(credentials, http=http)
break
except (httplib2.ServerNotFoundError, RuntimeError) as e:
if n != retries:
http.connections = {}
waitOnFailure(n, retries, str(e))
continue
systemErrorExit(4, e)
except google.auth.exceptions.RefreshError as e:
if isinstance(e.args, tuple):
e = e.args[0]
if showAuthError:
stderrErrorMsg('User {0}: {1}'.format(GM_Globals[GM_CURRENT_API_USER], str(e)))
return handleOAuthTokenError(str(e), True)
return service
def buildAlertCenterGAPIObject(user):
@@ -2053,11 +2059,11 @@ def doGetDomainInfo():
domainName = sys.argv[3]
result = callGAPI(cd.domains(), 'get', customer=GC_Values[GC_CUSTOMER_ID], domainName=domainName)
if 'creationTime' in result:
result['creationTime'] = str(datetime.datetime.fromtimestamp(int(result['creationTime'])/1000))
result['creationTime'] = utils.formatTimestampYMDHMSF(result['creationTime'])
if 'domainAliases' in result:
for i in range(0, len(result['domainAliases'])):
if 'creationTime' in result['domainAliases'][i]:
result['domainAliases'][i]['creationTime'] = str(datetime.datetime.fromtimestamp(int(result['domainAliases'][i]['creationTime'])/1000))
result['domainAliases'][i]['creationTime'] = utils.formatTimestampYMDHMSF(result['domainAliases'][i]['creationTime'])
print_json(None, result)
def doGetDomainAliasInfo():
@@ -2065,7 +2071,7 @@ def doGetDomainAliasInfo():
alias = sys.argv[3]
result = callGAPI(cd.domainAliases(), 'get', customer=GC_Values[GC_CUSTOMER_ID], domainAliasName=alias)
if 'creationTime' in result:
result['creationTime'] = str(datetime.datetime.fromtimestamp(int(result['creationTime'])/1000))
result['creationTime'] = utils.formatTimestampYMDHMSF(result['creationTime'])
print_json(None, result)
def doGetCustomerInfo():
@@ -2185,7 +2191,7 @@ def doPrintDomains(return_results=False):
if attr in ['kind', 'etag', 'domainAliases', 'isPrimary']:
continue
if attr in ['creationTime',]:
domain[attr] = str(datetime.datetime.fromtimestamp(int(domain[attr])/1000))
domain[attr] = utils.formatTimestampYMDHMSF(domain[attr])
if attr not in titles:
titles.append(attr)
domain_attributes[attr] = domain[attr]
@@ -2200,7 +2206,7 @@ def doPrintDomains(return_results=False):
if attr in ['kind', 'etag']:
continue
if attr in ['creationTime',]:
aliasdomain[attr] = str(datetime.datetime.fromtimestamp(int(aliasdomain[attr])/1000))
aliasdomain[attr] = utils.formatTimestampYMDHMSF(aliasdomain[attr])
if attr not in titles:
titles.append(attr)
aliasdomain_attributes[attr] = aliasdomain[attr]
@@ -2229,7 +2235,7 @@ def doPrintDomainAliases():
if attr in ['kind', 'etag']:
continue
if attr == 'creationTime':
domainAlias[attr] = str(datetime.datetime.fromtimestamp(int(domainAlias[attr])/1000))
domainAlias[attr] = utils.formatTimestampYMDHMSF(domainAlias[attr])
if attr not in titles:
titles.append(attr)
domainAlias_attributes[attr] = domainAlias[attr]
@@ -3074,9 +3080,8 @@ def doPrintPrintJobs():
jobCount = totalJobs
break
continue
updateTime = int(job['updateTime'])/1000
job['createTime'] = datetime.datetime.fromtimestamp(createTime).strftime('%Y-%m-%d %H:%M:%S')
job['updateTime'] = datetime.datetime.fromtimestamp(updateTime).strftime('%Y-%m-%d %H:%M:%S')
job['createTime'] = utils.formatTimestampYMDHMS(job['createTime'])
job['updateTime'] = utils.formatTimestampYMDHMS(job['updateTime'])
job['tags'] = ' '.join(job['tags'])
addRowTitlesToCSVfile(flatten_json(job), csvRows, titles)
if jobCount >= totalJobs:
@@ -3116,12 +3121,9 @@ def doPrintPrinters():
printers = callGAPI(cp.printers(), 'list', q=query, type=printer_type, connection_status=connection_status, extra_fields=extra_fields)
checkCloudPrintResult(printers)
for printer in printers['printers']:
createTime = int(printer['createTime'])/1000
accessTime = int(printer['accessTime'])/1000
updateTime = int(printer['updateTime'])/1000
printer['createTime'] = datetime.datetime.fromtimestamp(createTime).strftime('%Y-%m-%d %H:%M:%S')
printer['accessTime'] = datetime.datetime.fromtimestamp(accessTime).strftime('%Y-%m-%d %H:%M:%S')
printer['updateTime'] = datetime.datetime.fromtimestamp(updateTime).strftime('%Y-%m-%d %H:%M:%S')
printer['createTime'] = utils.formatTimestampYMDHMS(printer['createTime'])
printer['accessTime'] = utils.formatTimestampYMDHMS(printer['accessTime'])
printer['updateTime'] = utils.formatTimestampYMDHMS(printer['updateTime'])
printer['tags'] = ' '.join(printer['tags'])
addRowTitlesToCSVfile(flatten_json(printer), csvRows, titles)
writeCSVfile(csvRows, titles, 'Printers', todrive)
@@ -3544,12 +3546,9 @@ def doGetPrinterInfo():
result = callGAPI(cp.printers(), 'get', printerid=printerid)
checkCloudPrintResult(result)
printer_info = result['printers'][0]
createTime = int(printer_info['createTime'])/1000
accessTime = int(printer_info['accessTime'])/1000
updateTime = int(printer_info['updateTime'])/1000
printer_info['createTime'] = datetime.datetime.fromtimestamp(createTime).strftime('%Y-%m-%d %H:%M:%S')
printer_info['accessTime'] = datetime.datetime.fromtimestamp(accessTime).strftime('%Y-%m-%d %H:%M:%S')
printer_info['updateTime'] = datetime.datetime.fromtimestamp(updateTime).strftime('%Y-%m-%d %H:%M:%S')
printer_info['createTime'] = utils.formatTimestampYMDHMS(printer_info['createTime'])
printer_info['accessTime'] = utils.formatTimestampYMDHMS(printer_info['accessTime'])
printer_info['updateTime'] = utils.formatTimestampYMDHMS(printer_info['updateTime'])
printer_info['tags'] = ' '.join(printer_info['tags'])
if not everything:
del printer_info['capabilities']
@@ -3705,16 +3704,38 @@ def formatACLRule(rule):
return '(Scope: {0}:{1}, Role: {2})'.format(rule['scope']['type'], rule['scope']['value'], rule['role'])
return '(Scope: {0}, Role: {1})'.format(rule['scope']['type'], rule['role'])
def doCalendarShowACL():
def doCalendarPrintShowACLs(csvFormat):
calendarId, cal = buildCalendarDataGAPIObject(sys.argv[2])
if not cal:
return
acls = callGAPIpages(cal.acl(), 'list', 'items', calendarId=calendarId, fields='nextPageToken,items(role,scope)')
toDrive = False
i = 4
while i < len(sys.argv):
myarg = sys.argv[i].lower().replace('_', '')
if csvFormat and myarg == 'todrive':
toDrive = True
i += 1
else:
systemErrorExit(2, '%s is not a valid argument for "gam calendar <email> %s"' % (sys.argv[i], ['showacl', 'printacl'][csvFormat]))
acls = callGAPIpages(cal.acl(), 'list', 'items', calendarId=calendarId)
i = 0
count = len(acls)
if csvFormat:
titles = []
rows = []
else:
count = len(acls)
for rule in acls:
i += 1
print('Calendar: {0}, ACL: {1}{2}'.format(calendarId, formatACLRule(rule), currentCount(i, count)))
if csvFormat:
row = flatten_json(rule, None)
for key in row:
if key not in titles:
titles.append(key)
rows.append(row)
else:
print('Calendar: {0}, ACL: {1}{2}'.format(calendarId, formatACLRule(rule), currentCount(i, count)))
if csvFormat:
writeCSVfile(rows, titles, '%s Calendar ACLs' % calendarId, toDrive)
def _getCalendarACLScope(i, body):
body['scope'] = {}
@@ -3770,10 +3791,15 @@ def doCalendarDelACL():
calendarId, cal = buildCalendarDataGAPIObject(sys.argv[2])
if not cal:
return
body = {'role': 'none'}
_getCalendarACLScope(5, body)
print('Calendar: {0}, {1} ACL: {2}'.format(calendarId, 'Delete', formatACLScope(body)))
callGAPI(cal.acl(), 'insert', calendarId=calendarId, body=body, sendNotifications=False)
if sys.argv[4].lower() == 'id':
ruleId = sys.argv[5]
print('Removing rights for %s to %s' % (ruleId, calendarId))
callGAPI(cal.acl(), 'delete', calendarId=calendarId, ruleId=ruleId)
else:
body = {'role': 'none'}
_getCalendarACLScope(5, body)
print('Calendar: {0}, {1} ACL: {2}'.format(calendarId, 'Delete', formatACLScope(body)))
callGAPI(cal.acl(), 'insert', calendarId=calendarId, body=body, sendNotifications=False)
def doCalendarWipeData():
calendarId, cal = buildCalendarDataGAPIObject(sys.argv[2])
@@ -5318,6 +5344,34 @@ def transferDriveFiles(users):
if not skipped_files:
break
def sendOrDropEmail(users, method='send'):
body = subject = ''
recipient = None
labels = None
i = 4
while i < len(sys.argv):
myarg = sys.argv[i].lower().replace('_', '')
if myarg == 'message':
body = sys.argv[i+1]
i += 2
elif myarg == 'file':
filename = sys.argv[i+1]
i, encoding = getCharSet(i+2)
body = readFile(filename, encoding=encoding)
elif myarg == 'subject':
subject = sys.argv[i+1]
i += 2
elif myarg == 'recipient':
recipient = sys.argv[i+1]
i += 2
elif myarg == 'labels':
labels = sys.argv[i+1].split(',')
i += 2
else:
systemErrorExit(2, '%s is not a valid argument for "gam <users> sendemail"' % sys.argv[i])
for user in users:
send_email(subject, body, recipient, user, method, labels)
def doImap(users):
enable = getBoolean(sys.argv[4], 'gam <users> imap')
body = {'enabled': enable, 'autoExpunge': True, 'expungeBehavior': 'archive', 'maxFolderSize': 0}
@@ -5764,7 +5818,7 @@ def printShowSmime(users, csvFormat):
result = callGAPI(gmail.users().settings().sendAs().smimeInfo(), 'list', sendAsEmail=sendAsEmail, userId='me')
smimes = result.get('smimeInfo', [])
for j, _ in enumerate(smimes):
smimes[j]['expiration'] = datetime.datetime.fromtimestamp(int(smimes[j]['expiration'])/1000).strftime('%Y-%m-%d %H:%M:%S')
smimes[j]['expiration'] = utils.formatTimestampYMDHMS(smimes[j]['expiration'])
if csvFormat:
for smime in smimes:
addRowTitlesToCSVfile(flatten_json(smime, flattened={'User': user}), csvRows, titles)
@@ -6868,11 +6922,11 @@ def getVacation(users):
print(' Contacts Only: {0}'.format(result['restrictToContacts']))
print(' Domain Only: {0}'.format(result['restrictToDomain']))
if 'startTime' in result:
print(' Start Date: {0}'.format(datetime.datetime.fromtimestamp(int(result['startTime'])/1000).strftime('%Y-%m-%d')))
print(' Start Date: {0}'.format(utils.formatTimestampYMD(result['startTime'])))
else:
print(' Start Date: Started')
if 'endTime' in result:
print(' End Date: {0}'.format(datetime.datetime.fromtimestamp(int(result['endTime'])/1000).strftime('%Y-%m-%d')))
print(' End Date: {0}'.format(utils.formatTimestampYMD(result['endTime'])))
else:
print(' End Date: Not specified')
print(utils.convertUTF8(' Subject: {0}'.format(result.get('responseSubject', 'None'))))
@@ -7555,10 +7609,11 @@ def getCRMService(login_hint):
discoveryServiceUrl=googleapiclient.discovery.V2_DISCOVERY_URI),
http)
def getGAMProjectAPIs():
def getGAMProjectFile(filepath):
file_url = GAM_PROJECT_FILEPATH+filepath
httpObj = _createHttpObj()
_, c = httpObj.request(GAM_PROJECT_APIS, 'GET')
return httpObj, c.decode(UTF8).splitlines()
_, c = httpObj.request(file_url, 'GET')
return c.decode(UTF8)
def enableGAMProjectAPIs(GAMProjectAPIs, httpObj, projectId, checkEnabled, i=0, count=0):
apis = GAMProjectAPIs[:]
@@ -7638,7 +7693,7 @@ def _createClientSecretsOauth2service(httpObj, projectId):
print('Unknown error: %s' % content)
return False
simplehttp, GAMProjectAPIs = getGAMProjectAPIs()
GAMProjectAPIs = getGAMProjectFile('src/project-apis.txt').splitlines()
enableGAMProjectAPIs(GAMProjectAPIs, httpObj, projectId, False)
iam = googleapiclient.discovery.build('iam', 'v1',
http=httpObj, cache_discovery=False,
@@ -7683,6 +7738,7 @@ def _createClientSecretsOauth2service(httpObj, projectId):
client_secret = input('Enter your Client Secret: ').strip()
if not client_secret:
client_secret = input().strip()
simplehttp = _createHttpObj()
client_valid = _checkClientAndSecret(simplehttp, client_id, client_secret)
if client_valid:
break
@@ -7889,7 +7945,7 @@ def doUseProject():
def doUpdateProjects():
_, httpObj, login_hint, projects, _ = _getLoginHintProjects(False)
_, GAMProjectAPIs = getGAMProjectAPIs()
GAMProjectAPIs = getGAMProjectFile('src/project-apis.txt').splitlines()
count = len(projects)
print('User: {0}, Update {1} Projects'.format(login_hint, count))
i = 0
@@ -10322,6 +10378,9 @@ def doGetMobileInfo():
info = callGAPI(cd.mobiledevices(), 'get', customerId=GC_Values[GC_CUSTOMER_ID], resourceId=resourceId)
if 'deviceId' in info:
info['deviceId'] = info['deviceId'].encode('unicode-escape').decode(UTF8)
attrib = 'securityPatchLevel'
if attrib in info and int(info[attrib]):
info[attrib] = utils.formatTimestampYMDHMS(info[attrib])
print_json(None, info)
def print_json(object_name, object_value, spacing=''):
@@ -10582,11 +10641,11 @@ def doGetASPs(users):
if asp['creationTime'] == '0':
created_date = 'Unknown'
else:
created_date = datetime.datetime.fromtimestamp(int(asp['creationTime'])/1000).strftime('%Y-%m-%d %H:%M:%S')
created_date = utils.formatTimestampYMDHMS(asp['creationTime'])
if asp['lastTimeUsed'] == '0':
used_date = 'Never'
else:
used_date = datetime.datetime.fromtimestamp(int(asp['lastTimeUsed'])/1000).strftime('%Y-%m-%d %H:%M:%S')
used_date = utils.formatTimestampYMDHMS(asp['lastTimeUsed'])
print(' ID: %s\n Name: %s\n Created: %s\n Last Used: %s\n' % (asp['codeId'], asp['name'], created_date, used_date))
else:
print(' no ASPs for %s\n' % user)
@@ -10869,17 +10928,33 @@ def doDeleteOrg():
print("Deleting organization %s" % name)
callGAPI(cd.orgunits(), 'delete', customerId=GC_Values[GC_CUSTOMER_ID], orgUnitPath=encodeOrgUnitPath(makeOrgUnitPathRelative(name)))
# Send an email
def send_email(msg_subj, msg_txt, msg_rcpt=None):
userId, gmail = buildGmailGAPIObject(_getValueFromOAuth('email'))
if not msg_rcpt:
msg_rcpt = userId
msg = MIMEText(msg_txt)
msg['Subject'] = msg_subj
def send_email(subject, body, recipient=None, sender=None, method='send', labels=None):
api_body = {}
kwargs = {}
if not sender:
sender = _getValueFromOAuth('email')
userId, gmail = buildGmailGAPIObject(sender)
resource = gmail.users().messages()
if labels and method in ['insert', 'import']:
api_body['labelIds'] = labelsToLabelIds(gmail, labels)
elif labels:
systemErrorExit(3, 'labels argument is only valid for importemail and insertemail')
if not recipient:
recipient = userId
msg = message_from_string(body)
msg['Subject'] = subject
msg['From'] = userId
msg['To'] = msg_rcpt
callGAPI(gmail.users().messages(), 'send',
userId=userId, body={'raw': base64.urlsafe_b64encode(msg.as_bytes()).decode()})
msg['To'] = recipient
api_body['raw'] = base64.urlsafe_b64encode(msg.as_bytes()).decode()
if method == 'draft':
resource = gmail.users().drafts()
method = 'create'
api_body = {'message': api_body}
elif method in ['insert', 'import']:
kwargs['internalDateSource'] = 'dateHeader'
if method == 'import':
method = 'import_'
callGAPI(resource, method, userId=userId, body=api_body, **kwargs)
def addFieldToFieldsList(fieldName, fieldsChoiceMap, fieldsList):
fields = fieldsChoiceMap[fieldName.lower()]
@@ -10943,7 +11018,7 @@ def sortCSVTitles(firstTitle, titles):
def writeCSVfile(csvRows, titles, list_type, todrive):
def rowDateTimeFilterMatch(dateMode, rowDate, op, filterDate):
if not rowDate:
if not rowDate or not isinstance(rowDate, str):
return False
try:
rowTime = dateutil.parser.parse(rowDate, ignoretz=True)
@@ -10999,7 +11074,9 @@ def writeCSVfile(csvRows, titles, list_type, todrive):
sys.stderr.write('WARNING: Row filter column "{0}" is not in output columns\n'.format(column))
continue
if filterVal[0] == 'regex':
csvRows = [row for row in csvRows if filterVal[1].search(row.get(column, ''))]
csvRows = [row for row in csvRows if filterVal[1].search(str(row.get(column, '')))]
elif filterVal[0] == 'notregex':
csvRows = [row for row in csvRows if not filterVal[1].search(str(row.get(column, '')))]
elif filterVal[0] in ['date', 'time']:
csvRows = [row for row in csvRows if rowDateTimeFilterMatch(filterVal[0] == 'date', row.get(column, ''), filterVal[1], filterVal[2])]
elif filterVal[0] == 'count':
@@ -12021,10 +12098,12 @@ def doPrintMobileDevices():
else:
if attrib not in titles:
titles.append(attrib)
if attrib != 'deviceId':
row[attrib] = mobile[attrib]
else:
if attrib == 'deviceId':
row[attrib] = mobile[attrib].encode('unicode-escape').decode(UTF8)
elif attrib == 'securityPatchLevel' and int(mobile[attrib]):
row[attrib] = utils.formatTimestampYMDHMS(mobile[attrib])
else:
row[attrib] = mobile[attrib]
csvRows.append(row)
sortCSVTitles(['resourceId', 'deviceId', 'serialNumber', 'name', 'email', 'status'], titles)
writeCSVfile(csvRows, titles, 'Mobile', todrive)
@@ -12139,12 +12218,14 @@ def _checkTPMVulnerability(cros):
cros['tpmVersionInfo']['tpmVulnerability'] = 'NOT IMPACTED'
def _guessAUE(cros, guessedAUEs):
if not GC_Values.get('CROS_AUE_DATES', None):
GC_Values['CROS_AUE_DATES'] = json.loads(getGAMProjectFile('src/cros-aue-dates.json'))
crosModel = cros.get('model')
if crosModel:
if crosModel not in guessedAUEs:
closest_match = difflib.get_close_matches(crosModel.lower(), CROS_AUE_DATES, n=1)
closest_match = difflib.get_close_matches(crosModel.lower(), GC_Values['CROS_AUE_DATES'], n=1)
if closest_match:
guessedAUEs[crosModel] = {'guessedAUEDate': CROS_AUE_DATES[closest_match[0]],
guessedAUEs[crosModel] = {'guessedAUEDate': GC_Values['CROS_AUE_DATES'][closest_match[0]],
'guessedAUEModel': closest_match[0]}
else:
guessedAUEs[crosModel] = {'guessedAUEDate': u'',
@@ -14043,7 +14124,9 @@ def ProcessGAMCommand(args):
elif command == 'calendar':
argument = sys.argv[3].lower()
if argument == 'showacl':
doCalendarShowACL()
doCalendarPrintShowACLs(False)
elif argument == 'printacl':
doCalendarPrintShowACLs(True)
elif argument == 'add':
doCalendarAddACL('Add')
elif argument in ['del', 'delete']:
@@ -14390,6 +14473,14 @@ def ProcessGAMCommand(args):
elif command == 'imap':
#doImap(users)
runCmdForUsers(doImap, users, default_to_batch=True)
elif command == 'sendemail':
sendOrDropEmail(users, 'send')
elif command == 'importemail':
sendOrDropEmail(users, 'import')
elif command == 'insertemail':
sendOrDropEmail(users, 'insert')
elif command == 'draftemail':
sendOrDropEmail(users, 'draft')
elif command == 'language':
doLanguage(users)
elif command in ['pop', 'pop3']:

View File

@@ -3,6 +3,6 @@ dnspython
google-api-python-client
google-auth
google-auth-httplib2
google-auth-oauthlib
google-auth-oauthlib==0.4.0
httplib2>=0.13.0
passlib

View File

@@ -4,6 +4,8 @@ from xml.etree import ElementTree as ET
import requests
from html.parser import HTMLParser
import string
import sys
import json
import dateutil.parser
class MyHTMLParser(HTMLParser):
@@ -27,18 +29,18 @@ class MyHTMLParser(HTMLParser):
fullname = '%s %s' % (oem, model)
fullname = fullname.lower()
date = dateutil.parser.parse(data).replace(day=1).strftime('%Y-%m-%dT00:00:00.000Z')
output_rows.append(" '%s': '%s'," % (fullname, date))
output_rows[fullname] = date
if fullname in exceptions:
for value in exceptions[fullname]:
output_rows.append(" '%s': '%s'," % (value, date))
output_rows[value] = date
data_is_date = False
else:
model = ''.join(filter(lambda x: x in printable, data)).replace('"', '\\"')
model = ''.join(filter(lambda x: x in printable, data))
data_is_date = True
next_data_is_td = False
global oem, next_data_is_oem, next_data_is_td, data_is_date, model, printable, exceptions, output_rows
output_rows = []
output_rows = {}
printable = set(string.printable)
exceptions = {
# 'AUE OEM MODEL': ['API MODEL 1', ...]
@@ -95,10 +97,6 @@ next_data_is_oem = False
next_data_is_td = False
data_is_date = False
auepage = requests.get('https://support.google.com/chrome/a/answer/6220366?hl=en')
print('CROS_AUE_DATES = {')
parser = MyHTMLParser()
parser.feed(auepage.content.decode('utf-8'))
output_rows.sort(key=str.lower)
for row in output_rows:
print(row)
print('}')
print(json.dumps(output_rows, indent=2, sort_keys=True))

View File

@@ -1,3 +1,4 @@
import datetime
import re
import sys
from html.entities import name2codepoint
@@ -22,8 +23,7 @@ class _DeHTMLParser(HTMLParser):
self.__text.append(data)
def handle_charref(self, name):
self.__text.append(
chr(int(name[1:], 16)) if name.startswith('x') else chr(int(name)))
self.__text.append(chr(int(name[1:], 16)) if name.startswith('x') else chr(int(name)))
def handle_entityref(self, name):
cp = name2codepoint.get(name)
@@ -73,6 +73,16 @@ def indentMultiLineText(message, n=0):
return message.replace('\n', '\n{0}'.format(' ' * n)).rstrip()
def formatTimestampYMD(timestamp):
return datetime.datetime.fromtimestamp(int(timestamp)/1000).strftime('%Y-%m-%d')
def formatTimestampYMDHMS(timestamp):
return datetime.datetime.fromtimestamp(int(timestamp)/1000).strftime('%Y-%m-%d %H:%M:%S')
def formatTimestampYMDHMSF(timestamp):
return str(datetime.datetime.fromtimestamp(int(timestamp)/1000))
def formatFileSize(fileSize):
if fileSize == 0:
return '0kb'

View File

@@ -6,7 +6,7 @@ import platform
import re
gam_author = 'Jay Lee <jay0lee@gmail.com>'
gam_version = '4.86'
gam_version = '4.88'
gam_license = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
GAM_URL = 'https://git.io/gam'
@@ -20,7 +20,7 @@ GAM_RELEASES = 'https://github.com/jay0lee/GAM/releases'
GAM_WIKI = 'https://github.com/jay0lee/GAM/wiki'
GAM_ALL_RELEASES = 'https://api.github.com/repos/jay0lee/GAM/releases'
GAM_LATEST_RELEASE = GAM_ALL_RELEASES+'/latest'
GAM_PROJECT_APIS = 'https://raw.githubusercontent.com/jay0lee/GAM/master/src/project-apis.txt'
GAM_PROJECT_FILEPATH = 'https://raw.githubusercontent.com/jay0lee/GAM/master/'
true_values = ['on', 'yes', 'enabled', 'true', '1']
false_values = ['off', 'no', 'disabled', 'false', '0']
@@ -600,266 +600,6 @@ CROS_END_ARGUMENTS = ['end', 'enddate']
CROS_TPM_VULN_VERSIONS = ['41f', '420', '628', '8520',]
CROS_TPM_FIXED_VERSIONS = ['422', '62b', '8521',]
# parsed from https://support.google.com/chrome/a/answer/6220366?hl=en
# using src/tools/parse-aue.py
CROS_AUE_DATES = {
'acer ac700': '2016-08-01T00:00:00.000Z',
'acer c7 chromebook (c710)': '2017-10-01T00:00:00.000Z',
'acer c7 chromebook': '2017-10-01T00:00:00.000Z',
'acer c720 chromebook': '2019-06-01T00:00:00.000Z',
'acer c740 chromebook': '2019-06-01T00:00:00.000Z',
'acer chromebase 24': '2021-06-01T00:00:00.000Z',
'acer chromebase': '2020-08-01T00:00:00.000Z',
'acer chromebook 11 (c720, c720p)': '2019-06-01T00:00:00.000Z',
'acer chromebook 11 (c732, c732t, c732l, c732lt)': '2023-11-01T00:00:00.000Z',
'acer chromebook 11 (c740)': '2020-06-01T00:00:00.000Z',
'acer chromebook 11 (c771, c771t)': '2022-11-01T00:00:00.000Z',
'acer chromebook 11 (cb3-111, c730, c730e)': '2019-08-01T00:00:00.000Z',
'acer chromebook 11 (cb3-131, c735)': '2021-01-01T00:00:00.000Z',
'acer chromebook 11 (cb311-8h, cb311-8ht)': '2023-11-01T00:00:00.000Z',
'acer chromebook 11 n7 (c731, c731t)': '2022-01-01T00:00:00.000Z',
'acer chromebook 13 (cb5-311)': '2019-09-01T00:00:00.000Z',
'acer chromebook 13 (cb713-1w)': '2024-06-01T00:00:00.000Z',
'acer chromebook 13(cb5-311, c810)': '2019-09-01T00:00:00.000Z',
'acer chromebook 14 (cb3-431)': '2021-06-01T00:00:00.000Z',
'acer chromebook 14 for work (cp5-471)': '2022-11-01T00:00:00.000Z',
'acer chromebook 15 (c910 / cb5-571)': '2020-06-01T00:00:00.000Z',
'acer chromebook 15 (cb3-531)': '2020-06-01T00:00:00.000Z',
'acer chromebook 15 (cb3-532)': '2021-08-01T00:00:00.000Z',
'acer chromebook 15 (cb315-1h,cb315-1ht)': '2023-11-01T00:00:00.000Z',
'acer chromebook 15 (cb5-571, c910)': '2020-06-01T00:00:00.000Z',
'acer chromebook 15 (cb515-1h,cb515-1ht)': '2023-11-01T00:00:00.000Z',
'acer chromebook 311 (c721, c733, c733u, c733t)': '2025-06-01T00:00:00.000Z',
'acer chromebook 311': '2025-06-01T00:00:00.000Z',
'acer chromebook 311': '2025-06-01T00:00:00.000Z',
'acer chromebook 315 (cb315-2h)': '2025-06-01T00:00:00.000Z',
'acer chromebook 315': '2025-06-01T00:00:00.000Z',
'acer chromebook 512 (c851, c851t)': '2025-06-01T00:00:00.000Z',
'acer chromebook 514': '2023-11-01T00:00:00.000Z',
'acer chromebook 714 (cb714-1w / cb714-1wt)': '2024-06-01T00:00:00.000Z',
'acer chromebook 715 (cb715-1w / cb715-1wt)': '2024-06-01T00:00:00.000Z',
'acer chromebook r11 (cb5-132t, c738t)': '2021-06-01T00:00:00.000Z',
'acer chromebook r13 (cb5-312t)': '2021-09-01T00:00:00.000Z',
'acer chromebook spin 11 (cp311-h1, cp311-1hn)': '2023-11-01T00:00:00.000Z',
'acer chromebook spin 11 (r751t)': '2023-11-01T00:00:00.000Z',
'acer chromebook spin 13 (cp713-1wn)': '2024-06-01T00:00:00.000Z',
'acer chromebook spin 15 (cp315)': '2023-11-01T00:00:00.000Z',
'acer chromebook spin 311 (r721t)': '2025-06-01T00:00:00.000Z',
'acer chromebook spin 511 (r752t, r752tn)': '2025-06-01T00:00:00.000Z',
'acer chromebook spin 511': '2025-06-01T00:00:00.000Z',
'acer chromebook spin 512 (r851tn)': '2025-06-01T00:00:00.000Z',
'acer chromebook tab 10': '2023-08-01T00:00:00.000Z',
'acer chromebox cxi2 / cxv2': '2020-06-01T00:00:00.000Z',
'acer chromebox cxi2': '2020-06-01T00:00:00.000Z',
'acer chromebox cxi3': '2024-06-01T00:00:00.000Z',
'acer chromebox': '2019-09-01T00:00:00.000Z',
'aopen chromebase commercial': '2020-09-01T00:00:00.000Z',
'aopen chromebase mini': '2022-02-01T00:00:00.000Z',
'aopen chromebox commercial 2': '2024-06-01T00:00:00.000Z',
'aopen chromebox commercial': '2020-09-01T00:00:00.000Z',
'aopen chromebox mini': '2022-02-01T00:00:00.000Z',
'asi chromebook': '2020-06-01T00:00:00.000Z',
'asus chromebit cs10': '2020-11-01T00:00:00.000Z',
'asus chromebook c200': '2019-06-01T00:00:00.000Z',
'asus chromebook c200ma': '2019-06-01T00:00:00.000Z',
'asus chromebook c201pa': '2020-06-01T00:00:00.000Z',
'asus chromebook c201pa': '2020-06-01T00:00:00.000Z',
'asus chromebook c202sa': '2021-06-01T00:00:00.000Z',
'asus chromebook c204': '2025-06-01T00:00:00.000Z',
'asus chromebook c204': '2025-06-01T00:00:00.000Z',
'asus chromebook c213na': '2023-11-01T00:00:00.000Z',
'asus chromebook c223': '2023-11-01T00:00:00.000Z',
'asus chromebook c300': '2019-08-01T00:00:00.000Z',
'asus chromebook c300ma': '2019-08-01T00:00:00.000Z',
'asus chromebook c300sa / c301sa': '2021-06-01T00:00:00.000Z',
'asus chromebook c403': '2023-11-01T00:00:00.000Z',
'asus chromebook c423': '2023-11-01T00:00:00.000Z',
'asus chromebook c523': '2023-11-01T00:00:00.000Z',
'asus chromebook flip c100pa': '2020-07-01T00:00:00.000Z',
'asus chromebook flip c101pa': '2023-08-01T00:00:00.000Z',
'asus chromebook flip c213': '2023-11-01T00:00:00.000Z',
'asus chromebook flip c214': '2025-06-01T00:00:00.000Z',
'asus chromebook flip c302': '2022-11-01T00:00:00.000Z',
'asus chromebook flip c434': '2024-06-01T00:00:00.000Z',
'asus chromebook tablet ct100': '2023-08-01T00:00:00.000Z',
'asus chromebox (cn60)': '2019-09-01T00:00:00.000Z',
'asus chromebox 2 (cn62)': '2021-06-01T00:00:00.000Z',
'asus chromebox 3 (cn65)': '2024-06-01T00:00:00.000Z',
'asus chromebox 3': '2024-06-01T00:00:00.000Z',
'asus chromebox cn60': '2019-09-01T00:00:00.000Z',
'asus chromebox cn62': '2021-06-01T00:00:00.000Z',
'bobicus chromebook 11': '2020-06-01T00:00:00.000Z',
'chromebook 11 (c730 / cb3-111)': '2019-08-01T00:00:00.000Z',
'chromebook 11 (c735)': '2021-01-01T00:00:00.000Z',
'chromebook 15 (cb515 - 1ht / 1h)': '2023-11-01T00:00:00.000Z',
'chromebook 311 (c721)': '2025-06-01T00:00:00.000Z',
'chromebook pcm-116e': '2020-06-01T00:00:00.000Z',
'consumer chromebook': '2020-06-01T00:00:00.000Z',
'cr-48': '2015-12-01T00:00:00.000Z',
'crambo chromebook': '2020-06-01T00:00:00.000Z',
'ctl chromebook j41 / j41t': '2023-11-01T00:00:00.000Z',
'ctl chromebook nl7 / nl7t-360 / nl7tw-360': '2023-11-01T00:00:00.000Z',
'ctl chromebook nl7': '2023-11-01T00:00:00.000Z',
'ctl chromebook tab tx1': '2023-08-01T00:00:00.000Z',
'ctl chromebook tablet tx1 for education': '2023-08-01T00:00:00.000Z',
'ctl chromebox cbx1': '2024-06-01T00:00:00.000Z',
'ctl j2 / j4 chromebook': '2020-06-01T00:00:00.000Z',
'ctl j5 chromebook': '2021-08-01T00:00:00.000Z',
'ctl n6 education chromebook': '2020-06-01T00:00:00.000Z',
'ctl nl61 chromebook': '2021-08-01T00:00:00.000Z',
'dell chromebook 11 (3120)': '2020-06-01T00:00:00.000Z',
'dell chromebook 11 (3180)': '2022-05-01T00:00:00.000Z',
'dell chromebook 11 (5190)': '2023-11-01T00:00:00.000Z',
'dell chromebook 11 2-in-1 (3189)': '2022-05-01T00:00:00.000Z',
'dell chromebook 11 2-in-1 (5190)': '2023-11-01T00:00:00.000Z',
'dell chromebook 11': '2019-06-01T00:00:00.000Z',
'dell chromebook 13 (3380)': '2022-11-01T00:00:00.000Z',
'dell chromebook 13 (7310)': '2020-09-01T00:00:00.000Z',
'dell chromebook 3100 2-in-1': '2025-06-01T00:00:00.000Z',
'dell chromebook 3100': '2025-06-01T00:00:00.000Z',
'dell chromebook 3400': '2025-06-01T00:00:00.000Z',
'dell chromebox': '2019-09-01T00:00:00.000Z',
'dell inspiron chromebook 14 2-in-1 (7486)': '2024-06-01T00:00:00.000Z',
'edugear chromebook k': '2020-06-01T00:00:00.000Z',
'edugear chromebook m': '2020-06-01T00:00:00.000Z',
'edugear chromebook r': '2020-06-01T00:00:00.000Z',
'edugear cmt chromebook': '2021-08-01T00:00:00.000Z',
'edxis chromebook': '2020-06-01T00:00:00.000Z',
'edxis education chromebook': '2020-06-01T00:00:00.000Z',
'epik 11.6" chromebook elb1101': '2020-06-01T00:00:00.000Z',
'google chromebook pixel (2015)': '2020-06-01T00:00:00.000Z',
'google chromebook pixel': '2018-06-01T00:00:00.000Z',
'google cr-48': '2015-12-01T00:00:00.000Z',
'google pixel slate': '2024-06-01T00:00:00.000Z',
'google pixelbook': '2024-06-01T00:00:00.000Z',
'haier chromebook 11 c': '2021-08-01T00:00:00.000Z',
'haier chromebook 11 g2': '2020-09-01T00:00:00.000Z',
'haier chromebook 11': '2020-06-01T00:00:00.000Z',
'haier chromebook 11e': '2020-06-01T00:00:00.000Z',
'hexa chromebook pi': '2020-06-01T00:00:00.000Z',
'hisense chromebook 11': '2020-06-01T00:00:00.000Z',
'hp chromebook 11 1100-1199 / hp chromebook 11 g1': '2018-10-01T00:00:00.000Z',
'hp chromebook 11 2000-2099 / hp chromebook 11 g2': '2019-06-01T00:00:00.000Z',
'hp chromebook 11 2100-2199 / hp chromebook 11 g3': '2020-06-01T00:00:00.000Z',
'hp chromebook 11 2200-2299 / hp chromebook 11 g4/g4 ee': '2020-06-01T00:00:00.000Z',
'hp chromebook 11 g1': '2018-10-01T00:00:00.000Z',
'hp chromebook 11 g2': '2019-06-01T00:00:00.000Z',
'hp chromebook 11 g3': '2020-06-01T00:00:00.000Z',
'hp chromebook 11 g4/g4 ee': '2020-06-01T00:00:00.000Z',
'hp chromebook 11 g5 / hp chromebook 11-vxxx': '2021-07-01T00:00:00.000Z',
'hp chromebook 11 g5 ee': '2022-01-01T00:00:00.000Z',
'hp chromebook 11 g5': '2021-07-01T00:00:00.000Z',
'hp chromebook 11 g6 ee': '2023-11-01T00:00:00.000Z',
'hp chromebook 11 g7 ee': '2025-06-01T00:00:00.000Z',
'hp chromebook 11a g6 ee': '2025-06-01T00:00:00.000Z',
'hp chromebook 13 g1': '2022-11-01T00:00:00.000Z',
'hp chromebook 14 / hp chromebook 14 g5': '2023-11-01T00:00:00.000Z',
'hp chromebook 14 ak000-099 / hp chromebook 14 g4': '2021-09-01T00:00:00.000Z',
'hp chromebook 14 db0000-db0999': '2025-06-01T00:00:00.000Z',
'hp chromebook 14 g3': '2019-10-01T00:00:00.000Z',
'hp chromebook 14 g4': '2021-09-01T00:00:00.000Z',
'hp chromebook 14 g5': '2023-11-01T00:00:00.000Z',
'hp chromebook 14 x000-x999 / hp chromebook 14 g3': '2019-10-01T00:00:00.000Z',
'hp chromebook 14': '2019-06-01T00:00:00.000Z',
'hp chromebook 14a g5': '2025-06-01T00:00:00.000Z',
'hp chromebook 15 g1': '2024-06-01T00:00:00.000Z',
'hp chromebook x2 ': '2024-06-01T00:00:00.000Z',
'hp chromebook x360 11 g1 ee': '2023-11-01T00:00:00.000Z',
'hp chromebook x360 11 g2 ee': '2025-06-01T00:00:00.000Z',
'hp chromebook x360 14 g1': '2024-06-01T00:00:00.000Z',
'hp chromebook x360 14': '2024-06-01T00:00:00.000Z',
'hp chromebox cb1-(000-099) / hp chromebox g1/ hp chromebox for meetings': '2019-09-01T00:00:00.000Z',
'hp chromebox g1': '2019-09-01T00:00:00.000Z',
'hp chromebox g2': '2024-06-01T00:00:00.000Z',
'hp pavilion chromebook 14': '2018-02-01T00:00:00.000Z',
'jp sa couto chromebook': '2020-06-01T00:00:00.000Z',
'lava xolo chromebook': '2020-06-01T00:00:00.000Z',
'lenovo 100e chromebook 2nd gen mtk': '2025-06-01T00:00:00.000Z',
'lenovo 100e chromebook 2nd gen': '2025-06-01T00:00:00.000Z',
'lenovo 100e chromebook': '2023-11-01T00:00:00.000Z',
'lenovo 100s chromebook': '2020-09-01T00:00:00.000Z',
'lenovo 14e chromebook': '2025-06-01T00:00:00.000Z',
'lenovo 300e chromebook 2nd gen mtk': '2025-06-01T00:00:00.000Z',
'lenovo 300e chromebook 2nd gen': '2025-06-01T00:00:00.000Z',
'lenovo 300e chromebook': '2025-06-01T00:00:00.000Z',
'lenovo 500e chromebook 2nd gen': '2025-06-01T00:00:00.000Z',
'lenovo 500e chromebook': '2023-11-01T00:00:00.000Z',
'lenovo chromebook c330': '2022-06-01T00:00:00.000Z',
'lenovo chromebook s330': '2022-06-01T00:00:00.000Z',
'lenovo flex 11 chromebook': '2022-06-01T00:00:00.000Z',
'lenovo ideapad c330 chromebook': '2022-06-01T00:00:00.000Z',
'lenovo ideapad s330 chromebook': '2022-06-01T00:00:00.000Z',
'lenovo n20 chromebook': '2019-06-01T00:00:00.000Z',
'lenovo n21 chromebook': '2020-06-01T00:00:00.000Z',
'lenovo n22 chromebook': '2021-06-01T00:00:00.000Z',
'lenovo n23 chromebook': '2021-06-01T00:00:00.000Z',
'lenovo n23 yoga chromebook': '2022-06-01T00:00:00.000Z',
'lenovo n42 chromebook': '2021-06-01T00:00:00.000Z',
'lenovo thinkcentre chromebox': '2020-06-01T00:00:00.000Z',
'lenovo thinkpad 11e 3rd gen chromebook': '2021-06-01T00:00:00.000Z',
'lenovo thinkpad 11e 4th gen chromebook': '2023-11-01T00:00:00.000Z',
'lenovo thinkpad 11e chromebook (4th gen)/lenovo thinkpad yoga 11e chromebook (4th gen)': '2023-11-01T00:00:00.000Z',
'lenovo thinkpad 11e chromebook': '2019-06-01T00:00:00.000Z',
'lenovo thinkpad 13': '2022-11-01T00:00:00.000Z',
'lenovo thinkpad x131e chromebook': '2018-06-01T00:00:00.000Z',
'lenovo yoga c630 chromebook': '2024-06-01T00:00:00.000Z',
'lg chromebase (22cb25s)': '2020-06-01T00:00:00.000Z',
'lg chromebase (22cv241)': '2019-06-01T00:00:00.000Z',
'lumos education chromebook': '2020-06-01T00:00:00.000Z',
'm&a chromebook': '2020-06-01T00:00:00.000Z',
'mecer chromebook': '2020-06-01T00:00:00.000Z',
'mecer v2 chromebook': '2021-08-01T00:00:00.000Z',
'medion chromebook akoya s2013 ': '2020-06-01T00:00:00.000Z',
'medion chromebook s2015': '2020-06-01T00:00:00.000Z',
'multilaser chromebook m11c': '2021-08-01T00:00:00.000Z',
'ncomputing chromebook cx100': '2020-06-01T00:00:00.000Z',
'ncomputing chromebook cx110': '2020-06-01T00:00:00.000Z',
'nexian chromebook 11.6\"': '2020-06-01T00:00:00.000Z',
'pcmerge chromebook al116': '2023-11-01T00:00:00.000Z',
'pcmerge chromebookpcm-116e/pcm-116eb': '2020-06-01T00:00:00.000Z',
'pcmerge chromebookpcm-116t-432b': '2021-08-01T00:00:00.000Z',
'poin2 chromebook 11': '2020-06-01T00:00:00.000Z',
'poin2 chromebook 11c': '2022-03-01T00:00:00.000Z',
'poin2 chromebook 14': '2022-03-01T00:00:00.000Z',
'positivo chromebook c216b': '2021-08-01T00:00:00.000Z',
'positivo chromebook ch1190': '2020-06-01T00:00:00.000Z',
'prowise 11.6" entry line chromebook': '2020-06-01T00:00:00.000Z',
'prowise chromebook eduline': '2023-11-01T00:00:00.000Z',
'prowise chromebook entryline': '2020-06-01T00:00:00.000Z',
'prowise chromebook proline': '2021-08-01T00:00:00.000Z',
'prowise proline chromebook': '2021-08-01T00:00:00.000Z',
'rgs education chromebook': '2020-06-01T00:00:00.000Z',
'samsung chromebook - xe303': '2018-07-01T00:00:00.000Z',
'samsung chromebook 2 11\" - xe500c12': '2020-06-01T00:00:00.000Z',
'samsung chromebook 2 11\"': '2019-06-01T00:00:00.000Z',
'samsung chromebook 2 13\"': '2019-06-01T00:00:00.000Z',
'samsung chromebook 3': '2021-06-01T00:00:00.000Z',
'samsung chromebook plus (v2)': '2024-06-01T00:00:00.000Z',
'samsung chromebook plus': '2023-08-01T00:00:00.000Z',
'samsung chromebook pro': '2022-11-01T00:00:00.000Z',
'samsung chromebook series 5 550': '2017-05-01T00:00:00.000Z',
'samsung chromebook series 5': '2016-06-01T00:00:00.000Z',
'samsung chromebook': '2018-07-01T00:00:00.000Z',
'samsung chromebox series 3': '2018-03-01T00:00:00.000Z',
'sector 5 e1 rugged chromebook': '2020-06-01T00:00:00.000Z',
'sector 5 e3 chromebook': '2023-11-01T00:00:00.000Z',
'senkatel c1101 chromebook': '2020-06-01T00:00:00.000Z',
'thinkpad 11e chromebook 3rd gen (yoga/clamshell)': '2021-06-01T00:00:00.000Z',
'thinkpad 13 chromebook': '2022-11-01T00:00:00.000Z',
'toshiba chromebook 2 (2015 edition)': '2020-09-01T00:00:00.000Z',
'toshiba chromebook 2': '2020-06-01T00:00:00.000Z',
'toshiba chromebook': '2019-06-01T00:00:00.000Z',
'true idc chromebook 11': '2020-06-01T00:00:00.000Z',
'true idc chromebook': '2020-06-01T00:00:00.000Z',
'videonet chromebook bl10': '2020-06-01T00:00:00.000Z',
'videonet chromebook': '2020-06-01T00:00:00.000Z',
'viewsonic nmp660 chromebox': '2024-06-01T00:00:00.000Z',
'viglen chromebook 11': '2020-06-01T00:00:00.000Z',
'viglen chromebook 11c': '2023-11-01T00:00:00.000Z',
'viglen chromebook 360': '2021-08-01T00:00:00.000Z',
'xolo chromebook': '2020-06-01T00:00:00.000Z',
}
COLLABORATIVE_ACL_CHOICES = {
'members': 'ALL_MEMBERS',
'managersonly': 'MANAGERS_ONLY',