mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-16 20:21:37 +00:00
Compare commits
17 Commits
20241015.2
...
20241023.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
197bcb3599 | ||
|
|
1474335a79 | ||
|
|
0f8c361dcd | ||
|
|
beb75dbc20 | ||
|
|
cbb95a47f8 | ||
|
|
d7e36bc5eb | ||
|
|
ef14359d9b | ||
|
|
b1444d7c04 | ||
|
|
c3c7d629f7 | ||
|
|
eb33b6521b | ||
|
|
932fe5db02 | ||
|
|
6885bcae92 | ||
|
|
d35e9fcae4 | ||
|
|
861279e614 | ||
|
|
b80dd15f4b | ||
|
|
ae95c8fdea | ||
|
|
090b5937ab |
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
@@ -115,7 +115,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
cache.tar.xz
|
||||
key: gam-${{ matrix.jid }}-20241014
|
||||
key: gam-${{ matrix.jid }}-20241022
|
||||
|
||||
- name: Untar Cache archive
|
||||
if: matrix.goal == 'build' && steps.cache-python-ssl.outputs.cache-hit == 'true'
|
||||
@@ -702,6 +702,7 @@ jobs:
|
||||
export MSI_FILENAME="${GITHUB_WORKSPACE}/gam-${GAMVERSION}-windows-${GAM_ARCHIVE_ARCH}.msi"
|
||||
# auto-generate a lib.wxs based on the files PyInstaller created for the lib/ directory
|
||||
/c/Program\ Files\ \(x86\)/WiX\ Toolset\ v3.14/bin/heat.exe dir "${gampath}/lib" -ke -srd -cg Lib -gg -dr lib -directoryid lib -out lib.wxs
|
||||
$PYTHON tools/gen-wix-xml-filelist.py lib.wxs
|
||||
echo "-- begin lib.wxs --"
|
||||
cat lib.wxs
|
||||
echo "-- end lib.wxs --"
|
||||
|
||||
@@ -163,12 +163,11 @@ as required by Google for headless computers/cloud shells; this is required as o
|
||||
```
|
||||
## Manage Projects
|
||||
In all of the project commands, the Google Workspace admin/GCP project manager `<EmailAddress>` can be omitted; you will be prompted for a value.
|
||||
You must enter a full address, i.e., user@domain.com; you will be required to enter the password.
|
||||
You must enter a full address, i.e., user@domain.com; you will be required to authenticate.
|
||||
|
||||
For `print|show projects`, you can eliminate the password requirement by enabling the following scope in `gam update serviceaccount`;
|
||||
GAM will then use Service Account access to display projects.
|
||||
For `print|show projects`, you can eliminate the password prompt and authentication requirement by specifying the super admin emailaddress used in `gam oauth create`.
|
||||
```
|
||||
[*] 9) Cloud Resource Manager API v3
|
||||
gam print projects admin admin@domain.com
|
||||
```
|
||||
|
||||
## Authorize a super admin to create projects
|
||||
@@ -362,7 +361,7 @@ gam update project [[admin] <EmailAddress>] [<ProjectIDEntity>]
|
||||
* `<EmailAddress>` - A Google Workspace admin/GCP project manager; if omitted, you will be prompted for the address
|
||||
|
||||
Use these options to select projects.
|
||||
* `current` - The project referenced in `client_secret.json`; this is the default
|
||||
* `current` - The project referenced in `client_secrets.json`; this is the default
|
||||
* `gam` - Projects accessible by the administrator that were created by Gam, i.e, their project ID begins with `gam-project-`
|
||||
* `<ProjectID>` - A Google API project ID
|
||||
* `filter <String>` - A filter to select projects accessible by the administrator; see the API documentation
|
||||
@@ -374,7 +373,7 @@ gam delete project [[admin] <EmailAddress>] [<ProjectIDEntity>]
|
||||
* `<EmailAddress>` - A Google Workspace admin/GCP project manager; if omitted, you will be prompted for the address
|
||||
|
||||
Use these options to select projects.
|
||||
* `current` - The project referenced in `client_secret.json`; this is the default
|
||||
* `current` - The project referenced in `client_secrets.json`; this is the default
|
||||
* `gam` - Projects accessible by the administrator that were created by Gam, i.e, their project ID begins with `gam-project-`
|
||||
* `<ProjectID>` - A Google API project ID
|
||||
* `filter <String>` - A filter to select projects accessible by the administrator; see the API documentation
|
||||
@@ -394,7 +393,7 @@ gam show projects [[admin] <EmailAddress>] [all|<ProjectIDEntity>]
|
||||
|
||||
Use these options to select projects.
|
||||
* `all` - All projects accessible by the administrator; this is the default
|
||||
* `current` - The project referenced in `client_secret.json`
|
||||
* `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-`
|
||||
* `<ProjectID>` - A Google API project ID
|
||||
* `filter <String>` - A filter to select projects accessible by the administrator; see the API documentation
|
||||
@@ -412,7 +411,7 @@ gam print projects [[admin] <EmailAddress>] [all|<ProjectIDEntity>] [todrive <To
|
||||
|
||||
Use these options to select projects.
|
||||
* `all` - All projects accessible by the administrator; this is the default
|
||||
* `current` - The project referenced in `client_secret.json`
|
||||
* `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-`
|
||||
* `<ProjectID>` - A Google API project ID
|
||||
* `filter <String>` - A filter to select projects accessible by the administrator; see the API documentation
|
||||
@@ -698,7 +697,7 @@ gam create|add svcacct [[admin] <EmailAddress>] [<ProjectIDEntity>]
|
||||
* `<EmailAddress>` - Google Workspace admin/GCP project manager; if omitted, you will be prompted for the address
|
||||
|
||||
Use these options to select projects.
|
||||
* `current` - The project referenced in `client_secret.json`; this is the default
|
||||
* `current` - The project referenced in `client_secrets.json`; this is the default
|
||||
* `gam` - Projects accessible by the administrator that were created by Gam, i.e, their project ID begins with `gam-project-`
|
||||
* `<ProjectID>` - A Google API project ID
|
||||
* `filter <String>` - A filter to select projects accessible by the administrator; see the API documentation
|
||||
@@ -721,7 +720,7 @@ gam delete svcacct [[admin] <EmailAddress>] [<ProjectIDEntity>]
|
||||
* `<EmailAddress>` - Google Workspace admin/GCP project manager; if omitted, you will be prompted for the address
|
||||
|
||||
Use these options to select projects.
|
||||
* `current` - The project referenced in `client_secret.json`; this is the default
|
||||
* `current` - The project referenced in `client_secrets.json`; this is the default
|
||||
* `gam` - Projects accessible by the administrator that were created by Gam, i.e, their project ID begins with `gam-project-`
|
||||
* `<ProjectID>` - A Google API project ID
|
||||
* `filter <String>` - A filter to select projects accessible by the administrator; see the API documentation
|
||||
@@ -742,7 +741,7 @@ gam print svcaccts [[admin] <EmailAddress>] [all|<ProjectIDEntity>]
|
||||
|
||||
Use these options to select projects.
|
||||
* `all` - All projects accessible by the administrator; this is the default
|
||||
* `current` - The project referenced in `client_secret.json`
|
||||
* `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-`
|
||||
* `<ProjectID>` - A Google API project ID
|
||||
* `filter <String>` - A filter to select projects accessible by the administrator; see the API documentation
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
!# CSV Output Filtering
|
||||
# CSV Output Filtering
|
||||
- [Python Regular Expressions](Python-Regular-Expressions) Search function
|
||||
- [Definitions](#definitions)
|
||||
- [Quoting rules](#quoting-rules)
|
||||
@@ -11,9 +11,11 @@
|
||||
- [Column row limiting](#column-row-limiting)
|
||||
- [Saving filters in gam.cfg](#saving-filters-in-gamcfg)
|
||||
|
||||
There are five values in `gam.cfg` that can be used to filter the output from `gam print` commands.
|
||||
There are seven values in `gam.cfg` that can be used to filter the output from `gam print` commands.
|
||||
* `csv_output_header_filter` - A list of `<RegularExpressions>` used to select specific column headers to include
|
||||
* `csv_output_header_drop_filter` - A list of `<RegularExpressions>` used to select specific column headers to exclude
|
||||
* `csv_output_header_force` - A list of <Strings> used to specify the exact column headers to include
|
||||
* `csv_output_header_order` - A list of <Strings> used to specify the column header order; any headers in the file but not in the list will appear after the headers in the list.
|
||||
* `csv_output_row_filter` - A list or JSON dictionary used to include specific rows based on column values
|
||||
* `csv_output_row_drop_filter` - A list or JSON dictionary used to exclude specific rows based on column values
|
||||
* `csv_output_row_limit` - A limit on the number of rows written
|
||||
@@ -334,7 +336,7 @@ gam config csv_output_row_limit 10 auto_batch_min 1 redirect csv ./BigQuotaFiles
|
||||
```
|
||||
|
||||
## Saving filters in gam.cfg
|
||||
If you define a value for `csv_output_header_filter`, `csv_output_header_drop_filter`, `csv_output_row_filter`, `csv_output_row_drop_filter` or `csv_output_row_limit` in the `[DEFAULT]` section of `gam.cfg`,
|
||||
If you define a value for `csv_output_header_filter`, `csv_output_header_drop_filter`, `csv_output_header_force`, `csv_output_header_order`, `csv_output_row_filter`, `csv_output_row_drop_filter` or `csv_output_row_limit` in the `[DEFAULT]` section of `gam.cfg`,
|
||||
it will apply to every `gam print` command which is probably not desirable. You can store them in `gam.cfg` in named sections.
|
||||
```
|
||||
[Filter510]
|
||||
|
||||
@@ -10,6 +10,58 @@ 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.00.27
|
||||
|
||||
Updated `gam <UserTypeEntity> collect orphans` and all commands that print file paths to recognize
|
||||
that a file owned by a user that has no parents is not an orphan if `sharedWithMeTime` is set.
|
||||
This occurs when user A creates a file in a shared folder owned by user B and user B then removes
|
||||
user A's access to the folder.
|
||||
|
||||
Added commands to display Cloud Identity policies.
|
||||
```
|
||||
gam print policies [todrive <ToDriveAttribute>*]
|
||||
(query <String>) [nowarnings]
|
||||
[formatjson [quotechar <Character>]]
|
||||
gam show policies (query <String>) [nowarnings]
|
||||
[formatjson]
|
||||
```
|
||||
|
||||
### 7.00.26
|
||||
|
||||
Updated `drive_dir` in `gam.cfg` to allow the value `.` that causes `redirect csv|stdout|stderr <FileName>`
|
||||
to write `<FileName>` in the current directory without having to prefix `<FileName>` with `./`.
|
||||
|
||||
Upgraded to OpenSSL 3.4.0.
|
||||
|
||||
### 7.00.25
|
||||
|
||||
Updated authentication process for `gam print|show projects`.
|
||||
|
||||
### 7.00.24
|
||||
|
||||
Updated `gam print|show projects ... showiampolicies 0|1|3` to use non-service account authentication.
|
||||
|
||||
### 7.00.23
|
||||
|
||||
Updated `gam <UserTypeEntity> create|delete chatmember` to accept external (non-domain) email addresses.
|
||||
|
||||
### 7.00.22
|
||||
|
||||
Fixed bug in `gam create vaultmatter ... showdetails` that caused a trap.
|
||||
|
||||
### 7.00.21
|
||||
|
||||
Added `csv_output_header_order` variable to `gam.cfg` that is a list of `<Strings>`
|
||||
that are used to specify the order of column headers in the CSV file written by a gam print command.
|
||||
Any headers in the file but not in the list will appear after the headers in the list.
|
||||
|
||||
This might be used when the CSV file data is to be processed by another program
|
||||
that requires that the headers be in a particular order.
|
||||
|
||||
### 7.00.20
|
||||
|
||||
Fix Windows MSI installer issues on version upgrade. If you are having issues upgrading from a version older than 7.00.20 to this version or newer you may need to do a one time uninstall of GAM7 and then reinstall the new version. No configuration files will be lost during the uninstall / reinstall.
|
||||
|
||||
### 7.00.19
|
||||
|
||||
Updated `gam update shareddrive <SharedDriveEntity> ou <OrgUnitItem>` to handle the following error
|
||||
|
||||
@@ -251,7 +251,7 @@ 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.00.19 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM 7.00.26 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.13.0 64-bit final
|
||||
MacOS Sonoma 14.5 x86_64
|
||||
@@ -923,7 +923,7 @@ 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
|
||||
GAM7 7.00.19 - https://github.com/GAM-team/GAM - pythonsource
|
||||
GAM7 7.00.26 - https://github.com/GAM-team/GAM - pythonsource
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.13.0 64-bit final
|
||||
Windows-10-10.0.17134 AMD64
|
||||
|
||||
@@ -51,6 +51,7 @@ The only `<VariableNames>` recognized in this `<Section>` are:
|
||||
* `csv_output_header_filter`
|
||||
* `csv_output_header_drop_filter`
|
||||
* `csv_output_header_force`
|
||||
* `csv_output_header_order`
|
||||
* `csv_output_row_filter`
|
||||
* `csv_output_row_filter_mode`
|
||||
* `csv_output_row_drop_filter`
|
||||
|
||||
@@ -434,6 +434,7 @@ gam <UserTypeEntity> remove chatmember members <ChatMemberList>
|
||||
```
|
||||
|
||||
### Add members to a chat space, asadmin
|
||||
Creating memberships for users outside the administrator's Google Workspace organization isn't supported using asadmin.
|
||||
```
|
||||
gam <UserItem> create chatmember asadmin <ChatSpace>
|
||||
[type human|bot] [role member|manager]
|
||||
|
||||
@@ -811,7 +811,7 @@ User: testuser@domain.com, Drive Files/Folders: 261, Size: 13822521
|
||||
```
|
||||
Print file counts for a user.
|
||||
```
|
||||
$ gam user testuser@domain,com print filecounts showsize
|
||||
$ gam user testuser@domain.com print filecounts showsize
|
||||
Getting all Drive Files/Folders that match query ('me' in owners) for testuser@domain.com
|
||||
Got 261 Drive Files/Folders that matched query ('me' in owners) for testuser@domain.com...
|
||||
User,Total,Size,application/octet-stream,application/pdf,application/vnd.google-apps.document,application/vnd.google-apps.drawing,application/vnd.google-apps.drive-sdk.423565144751,application/vnd.google-apps.folder,application/vnd.google-apps.form,application/vnd.google-apps.jam,application/vnd.google-apps.presentation,application/vnd.google-apps.shortcut,application/vnd.google-apps.site,application/vnd.google-apps.spreadsheet,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.openxmlformats-officedocument.wordprocessingml.template,application/x-gzip,application/zip,image/jpeg,image/vnd.adobe.photoshop,text/csv,text/plain,text/rtf,text/x-sh
|
||||
@@ -825,6 +825,14 @@ Got 261 Drive Files/Folders that matched query ('me' in owners) for testuser@dom
|
||||
User,Total,Size,application/octet-stream,application/octet-stream-size,application/pdf,application/pdf-size,application/vnd.google-apps.document,application/vnd.google-apps.document-size,application/vnd.google-apps.drawing,application/vnd.google-apps.drawing-size,application/vnd.google-apps.drive-sdk.423565144751,application/vnd.google-apps.drive-sdk.423565144751-size,application/vnd.google-apps.folder,application/vnd.google-apps.folder-size,application/vnd.google-apps.form,application/vnd.google-apps.form-size,application/vnd.google-apps.jam,application/vnd.google-apps.jam-size,application/vnd.google-apps.presentation,application/vnd.google-apps.presentation-size,application/vnd.google-apps.shortcut,application/vnd.google-apps.shortcut-size,application/vnd.google-apps.site,application/vnd.google-apps.site-size,application/vnd.google-apps.spreadsheet,application/vnd.google-apps.spreadsheet-size,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet-size,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.openxmlformats-officedocument.wordprocessingml.document-size,application/vnd.openxmlformats-officedocument.wordprocessingml.template,application/vnd.openxmlformats-officedocument.wordprocessingml.template-size,application/x-gzip,application/x-gzip-size,application/zip,application/zip-size,image/jpeg,image/jpeg-size,image/vnd.adobe.photoshop,image/vnd.adobe.photoshop-size,text/csv,text/csv-size,text/plain,text/plain-size,text/rtf,text/rtf-size,text/x-sh,text/x-sh-size
|
||||
testuser@domain.com,261,13822521,8,17,1,9879,98,52858,2,2048,1,0,68,0,3,0,1,1024,1,0,14,0,1,0,24,11264,1,8157,3,34407,1,25906,4,2768,2,765,8,16498,1,13613198,2,397,13,41461,3,1738,1,136
|
||||
```
|
||||
Print file counts for a Shared Drive
|
||||
```
|
||||
$ gam user testuser@domain.com print filecounts select <SharedDriveID> showsize
|
||||
Getting all Drive Files/Folders for testuser@domain.com
|
||||
Got 261 Drive Files/Folders for testuser@domain.com...
|
||||
User,id,name,Total,Size,Item cap,application/octet-stream,application/pdf,application/vnd.google-apps.document,application/vnd.google-apps.drawing,application/vnd.google-apps.drive-sdk.423565144751,application/vnd.google-apps.folder,application/vnd.google-apps.form,application/vnd.google-apps.jam,application/vnd.google-apps.presentation,application/vnd.google-apps.shortcut,application/vnd.google-apps.site,application/vnd.google-apps.spreadsheet,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.openxmlformats-officedocument.wordprocessingml.template,application/x-gzip,application/zip,image/jpeg,image/vnd.adobe.photoshop,text/csv,text/plain,text/rtf,text/x-sh
|
||||
testuser@domain.com,0AMzwfhFBpwLHUkWXYZ,Shared Drive Name,261,13822521,3.45%,8,1,98,2,1,68,3,1,1,14,1,24,1,3,1,4,2,8,1,2,13,3,1
|
||||
```
|
||||
Get file count summaries by OU; top level selector is ou, sub level selectors are ou_and_children
|
||||
```
|
||||
gam redirect csv ./TopLevelOUs.csv print ous showparent toplevelonly parentselector ou childselector ou_and_children fields orgunitpath
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
- [API documentation](#api-documentation)
|
||||
- [Query documentation](Users-Drive-Query)
|
||||
- [Python Regular Expressions](Python-Regular-Expressions) Sub function
|
||||
- [Folders with Limited Access Beta](#folders-with-limited-access-beta)
|
||||
- [Permission Matches](Permission-Matches)
|
||||
- [Definitions](#definitions)
|
||||
- [Create files](#create-files)
|
||||
@@ -31,6 +32,15 @@
|
||||
* https://support.google.com/a/users/answer/7338880
|
||||
* https://developers.google.com/docs/api/reference/rest
|
||||
|
||||
## Folders with Limited Access Beta
|
||||
|
||||
If you are enrolled in the Beta and want to access the `inheritedpermissionsdisabled` field,
|
||||
you must turn on Drive API v3 beta.
|
||||
|
||||
```
|
||||
gam config drive_v3_beta true user user@domain.com update drivefile <FolderID> inheritedpermissionsdisabled true
|
||||
```
|
||||
|
||||
## Definitions
|
||||
* [`<DriveFileEntity>`](Drive-File-Selection)
|
||||
* [`<UserTypeEntity>`](Collections-of-Users)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Print the current version of Gam with details
|
||||
```
|
||||
gam version
|
||||
GAM 7.00.19 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM 7.00.26 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.13.0 64-bit final
|
||||
MacOS Sonoma 14.5 x86_64
|
||||
@@ -15,7 +15,7 @@ Time: 2023-06-02T21:10:00-07:00
|
||||
Print the current version of Gam with details and time offset information
|
||||
```
|
||||
gam version timeoffset
|
||||
GAM 7.00.19 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM 7.00.26 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.13.0 64-bit final
|
||||
MacOS Sonoma 14.5 x86_64
|
||||
@@ -27,7 +27,7 @@ Your system time differs from www.googleapis.com by less than 1 second
|
||||
Print the current version of Gam with extended details and SSL information
|
||||
```
|
||||
gam version extended
|
||||
GAM 7.00.19 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM 7.00.26 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.13.0 64-bit final
|
||||
MacOS Sonoma 14.5 x86_64
|
||||
@@ -35,17 +35,17 @@ Path: /Users/Admin/bin/gam7
|
||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
Time: 2023-06-02T21:10:00-07:00
|
||||
Your system time differs from admin.googleapis.com by less than 1 second
|
||||
OpenSSL 3.1.1 30 May 2023
|
||||
cryptography 41.0.1
|
||||
filelock 3.13.0
|
||||
google-api-python-client 2.88.0
|
||||
google-auth-httplib2 0.1.0
|
||||
google-auth-oauthlib 1.0.0
|
||||
google-auth 2.19.1
|
||||
OpenSSL 3.4.0 22 Oct Sep 2024
|
||||
cryptography 43.0.3
|
||||
filelock 3.16.1
|
||||
google-api-python-client 2.149.0
|
||||
google-auth-httplib2 0.2.0
|
||||
google-auth-oauthlib 1.2.1
|
||||
google-auth 2.35.0
|
||||
httplib2 0.22.0
|
||||
passlib 1.7.4
|
||||
python-dateutil 2.8.2
|
||||
yubikey-manager 5.1.1
|
||||
python-dateutil 2.9.0.post0
|
||||
yubikey-manager 5.5.1
|
||||
admin.googleapis.com connects using TLSv1.3 TLS_AES_256_GCM_SHA384
|
||||
```
|
||||
|
||||
@@ -64,7 +64,7 @@ MacOS High Sierra 10.13.6 x86_64
|
||||
Path: /Users/Admin/bin/gam7
|
||||
Version Check:
|
||||
Current: 5.35.08
|
||||
Latest: 7.00.19
|
||||
Latest: 7.00.26
|
||||
echo $?
|
||||
1
|
||||
```
|
||||
@@ -72,7 +72,7 @@ echo $?
|
||||
Print the current version number without details
|
||||
```
|
||||
gam version simple
|
||||
7.00.19
|
||||
7.00.26
|
||||
```
|
||||
In Linux/MacOS you can do:
|
||||
```
|
||||
@@ -82,7 +82,7 @@ echo $VER
|
||||
Print the current version of Gam and address of this Wiki
|
||||
```
|
||||
gam help
|
||||
GAM 7.00.19 - https://github.com/GAM-team/GAM
|
||||
GAM 7.00.26 - https://github.com/GAM-team/GAM
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.13.0 64-bit final
|
||||
MacOS Sonoma 14.5 x86_64
|
||||
|
||||
@@ -214,6 +214,12 @@ csv_output_header_force
|
||||
A list of <Strings> used to specify the exact column headers
|
||||
for inclusion in the CSV file written by a gam print command
|
||||
Default: ''
|
||||
csv_output_header_order
|
||||
A list of <Strings> used to specify the order of column headers
|
||||
for inclusion in the CSV file written by a gam print command
|
||||
Any headers in the file but not in the list will appear after
|
||||
the headers in the list
|
||||
Default: ''
|
||||
csv_output_line_terminator
|
||||
Allowed values: cr, lf, crlf
|
||||
Designates character(s) used to terminate the lines of a CSV file.
|
||||
@@ -305,6 +311,9 @@ drive_max_results
|
||||
how many should be retrieved in each API call
|
||||
Default: 1000
|
||||
Range: 1 - 1000
|
||||
drive_v3_beta
|
||||
Enable/disable use of Drive API v3 beta for Limited Folder Access testing
|
||||
Default: False
|
||||
drive_v3_native_names
|
||||
Enable/disable use of Drive API v3 native column names
|
||||
in all gam print/show commands related to Google Drive
|
||||
|
||||
@@ -4067,6 +4067,14 @@ gam update deviceuserstate <DeviceUserEntity> [clientid <String>]
|
||||
[healthscore very_poor|poor|neutral|good|very_good] [scorereason clear|<String>]
|
||||
(customvalue (bool|boolean <Boolean>)|(number <Integer>)|(string <String>))*
|
||||
|
||||
# Cloud Identity Policies
|
||||
|
||||
gam print policies [todrive <ToDriveAttribute>*]
|
||||
(query <String>) [nowarnings]
|
||||
[formatjson [quotechar <Character>]]
|
||||
gam show policies (query <String>) [nowarnings]
|
||||
[formatjson]
|
||||
|
||||
# Inbound SSO
|
||||
|
||||
<SSOProfileDisplayName> ::= <String>
|
||||
|
||||
@@ -1,3 +1,56 @@
|
||||
7.00.27
|
||||
|
||||
Updated `gam <UserTypeEntity> collect orphans` and all commands that print file paths to recognize
|
||||
that a file owned by a user that has no parents is not an orphan if `sharedWithMeTime` is set.
|
||||
This occurs when user A creates a file in a shared folder owned by user B and user B then removes
|
||||
user A's access to the folder.
|
||||
|
||||
Added commands to display Cloud Identity policies.
|
||||
```
|
||||
gam print policies [todrive <ToDriveAttribute>*]
|
||||
(query <String>) [nowarnings]
|
||||
[formatjson [quotechar <Character>]]
|
||||
gam show policies (query <String>) [nowarnings]
|
||||
[formatjson]
|
||||
```
|
||||
|
||||
7.00.26
|
||||
|
||||
Updated `drive_dir` in `gam.cfg` to allow the value `.` that causes `redirect csv|stdout|stderr <FileName>`
|
||||
to write `<FileName>` in the current directory without having to prefix `<FileName>` with `./`.
|
||||
|
||||
Upgraded to OpenSSL 3.4.0 where possible.
|
||||
|
||||
7.00.25
|
||||
|
||||
Updated authentication process for `gam print|show projects`.
|
||||
|
||||
7.00.24
|
||||
|
||||
Updated `gam print|show projects ... showiampolicies 0|1|3` to use non-service account authentication.
|
||||
|
||||
7.00.23
|
||||
|
||||
Updated `gam <UserTypeEntity> create|delete chatmember` to accept external (non-domain) email addresses.
|
||||
|
||||
7.00.22
|
||||
|
||||
Fixed bug in `gam create vaultmatter ... showdetails` that caused a trap.
|
||||
|
||||
7.00.21
|
||||
|
||||
Added `csv_output_header_order` variable to `gam.cfg` that is a list of `<Strings>`
|
||||
that are used to specify the order of column headers in the CSV file written by a gam print command.
|
||||
Any headers in the file but not in the list will appear after the headers in the list.
|
||||
|
||||
This might be used when the CSV file data is to be processed by another program
|
||||
that requires that the headers be in a particular order.
|
||||
|
||||
7.00.20
|
||||
|
||||
Fix Windows MSI installer issues on version upgrade. If you are having issues upgrading from a version older than 7.00.20 to this version or newer you may need to do a one time uninstall of GAM7 and then reinstall the new version.
|
||||
No configuration files will be lost during the uninstall / reinstall.
|
||||
|
||||
7.00.19
|
||||
|
||||
Updated `gam update shareddrive <SharedDriveEntity> ou <OrgUnitItem>` to handle the following error
|
||||
@@ -1188,7 +1241,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>
|
||||
|
||||
@@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki
|
||||
"""
|
||||
|
||||
__author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
|
||||
__version__ = '7.00.19'
|
||||
__version__ = '7.00.27'
|
||||
__license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
||||
|
||||
#pylint: disable=wrong-import-position
|
||||
@@ -3671,7 +3671,7 @@ def SetGlobalVariables():
|
||||
dirPath = os.path.expanduser(_stripStringQuotes(GM.Globals[GM.PARSER].get(sectionName, itemName)))
|
||||
if (not dirPath) and (itemName in {GC.GMAIL_CSE_INCERT_DIR, GC.GMAIL_CSE_INKEY_DIR}):
|
||||
return dirPath
|
||||
if (not dirPath) or (not os.path.isabs(dirPath)):
|
||||
if (not dirPath) or (not os.path.isabs(dirPath) and dirPath != '.'):
|
||||
if (sectionName != configparser.DEFAULTSECT) and (GM.Globals[GM.PARSER].has_option(sectionName, itemName)):
|
||||
dirPath = os.path.join(os.path.expanduser(_stripStringQuotes(GM.Globals[GM.PARSER].get(configparser.DEFAULTSECT, itemName))), dirPath)
|
||||
if not os.path.isabs(dirPath):
|
||||
@@ -4008,7 +4008,7 @@ def SetGlobalVariables():
|
||||
GC.Values[itemName] = _getCfgPassword(sectionName, itemName)
|
||||
elif varType == GC.TYPE_STRING:
|
||||
GC.Values[itemName] = _getCfgString(sectionName, itemName)
|
||||
elif varType in {GC.TYPE_STRINGLIST, GC.TYPE_HEADERFORCE}:
|
||||
elif varType in {GC.TYPE_STRINGLIST, GC.TYPE_HEADERFORCE, GC.TYPE_HEADERORDER}:
|
||||
GC.Values[itemName] = _getCfgStringList(sectionName, itemName)
|
||||
elif varType == GC.TYPE_FILE:
|
||||
GC.Values[itemName] = _getCfgFile(sectionName, itemName)
|
||||
@@ -4031,6 +4031,7 @@ def SetGlobalVariables():
|
||||
else:
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_FILTER] = _getCfgHeaderFilter(outputFilterSectionName, GC.CSV_OUTPUT_HEADER_FILTER)
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_DROP_FILTER] = _getCfgHeaderFilter(outputFilterSectionName, GC.CSV_OUTPUT_HEADER_DROP_FILTER)
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_ORDER] = _getCfgStringList(outputFilterSectionName, GC.CSV_OUTPUT_HEADER_ORDER)
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_FILTER] = _getCfgRowFilter(outputFilterSectionName, GC.CSV_OUTPUT_ROW_FILTER)
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_FILTER_MODE] = _getCfgChoice(outputFilterSectionName, GC.CSV_OUTPUT_ROW_FILTER_MODE)
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_DROP_FILTER] = _getCfgRowFilter(outputFilterSectionName, GC.CSV_OUTPUT_ROW_DROP_FILTER)
|
||||
@@ -4151,7 +4152,7 @@ def SetGlobalVariables():
|
||||
if GM.Globals[GM.PID] == 0:
|
||||
for itemName, itemEntry in sorted(iter(GC.VAR_INFO.items())):
|
||||
varType = itemEntry[GC.VAR_TYPE]
|
||||
if varType in {GC.TYPE_HEADERFILTER, GC.TYPE_HEADERFORCE, GC.TYPE_ROWFILTER}:
|
||||
if varType in {GC.TYPE_HEADERFILTER, GC.TYPE_HEADERFORCE, GC.TYPE_HEADERORDER, GC.TYPE_ROWFILTER}:
|
||||
GM.Globals[GM.PARSER].set(sectionName, itemName, '')
|
||||
elif (varType == GC.TYPE_INTEGER) and itemName in {GC.CSV_INPUT_ROW_LIMIT, GC.CSV_OUTPUT_ROW_LIMIT}:
|
||||
GM.Globals[GM.PARSER].set(sectionName, itemName, '0')
|
||||
@@ -4164,6 +4165,8 @@ def SetGlobalVariables():
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_DROP_FILTER] = GM.Globals[GM.CSV_OUTPUT_HEADER_DROP_FILTER][:]
|
||||
if not GC.Values[GC.CSV_OUTPUT_HEADER_FORCE]:
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_FORCE] = GM.Globals[GM.CSV_OUTPUT_HEADER_FORCE][:]
|
||||
if not GC.Values[GC.CSV_OUTPUT_HEADER_ORDER]:
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_ORDER] = GM.Globals[GM.CSV_OUTPUT_HEADER_ORDER][:]
|
||||
if not GC.Values[GC.CSV_OUTPUT_ROW_FILTER]:
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_FILTER] = GM.Globals[GM.CSV_OUTPUT_ROW_FILTER][:]
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_FILTER_MODE] = GM.Globals[GM.CSV_OUTPUT_ROW_FILTER_MODE]
|
||||
@@ -7731,6 +7734,7 @@ class CSVPrintFile():
|
||||
if not self.headerForce and titles is not None:
|
||||
self.SetTitles(titles)
|
||||
self.SetJSONTitles(titles)
|
||||
self.SetHeaderOrder(GC.Values[GC.CSV_OUTPUT_HEADER_ORDER])
|
||||
if GM.Globals.get(GM.CSV_OUTPUT_COLUMN_DELIMITER) is None:
|
||||
GM.Globals[GM.CSV_OUTPUT_COLUMN_DELIMITER] = GC.Values.get(GC.CSV_OUTPUT_COLUMN_DELIMITER, ',')
|
||||
self.SetColumnDelimiter(GM.Globals[GM.CSV_OUTPUT_COLUMN_DELIMITER])
|
||||
@@ -7740,10 +7744,12 @@ class CSVPrintFile():
|
||||
GM.Globals[GM.CSV_OUTPUT_NO_ESCAPE_CHAR] = GC.Values.get(GC.CSV_OUTPUT_NO_ESCAPE_CHAR, False)
|
||||
self.SetNoEscapeChar(GM.Globals[GM.CSV_OUTPUT_NO_ESCAPE_CHAR])
|
||||
self.SetQuoteChar(GM.Globals[GM.CSV_OUTPUT_QUOTE_CHAR])
|
||||
if GM.Globals.get(GM.CSV_OUTPUT_SORT_HEADERS) is None:
|
||||
# if GM.Globals.get(GM.CSV_OUTPUT_SORT_HEADERS) is None:
|
||||
if not GM.Globals.get(GM.CSV_OUTPUT_SORT_HEADERS):
|
||||
GM.Globals[GM.CSV_OUTPUT_SORT_HEADERS] = GC.Values.get(GC.CSV_OUTPUT_SORT_HEADERS, [])
|
||||
self.SetSortHeaders(GM.Globals[GM.CSV_OUTPUT_SORT_HEADERS])
|
||||
if GM.Globals.get(GM.CSV_OUTPUT_TIMESTAMP_COLUMN) is None:
|
||||
# if GM.Globals.get(GM.CSV_OUTPUT_TIMESTAMP_COLUMN) is None:
|
||||
if not GM.Globals.get(GM.CSV_OUTPUT_TIMESTAMP_COLUMN):
|
||||
GM.Globals[GM.CSV_OUTPUT_TIMESTAMP_COLUMN] = GC.Values.get(GC.CSV_OUTPUT_TIMESTAMP_COLUMN, '')
|
||||
self.SetTimestampColumn(GM.Globals[GM.CSV_OUTPUT_TIMESTAMP_COLUMN])
|
||||
self.SetFormatJSON(False)
|
||||
@@ -8431,6 +8437,15 @@ class CSVPrintFile():
|
||||
self.SetTitles(headerForce)
|
||||
self.SetJSONTitles(headerForce)
|
||||
|
||||
def SetHeaderOrder(self, headerOrder):
|
||||
self.headerOrder = headerOrder
|
||||
|
||||
def orderHeaders(self, titlesList):
|
||||
for title in self.headerOrder:
|
||||
if title in titlesList:
|
||||
titlesList.remove(title)
|
||||
return self.headerOrder+titlesList
|
||||
|
||||
@staticmethod
|
||||
def HeaderFilterMatch(filters, title):
|
||||
for filterStr in filters:
|
||||
@@ -8870,14 +8885,21 @@ class CSVPrintFile():
|
||||
self.FixNodataTitles()
|
||||
if self.mapDrive3Titles:
|
||||
self. MapDrive3TitlesToDrive2()
|
||||
else:
|
||||
self.titlesList = self.headerForce
|
||||
if self.timestampColumn:
|
||||
self.AddTitle(self.timestampColumn)
|
||||
if self.headerOrder:
|
||||
self.titlesList = self.orderHeaders(self.titlesList)
|
||||
titlesList = self.titlesList
|
||||
else:
|
||||
if self.fixPaths:
|
||||
self.FixPathsTitles(self.JSONtitlesList)
|
||||
if not self.rows and self.nodataFields is not None:
|
||||
self.FixNodataTitles()
|
||||
if not self.headerForce:
|
||||
if self.fixPaths:
|
||||
self.FixPathsTitles(self.JSONtitlesList)
|
||||
if not self.rows and self.nodataFields is not None:
|
||||
self.FixNodataTitles()
|
||||
else:
|
||||
self.JSONtitlesList = self.headerForce
|
||||
if self.timestampColumn:
|
||||
for i, v in enumerate(self.JSONtitlesList):
|
||||
if v.startswith('JSON'):
|
||||
@@ -8886,6 +8908,8 @@ class CSVPrintFile():
|
||||
break
|
||||
else:
|
||||
self.AddJSONTitle(self.timestampColumn)
|
||||
if self.headerOrder:
|
||||
self.JSONtitlesList = self.orderHeaders(self.JSONtitlesList)
|
||||
titlesList = self.JSONtitlesList
|
||||
normalizeSortHeaders()
|
||||
if (not self.todrive) or self.todrive['localcopy']:
|
||||
@@ -9623,7 +9647,7 @@ def ProcessGAMCommandMulti(pid, numItems, logCmd, mpQueueCSVFile, mpQueueStdout,
|
||||
csvColumnDelimiter, csvNoEscapeChar, csvQuoteChar,
|
||||
csvSortHeaders, csvTimestampColumn,
|
||||
csvHeaderFilter, csvHeaderDropFilter,
|
||||
csvHeaderForce,
|
||||
csvHeaderForce, csvHeaderOrder,
|
||||
csvRowFilter, csvRowFilterMode, csvRowDropFilter, csvRowDropFilterMode,
|
||||
csvRowLimit,
|
||||
showGettings, showGettingsGotNL,
|
||||
@@ -9647,13 +9671,14 @@ def ProcessGAMCommandMulti(pid, numItems, logCmd, mpQueueCSVFile, mpQueueStdout,
|
||||
GM.Globals[GM.CSV_OUTPUT_HEADER_DROP_FILTER] = csvHeaderDropFilter[:]
|
||||
GM.Globals[GM.CSV_OUTPUT_HEADER_FILTER] = csvHeaderFilter[:]
|
||||
GM.Globals[GM.CSV_OUTPUT_HEADER_FORCE] = csvHeaderForce[:]
|
||||
GM.Globals[GM.CSV_OUTPUT_HEADER_ORDER] = csvHeaderOrder[:]
|
||||
GM.Globals[GM.CSV_OUTPUT_QUOTE_CHAR] = csvQuoteChar
|
||||
GM.Globals[GM.CSV_OUTPUT_ROW_DROP_FILTER] = csvRowDropFilter[:]
|
||||
GM.Globals[GM.CSV_OUTPUT_ROW_DROP_FILTER_MODE] = csvRowDropFilterMode
|
||||
GM.Globals[GM.CSV_OUTPUT_ROW_FILTER] = csvRowFilter[:]
|
||||
GM.Globals[GM.CSV_OUTPUT_ROW_FILTER_MODE] = csvRowFilterMode
|
||||
GM.Globals[GM.CSV_OUTPUT_ROW_LIMIT] = csvRowLimit
|
||||
GM.Globals[GM.CSV_OUTPUT_SORT_HEADERS] = csvSortHeaders
|
||||
GM.Globals[GM.CSV_OUTPUT_SORT_HEADERS] = csvSortHeaders[:]
|
||||
GM.Globals[GM.CSV_OUTPUT_TIMESTAMP_COLUMN] = csvTimestampColumn
|
||||
GM.Globals[GM.CSV_TODRIVE] = todrive.copy()
|
||||
GM.Globals[GM.DEBUG_LEVEL] = debugLevel
|
||||
@@ -9661,9 +9686,9 @@ def ProcessGAMCommandMulti(pid, numItems, logCmd, mpQueueCSVFile, mpQueueStdout,
|
||||
GM.Globals[GM.OUTPUT_TIMEFORMAT] = output_timeformat
|
||||
GM.Globals[GM.NUM_BATCH_ITEMS] = numItems
|
||||
GM.Globals[GM.PID] = pid
|
||||
GM.Globals[GM.PRINT_AGU_DOMAINS] = printAguDomains
|
||||
GM.Globals[GM.PRINT_CROS_OUS] = printCrosOUs
|
||||
GM.Globals[GM.PRINT_CROS_OUS_AND_CHILDREN] = printCrosOUsAndChildren
|
||||
GM.Globals[GM.PRINT_AGU_DOMAINS] = printAguDomains[:]
|
||||
GM.Globals[GM.PRINT_CROS_OUS] = printCrosOUs[:]
|
||||
GM.Globals[GM.PRINT_CROS_OUS_AND_CHILDREN] = printCrosOUsAndChildren[:]
|
||||
GM.Globals[GM.SAVED_STDOUT] = None
|
||||
GM.Globals[GM.SHOW_GETTINGS] = showGettings
|
||||
GM.Globals[GM.SHOW_GETTINGS_GOT_NL] = showGettingsGotNL
|
||||
@@ -9870,6 +9895,7 @@ def MultiprocessGAMCommands(items, showCmds):
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_FILTER],
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_DROP_FILTER],
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_FORCE],
|
||||
GC.Values[GC.CSV_OUTPUT_HEADER_ORDER],
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_FILTER],
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_FILTER_MODE],
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_DROP_FILTER],
|
||||
@@ -11355,19 +11381,21 @@ def _getProjects(crm, pfilter, returnNF=False):
|
||||
query=pfilter)
|
||||
if projects:
|
||||
return projects
|
||||
if not pfilter:
|
||||
if (not pfilter) or pfilter == GAM_PROJECT_FILTER:
|
||||
return []
|
||||
if pfilter.startswith('id:'):
|
||||
projects = [callGAPI(crm.projects(), 'get',
|
||||
throwReasons=[GAPI.BAD_REQUEST, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED],
|
||||
name=f'projects/{pfilter[3:]}')]
|
||||
if projects or not returnNF:
|
||||
return projects
|
||||
return [{'projectId': pfilter[3:], 'state': 'NF'}]
|
||||
if projects or not returnNF:
|
||||
return projects
|
||||
return []
|
||||
except (GAPI.badRequest, GAPI.invalidArgument) as e:
|
||||
entityActionFailedExit([Ent.PROJECT, pfilter], str(e))
|
||||
except GAPI.permissionDenied:
|
||||
return []
|
||||
if (not pfilter) or (not pfilter.startswith('id:')) or (not returnNF):
|
||||
return []
|
||||
return [{'projectId': pfilter[3:], 'state': 'NF'}]
|
||||
|
||||
def _checkProjectFound(project, i, count):
|
||||
if project.get('state', '') != 'NF':
|
||||
@@ -11535,6 +11563,8 @@ def _getLoginHintProjects(createSvcAcctCmd=False, deleteSvcAcctCmd=False, printS
|
||||
if login_hint and login_hint.find('@') == -1:
|
||||
Cmd.Backup()
|
||||
login_hint = None
|
||||
if readOnly and login_hint and login_hint != _getAdminEmail():
|
||||
readOnly = False
|
||||
projectIds = None
|
||||
pfilter = getString(Cmd.OB_STRING, optional=True)
|
||||
if not pfilter:
|
||||
@@ -11576,15 +11606,9 @@ def _getLoginHintProjects(createSvcAcctCmd=False, deleteSvcAcctCmd=False, printS
|
||||
login_hint = _getValidateLoginHint(login_hint, projectId)
|
||||
crm = None
|
||||
if readOnly:
|
||||
_getSvcAcctData()
|
||||
if (GM.Globals[GM.SVCACCT_SCOPES_DEFINED] and
|
||||
(API.CLOUDRESOURCEMANAGER in GM.Globals[GM.SVCACCT_SCOPES] or
|
||||
API.CLOUDRESOURCEMANAGER_V1 in GM.Globals[GM.SVCACCT_SCOPES])): #Backwards compatibility hack
|
||||
# Removed 6.21.05
|
||||
# _, crm = buildGAPIServiceObject(API.CLOUDRESOURCEMANAGER, login_hint)
|
||||
_, crm = buildGAPIServiceObject(API.CLOUDRESOURCEMANAGER, None)
|
||||
if crm:
|
||||
httpObj = crm._http
|
||||
_, crm = buildGAPIServiceObject(API.CLOUDRESOURCEMANAGER, None)
|
||||
if crm:
|
||||
httpObj = crm._http
|
||||
if not crm:
|
||||
httpObj, crm = getCRMService(login_hint)
|
||||
if projectIds is None:
|
||||
@@ -11594,7 +11618,7 @@ def _getLoginHintProjects(createSvcAcctCmd=False, deleteSvcAcctCmd=False, printS
|
||||
else:
|
||||
projects = _getProjects(crm, f'id:{projectId}', returnNF=True)
|
||||
else:
|
||||
projects = _getProjects(crm, pfilter)
|
||||
projects = _getProjects(crm, pfilter, returnNF=printShowCmd)
|
||||
else:
|
||||
projects = []
|
||||
for projectId in projectIds:
|
||||
@@ -11825,10 +11849,11 @@ def doPrintShowProjects():
|
||||
resource=project['name'], body=policyBody)
|
||||
return policy
|
||||
except (GAPI.forbidden, GAPI.permissionDenied) as e:
|
||||
entityActionFailedWarning([Ent.PROJECT, project['projectId'], Ent.IAM_POLICY], str(e), i, count)
|
||||
entityActionFailedWarning([Ent.PROJECT, project['projectId'], Ent.IAM_POLICY, None], str(e), i, count)
|
||||
return {}
|
||||
|
||||
crm, _, login_hint, projects = _getLoginHintProjects(printShowCmd=True, readOnly=True)
|
||||
readOnly = not Cmd.ArgumentIsAhead('showiampolicies')
|
||||
crm, _, login_hint, projects = _getLoginHintProjects(printShowCmd=True, readOnly=readOnly)
|
||||
csvPF = CSVPrintFile(['User', 'projectId']) if Act.csvFormat() else None
|
||||
FJQC = FormatJSONQuoteChar(csvPF)
|
||||
oneMemberPerRow = False
|
||||
@@ -25780,7 +25805,7 @@ CHAT_UPDATE_SPACE_PERMISSIONS_MAP = {
|
||||
'managewebhooks': 'manageWebhooks',
|
||||
'replymessages': 'replyMessages',
|
||||
}
|
||||
|
||||
|
||||
# gam <UserTypeEntity> update chatspace <ChatSpace>
|
||||
# [restricted|(audience <String>)]|
|
||||
# ([displayname <String>]
|
||||
@@ -26089,10 +26114,13 @@ def _getChatMemberEmail(cd, member):
|
||||
_, memberUid = member['groupMember']['name'].split('/')
|
||||
member['groupMember']['email'], _ = convertUIDtoEmailAddressWithType(f'uid:{memberUid}', cd, None, emailTypes=['group'])
|
||||
|
||||
def normalizeUserMember(cd, user, userList):
|
||||
def normalizeUserMember(user, userList):
|
||||
userList.append(normalizeEmailAddressOrUID(user))
|
||||
|
||||
def getUserMemberID(cd, user, userList):
|
||||
userList.append(convertEmailAddressToUID(user, cd, emailType='user'))
|
||||
|
||||
def normalizeGroupMember(cd, group, groupList):
|
||||
def getGroupMemberID(cd, group, groupList):
|
||||
groupList.append(convertEmailAddressToUID(group, cd, emailType='group'))
|
||||
|
||||
# gam <UserTypeEntity> create chatmember <ChatSpace>
|
||||
@@ -26155,16 +26183,16 @@ def createChatMember(users):
|
||||
if myarg == 'space' or myarg.startswith('spaces/') or myarg.startswith('space/'):
|
||||
parent = getSpaceName(myarg)
|
||||
elif myarg == 'user':
|
||||
normalizeUserMember(cd, getEmailAddress(returnUIDprefix='uid:'), userList)
|
||||
normalizeUserMember(getEmailAddress(returnUIDprefix='uid:'), userList)
|
||||
elif myarg in {'member', 'members'}:
|
||||
_, members = getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS)
|
||||
for user in members:
|
||||
normalizeUserMember(cd, user, userList)
|
||||
normalizeUserMember(user, userList)
|
||||
elif myarg == 'group':
|
||||
normalizeGroupMember(cd, getEmailAddress(returnUIDprefix='uid:'), groupList)
|
||||
getGroupMemberID(cd, getEmailAddress(returnUIDprefix='uid:'), groupList)
|
||||
elif myarg == 'groups':
|
||||
for group in getEntityList(Cmd.OB_GROUP_ENTITY):
|
||||
normalizeGroupMember(cd, group, groupList)
|
||||
getGroupMemberID(cd, group, groupList)
|
||||
elif myarg == 'role':
|
||||
role = getChoice(CHAT_MEMBER_ROLE_MAP, mapChoice=True)
|
||||
elif myarg == 'type':
|
||||
@@ -26262,16 +26290,16 @@ def deleteUpdateChatMember(users):
|
||||
if myarg == 'space' or myarg.startswith('spaces/') or myarg.startswith('space/'):
|
||||
parent = getSpaceName(myarg)
|
||||
elif myarg == 'user':
|
||||
normalizeUserMember(cd, getEmailAddress(returnUIDprefix='uid:'), userList)
|
||||
normalizeUserMember(getEmailAddress(returnUIDprefix='uid:'), userList)
|
||||
elif myarg in {'member', 'members'}:
|
||||
_, members = getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS)
|
||||
for user in members:
|
||||
normalizeUserMember(cd, user, userList)
|
||||
normalizeUserMember(user, userList)
|
||||
elif deleteMode and myarg == 'group':
|
||||
normalizeGroupMember(cd, getEmailAddress(returnUIDprefix='uid:'), groupList)
|
||||
getGroupMemberID(cd, getEmailAddress(returnUIDprefix='uid:'), groupList)
|
||||
elif deleteMode and myarg == 'groups':
|
||||
for group in getEntityList(Cmd.OB_GROUP_ENTITY):
|
||||
normalizeGroupMember(cd, group, groupList)
|
||||
getGroupMemberID(cd, group, groupList)
|
||||
else:
|
||||
unknownArgumentExit()
|
||||
if not deleteMode and 'role' not in body:
|
||||
@@ -26415,16 +26443,16 @@ def syncChatMembers(users):
|
||||
csvPF = CSVPrintFile(CHAT_SYNC_PREVIEW_TITLES)
|
||||
elif myarg == 'users':
|
||||
for user in getEntityList(Cmd.OB_USER_ENTITY):
|
||||
normalizeUserMember(cd, user, userList)
|
||||
getUserMemberID(cd, user, userList)
|
||||
usersSpecified = True
|
||||
elif myarg in {'member', 'members'}:
|
||||
_, members = getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS)
|
||||
for user in members:
|
||||
normalizeUserMember(cd, user, userList)
|
||||
getUserMemberID(cd, user, userList)
|
||||
usersSpecified = True
|
||||
elif myarg == 'groups':
|
||||
for group in getEntityList(Cmd.OB_GROUP_ENTITY):
|
||||
normalizeGroupMember(cd, group, groupList)
|
||||
getGroupMemberID(cd, group, groupList)
|
||||
groupsSpecified = True
|
||||
else:
|
||||
unknownArgumentExit()
|
||||
@@ -35054,6 +35082,108 @@ def updateFieldsForCIGroupMatchPatterns(matchPatterns, fieldsList, csvPF=None):
|
||||
else:
|
||||
fieldsList.append(field)
|
||||
|
||||
CIPOLICY_TIME_OBJECTS = {'createTime', 'updateTime'}
|
||||
|
||||
# gam print policies [todrive <ToDriveAttribute>*]
|
||||
# (query <String>) [nowarnings]
|
||||
# [formatjson [quotechar <Character>]]
|
||||
# gam show policies (query <String>) [nowarnings]
|
||||
# [formatjson]
|
||||
def doPrintCIPolicies():
|
||||
|
||||
def _showPolicy(policy, FJQC, i=0, count=0):
|
||||
if FJQC is not None and FJQC.formatJSON:
|
||||
printLine(json.dumps(cleanJSON(policy, timeObjects=CIPOLICY_TIME_OBJECTS),
|
||||
ensure_ascii=False,
|
||||
sort_keys=True))
|
||||
return
|
||||
printEntity([Ent.POLICY, policy['name']], i, count)
|
||||
Ind.Increment()
|
||||
policy.pop('name')
|
||||
showJSON(None, policy, timeObjects=CIPOLICY_TIME_OBJECTS)
|
||||
printBlankLine()
|
||||
Ind.Decrement()
|
||||
|
||||
def _printPolicy(policy):
|
||||
row = flattenJSON(policy, timeObjects=CIPOLICY_TIME_OBJECTS)
|
||||
if not FJQC.formatJSON:
|
||||
csvPF.WriteRowTitles(row)
|
||||
elif csvPF.CheckRowTitles(row):
|
||||
csvPF.WriteRowNoFilter({'name': policy['name'],
|
||||
'JSON': json.dumps(cleanJSON(policy, timeObjects=CIPOLICY_TIME_OBJECTS),
|
||||
ensure_ascii=False,
|
||||
sort_keys=True)})
|
||||
|
||||
# Policies where GAM should offer additional guidance and information
|
||||
warnings = {
|
||||
'settings/drive_and_docs.external_sharing': {
|
||||
'warningType': 'SUPERSEDED_POLICY',
|
||||
'warningMessage': 'CAUTION: Drive Sharing settings are superseded by Drive Trust Rules if Trust Rules has been enabled for your domain. Drive Trust Rule settings are not available in the Policy API today so GAM is not able to check if Trust Rules is enabled and if the settings/drive_and_docs.external_sharing policies are actually in effect for your domain. If Drive Trust Rules is enabled for your domain then this settings/drive_and_docs.external_sharing policy does not accurately reflect your current Drive sharing settings.'
|
||||
}
|
||||
}
|
||||
groups_ci = buildGAPIObject(API.CLOUDIDENTITY_GROUPS)
|
||||
ci = buildGAPIObject(API.CLOUDIDENTITY_POLICY)
|
||||
cd = buildGAPIObject(API.DIRECTORY)
|
||||
csvPF = CSVPrintFile(['name']) if Act.csvFormat() else None
|
||||
FJQC = FormatJSONQuoteChar(csvPF)
|
||||
fields = 'nextPageToken,policies(name,policyQuery(group,orgUnit,sortOrder),type,setting)'
|
||||
ifilter = None
|
||||
add_warnings = True
|
||||
while Cmd.ArgumentsRemaining():
|
||||
myarg = getArgument()
|
||||
if csvPF and myarg == 'todrive':
|
||||
csvPF.GetTodriveParameters()
|
||||
elif myarg == 'filter':
|
||||
ifilter = getString(Cmd.OB_STRING)
|
||||
elif myarg == 'nowarnings':
|
||||
add_warnings = False
|
||||
else:
|
||||
FJQC.GetFormatJSONQuoteChar(myarg, True)
|
||||
printGettingAllAccountEntities(Ent.POLICY, ifilter)
|
||||
pageMessage = getPageMessage()
|
||||
throwReasons = [GAPI.INVALID, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED]
|
||||
try:
|
||||
policies = callGAPIpages(ci.policies(),
|
||||
'list',
|
||||
'policies',
|
||||
throwReasons=throwReasons,
|
||||
pageMessage=pageMessage,
|
||||
filter=ifilter,
|
||||
fields=fields,
|
||||
pageSize=100)
|
||||
except (GAPI.invalid, GAPI.invalidArgument, GAPI.permissionDenied) as e:
|
||||
entityActionFailedWarning([Ent.POLICY, None], str(e))
|
||||
return
|
||||
# Google returns unordered results, sort them by setting type
|
||||
policies = sorted(policies, key=lambda p: p.get('setting', {}).get('type', ''))
|
||||
for policy in policies:
|
||||
# convert any wordlists into spaced strings to reduce output complexity
|
||||
if policy['setting']['type'] == 'settings/detector.word_list':
|
||||
policy['setting']['value']['wordList'] = ' '.join(policy['setting']['value']['wordList']['words'])
|
||||
# add any warnings to applicable policies
|
||||
if add_warnings and policy['setting']['type'] in warnings:
|
||||
policy['warning'] = warnings[policy['setting']['type']]
|
||||
if groupId := policy['policyQuery'].get('group'):
|
||||
_, _, policy['policyQuery']['groupEmail'] = convertGroupCloudIDToEmail(groups_ci, groupId)
|
||||
# all groups are in the root OU so the orgUnit attribute is useless
|
||||
policy['policyQuery'].pop('orgUnit')
|
||||
elif orgId := policy['policyQuery'].get('orgUnit'):
|
||||
policy['policyQuery']['orgUnitPath'] = convertOrgUnitIDtoPath(cd, orgId)
|
||||
if not csvPF:
|
||||
jcount = len(policies)
|
||||
performActionNumItems(jcount, Ent.POLICY)
|
||||
Ind.Increment()
|
||||
j = 0
|
||||
for policy in policies:
|
||||
j += 1
|
||||
_showPolicy(policy, FJQC, j, jcount)
|
||||
Ind.Decrement()
|
||||
else:
|
||||
for policy in policies:
|
||||
_printPolicy(policy)
|
||||
if csvPF:
|
||||
csvPF.writeCSVfile('Policies')
|
||||
|
||||
PRINT_CIGROUPS_JSON_TITLES = ['email', 'JSON']
|
||||
|
||||
# gam print cigroups [todrive <ToDriveAttribute>*]
|
||||
@@ -41128,7 +41258,7 @@ def doCreateVaultMatter():
|
||||
break
|
||||
Ind.Decrement()
|
||||
if showDetails:
|
||||
_showVaultMatter(None, matter, cd, None)
|
||||
_showVaultMatter(matter, cd, None)
|
||||
|
||||
VAULT_MATTER_ACTIONS = {
|
||||
'close': Act.CLOSE,
|
||||
@@ -54476,7 +54606,7 @@ def extendFileTree(fileTree, feed, DLP, stripCRsFromName):
|
||||
if f_file['mimeType'] == MIMETYPE_GA_FOLDER and f_file['name'] == MY_DRIVE:
|
||||
f_file['parents'] = []
|
||||
else:
|
||||
f_file['parents'] = [ORPHANS] if f_file.get('ownedByMe', False) else [SHARED_WITHME]
|
||||
f_file['parents'] = [ORPHANS] if f_file.get('ownedByMe', False) and 'sharedWithMeTime' not in f_file else [SHARED_WITHME]
|
||||
else:
|
||||
f_file['parents'] = [SHARED_DRIVES] if 'sharedWithMeTime' not in f_file else [SHARED_WITHME]
|
||||
if fileId not in fileTree:
|
||||
@@ -54496,11 +54626,11 @@ def extendFileTreeParents(drive, fileTree, fields):
|
||||
fileId=fileId, fields=fields, supportsAllDrives=True)
|
||||
if not result.get('parents', []):
|
||||
if not result.get('driveId'):
|
||||
result['parents'] = [ORPHANS] if result.get('ownedByMe', False) else [SHARED_WITHME]
|
||||
result['parents'] = [ORPHANS] if result.get('ownedByMe', False) and 'sharedWithMeTime' not in result else [SHARED_WITHME]
|
||||
else:
|
||||
if result['name'] == TEAM_DRIVE:
|
||||
result['name'] = _getSharedDriveNameFromId(drive, result['driveId'])
|
||||
result['parents'] = [SHARED_DRIVES] if 'sharedWithMeTime' not in f_file else [SHARED_WITHME]
|
||||
result['parents'] = [SHARED_DRIVES] if 'sharedWithMeTime' not in result else [SHARED_WITHME]
|
||||
fileTree[fileId]['info'] = result
|
||||
fileTree[fileId]['info']['noDisplay'] = True
|
||||
for parentId in result['parents']:
|
||||
@@ -57236,7 +57366,7 @@ def createDriveFile(users):
|
||||
continue
|
||||
result = callGAPI(drive.files(), 'create',
|
||||
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.FORBIDDEN, GAPI.INSUFFICIENT_PERMISSIONS, GAPI.INSUFFICIENT_PARENT_PERMISSIONS,
|
||||
GAPI.INVALID, GAPI.BAD_REQUEST, GAPI.CANNOT_ADD_PARENT,
|
||||
GAPI.PERMISSION_DENIED, GAPI.INVALID, GAPI.BAD_REQUEST, GAPI.CANNOT_ADD_PARENT,
|
||||
GAPI.FILE_NOT_FOUND, GAPI.UNKNOWN_ERROR, GAPI.INTERNAL_ERROR,
|
||||
GAPI.STORAGE_QUOTA_EXCEEDED, GAPI.TEAMDRIVES_SHARING_RESTRICTION_NOT_ALLOWED,
|
||||
GAPI.TEAMDRIVE_FILE_LIMIT_EXCEEDED, GAPI.TEAMDRIVE_HIERARCHY_TOO_DEEP,
|
||||
@@ -57275,7 +57405,7 @@ def createDriveFile(users):
|
||||
row.update(addCSVData)
|
||||
csvPF.WriteRow(row)
|
||||
except (GAPI.forbidden, GAPI.insufficientFilePermissions, GAPI.insufficientParentPermissions,
|
||||
GAPI.invalidQuery, GAPI.invalid, GAPI.badRequest, GAPI.cannotAddParent,
|
||||
GAPI.invalidQuery, GAPI.permissionDenied, GAPI.invalid, GAPI.badRequest, GAPI.cannotAddParent,
|
||||
GAPI.fileNotFound, GAPI.unknownError, GAPI.storageQuotaExceeded, GAPI.teamDrivesSharingRestrictionNotAllowed,
|
||||
GAPI.teamDriveFileLimitExceeded, GAPI.teamDriveHierarchyTooDeep,
|
||||
GAPI.uploadTooLarge, GAPI.teamDrivesShortcutFileNotSupported) as e:
|
||||
@@ -60695,7 +60825,8 @@ def collectOrphans(users):
|
||||
pageMessage=getPageMessageForWhom(),
|
||||
throwReasons=GAPI.DRIVE_USER_THROW_REASONS,
|
||||
retryReasons=[GAPI.UNKNOWN_ERROR],
|
||||
q=query, orderBy=OBY.orderBy, fields='nextPageToken,files(id,name,parents,mimeType,capabilities(canMoveItemWithinDrive))',
|
||||
q=query, orderBy=OBY.orderBy,
|
||||
fields='nextPageToken,files(id,name,parents,mimeType,sharedWithMeTime,capabilities(canMoveItemWithinDrive))',
|
||||
pageSize=GC.Values[GC.DRIVE_MAX_RESULTS])
|
||||
if targetUserFolderPattern:
|
||||
trgtUserFolderName = _substituteForUser(targetUserFolderPattern, user, userName)
|
||||
@@ -60707,7 +60838,7 @@ def collectOrphans(users):
|
||||
continue
|
||||
orphanDriveFiles = []
|
||||
for fileEntry in feed:
|
||||
if not fileEntry.get('parents'):
|
||||
if not fileEntry.get('parents') and 'sharedWithMeTime' not in fileEntry:
|
||||
orphanDriveFiles.append(fileEntry)
|
||||
jcount = len(orphanDriveFiles)
|
||||
entityPerformActionNumItemsModifier([Ent.USER, user], jcount, Ent.DRIVE_ORPHAN_FILE_OR_FOLDER,
|
||||
@@ -75063,6 +75194,7 @@ MAIN_COMMANDS_WITH_OBJECTS = {
|
||||
Cmd.ARG_CHROMEVERSIONS: doPrintShowChromeVersions,
|
||||
Cmd.ARG_CIGROUP: doPrintCIGroups,
|
||||
Cmd.ARG_CIGROUPMEMBERS: doPrintCIGroupMembers,
|
||||
Cmd.ARG_CIPOLICIES: doPrintCIPolicies,
|
||||
Cmd.ARG_CLASSROOMINVITATION: doPrintShowClassroomInvitations,
|
||||
Cmd.ARG_CONTACT: doPrintShowDomainContacts,
|
||||
Cmd.ARG_COURSE: doPrintCourses,
|
||||
@@ -75191,6 +75323,7 @@ MAIN_COMMANDS_WITH_OBJECTS = {
|
||||
Cmd.ARG_CHROMESCHEMA: doPrintShowChromeSchemas,
|
||||
Cmd.ARG_CHROMEVERSIONS: doPrintShowChromeVersions,
|
||||
Cmd.ARG_CIGROUPMEMBERS: doShowCIGroupMembers,
|
||||
Cmd.ARG_CIPOLICIES: doPrintCIPolicies,
|
||||
Cmd.ARG_CLASSROOMINVITATION: doPrintShowClassroomInvitations,
|
||||
Cmd.ARG_CONTACT: doPrintShowDomainContacts,
|
||||
Cmd.ARG_CROSTELEMETRY: doInfoPrintShowCrOSTelemetry,
|
||||
|
||||
@@ -46,10 +46,10 @@ CLOUDIDENTITY_DEVICES = 'cloudidentitydevices'
|
||||
CLOUDIDENTITY_GROUPS = 'cloudidentitygroups'
|
||||
CLOUDIDENTITY_INBOUND_SSO = 'cloudidentityinboundsso'
|
||||
CLOUDIDENTITY_ORGUNITS = 'cloudidentityorgunits'
|
||||
CLOUDIDENTITY_POLICY = 'cloudidentitypolicy'
|
||||
CLOUDIDENTITY_ORGUNITS_BETA = 'cloudidentityorgunitsbeta'
|
||||
CLOUDIDENTITY_USERINVITATIONS = 'cloudidentityuserinvitations'
|
||||
CLOUDRESOURCEMANAGER = 'cloudresourcemanager'
|
||||
CLOUDRESOURCEMANAGER_V1 = 'cloudresourcemanager1'
|
||||
CONTACTS = 'contacts'
|
||||
CONTACTDELEGATION = 'contactdelegation'
|
||||
DATATRANSFER = 'datatransfer'
|
||||
@@ -227,6 +227,7 @@ _INFO = {
|
||||
CLOUDIDENTITY_INBOUND_SSO: {'name': 'Cloud Identity Inbound SSO API', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
|
||||
CLOUDIDENTITY_ORGUNITS: {'name': 'Cloud Identity OrgUnits API', 'version': 'v1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
|
||||
CLOUDIDENTITY_ORGUNITS_BETA: {'name': 'Cloud Identity OrgUnits API', 'version': 'v1beta1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
|
||||
CLOUDIDENTITY_POLICY: {'name': 'Cloud Identity Policy API', 'version': 'v1beta1', 'v2discovery': True, 'mappedAPI': 'cloudidentity'},
|
||||
CLOUDIDENTITY_USERINVITATIONS: {'name': 'Cloud Identity User Invitations API', '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},
|
||||
@@ -365,6 +366,11 @@ _CLIENT_SCOPES = [
|
||||
'api': CLOUDIDENTITY_ORGUNITS_BETA,
|
||||
'subscopes': READONLY,
|
||||
'scope': 'https://www.googleapis.com/auth/cloud-identity.orgunits'},
|
||||
{'name': 'Cloud Identity - Policy',
|
||||
'api': CLOUDIDENTITY_POLICY,
|
||||
'subscopes': [],
|
||||
'scope': 'https://www.googleapis.com/auth/cloud-identity.policies.readonly'
|
||||
},
|
||||
{'name': 'Cloud Identity User Invitations API',
|
||||
'api': CLOUDIDENTITY_USERINVITATIONS,
|
||||
'subscopes': READONLY,
|
||||
@@ -695,10 +701,6 @@ _SVCACCT_SCOPES = [
|
||||
]
|
||||
|
||||
_SVCACCT_SPECIAL_SCOPES = [
|
||||
{'name': 'Cloud Resource Manager API v3',
|
||||
'api': CLOUDRESOURCEMANAGER,
|
||||
'subscopes': [],
|
||||
'scope': CLOUD_PLATFORM_SCOPE},
|
||||
{'name': 'Drive API - todrive',
|
||||
'api': DRIVETD,
|
||||
'subscopes': [],
|
||||
|
||||
@@ -117,6 +117,8 @@ CSV_OUTPUT_HEADER_FILTER = 'csv_output_header_filter'
|
||||
CSV_OUTPUT_HEADER_DROP_FILTER = 'csv_output_header_drop_filter'
|
||||
# Force output column headers
|
||||
CSV_OUTPUT_HEADER_FORCE = 'csv_output_header_force'
|
||||
# Orde output column headers
|
||||
CSV_OUTPUT_HEADER_ORDER = 'csv_output_header_order'
|
||||
# Line terminator in CSV output file
|
||||
CSV_OUTPUT_LINE_TERMINATOR = 'csv_output_line_terminator'
|
||||
# Quote character in CSV output file
|
||||
@@ -309,7 +311,8 @@ CSV_INPUT_ROW_FILTER_ITEMS = {CSV_INPUT_ROW_FILTER, CSV_INPUT_ROW_FILTER_MODE,
|
||||
CSV_INPUT_ROW_DROP_FILTER, CSV_INPUT_ROW_DROP_FILTER_MODE,
|
||||
CSV_INPUT_ROW_LIMIT}
|
||||
|
||||
CSV_OUTPUT_ROW_FILTER_ITEMS = {CSV_OUTPUT_HEADER_FILTER, CSV_OUTPUT_HEADER_DROP_FILTER, CSV_OUTPUT_HEADER_FORCE,
|
||||
CSV_OUTPUT_ROW_FILTER_ITEMS = {CSV_OUTPUT_HEADER_FILTER, CSV_OUTPUT_HEADER_DROP_FILTER,
|
||||
CSV_OUTPUT_HEADER_FORCE, CSV_OUTPUT_HEADER_ORDER,
|
||||
CSV_OUTPUT_ROW_FILTER, CSV_OUTPUT_ROW_FILTER_MODE,
|
||||
CSV_OUTPUT_ROW_DROP_FILTER, CSV_OUTPUT_ROW_DROP_FILTER_MODE,
|
||||
CSV_OUTPUT_ROW_LIMIT}
|
||||
@@ -351,6 +354,7 @@ Defaults = {
|
||||
CSV_OUTPUT_HEADER_FILTER: '',
|
||||
CSV_OUTPUT_HEADER_DROP_FILTER: '',
|
||||
CSV_OUTPUT_HEADER_FORCE: '',
|
||||
CSV_OUTPUT_HEADER_ORDER: '',
|
||||
CSV_OUTPUT_LINE_TERMINATOR: 'lf',
|
||||
CSV_OUTPUT_QUOTE_CHAR: '\'"\'',
|
||||
CSV_OUTPUT_ROW_FILTER: '',
|
||||
@@ -460,6 +464,7 @@ TYPE_FILE = 'file'
|
||||
TYPE_FLOAT = 'floa'
|
||||
TYPE_HEADERFILTER = 'heaf'
|
||||
TYPE_HEADERFORCE = 'hefo'
|
||||
TYPE_HEADERORDER = 'heor'
|
||||
TYPE_INTEGER = 'inte'
|
||||
TYPE_LANGUAGE = 'lang'
|
||||
TYPE_LOCALE = 'locl'
|
||||
@@ -514,6 +519,7 @@ VAR_INFO = {
|
||||
CSV_OUTPUT_HEADER_FILTER: {VAR_TYPE: TYPE_HEADERFILTER},
|
||||
CSV_OUTPUT_HEADER_DROP_FILTER: {VAR_TYPE: TYPE_HEADERFILTER},
|
||||
CSV_OUTPUT_HEADER_FORCE: {VAR_TYPE: TYPE_HEADERFORCE},
|
||||
CSV_OUTPUT_HEADER_ORDER: {VAR_TYPE: TYPE_HEADERORDER},
|
||||
CSV_OUTPUT_LINE_TERMINATOR: {VAR_TYPE: TYPE_CHOICE, VAR_CHOICES: {'cr': '\r', 'lf': '\n', 'crlf': '\r\n'}},
|
||||
CSV_OUTPUT_QUOTE_CHAR: {VAR_TYPE: TYPE_CHARACTER},
|
||||
CSV_OUTPUT_ROW_FILTER: {VAR_TYPE: TYPE_ROWFILTER},
|
||||
|
||||
@@ -493,6 +493,7 @@ class GamCLArgs():
|
||||
ARG_CIGROUPSMEMBERS = 'cigroupsmembers'
|
||||
ARG_CIMEMBER = 'cimember'
|
||||
ARG_CIMEMBERS = 'cimembers'
|
||||
ARG_CIPOLICIES = 'policies'
|
||||
ARG_CLASS = 'class'
|
||||
ARG_CLASSES = 'classes'
|
||||
ARG_CLASSPARTICIPANTS = 'classparticipants'
|
||||
|
||||
@@ -302,6 +302,7 @@ class GamEntity():
|
||||
PERMITTEE = 'prmt'
|
||||
PERSONAL_DEVICE = 'pedv'
|
||||
PHOTO = 'phot'
|
||||
POLICY = 'poli'
|
||||
POP_ENABLED = 'popa'
|
||||
PRESENTATION = 'pres'
|
||||
PRINTER = 'prin'
|
||||
@@ -653,6 +654,7 @@ class GamEntity():
|
||||
PERMITTEE: ['Permittees', 'Permittee'],
|
||||
PERSONAL_DEVICE: ['Personal Devices', 'Personal Device'],
|
||||
PHOTO: ['Photos', 'Photo'],
|
||||
POLICY: ['Policies', 'Policy'],
|
||||
POP_ENABLED: ['POP Enabled', 'POP Enabled'],
|
||||
PRESENTATION: ['Presentations', 'Presentation'],
|
||||
PRINTER: ['Printers', 'Printer'],
|
||||
|
||||
@@ -65,6 +65,8 @@ CSV_OUTPUT_HEADER_DROP_FILTER = 'cohd'
|
||||
CSV_OUTPUT_HEADER_FILTER = 'cohf'
|
||||
# Force output column headers
|
||||
CSV_OUTPUT_HEADER_FORCE = 'cofh'
|
||||
# Order output column headers
|
||||
CSV_OUTPUT_HEADER_ORDER = 'coho'
|
||||
# No escape character in CSV output file
|
||||
CSV_OUTPUT_NO_ESCAPE_CHAR = 'cone'
|
||||
# Quote character in CSV output file
|
||||
@@ -80,7 +82,7 @@ CSV_OUTPUT_ROW_FILTER_MODE = 'corm'
|
||||
# Limit number of output rows
|
||||
CSV_OUTPUT_ROW_LIMIT = 'corl'
|
||||
# Add timestamp column to CSV output file
|
||||
CSV_OUTPUT_TIMESTAMP_COLUMN = 'csv_output_timestamp_column'
|
||||
CSV_OUTPUT_TIMESTAMP_COLUMN = 'cotc'
|
||||
# Output sort headers
|
||||
CSV_OUTPUT_SORT_HEADERS = 'cosh'
|
||||
# CSV todrive options
|
||||
@@ -235,6 +237,7 @@ Globals = {
|
||||
CSV_OUTPUT_HEADER_DROP_FILTER: [],
|
||||
CSV_OUTPUT_HEADER_FILTER: [],
|
||||
CSV_OUTPUT_HEADER_FORCE: [],
|
||||
CSV_OUTPUT_HEADER_ORDER: [],
|
||||
CSV_OUTPUT_NO_ESCAPE_CHAR: None,
|
||||
CSV_OUTPUT_QUOTE_CHAR: None,
|
||||
CSV_OUTPUT_ROW_DROP_FILTER: [],
|
||||
|
||||
@@ -1,441 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
usage()
|
||||
{
|
||||
cat << EOF
|
||||
GAM installation script.
|
||||
|
||||
OPTIONS:
|
||||
-h show help.
|
||||
-d Directory where gam folder will be installed. Default is \$HOME/bin/
|
||||
-a Architecture to install (i386, x86_64, x86_64_legacy, arm, arm64). Default is to detect your arch with "uname -m".
|
||||
-o OS we are running (linux, macos). Default is to detect your OS with "uname -s".
|
||||
-b OS version. Default is to detect on MacOS and Linux.
|
||||
-l Just upgrade GAM to latest version. Skips project creation and auth.
|
||||
-p Profile update (true, false). Should script add gam command to environment. Default is true.
|
||||
-u Admin user email address to use with GAM. Default is to prompt.
|
||||
-r Regular user email address. Used to test service account access to user data. Default is to prompt.
|
||||
-v Version to install (latest, prerelease, draft, 3.8, etc). Default is latest.
|
||||
-s Strip gam component from extracted files, files will be downloaded directly to $target_dir
|
||||
EOF
|
||||
}
|
||||
|
||||
target_dir="$HOME/bin"
|
||||
target_gam="gam7/gam"
|
||||
gamarch=$(uname -m)
|
||||
gamos=$(uname -s)
|
||||
osversion=""
|
||||
update_profile=true
|
||||
upgrade_only=false
|
||||
gamversion="latest"
|
||||
adminuser=""
|
||||
regularuser=""
|
||||
strip_gam="--strip-components 0"
|
||||
|
||||
while getopts "hd:a:o:b:lp:u:r:v:s" OPTION
|
||||
do
|
||||
case $OPTION in
|
||||
h) usage; exit;;
|
||||
d) target_dir="$OPTARG";;
|
||||
a) gamarch="$OPTARG";;
|
||||
o) gamos="$OPTARG";;
|
||||
b) osversion="$OPTARG";;
|
||||
l) upgrade_only=true;;
|
||||
p) update_profile="$OPTARG";;
|
||||
u) adminuser="$OPTARG";;
|
||||
r) regularuser="$OPTARG";;
|
||||
v) gamversion="$OPTARG";;
|
||||
s) strip_gam="--strip-components 1"; target_gam="gam";;
|
||||
?) usage; exit;;
|
||||
esac
|
||||
done
|
||||
|
||||
# remove possible / from end of target_dir
|
||||
target_dir=${target_dir%/}
|
||||
|
||||
update_profile() {
|
||||
[ "$2" -eq 1 ] || [ -f "$1" ] || return 1
|
||||
|
||||
grep -F "$alias_line" "$1" > /dev/null 2>&1
|
||||
if [ $? -ne 0 ]; then
|
||||
echo_yellow "Adding gam alias to profile file $1."
|
||||
echo -e "\n$alias_line" >> "$1"
|
||||
else
|
||||
echo_yellow "gam alias already exists in profile file $1. Skipping add."
|
||||
fi
|
||||
}
|
||||
|
||||
echo_red()
|
||||
{
|
||||
echo -e "\x1B[1;31m$1"
|
||||
echo -e '\x1B[0m'
|
||||
}
|
||||
|
||||
echo_green()
|
||||
{
|
||||
echo -e "\x1B[1;32m$1"
|
||||
echo -e '\x1B[0m'
|
||||
}
|
||||
|
||||
echo_yellow()
|
||||
{
|
||||
echo -e "\x1B[1;33m$1"
|
||||
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
|
||||
}
|
||||
|
||||
if [ "$gamversion" == "latest" ]; then
|
||||
release_url="https://api.github.com/repos/GAM-team/GAM/releases/latest"
|
||||
elif [ "$gamversion" == "prerelease" -o "$gamversion" == "draft" ]; then
|
||||
release_url="https://api.github.com/repos/GAM-team/GAM/releases"
|
||||
else
|
||||
release_url="https://api.github.com/repos/GAM-team/GAM/releases/tags/v$gamversion"
|
||||
fi
|
||||
|
||||
if [ -z ${GHCLIENT+x} ]; then
|
||||
check_type="unauthenticated"
|
||||
curl_opts=( )
|
||||
else
|
||||
check_type="authenticated"
|
||||
curl_opts=( "$GHCLIENT" )
|
||||
fi
|
||||
echo_yellow "Checking GitHub URL $release_url for $gamversion GAM release ($check_type)..."
|
||||
release_json=$(curl \
|
||||
--silent \
|
||||
"${curl_opts[@]}" \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
"$release_url" \
|
||||
2>&1 /dev/null)
|
||||
|
||||
echo_yellow "Getting file and download URL..."
|
||||
# Python is sadly the nearest to universal way to safely handle JSON with Bash
|
||||
# At least this code should be compatible with just about any Python version ever
|
||||
# unlike GAM itself. If some users don't have Python we can try grep / sed / etc
|
||||
# but that gets really ugly
|
||||
pycode="import json
|
||||
import sys
|
||||
|
||||
attrib = sys.argv[1]
|
||||
gamversion = sys.argv[2]
|
||||
|
||||
release = json.load(sys.stdin)
|
||||
if type(release) is list:
|
||||
for a_release in release:
|
||||
if a_release['prerelease'] and gamversion != 'prerelease':
|
||||
continue
|
||||
elif a_release['draft'] and gamversion != 'draft':
|
||||
continue
|
||||
release = a_release
|
||||
break
|
||||
try:
|
||||
for asset in release['assets']:
|
||||
print(asset[attrib])
|
||||
#else:
|
||||
# print('ERROR: Attribute: {0} for version {1} not found'.format(attrib, gamversion))
|
||||
except KeyError:
|
||||
print('ERROR: assets value not found in JSON value of:\n\n%s' % release)"
|
||||
|
||||
pycmd="python3"
|
||||
$pycmd -V >/dev/null 2>&1
|
||||
rc=$?
|
||||
if (( $rc != 0 )); then
|
||||
pycmd="python"
|
||||
fi
|
||||
$pycmd -V >/dev/null 2>&1
|
||||
rc=$?
|
||||
if (( $rc != 0 )); then
|
||||
pycmd="/usr/bin/python3"
|
||||
fi
|
||||
$pycmd -V >/dev/null 2>&1
|
||||
rc=$?
|
||||
if (( $rc != 0 )); then
|
||||
pycmd="python2"
|
||||
fi
|
||||
$pycmd -V >/dev/null 2>&1
|
||||
rc=$?
|
||||
if (( $rc != 0 )); then
|
||||
echo_red "ERROR: No version of python installed."
|
||||
exit
|
||||
fi
|
||||
download_urls=$(echo "$release_json" | $pycmd -c "$pycode" browser_download_url "$gamversion")
|
||||
if [[ ${download_urls:0:5} = "ERROR" ]]; then
|
||||
echo_red "${download_urls}"
|
||||
exit
|
||||
fi
|
||||
|
||||
case $gamos in
|
||||
[lL]inux)
|
||||
gamos="linux"
|
||||
download_urls=$(echo -e "$download_urls" | grep "\-linux-")
|
||||
if [ "$osversion" == "" ]; then
|
||||
this_glibc_ver=$(ldd --version | awk '/ldd/{print $NF}')
|
||||
else
|
||||
this_glibc_ver=$osversion
|
||||
fi
|
||||
echo "This Linux distribution uses glibc $this_glibc_ver"
|
||||
case $gamarch in
|
||||
x86_64)
|
||||
download_urls=$(echo -e "$download_urls" | grep "\-x86_64-")
|
||||
gam_x86_64_glibc_vers=$(echo -e "$download_urls" | \
|
||||
grep --only-matching 'glibc[0-9\.]*\.tar\.xz$' \
|
||||
| cut -c 6-9 )
|
||||
useglibc="legacy"
|
||||
for gam_glibc_ver in $gam_x86_64_glibc_vers; do
|
||||
if version_gt $this_glibc_ver $gam_glibc_ver; then
|
||||
useglibc="glibc$gam_glibc_ver"
|
||||
echo_green "Using GAM compiled against $useglibc"
|
||||
break
|
||||
fi
|
||||
done
|
||||
download_url=$(echo -e "$download_urls" | grep "$useglibc")
|
||||
;;
|
||||
arm|arm64|aarch64)
|
||||
download_urls=$(echo -e "$download_urls" | grep "\-aarch64-")
|
||||
gam_arm64_glibc_vers=$(echo -e "$download_urls" | \
|
||||
grep --only-matching 'glibc[0-9\.]*\.tar\.xz$' \
|
||||
| cut -c 6-9 )
|
||||
useglibc="legacy"
|
||||
for gam_glibc_ver in $gam_arm64_glibc_vers; do
|
||||
if version_gt $this_glibc_ver $gam_glibc_ver; then
|
||||
useglibc="glibc$gam_glibc_ver"
|
||||
echo_green "Using GAM compiled against $useglibc"
|
||||
break
|
||||
fi
|
||||
done
|
||||
download_url=$(echo -e "$download_urls" | grep "$useglibc")
|
||||
;;
|
||||
*)
|
||||
echo_red "ERROR: this installer currently only supports x86_64 and arm64 Linux. Looks like you're running on $gamarch. Exiting."
|
||||
exit
|
||||
esac
|
||||
;;
|
||||
[Mm]ac[Oo][sS]|[Dd]arwin)
|
||||
gamos="macos"
|
||||
fullversion=$(sw_vers -productVersion)
|
||||
# override osversion only if it wasn't set by cli arguments
|
||||
osversion=${osversion:-${fullversion:0:2}}
|
||||
download_urls=$(echo -e "$download_urls" | grep "\-macos-")
|
||||
case $gamarch in
|
||||
x86_64)
|
||||
download_url=$(echo -e "$download_urls" | grep "\-x86_64")
|
||||
minimum_version=13
|
||||
;;
|
||||
arm|arm64|aarch64)
|
||||
download_url=$(echo -e "$download_urls" | grep "\-aarch64")
|
||||
minimum_version=14
|
||||
;;
|
||||
*)
|
||||
echo_red "ERROR: this installer currently only supports x86_64 and arm64 MacOS. Looks like you're running on ${gamarch}. Exiting."
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
if [[ "$osversion" -ge "$minimum_version" ]]; then
|
||||
echo_green "You are running MacOS ${fullversion}, good. Using GAM with ${download_url}."
|
||||
else
|
||||
echo_red "Sorry, you are running MacOS ${fullversion} but GAM on ${gamarch} requires MacOS ${minimum_version}. Exiting."
|
||||
exit
|
||||
fi
|
||||
;;
|
||||
MINGW64_NT*)
|
||||
gamos="windows"
|
||||
echo "You are running Windows"
|
||||
download_url=$(echo -e "$download_urls" | grep "\-windows-" | grep ".zip")
|
||||
;;
|
||||
*)
|
||||
echo_red "Sorry, this installer currently only supports Linux and MacOS. Looks like you're running on ${gamos}. Exiting."
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
|
||||
# Temp dir for archive
|
||||
temp_archive_dir=$(mktemp -d 2>/dev/null || mktemp -d -t 'mytmpdir')
|
||||
|
||||
# Clean up after ourselves even if we are killed with CTRL-C
|
||||
trap "rm -rf $temp_archive_dir" EXIT
|
||||
|
||||
# hack to grab the end of the URL which should be the filename.
|
||||
name=$(echo -e "$download_url" | rev | cut -f1 -d "/" | rev)
|
||||
|
||||
echo_yellow "Downloading ${download_url} to $temp_archive_dir ($check_type)..."
|
||||
# Save archive to temp w/o losing our path
|
||||
(cd "$temp_archive_dir" && curl -O -L -s "${curl_opts[@]}" "$download_url")
|
||||
|
||||
mkdir -p "$target_dir"
|
||||
|
||||
echo_yellow "Extracting archive to $target_dir"
|
||||
if [[ "$name" =~ tar.xz|tar.gz|tar ]]; then
|
||||
tar $strip_gam -xf "$temp_archive_dir"/"$name" -C "$target_dir"
|
||||
elif [[ "$name" == *.zip ]]; then
|
||||
unzip -o "${temp_archive_dir}/${name}" -d "${target_dir}"
|
||||
else
|
||||
echo "I don't know what to do with files like ${name}. Giving up."
|
||||
exit 1
|
||||
fi
|
||||
rc=$?
|
||||
if (( $rc != 0 )); then
|
||||
echo_red "ERROR: extracting the GAM archive with tar failed with error $rc. Exiting."
|
||||
exit
|
||||
else
|
||||
echo_green "Finished extracting GAM archive."
|
||||
fi
|
||||
|
||||
# Update profile to add gam command
|
||||
if [ "$update_profile" = true ]; then
|
||||
alias_line="alias gam=\"${target_dir// /\\ }/$target_gam\""
|
||||
if [ "$gamos" == "linux" ]; then
|
||||
update_profile "$HOME/.bash_aliases" 0 || update_profile "$HOME/.bash_profile" 0 || update_profile "$HOME/.bashrc" 0
|
||||
update_profile "$HOME/.zshrc" 0
|
||||
elif [ "$gamos" == "macos" ]; then
|
||||
update_profile "$HOME/.bash_aliases" 0 || update_profile "$HOME/.bash_profile" 0 || update_profile "$HOME/.bashrc" 0 || update_profile "$HOME/.profile" 1
|
||||
update_profile "$HOME/.zshrc" 1
|
||||
fi
|
||||
else
|
||||
echo_yellow "skipping profile update."
|
||||
fi
|
||||
|
||||
if [ "$upgrade_only" = true ]; then
|
||||
echo_green "Here's information about your GAM upgrade:"
|
||||
"$target_dir/$target_gam" version extended
|
||||
rc=$?
|
||||
if (( $rc != 0 )); then
|
||||
echo_red "ERROR: Failed running GAM for the first time with return code $rc. Please report this error to GAM mailing list. Exiting."
|
||||
exit
|
||||
fi
|
||||
|
||||
echo_green "GAM upgrade complete!"
|
||||
exit
|
||||
fi
|
||||
|
||||
# Set config command
|
||||
#config_cmd="config no_browser false"
|
||||
|
||||
while true; do
|
||||
read -p "Can you run a full browser on this machine? (usually Y for MacOS, N for Linux if you SSH into this machine) " yn
|
||||
case $yn in
|
||||
[Yy]*)
|
||||
break
|
||||
;;
|
||||
[Nn]*)
|
||||
# config_cmd="config no_browser true"
|
||||
touch "$target_dir/gam/nobrowser.txt" > /dev/null 2>&1
|
||||
break
|
||||
;;
|
||||
*)
|
||||
echo_red "Please answer yes or no."
|
||||
;;
|
||||
esac
|
||||
done
|
||||
echo
|
||||
|
||||
project_created=false
|
||||
while true; do
|
||||
read -p "GAM is now installed. Are you ready to set up a Google API project for GAM? (yes or no) " yn
|
||||
case $yn in
|
||||
[Yy]*)
|
||||
if [ "$adminuser" == "" ]; then
|
||||
read -p "Please enter your Google Workspace admin email address: " adminuser
|
||||
fi
|
||||
# "$target_dir/$target_gam" $config_cmd create project $adminuser
|
||||
"$target_dir/$target_gam" create project $adminuser
|
||||
rc=$?
|
||||
if (( $rc == 0 )); then
|
||||
echo_green "Project creation complete."
|
||||
project_created=true
|
||||
break
|
||||
else
|
||||
echo_red "Project creation failed. Trying again. Say N to skip project creation."
|
||||
fi
|
||||
;;
|
||||
[Nn]*)
|
||||
echo -e "\nYou can create an API project later by running:\n\ngam create project\n"
|
||||
break
|
||||
;;
|
||||
*)
|
||||
echo_red "Please answer yes or no."
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
admin_authorized=false
|
||||
while $project_created; do
|
||||
read -p "Are you ready to authorize GAM to perform Google Workspace management operations as your admin account? (yes or no) " yn
|
||||
case $yn in
|
||||
[Yy]*)
|
||||
# "$target_dir/$target_gam" $config_cmd oauth create $adminuser
|
||||
"$target_dir/$target_gam" oauth create $adminuser
|
||||
rc=$?
|
||||
if (( $rc == 0 )); then
|
||||
echo_green "Admin authorization complete."
|
||||
admin_authorized=true
|
||||
break
|
||||
else
|
||||
echo_red "Admin authorization failed. Trying again. Say N to skip admin authorization."
|
||||
fi
|
||||
;;
|
||||
[Nn]*)
|
||||
echo -e "\nYou can authorize an admin later by running:\n\ngam oauth create\n"
|
||||
break
|
||||
;;
|
||||
*)
|
||||
echo_red "Please answer yes or no."
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
service_account_authorized=false
|
||||
while $admin_authorized; do
|
||||
read -p "Are you ready to authorize GAM to manage Google Workspace user data and settings? (yes or no) " yn
|
||||
case $yn in
|
||||
[Yy]*)
|
||||
if [ "$regularuser" == "" ]; then
|
||||
read -p "Please enter the email address of a regular Google Workspace user: " regularuser
|
||||
fi
|
||||
echo_yellow "Great! Checking service account scopes.This will fail the first time. Follow the steps to authorize and retry. It can take a few minutes for scopes to PASS after they've been authorized in the admin console."
|
||||
# "$target_dir/$target_gam" $config_cmd user $regularuser check serviceaccount
|
||||
"$target_dir/$target_gam" user $regularuser check serviceaccount
|
||||
rc=$?
|
||||
if (( $rc == 0 )); then
|
||||
echo_green "Service account authorization complete."
|
||||
service_account_authorized=true
|
||||
break
|
||||
else
|
||||
echo_red "Service account authorization failed. Confirm you entered the scopes correctly in the admin console. It can take a few minutes for scopes to PASS after they are entered in the admin console so if you're sure you entered them correctly, go grab a coffee and then hit Y to try again. Say N to skip admin authorization."
|
||||
fi
|
||||
;;
|
||||
[Nn]*)
|
||||
echo -e "\nYou can authorize a service account later by running:\n\ngam user $adminuser check serviceaccount\n"
|
||||
break
|
||||
;;
|
||||
*)
|
||||
echo_red "Please answer yes or no."
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo_green "Here's information about your new GAM installation:"
|
||||
#"$target_dir/$target_gam" $config_cmd save version extended
|
||||
"$target_dir/$target_gam" version extended
|
||||
rc=$?
|
||||
if (( $rc != 0 )); then
|
||||
echo_red "ERROR: Failed running GAM for the first time with $rc. Please report this error to GAM mailing list. Exiting."
|
||||
exit
|
||||
fi
|
||||
|
||||
echo_green "GAM installation and setup complete!"
|
||||
if [ "$update_profile" = true ]; then
|
||||
echo_green "Please restart your terminal shell or to get started right away run:\n\n$alias_line"
|
||||
fi
|
||||
@@ -1,58 +1,23 @@
|
||||
import os
|
||||
import sys
|
||||
import uuid
|
||||
from lxml import etree
|
||||
import sys
|
||||
|
||||
source_dir = sys.argv[1]
|
||||
template_file = sys.argv[2]
|
||||
target_file = sys.argv[3]
|
||||
# Hacky solution to create a Guid for all files
|
||||
# so Wix is happy and Guid is stable every time.
|
||||
# uuid5 is used for the Guid and the input is the
|
||||
# source filename so the Guid will be the same
|
||||
# every time as long as the source file name is
|
||||
# the same.
|
||||
|
||||
existing_components = {
|
||||
'gam.exe': ''' <Component Id="gam_exe" Guid="d046ea24-c9f8-40ca-84db-70b0119933ff">
|
||||
<File Name="gam.exe" KeyPath="yes" />
|
||||
<Environment Id="PATH" Name="PATH" Value="[INSTALLFOLDER]" Permanent="yes" Part="last" Action="set" System="yes" />
|
||||
</Component>
|
||||
''',
|
||||
'LICENSE': ''' <Component Id="license" Guid="c76864c5-d005-44d5-bb7c-a27e5923792d">
|
||||
<File Name="LICENSE" KeyPath="yes" />
|
||||
</Component>
|
||||
''',
|
||||
'gam-setup.bat': ''' <Component Id="gam_setup_bat" Guid="5e6bbacb-d86f-4d80-a10b-89b81ee63fcb">
|
||||
<File Name="gam-setup.bat" KeyPath="yes" />
|
||||
</Component>
|
||||
''',
|
||||
'GamCommands.txt': ''' <Component Id="GamCommands_txt" Guid="a2dca862-b222-469e-a637-95ea2a1c53e7">
|
||||
<File Name="GamCommands.txt" KeyPath="yes" />
|
||||
</Component>
|
||||
''',
|
||||
'GamUpdate.txt': ''' <Component Id="GamUpdate_txt" Guid="1b7cdd48-0fff-4943-a219-102fcd14c755">
|
||||
<File Name="GamUpdate.txt" KeyPath="yes" />
|
||||
</Component>
|
||||
''',
|
||||
'cacerts.pem': ''' <Component Id="cacerts_pem" Guid="61fe2b2d-1646-4bed-b844-193965e97727">
|
||||
<File Name="cacerts.pem" KeyPath="yes" />
|
||||
</Component>
|
||||
''',
|
||||
}
|
||||
|
||||
component_xml = ''
|
||||
all_files = []
|
||||
for root, dirs, files in os.walk(source_dir):
|
||||
for filename in files:
|
||||
relpath = os.path.relpath(root, source_dir)
|
||||
if relpath == '.':
|
||||
all_files.append(filename)
|
||||
else:
|
||||
all_files.append(os.path.join(relpath, filename))
|
||||
all_files.sort()
|
||||
for filename in all_files:
|
||||
component_xml += existing_components.get(filename,
|
||||
f' <Component>\n <File Name="{filename}" KeyPath="yes"/>\n </Component>\n')
|
||||
|
||||
with open(template_file, 'r') as f:
|
||||
template = f.read()
|
||||
|
||||
full_xml = template.replace('REPLACE_ME_WITH_FILE_COMPONENTS', component_xml)
|
||||
|
||||
with open(target_file, 'w') as f:
|
||||
f.write(full_xml)
|
||||
rewrite_file = sys.argv[1]
|
||||
|
||||
with open(rewrite_file, 'rb') as f:
|
||||
input_xml = f.read()
|
||||
root = etree.fromstring(input_xml)
|
||||
for elem in root.getiterator():
|
||||
if 'Guid' in elem.attrib:
|
||||
source = elem.getchildren()[0].attrib['Source']
|
||||
stable_uuid = str(uuid.uuid5(uuid.NAMESPACE_URL, source))
|
||||
elem.attrib['Guid'] = stable_uuid
|
||||
with open(rewrite_file, 'w') as f:
|
||||
f.write(etree.tostring(root).decode())
|
||||
|
||||
Reference in New Issue
Block a user