mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-17 04:31:37 +00:00
Compare commits
12 Commits
20241009.2
...
20241016.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
861279e614 | ||
|
|
b80dd15f4b | ||
|
|
ae95c8fdea | ||
|
|
090b5937ab | ||
|
|
2323e130b1 | ||
|
|
6ef127f283 | ||
|
|
266f00d3a8 | ||
|
|
5c61867e1f | ||
|
|
0bbe1cc958 | ||
|
|
d1e02e4695 | ||
|
|
f707c83e1a | ||
|
|
ae67319975 |
38
.github/workflows/build.yml
vendored
38
.github/workflows/build.yml
vendored
@@ -17,7 +17,7 @@ defaults:
|
||||
working-directory: src
|
||||
|
||||
env:
|
||||
SCRATCH_COUNTER: 2
|
||||
SCRATCH_COUNTER: 3
|
||||
OPENSSL_CONFIG_OPTS: no-fips --api=3.0.0
|
||||
OPENSSL_INSTALL_PATH: ${{ github.workspace }}/bin/ssl
|
||||
OPENSSL_SOURCE_PATH: ${{ github.workspace }}/src/openssl
|
||||
@@ -36,58 +36,63 @@ jobs:
|
||||
goal: build
|
||||
arch: x86_64
|
||||
openssl_archs: linux-x86_64
|
||||
- os: [self-hosted, linux, arm64]
|
||||
- os: ubuntu-24.04
|
||||
jid: 2
|
||||
goal: build
|
||||
arch: x86_64
|
||||
openssl_archs: linux-x86_64
|
||||
- os: [self-hosted, linux, arm64]
|
||||
jid: 3
|
||||
goal: build
|
||||
arch: aarch64
|
||||
openssl_archs: linux-aarch64
|
||||
- os: ubuntu-22.04
|
||||
jid: 3
|
||||
jid: 4
|
||||
goal: build
|
||||
arch: x86_64
|
||||
openssl_archs: linux-x86_64
|
||||
staticx: yes
|
||||
- os: [self-hosted, linux, arm64]
|
||||
jid: 4
|
||||
jid: 5
|
||||
goal: build
|
||||
arch: aarch64
|
||||
openssl_archs: linux-aarch64
|
||||
staticx: yes
|
||||
- os: macos-13
|
||||
jid: 5
|
||||
jid: 6
|
||||
goal: build
|
||||
arch: x86_64
|
||||
openssl_archs: darwin64-x86_64
|
||||
- os: macos-14
|
||||
jid: 6
|
||||
jid: 7
|
||||
goal: build
|
||||
arch: aarch64
|
||||
openssl_archs: darwin64-arm64
|
||||
- os: windows-2022
|
||||
jid: 7
|
||||
jid: 8
|
||||
goal: build
|
||||
arch: Win64
|
||||
openssl_archs: VC-WIN64A
|
||||
- os: ubuntu-24.04
|
||||
goal: test
|
||||
python: "3.9"
|
||||
jid: 8
|
||||
arch: x86_64
|
||||
- os: ubuntu-24.04
|
||||
goal: test
|
||||
python: "3.10"
|
||||
jid: 9
|
||||
arch: x86_64
|
||||
- os: ubuntu-24.04
|
||||
goal: test
|
||||
python: "3.11"
|
||||
python: "3.10"
|
||||
jid: 10
|
||||
arch: x86_64
|
||||
- os: ubuntu-24.04
|
||||
goal: test
|
||||
python: "3.12"
|
||||
python: "3.11"
|
||||
jid: 11
|
||||
arch: x86_64
|
||||
- os: ubuntu-24.04
|
||||
goal: test
|
||||
python: "3.12"
|
||||
jid: 12
|
||||
arch: x86_64
|
||||
|
||||
steps:
|
||||
|
||||
@@ -110,7 +115,7 @@ jobs:
|
||||
with:
|
||||
path: |
|
||||
cache.tar.xz
|
||||
key: gam-${{ matrix.jid }}-20241008
|
||||
key: gam-${{ matrix.jid }}-20241014
|
||||
|
||||
- name: Untar Cache archive
|
||||
if: matrix.goal == 'build' && steps.cache-python-ssl.outputs.cache-hit == 'true'
|
||||
@@ -181,7 +186,7 @@ jobs:
|
||||
run: |
|
||||
echo "RUNNING: apt update..."
|
||||
sudo apt-get -qq --yes update
|
||||
sudo apt-get -qq --yes install swig libpcsclite-dev libxslt1-dev
|
||||
sudo apt-get -qq --yes install swig libpcsclite-dev libxslt1-dev libsqlite3-dev
|
||||
|
||||
- name: MacOS install tools
|
||||
if: runner.os == 'macOS'
|
||||
@@ -697,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 --"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# Authorization
|
||||
- [Introduction](#introduction)
|
||||
- [Headless computers and Cloud Shells](#headless-computers-and-cloud-shells)
|
||||
- [Version 5 Update](#version-5-update)
|
||||
- [API documentation](#api-documentation)
|
||||
- [Python Regular Expressions](Python-Regular-Expressions)
|
||||
- [Definitions](#definitions)
|
||||
@@ -127,25 +126,6 @@ as required by Google for headless computers/cloud shells; this is required as o
|
||||
* See: https://developers.googleblog.com/2022/02/making-oauth-flows-safer.html
|
||||
* OAuth out-of-band (oob) flow will be deprecated
|
||||
|
||||
## Version 5 Update
|
||||
GAM version `5.00.00` replaced the deprecated `oauth2client` library with the `google-auth` library.
|
||||
This change requires a one-time update of the client access file `oauth2.txt`; GAM will continue
|
||||
to use the old version of `oauth2.txt` until you perform the update. There is a small performance
|
||||
impact until the update is performed. However, you can't use the updated version of `oauth2.txt`
|
||||
in prior versions of GAM; if you want to run GAM `5.00.00` and prior versions of GAM,
|
||||
do not perform the update until you no longer need to run the prior versions of GAM.
|
||||
|
||||
If you are running any GAM version `4.85.00` or later, perform the following command
|
||||
after installing `5.00.00` to perform the update.
|
||||
```
|
||||
gam oauth refresh
|
||||
```
|
||||
If you are running any GAM version before `4.85.00`, perform the following command
|
||||
after installing `5.00.00` to perform the update.
|
||||
```
|
||||
gam oauth update
|
||||
```
|
||||
|
||||
## API documentation
|
||||
* https://cloud.google.com/resource-manager/docs/creating-managing-organization#adding_an_organization_administrator
|
||||
* https://cloud.google.com/service-usage/docs/reference/rest
|
||||
@@ -213,7 +193,7 @@ perform these steps and then retry the create project command.
|
||||
|
||||
## Authorize Service Account Key Uploads
|
||||
|
||||
If you try to create a project and get an error saying that Constraint `constraints/iam.disableServiceAccountKeyUpload violated for service account projects/gam-project-xxx`,
|
||||
If you try to create a project and get an error saying that Constraint `constraints/iam.disableServiceAccountKeyUpload violated for service account projects/gam-project-xxxxx`,
|
||||
perform these steps and then you should be able to authorize and use your project.
|
||||
|
||||
* Login as an existing super admin at console.cloud.google.com
|
||||
@@ -293,7 +273,7 @@ You can skip these steps if you know that untrusted third-party apps are allowed
|
||||
|
||||
### Default values
|
||||
* `<AppName>` - "GAM"
|
||||
* `<ProjectID>` - "gam-project-abc-def-jki" where "abc-def-ghi" are randomly generated
|
||||
* `<ProjectID>` - "gam-project-a1b2c" where "a1b2c" are randomly generated
|
||||
* `<ProjectName>` - "GAM Project"
|
||||
* `<ServiceAccountName>` - `<ProjectID>`
|
||||
* `<ServiceAccountDisplayName>` - `<ProjectName>`
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
- [Parameters for Basic Levels](#parameters-for-basic-levels)
|
||||
- [Create an Access Level](#create-an-access-level)
|
||||
- [Update an Access Level](#update-an-access-level)
|
||||
- [Update Access Levels with JSON](#update-access-levels-with-json)
|
||||
- [Delete an Access Level](#delete-an-access-level)
|
||||
- [Display all Access Levels](#display-all-access-levels)
|
||||
- [CAA Region Codes](#caa-region-codes)
|
||||
@@ -41,6 +42,8 @@ In order for GAM to manage CAA access levels, you need to grant your service acc
|
||||
|
||||
## Definitions
|
||||
```
|
||||
<JSONData> ::= (json [charset <Charset>] <String>) | (json file <FileName> [charset <Charset>]) |
|
||||
|
||||
<QueryCEL> ::= <String>
|
||||
See: https://cloud.google.com/access-context-manager/docs/custom-access-level-spec
|
||||
|
||||
@@ -144,7 +147,7 @@ basic
|
||||
## Create an Access Level
|
||||
Create a new access level. CAA supports basic and custom conditions.
|
||||
```
|
||||
gam create caalevel <String> [description <String>] (basic <CAABasicAttribute>+)|(custom <QueryCEL>)
|
||||
gam create caalevel <String> [description <String>] (basic <CAABasicAttribute>+)|(custom <QueryCEL>)|<JSONData>
|
||||
```
|
||||
|
||||
## Example
|
||||
@@ -166,7 +169,7 @@ gam create caalevel CORP_IPS basic condition ipsubnetworks 1.2.3.0/24,4.5.6.0/24
|
||||
## Update an Access Level
|
||||
Updates an existing access level. CAA supports basic and custom conditions.
|
||||
```
|
||||
gam update caalevel <CAALevelName> [description <String>] (basic <CAABasicAttribute>+)|(custom <QueryCEL>)
|
||||
gam update caalevel <CAALevelName> [description <String>] (basic <CAABasicAttribute>+)|(custom <QueryCEL>)|<JSONData>
|
||||
```
|
||||
|
||||
## Examples
|
||||
@@ -175,6 +178,27 @@ This example adds UK to the allowed regions for CORP_COUNTRIES
|
||||
gam update caalevel CORP_COUNTRIES basic condition regions US,CA,UK endcondition
|
||||
```
|
||||
|
||||
## Update Access Levels with JSON
|
||||
Update existing CAA levels via their JSON data; create a CSV file of CAA levels.
|
||||
```
|
||||
gam redirect csv ./CAAlevels.csv print caalevels formatjson quotechar "'"
|
||||
```
|
||||
Edit the JSON column for the desired CAA level(s) in CAAlevels.csv.
|
||||
Update the desired CAA level by selecting the row by it's title; repeat for each title to update.
|
||||
```
|
||||
gam config csv_input_row_filter "title:text='Example Title'" csv CAAlevels.csv quotechar "'" gam update caalevel "~name" json "~JSON"
|
||||
```
|
||||
|
||||
## Example
|
||||
Edit CAAlevels.csv and add UK to the allowed regions for CORP_COUNTRIES
|
||||
```
|
||||
{"regions": ["US", "CA", "UK"]}
|
||||
```
|
||||
Do the update.
|
||||
```
|
||||
gam config csv_input_row_filter "title:text='CORP_COUNTRIES'" csv CAAlevels.csv quotechar "'" gam update caalevel "~name" json "~JSON"
|
||||
```
|
||||
|
||||
## Delete an Access Level
|
||||
Deletes the specified access level.
|
||||
```
|
||||
|
||||
@@ -10,6 +10,39 @@ 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.19
|
||||
|
||||
Updated `gam update shareddrive <SharedDriveEntity> ou <OrgUnitItem>` to handle the following error
|
||||
that occurs when an invalid `<SharedDriveEntity>` is specified.
|
||||
```
|
||||
ERROR: 400: invalidArgument - Invalid org membership name 0AJ3b2FTPakToUk9PVAxx.~
|
||||
```
|
||||
|
||||
Updated `gam print browsers` to properly format the time field `deviceIdentifiersHistory.records.0.firstRecordTime`.
|
||||
|
||||
### 7.00.18
|
||||
|
||||
Updated `gam create project` to use a default project name of `gam-project-a1b2c` (`a1b2c` is a random string of 5 characters)
|
||||
instead of `gam-project-abc-123-xyz` to avoid the following warning:
|
||||
```
|
||||
Project: gam-project-abc-123-xyz, Service Account: gam-project-abc-123-xyz@gam-project-abc-123-xyz.iam.gserviceaccount.com, Extracting public certificate
|
||||
init.py:12382: UserWarning: Attribute's length must be >= 1 and <= 64, but it was 70
|
||||
init.py:12383: UserWarning: Attribute's length must be >= 1 and <= 64, but it was 70
|
||||
Project: gam-project-abc-123-xyz, Service Account: gam-project-abc-123-xyz@gam-project-abc-123-xyz.iam.gserviceaccount.com, Done generating private key and public certificate
|
||||
```
|
||||
|
||||
### 7.00.17
|
||||
|
||||
Update all user calendar commands to disable falling back to client access if service account
|
||||
authorization has never been performed.
|
||||
|
||||
### 7.00.16
|
||||
|
||||
Updated `gam <UserTypeEntity> claim|transfer ownership` to show `Got N Drive Files/Folders that matched query` messages
|
||||
as files/folders are being identified for processing.
|
||||
|
||||
Added option `<JSONData>` to `gam create|update caalevel`.
|
||||
|
||||
### 7.00.15
|
||||
|
||||
Added options `timestamp [<Boolean>]` and `timeformat <String>` to `gam <UserTypeEntity> create|update drivefile` that allow
|
||||
|
||||
@@ -251,9 +251,9 @@ 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.15 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM 7.00.19 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.12.7 64-bit final
|
||||
Python 3.13.0 64-bit final
|
||||
MacOS Sonoma 14.5 x86_64
|
||||
Path: /Users/admin/bin/gam7
|
||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
@@ -923,9 +923,9 @@ 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.15 - https://github.com/GAM-team/GAM - pythonsource
|
||||
GAM7 7.00.19 - https://github.com/GAM-team/GAM - pythonsource
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.12.7 64-bit final
|
||||
Python 3.13.0 64-bit final
|
||||
Windows-10-10.0.17134 AMD64
|
||||
Path: C:\GAM7
|
||||
Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
|
||||
@@ -427,6 +427,15 @@ gam info ou <OrgUnitPath> nousers
|
||||
gam show teamdrives query "orgUnitId='03ph8a2z21rexy'" fields id,name,orgunit,createdtime
|
||||
gam print teamdrives query "orgUnitId='03ph8a2z21rexy'" fields id,name,orgunit,createdtime
|
||||
```
|
||||
Alternative method; `<OrgUnitPath>` defaults to `/`.
|
||||
```
|
||||
gam show oushareddrives
|
||||
[ou|org|orgunit <OrgUnitPath>]
|
||||
[formatjson]
|
||||
gam print oushareddrives [todrive <ToDriveAttribute>*]
|
||||
[ou|org|orgunit <OrgUnitPath>]
|
||||
[formatjson [quotechar <Character>]]
|
||||
```
|
||||
|
||||
## Manage Shared Drive access
|
||||
These commands are used to manage the ACLs on Shared Drives themselves, not the files/folders on the Shared Drives.
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
- [Print user domain counts](#print-user-domain-counts)
|
||||
- [Print domain counts for users in a specific domain and/or selected by a query](#print-domain-counts-for-users-in-a-specific-domain-and-or-selected-by-a-query)
|
||||
- [Print domain counts for users specified by `<UserTypeEntity>`](#print-domain-counts-for-users-specified-by-usertypeentity)
|
||||
- [Print user counts by OrgUnit](print-user-counts-by-orgunit)
|
||||
- [Print user counts by OrgUnit](#print-user-counts-by-orgunit)
|
||||
- [Print user list](#print-user-list)
|
||||
- [Display user counts](#display-user-counts)
|
||||
- [Verify domain membership]($verify-domain-membership)
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
Print the current version of Gam with details
|
||||
```
|
||||
gam version
|
||||
GAM 7.00.15 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM 7.00.19 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.12.7 64-bit final
|
||||
Python 3.13.0 64-bit final
|
||||
MacOS Sonoma 14.5 x86_64
|
||||
Path: /Users/Admin/bin/gam7
|
||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
@@ -15,9 +15,9 @@ 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.15 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM 7.00.19 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.12.7 64-bit final
|
||||
Python 3.13.0 64-bit final
|
||||
MacOS Sonoma 14.5 x86_64
|
||||
Path: /Users/Admin/bin/gam7
|
||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
@@ -27,9 +27,9 @@ 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.15 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM 7.00.19 - https://github.com/GAM-team/GAM - pyinstaller
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.12.7 64-bit final
|
||||
Python 3.13.0 64-bit final
|
||||
MacOS Sonoma 14.5 x86_64
|
||||
Path: /Users/Admin/bin/gam7
|
||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
@@ -37,7 +37,7 @@ 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.12.7
|
||||
filelock 3.13.0
|
||||
google-api-python-client 2.88.0
|
||||
google-auth-httplib2 0.1.0
|
||||
google-auth-oauthlib 1.0.0
|
||||
@@ -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.15
|
||||
Latest: 7.00.19
|
||||
echo $?
|
||||
1
|
||||
```
|
||||
@@ -72,7 +72,7 @@ echo $?
|
||||
Print the current version number without details
|
||||
```
|
||||
gam version simple
|
||||
7.00.15
|
||||
7.00.19
|
||||
```
|
||||
In Linux/MacOS you can do:
|
||||
```
|
||||
@@ -82,9 +82,9 @@ echo $VER
|
||||
Print the current version of Gam and address of this Wiki
|
||||
```
|
||||
gam help
|
||||
GAM 7.00.15 - https://github.com/GAM-team/GAM
|
||||
GAM 7.00.19 - https://github.com/GAM-team/GAM
|
||||
GAM Team <google-apps-manager@googlegroups.com>
|
||||
Python 3.12.7 64-bit final
|
||||
Python 3.13.0 64-bit final
|
||||
MacOS Sonoma 14.5 x86_64
|
||||
Path: /Users/Admin/bin/gam7
|
||||
Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: domain.com
|
||||
|
||||
@@ -2770,8 +2770,8 @@ gam print chromesnvalidity [todrive <ToDriveAttribute>*]
|
||||
(combiningfunction <CAACombiningFunction>) |
|
||||
(condition <CAAConditionAttribute>+ endcondition)
|
||||
|
||||
gam create caalevel <String> [description <String>] (basic <CAABasicAttribute>+)|(custom <QueryCEL>)
|
||||
gam update caalevel <CAALevelName> [description <String>] (basic <CAABasicAttribute>+)|(custom <QueryCEL>)
|
||||
gam create caalevel <String> [description <String>] (basic <CAABasicAttribute>+)|(custom <QueryCEL>)|<JSONData>
|
||||
gam update caalevel <CAALevelName> [description <String>] (basic <CAABasicAttribute>+)|(custom <QueryCEL>)|<JSONData>
|
||||
gam delete caalevel <CAALevelName>
|
||||
gam show caalevels
|
||||
[formatjson]
|
||||
|
||||
@@ -1,3 +1,37 @@
|
||||
7.00.19
|
||||
|
||||
Updated `gam update shareddrive <SharedDriveEntity> ou <OrgUnitItem>` to handle the following error
|
||||
that occurs when an invalid `<SharedDriveEntity>` is specified.
|
||||
```
|
||||
ERROR: 400: invalidArgument - Invalid org membership name 0AJ3b2FTPakToUk9PVAxx.~
|
||||
```
|
||||
|
||||
Updated `gam print browsers` to properly format the time field `deviceIdentifiersHistory.records.0.firstRecordTime`.
|
||||
|
||||
7.00.18
|
||||
|
||||
Updated `gam create project` to use a default project name of `gam-project-a1b2c` (`a1b2c` is a random string of 5 characters)
|
||||
instead of `gam-project-abc-123-xyz` to avoid the following warning:
|
||||
```
|
||||
Project: gam-project-abc-123-xyz, Service Account: gam-project-abc-123-xyz@gam-project-abc-123-xyz.iam.gserviceaccount.com, Extracting public certificate
|
||||
init.py:12382: UserWarning: Attribute's length must be >= 1 and <= 64, but it was 70
|
||||
init.py:12383: UserWarning: Attribute's length must be >= 1 and <= 64, but it was 70
|
||||
Project: gam-project-abc-123-xyz, Service Account: gam-project-abc-123-xyz@gam-project-abc-123-xyz.iam.gserviceaccount.com, Done generating private key and public certificate
|
||||
```
|
||||
|
||||
7.00.17
|
||||
|
||||
Update all user calendar commands to disable falling back to client access if service account
|
||||
authorization has never been performed. Previously, in this circumstance, the admin's calendars
|
||||
rather than the user's calendars were processed.
|
||||
|
||||
7.00.16
|
||||
|
||||
Updated `gam <UserTypeEntity> claim|transfer ownership` to show `Got N Drive Files/Folders that matched query` messages
|
||||
as files/folders are being identified for processing.
|
||||
|
||||
Added option `<JSONData>` to `gam create|update caalevel`.
|
||||
|
||||
7.00.15
|
||||
|
||||
Added options `timestamp [<Boolean>]` and `timeformat <String>` to `gam <UserTypeEntity> create|update drivefile` that allow
|
||||
|
||||
@@ -25,7 +25,7 @@ https://github.com/GAM-team/GAM/wiki
|
||||
"""
|
||||
|
||||
__author__ = 'GAM Team <google-apps-manager@googlegroups.com>'
|
||||
__version__ = '7.00.15'
|
||||
__version__ = '7.00.19'
|
||||
__license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
|
||||
|
||||
#pylint: disable=wrong-import-position
|
||||
@@ -6056,7 +6056,7 @@ def checkGroupExists(cd, ci, ciGroupsAPI, group, i=0, count=0):
|
||||
|
||||
# Turn the entity into a list of Users/CrOS devices
|
||||
def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isArchived=None,
|
||||
groupMemberType=Ent.TYPE_USER, noListConversion=False):
|
||||
groupMemberType=Ent.TYPE_USER, noListConversion=False, recursive=False, noCLArgs=False):
|
||||
def _incrEntityDoesNotExist(entityType):
|
||||
entityError['entityType'] = entityType
|
||||
entityError[ENTITY_ERROR_DNE] += 1
|
||||
@@ -6227,32 +6227,33 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
|
||||
isSuspended = True
|
||||
cd = buildGAPIObject(API.DIRECTORY)
|
||||
groups = convertEntityToList(entity)
|
||||
includeDerivedMembership = recursive = False
|
||||
includeDerivedMembership = False
|
||||
domains = []
|
||||
rolesSet = set()
|
||||
while Cmd.ArgumentsRemaining():
|
||||
myarg = getArgument()
|
||||
if myarg in GROUP_ROLES_MAP:
|
||||
rolesSet.add(GROUP_ROLES_MAP[myarg])
|
||||
elif myarg == 'primarydomain':
|
||||
domains.append(GC.Values[GC.DOMAIN])
|
||||
elif myarg == 'domains':
|
||||
domains.extend(getEntityList(Cmd.OB_DOMAIN_NAME_ENTITY))
|
||||
elif myarg == 'recursive':
|
||||
recursive = True
|
||||
includeDerivedMembership = False
|
||||
elif myarg == 'includederivedmembership':
|
||||
includeDerivedMembership = True
|
||||
recursive = False
|
||||
elif entityType == Cmd.ENTITY_GROUP_USERS_SELECT and myarg in SUSPENDED_ARGUMENTS:
|
||||
isSuspended = _getIsSuspended(myarg)
|
||||
elif entityType == Cmd.ENTITY_GROUP_USERS_SELECT and myarg in ARCHIVED_ARGUMENTS:
|
||||
isArchived = _getIsArchived(myarg)
|
||||
elif myarg == 'end':
|
||||
break
|
||||
else:
|
||||
Cmd.Backup()
|
||||
missingArgumentExit('end')
|
||||
if not noCLArgs:
|
||||
while Cmd.ArgumentsRemaining():
|
||||
myarg = getArgument()
|
||||
if myarg in GROUP_ROLES_MAP:
|
||||
rolesSet.add(GROUP_ROLES_MAP[myarg])
|
||||
elif myarg == 'primarydomain':
|
||||
domains.append(GC.Values[GC.DOMAIN])
|
||||
elif myarg == 'domains':
|
||||
domains.extend(getEntityList(Cmd.OB_DOMAIN_NAME_ENTITY))
|
||||
elif myarg == 'recursive':
|
||||
recursive = True
|
||||
includeDerivedMembership = False
|
||||
elif myarg == 'includederivedmembership':
|
||||
includeDerivedMembership = True
|
||||
recursive = False
|
||||
elif entityType == Cmd.ENTITY_GROUP_USERS_SELECT and myarg in SUSPENDED_ARGUMENTS:
|
||||
isSuspended = _getIsSuspended(myarg)
|
||||
elif entityType == Cmd.ENTITY_GROUP_USERS_SELECT and myarg in ARCHIVED_ARGUMENTS:
|
||||
isArchived = _getIsArchived(myarg)
|
||||
elif myarg == 'end':
|
||||
break
|
||||
else:
|
||||
Cmd.Backup()
|
||||
missingArgumentExit('end')
|
||||
if rolesSet:
|
||||
memberRoles = ','.join(sorted(rolesSet))
|
||||
for group in groups:
|
||||
@@ -6293,19 +6294,19 @@ def getItemsToModify(entityType, entity, memberRoles=None, isSuspended=None, isA
|
||||
elif entityType in {Cmd.ENTITY_CIGROUP_USERS}:
|
||||
ci = buildGAPIObject(API.CLOUDIDENTITY_GROUPS)
|
||||
groups = convertEntityToList(entity)
|
||||
recursive = False
|
||||
rolesSet = set()
|
||||
while Cmd.ArgumentsRemaining():
|
||||
myarg = getArgument()
|
||||
if myarg in GROUP_ROLES_MAP:
|
||||
rolesSet.add(GROUP_ROLES_MAP[myarg])
|
||||
elif myarg == 'recursive':
|
||||
recursive = True
|
||||
elif myarg == 'end':
|
||||
break
|
||||
else:
|
||||
Cmd.Backup()
|
||||
missingArgumentExit('end')
|
||||
if not noCLArgs:
|
||||
while Cmd.ArgumentsRemaining():
|
||||
myarg = getArgument()
|
||||
if myarg in GROUP_ROLES_MAP:
|
||||
rolesSet.add(GROUP_ROLES_MAP[myarg])
|
||||
elif myarg == 'recursive':
|
||||
recursive = True
|
||||
elif myarg == 'end':
|
||||
break
|
||||
else:
|
||||
Cmd.Backup()
|
||||
missingArgumentExit('end')
|
||||
if rolesSet:
|
||||
memberRoles = ','.join(sorted(rolesSet))
|
||||
for group in groups:
|
||||
@@ -11428,10 +11429,7 @@ def _getAppInfo(myarg, appInfo):
|
||||
return True
|
||||
|
||||
def _generateProjectSvcAcctId(prefix):
|
||||
psaId = prefix
|
||||
for _ in range(3):
|
||||
psaId += f'-{"".join(random.choice(LOWERNUMERIC_CHARS) for _ in range(3))}'
|
||||
return psaId
|
||||
return f'{prefix}-{"".join(random.choice(LOWERNUMERIC_CHARS) for _ in range(5))}'
|
||||
|
||||
def _getLoginHintProjectInfo(createCmd):
|
||||
login_hint = None
|
||||
@@ -24995,7 +24993,7 @@ def doDeleteBrowsers():
|
||||
except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden):
|
||||
checkEntityAFDNEorAccessErrorExit(None, Ent.CHROME_BROWSER, deviceId)
|
||||
|
||||
BROWSER_TIME_OBJECTS = {'lastActivityTime', 'lastPolicyFetchTime', 'lastRegistrationTime', 'lastStatusReportTime', 'safeBrowsingWarningsResetTime'}
|
||||
BROWSER_TIME_OBJECTS = {'firstRecordTime', 'lastActivityTime', 'lastPolicyFetchTime', 'lastRegistrationTime', 'lastStatusReportTime', 'safeBrowsingWarningsResetTime'}
|
||||
|
||||
def _showBrowser(browser, FJQC, i=0, count=0):
|
||||
if FJQC.formatJSON:
|
||||
@@ -37028,11 +37026,13 @@ def checkCalendarExists(cal, calId, showMessage=False):
|
||||
entityActionFailedWarning([Ent.CALENDAR, calId], str(e))
|
||||
return None
|
||||
|
||||
def validateCalendar(calId, i=0, count=0):
|
||||
def validateCalendar(calId, i=0, count=0, noClientAccess=False):
|
||||
cal = None
|
||||
if not calId.endswith('.calendar.google.com'):
|
||||
calId, cal = buildGAPIServiceObject(API.CALENDAR, calId, i, count, displayError=False)
|
||||
calId, cal = buildGAPIServiceObject(API.CALENDAR, calId, i, count, displayError=noClientAccess)
|
||||
if not cal:
|
||||
if noClientAccess:
|
||||
return (calId, None)
|
||||
cal = buildGAPIObject(API.CALENDAR)
|
||||
try:
|
||||
callGAPI(cal.calendars(), 'get',
|
||||
@@ -49759,7 +49759,7 @@ def _validateUserGetCalendarIds(user, i, count, calendarEntity,
|
||||
calIds = calendarEntity['dict'][user][:]
|
||||
else:
|
||||
calIds = calendarEntity['list'][:]
|
||||
user, cal = validateCalendar(user, i, count)
|
||||
user, cal = validateCalendar(user, i, count, noClientAccess=True)
|
||||
if not cal:
|
||||
return (user, None, None, 0)
|
||||
if calendarEntity['resourceIds']:
|
||||
@@ -50178,7 +50178,7 @@ def printShowCalendars(users):
|
||||
i, count, users = getEntityArgument(users)
|
||||
for user in users:
|
||||
i += 1
|
||||
user, cal = validateCalendar(user, i, count)
|
||||
user, cal = validateCalendar(user, i, count, noClientAccess=True)
|
||||
if not cal:
|
||||
continue
|
||||
if csvPF:
|
||||
@@ -50278,7 +50278,7 @@ def printShowCalSettings(users):
|
||||
i, count, users = getEntityArgument(users)
|
||||
for user in users:
|
||||
i += 1
|
||||
user, cal = validateCalendar(user, i, count)
|
||||
user, cal = validateCalendar(user, i, count, noClientAccess=True)
|
||||
if not cal:
|
||||
continue
|
||||
try:
|
||||
@@ -50438,7 +50438,7 @@ def transferCalendars(users):
|
||||
_getCalendarAttributes(targetListBody, returnOnUnknownArgument=True)
|
||||
else:
|
||||
unknownArgumentExit()
|
||||
targetUser, targetCal = validateCalendar(targetUser)
|
||||
targetUser, targetCal = validateCalendar(targetUser, noClientAccess=True)
|
||||
if not targetCal:
|
||||
return
|
||||
colorRgbFormat = 'backgroundColor' in targetListBody or 'foregroundColor' in targetListBody
|
||||
@@ -61687,11 +61687,15 @@ def transferOwnership(users):
|
||||
_identifyFilesToTransfer(childEntry)
|
||||
|
||||
def _identifyChildrenToTransfer(fileEntry, user, i, count):
|
||||
q = WITH_PARENTS.format(fileEntry['id'])
|
||||
setGettingAllEntityItemsForWhom(Ent.DRIVE_FILE_OR_FOLDER, user, query=q)
|
||||
pageMessage = getPageMessageForWhom(clearLastGotMsgLen=False)
|
||||
try:
|
||||
children = callGAPIpages(drive.files(), 'list', 'files',
|
||||
pageMessage=pageMessage, noFinalize=True,
|
||||
throwReasons=GAPI.DRIVE_USER_THROW_REASONS,
|
||||
retryReasons=[GAPI.UNKNOWN_ERROR],
|
||||
orderBy=OBY.orderBy, q=WITH_PARENTS.format(fileEntry['id']),
|
||||
orderBy=OBY.orderBy, q=q,
|
||||
fields='nextPageToken,files(id,name,parents,mimeType,ownedByMe,trashed)',
|
||||
pageSize=GC.Values[GC.DRIVE_MAX_RESULTS])
|
||||
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
|
||||
@@ -61972,11 +61976,15 @@ def claimOwnership(users):
|
||||
_identifyFilesToClaim(childEntry)
|
||||
|
||||
def _identifyChildrenToClaim(fileEntry, user, i, count):
|
||||
q = WITH_PARENTS.format(fileEntry['id'])
|
||||
setGettingAllEntityItemsForWhom(Ent.DRIVE_FILE_OR_FOLDER, user, query=q)
|
||||
pageMessage = getPageMessageForWhom(clearLastGotMsgLen=False)
|
||||
try:
|
||||
children = callGAPIpages(drive.files(), 'list', 'files',
|
||||
pageMessage=pageMessage, noFinalize=True,
|
||||
throwReasons=GAPI.DRIVE_USER_THROW_REASONS,
|
||||
retryReasons=[GAPI.UNKNOWN_ERROR],
|
||||
orderBy=OBY.orderBy, q=WITH_PARENTS.format(fileEntry['id']),
|
||||
orderBy=OBY.orderBy, q=q,
|
||||
fields='nextPageToken,files(id,name,parents,mimeType,ownedByMe,trashed,owners(emailAddress,permissionId))',
|
||||
pageSize=GC.Values[GC.DRIVE_MAX_RESULTS])
|
||||
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
|
||||
@@ -64277,13 +64285,15 @@ def _moveSharedDriveToOU(orgUnit, orgUnitId, driveId, user, i, count, ci, return
|
||||
'destinationOrgUnit': f'orgUnits/{orgUnitId[3:]}'}
|
||||
try:
|
||||
callGAPI(ci.orgUnits().memberships(), 'move',
|
||||
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.NOT_FOUND, GAPI.FORBIDDEN, GAPI.ABORTED],
|
||||
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.NOT_FOUND, GAPI.FORBIDDEN,
|
||||
GAPI.INVALID_ARGUMENT, GAPI.ABORTED],
|
||||
name=name, body=cibody)
|
||||
if not returnIdOnly:
|
||||
Act.Set(Act.MOVE)
|
||||
entityModifierNewValueActionPerformed([Ent.SHAREDDRIVE, driveId], Act.MODIFIER_TO, f'{Ent.Singular(Ent.ORGANIZATIONAL_UNIT)}: {orgUnit}', i, count)
|
||||
entityModifierNewValueActionPerformed([Ent.USER, user, Ent.SHAREDDRIVE, driveId], Act.MODIFIER_TO,
|
||||
f'{Ent.Singular(Ent.ORGANIZATIONAL_UNIT)}: {orgUnit}', i, count)
|
||||
except (GAPI.notFound, GAPI.forbidden, GAPI.aborted, GAPI.badRequest, GAPI.internalError,
|
||||
GAPI.noManageTeamDriveAdministratorPrivilege) as e:
|
||||
GAPI.noManageTeamDriveAdministratorPrivilege, GAPI.invalidArgument) as e:
|
||||
entityActionFailedWarning([Ent.USER, user, Ent.SHAREDDRIVE_ID, driveId], str(e), i, count)
|
||||
except (GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy) as e:
|
||||
userSvcNotApplicableOrDriveDisabled(user, str(e), i, count)
|
||||
@@ -74629,10 +74639,12 @@ def CAABuildLevel(body):
|
||||
body['custom'] = {'expr': {'expression': getString(Cmd.OB_STRING), 'title': 'expr'}}
|
||||
elif myarg == 'description':
|
||||
body['description'] = getString(Cmd.OB_STRING, minLen=0)
|
||||
elif myarg == 'json':
|
||||
body.update(getJSON(['name']))
|
||||
else:
|
||||
unknownArgumentExit()
|
||||
|
||||
# gam create caalevel <String> [description <String>] (basic <CAABasicAttribute>+)|(custom <String>)
|
||||
# gam create caalevel <String> [description <String>] (basic <CAABasicAttribute>+)|(custom <String>)|<JSONData>
|
||||
def doCreateCAALevel():
|
||||
caa = buildCAAServiceObject()
|
||||
ap_name = getAccessPolicy(caa)
|
||||
@@ -74652,7 +74664,7 @@ def doCreateCAALevel():
|
||||
except GAPI.permissionDenied:
|
||||
CAARoleErrorExit(caa)
|
||||
|
||||
# gam update caalevel <CAALevelName> [description <String>] (basic <CAABasicAttribute>+)|(custom <String>)
|
||||
# gam update caalevel <CAALevelName> [description <String>] (basic <CAABasicAttribute>+)|(custom <String>)|<JSONData>
|
||||
def doUpdateCAALevel():
|
||||
caa = buildCAAServiceObject()
|
||||
name = normalizeCAALevelName(caa, getString(Cmd.OB_ACCESS_LEVEL_NAME))
|
||||
|
||||
@@ -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