mirror of
https://github.com/GAM-team/GAM.git
synced 2026-06-24 16:11:36 +00:00
Updated print aliases|groups|group-members|users
Added option `verifyorganizer [<Boolean>]` to `gam <UserTypeEntity> copy|move drivefile`
This commit is contained in:
@@ -21,8 +21,12 @@
|
||||
* https://developers.google.com/admin-sdk/directory/v1/guides/search-users
|
||||
|
||||
## Definitions
|
||||
See [Collections of Items](Collections-of-Items)
|
||||
```
|
||||
<DomainName> ::= <String>(.<String>)+
|
||||
<DomainNameList> ::= "<DomainName>(,<DomainName>)*"
|
||||
<DomainNameEntity> ::=
|
||||
<DomainNameList> | <FileSelector> | <CSVFileSelector>
|
||||
<EmailAddress> ::= <String>@<DomainName>
|
||||
<EmailAddressList> ::= "<EmailAddress>(,<EmailAddress>)*"
|
||||
<EmailAddressEntity> ::= <EmailAddressList> | <FileSelector> | <CSVkmdSelector> | <CSVDataSelector>
|
||||
@@ -84,7 +88,8 @@ gam info alias|aliases <EmailAddressEntity>
|
||||
Display selected aliases.
|
||||
```
|
||||
gam print aliases [todrive <ToDriveAttribute>*]
|
||||
[domain <DomainName>] [(query <QueryUser>)|(queries <QueryUserList>)]
|
||||
([domain|domains <DomainNameEntity>] [(query <QueryUser>)|(queries <QueryUserList>)]
|
||||
[limittoou <OrgUnitItem>])
|
||||
[user|users <EmailAddressList>] [group|groups <EmailAddressList>]
|
||||
[select <UserTypeEntity>]
|
||||
[aliasmatchpattern <RegularExpression>]
|
||||
@@ -94,8 +99,9 @@ gam print aliases [todrive <ToDriveAttribute>*]
|
||||
(addcsvdata <FieldName> <String>)*
|
||||
```
|
||||
By default, group and user aliases in all domains in the account are selected; these options allow selection of subsets of aliases:
|
||||
* `domain <DomainName>` - Limit aliases to those in `<DomainName>`
|
||||
* `(query <QueryUser>)|(queries <QueryUserList>)` - Print aliases for selected users
|
||||
* `domain|domains <DomainNameEntity>` - Limit aliases to those in the domains specified by `<DomainNameEntity>`
|
||||
* `(query <QueryUser>)|(queries <QueryUserList>)` - Print aliases for users/groups that match a query; each query is run against each domain
|
||||
* `limittoou <OrgUnitItem>` - Print aliases for users in the specified `<OrgUnitItem>`
|
||||
* `user|users <EmailAddressList>` - Print aliases for users in `<EmailAddressList`
|
||||
* `select <UserTypeEntity>` - Print aliases for users in `<UserTypeEntity>`
|
||||
* `group|groups <EmailAddressList>` - Print aliases for groups in `<EmailAddressList`
|
||||
@@ -117,6 +123,21 @@ By default, the aliases in a list are separated by the `csv_output_field_delimit
|
||||
|
||||
Specifying both `onerowpertarget` and `suppressnoaliasrows` causes GAM to not display any targets that have no aliases.
|
||||
|
||||
When multiple domains are specified and a query/queries are specified, an API call is made for each domain/query combination.
|
||||
```
|
||||
$ gam print aliases domains school.org,students.school.org queries "'email:admin*','email:test*'"
|
||||
Getting all Users that match query (domain=school.org, query="email:admin*"), may take some time on a large Google Workspace Account...
|
||||
Got 3 Users: admin@school.org - admindirector@school.org
|
||||
Getting all Users that match query (domain=school.org, query="email:test*"), may take some time on a large Google Workspace Account...
|
||||
Got 20 Users: testusera@school.org - testuserx@school.org
|
||||
Getting all Users that match query (domain=students.school.org, query="email:admin*"), may take some time on a large Google Workspace Account...
|
||||
Got 1 User: admin@students.school.org - admin@students.school.org
|
||||
Getting all Users that match query (domain=students.school.org, query="email:test*"), may take some time on a large Google Workspace Account...
|
||||
Got 1 User: testuser1@students.school.org - testuser1@students.school.org
|
||||
Alias,Target,TargetType
|
||||
...
|
||||
```
|
||||
|
||||
## Bulk delete aliases
|
||||
You can bulk delete aliases as follows; use `(query <QueryUser>)|(queries <QueryUserList>)` and
|
||||
`aliasmatchpattern <RegularExpression>` as desired.
|
||||
|
||||
@@ -10,6 +10,37 @@ Add the `-s` option to the end of the above commands to suppress creating the `g
|
||||
|
||||
See [Downloads](https://github.com/taers232c/GAMADV-XTD3/wiki/Downloads) for Windows or other options, including manual installation.
|
||||
|
||||
### 6.63.14
|
||||
|
||||
Added option `verifyorganizer [<Boolean>]` to `gam <UserTypeEntity> copy|move drivefile`. When a copy/move
|
||||
operation involves a Shared Drive, GAM verifies that the user is an organizer. Unfortunatley, this fails
|
||||
when the user is not a direct organizer but is a member of a group that is an organizer. Specifying
|
||||
`verifyorganizer false` suppresses the verification.
|
||||
|
||||
Updated the following commands to be able to specify a list of domains rather than a single domain:
|
||||
```
|
||||
gam print aliases
|
||||
gam print groups
|
||||
gam print|show group-members
|
||||
gam print users
|
||||
```
|
||||
Added `print_agu_domains` variable to `gam.cfg` that provides a default list of domains for these commands.
|
||||
|
||||
When multiple domains are specified and a query/queries are specified, an API call is made for each domain/query combination.
|
||||
```
|
||||
$ gam print users domains school.org,students.school.org queries "'email:admin*','email:test*'"
|
||||
Getting all Users that match query (domain=school.org, query="email:admin*"), may take some time on a large Google Workspace Account...
|
||||
Got 3 Users: admin@school.org - admindirector@school.org
|
||||
Getting all Users that match query (domain=school.org, query="email:test*"), may take some time on a large Google Workspace Account...
|
||||
Got 20 Users: testusera@school.org - testuserx@school.org
|
||||
Getting all Users that match query (domain=students.school.org, query="email:admin*"), may take some time on a large Google Workspace Account...
|
||||
Got 1 User: admin@students.school.org - admin@students.school.org
|
||||
Getting all Users that match query (domain=students.school.org, query="email:test*"), may take some time on a large Google Workspace Account...
|
||||
Got 1 User: testuser1@students.school.org - testuser1@students.school.org
|
||||
primaryEmail
|
||||
...
|
||||
```
|
||||
|
||||
### 6.63.13
|
||||
|
||||
Updated `gam <UserTypeEntity> print filelist ... showdrivename` and `gam <UserTypeEntity> show fileinfo <DriveFileEntity> ... showdrivename`
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
* https://developers.google.com/admin-sdk/directory/v1/reference/members
|
||||
|
||||
## Definitions
|
||||
See [Collections of Items](Collections-of-Items)
|
||||
```
|
||||
<DeliverySetting> ::=
|
||||
allmail|
|
||||
@@ -27,6 +28,9 @@
|
||||
disabled|
|
||||
none|nomail
|
||||
<DomainName> ::= <String>(.<String>)+
|
||||
<DomainNameList> ::= "<DomainName>(,<DomainName>)*"
|
||||
<DomainNameEntity> ::=
|
||||
<DomainNameList> | <FileSelector> | <CSVFileSelector>
|
||||
<EmailAddress> ::= <String>@<DomainName>
|
||||
<EmailItem> ::= <EmailAddress>|<UniqueID>|<String>
|
||||
<UniqueID> ::= id:<String>
|
||||
@@ -41,6 +45,7 @@
|
||||
<GroupTypeList> ::= "<GroupType>(,<GroupType>)*"
|
||||
<QueryGroup> ::= <String>
|
||||
See: https://developers.google.com/admin-sdk/directory/v1/guides/search-groups
|
||||
<QueryGroupList> ::= "<QueryGroup>(,<QueryGroup>)*"
|
||||
|
||||
<MembersFieldName> ::=
|
||||
delivery|deliverysettings|
|
||||
@@ -563,7 +568,7 @@ gam info member|group-members <UserItem>|<UserTypeEntity> <GroupEntity>
|
||||
By default, delivery information is not displayed.
|
||||
```
|
||||
gam print group-members [todrive <ToDriveAttribute>*]
|
||||
[([domain <DomainName>] ([member|showownedby <EmailItem>]|[query <QueryGroup>]))|
|
||||
[([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryGroupList>)]))|
|
||||
(group|group_ns|group_susp <GroupItem>)|
|
||||
(select <GroupEntity>)]
|
||||
[emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
|
||||
@@ -581,10 +586,10 @@ gam print group-members [todrive <ToDriveAttribute>*]
|
||||
[formatjson [quotechar <Character>]]
|
||||
```
|
||||
By default, the group membership of all groups in the account are displayed, these options allow selection of subsets of groups:
|
||||
* `domain <DomainName>` - Limit display to groups in the domain `<DomainName>`
|
||||
* `domain|domains <DomainNameEntity>` - Limit display to groups in the domains specified by `<DomainNameEntity>`
|
||||
* `member <EmailItem>` - Limit display to groups that contain `<EmailItem>` as a member; mutually exclusive with `query <QueryGroup>`
|
||||
* `showownedby <EmailItem>` - Limit display to groups that contain `<EmailItem>` as an owner; mutually exclusive with `query <QueryGroup>`
|
||||
* `query <QueryGroup>` - Limit display to groups that match `<QueryGroup>`, matching is done at Google; mutually exclusive with `member <UserItem>`
|
||||
* `(query <QueryGroup>)|(queries <QueryGroupList>)` - Limit groups to those that match a query; each query is run against each domain
|
||||
* `group <GroupItem>` - Limit display to the single group `<GroupItem>`
|
||||
* `group_ns <GroupItem>` - Limit display to the single group `<GroupItem>`, display non-suspended members
|
||||
* `group_susp <GroupItem>` - Limit display to the single group `<GroupItem>`, display suspended members
|
||||
@@ -679,7 +684,7 @@ The `quotechar <Character>` option allows you to choose an alternate quote chara
|
||||
## Display group membership in hierarchical format
|
||||
```
|
||||
gam show group-members
|
||||
[([domain <DomainName>] ([member|showownedby <EmailItem>]|[query <QueryGroup>]))|
|
||||
[([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryGroupList>)]))|
|
||||
(group|group_ns|group_susp <GroupItem>)|
|
||||
(select <GroupEntity>)]
|
||||
[emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
|
||||
@@ -692,10 +697,10 @@ gam show group-members
|
||||
[includederivedmembership]
|
||||
```
|
||||
By default, the group membership of all groups in the account are displayed, these options allow selection of subsets of groups:
|
||||
* `domain <DomainName>` - Limit display to groups in the domain `<DomainName>`
|
||||
* `domain|domains <DomainNameEntity>` - Limit display to groups in the domains specified by `<DomainNameEntity>`
|
||||
* `member <EmailItem>` - Limit display to groups that contain `<EmailItem>` as a member; mutually exclusive with `query <QueryGroup>`
|
||||
* `showownedby <EmailItem>` - Limit display to groups that contain `<EmailItem>` as an owner; mutually exclusive with `query <QueryGroup>`
|
||||
* `query <QueryGroup>` - Limit display to groups that match `<QueryGroup>`, matching is done at Google; mutually exclusive with `member <UserItem>`
|
||||
* `(query <QueryGroup>)|(queries <QueryGroupList>)` - Limit groups to those that match a query; each query is run against each domain
|
||||
* `group <GroupItem>` - Limit display to the single group `<GroupItem>`
|
||||
* `group_ns <GroupItem>` - Limit display to the single group `<GroupItem>`, display non-suspended members
|
||||
* `group_susp <GroupItem>` - Limit display to the single group `<GroupItem>`, display suspended members
|
||||
|
||||
@@ -44,8 +44,12 @@
|
||||
* https://support.google.com/a/answer/167430
|
||||
|
||||
## Definitions
|
||||
See [Collections of Items](Collections-of-Items)
|
||||
```
|
||||
<DomainName> ::= <String>(.<String>)+
|
||||
<DomainNameList> ::= "<DomainName>(,<DomainName>)*"
|
||||
<DomainNameEntity> ::=
|
||||
<DomainNameList> | <FileSelector> | <CSVFileSelector>
|
||||
<EmailAddress> ::= <String>@<DomainName>
|
||||
<UniqueID> ::= id:<String>
|
||||
<EmailItem> ::= <EmailAddress>|<UniqueID>|<String>
|
||||
@@ -60,6 +64,7 @@
|
||||
<GroupTypeList> ::= "<GroupType>(,<GroupType>)*"
|
||||
<QueryGroup> ::= <String>
|
||||
See: https://developers.google.com/admin-sdk/directory/v1/guides/search-groups
|
||||
<QueryGroupList> ::= "<QueryGroup>(,<QueryGroup>)*"
|
||||
<QueryDynamicGroup> ::= <String>
|
||||
See: https://cloud.google.com/identity/docs/reference/rest/v1/groups#dynamicgroupquery
|
||||
|
||||
@@ -404,7 +409,7 @@ By default, Gam displays the information as an indented list of keys and values.
|
||||
This command displays information in CSV format.
|
||||
```
|
||||
gam print groups [todrive <ToDriveAttribute>*]
|
||||
[([domain <DomainName>] ([member|showownedby <EmailItem>]|[query <QueryGroup>]))|
|
||||
[([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryGroupList>)]))|
|
||||
(select <GroupEntity>)]
|
||||
[emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
|
||||
[descriptionmatchpattern [not] <RegularExpression>] (matchsetting [not] <GroupAttribute>)*
|
||||
@@ -423,12 +428,11 @@ gam print groups [todrive <ToDriveAttribute>*]
|
||||
[formatjson [quotechar <Character>]]
|
||||
```
|
||||
By default, all groups in the account are displayed, these options allow selection of subsets of groups:
|
||||
* `domain <DomainName>` - Limit display to groups in the domain `<DomainName>`
|
||||
* `domain|domains <DomainNameEntity>` - Limit display to groups in the domains specified by `<DomainNameEntity>`
|
||||
* `member <EmailItem>` - Limit display to groups that contain `<EmailItem>` as a member; mutually exclusive with `query <QueryGroup>`
|
||||
* `showownedby <EmailItem>` - Limit display to groups that contain `<EmailItem>` as an owner; mutually exclusive with `query <QueryGroup>`
|
||||
* `query <QueryGroup>` - Limit display to groups that match <QueryGroup>, matching is done at Google; mutually exclusive with `member <UserItem>`
|
||||
* `(query <QueryGroup>)|(queries <QueryGroupList>)` - Limit groups to those that match a query; each query is run against each domain
|
||||
* `select <GroupEntity>` - Limit display to the groups specified in `<GroupEntity>`
|
||||
* `showownedby <UserItem>` - Limit display to groups owned by `<UserItem>`
|
||||
|
||||
When using `query <QueryGroup>` with the `name:{PREFIX}*` query, `PREFIX` must contain at least three characters.
|
||||
|
||||
|
||||
@@ -174,6 +174,7 @@ Section: DEFAULT
|
||||
oauth2_txt = oauth2.txt ; /Users/admin/GAMConfig/oauth2.txt
|
||||
oauth2service_json = oauth2service.json ; /Users/admin/GAMConfig/oauth2service.json
|
||||
people_max_results = 100
|
||||
print_agu_domains = ''
|
||||
process_wait_limit = 0
|
||||
quick_cros_move = false
|
||||
quick_info_user = false
|
||||
@@ -330,7 +331,7 @@ writes the credentials into the file oauth2.txt.
|
||||
admin@server:/Users/admin/bin/gamadv-xtd3$ rm -f /Users/admin/GAMConfig/oauth2.txt
|
||||
admin@server:/Users/admin/bin/gamadv-xtd3$ ./gam version
|
||||
WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/admin/GAMConfig/oauth2.txt, Not Found
|
||||
GAMADV-XTD3 6.63.13 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
GAMADV-XTD3 6.63.14 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
Ross Scroggs <ross.scroggs@gmail.com>
|
||||
Python 3.10.8 64-bit final
|
||||
MacOS High Sierra 10.13.6 x86_64
|
||||
@@ -596,6 +597,7 @@ Section: DEFAULT
|
||||
oauth2_txt = oauth2.txt ; /Users/admin/GAMConfig/oauth2.txt
|
||||
oauth2service_json = oauth2service.json ; /Users/admin/GAMConfig/oauth2service.json
|
||||
people_max_results = 100
|
||||
print_agu_domains = ''
|
||||
process_wait_limit = 0
|
||||
quick_cros_move = false
|
||||
quick_info_user = False
|
||||
@@ -794,6 +796,7 @@ Section: DEFAULT
|
||||
oauth2_txt = oauth2.txt ; C:\GAMConfig\oauth2.txt
|
||||
oauth2service_json = oauth2service.json ; C:\GAMConfig\oauth2service.json
|
||||
people_max_results = 100
|
||||
print_agu_domains = ''
|
||||
process_wait_limit = 0
|
||||
quick_cros_move = false
|
||||
quick_info_user = False
|
||||
@@ -972,7 +975,7 @@ writes the credentials into the file oauth2.txt.
|
||||
C:\GAMADV-XTD3>del C:\GAMConfig\oauth2.txt
|
||||
C:\GAMADV-XTD3>gam version
|
||||
WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found
|
||||
GAMADV-XTD3 6.63.13 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
GAMADV-XTD3 6.63.14 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
Ross Scroggs <ross.scroggs@gmail.com>
|
||||
Python 3.11.5 64-bit final
|
||||
Windows-10-10.0.17134 AMD64
|
||||
@@ -1240,6 +1243,7 @@ Section: DEFAULT
|
||||
output_dateformat = ''
|
||||
output_timeformat = ''
|
||||
people_max_results = 100
|
||||
print_agu_domains = ''
|
||||
process_wait_limit = 0
|
||||
quick_cros_move = false
|
||||
quick_info_user = False
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
<QueryBrowserList> ::= "<QueryBrowser>(,<QueryBrowser>)*"
|
||||
<QueryCrOSList> ::= "<QueryCrOS>(,<QueryCrOS>)*"
|
||||
<QueryDeviceList> ::= "<QueryDevice>(,<QueryDevice>)*"
|
||||
<QueryGroupList> ::= "<QueryGroup>(,<QueryGroup>)*"
|
||||
<QueryMobileList> ::= "<QueryMobile>(,<QueryMobile>)*"
|
||||
<QueryUserList> ::= "<QueryUser>(,<QueryUser>)*"
|
||||
<ResourceIDList> ::= "<ResourceID>(,<ResourceID>)*"
|
||||
|
||||
@@ -179,6 +179,27 @@ When using the `formatjson` option, double quotes are used extensively in the da
|
||||
The `quotechar <Character>` option allows you to choose an alternate quote character, single quote for instance, that makes for readable/processable output.
|
||||
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.
|
||||
|
||||
### Display information about all chat spaces
|
||||
```
|
||||
# Local file
|
||||
gam config auto_batch_min 1 redirect csv ./AllChatSpaces.csv multiprocess redirect stdout - multiprocess redirect stderr stdout all users print chatspaces
|
||||
# Google sheet
|
||||
gam config auto_batch_min 1 redirect csv - todrive <ToDriveAttribute>* multiprocess redirect stdout - multiprocess redirect stderr stdout all users print chatspaces
|
||||
```
|
||||
Add these options as desired:
|
||||
```
|
||||
[types <ChatSpaceTypeList>]
|
||||
[formatjson [quotechar <Character>]]
|
||||
```
|
||||
By default, Gam displays the information as columns of fields; the following option causes the output to be in JSON format,
|
||||
* `formatjson` - Display the fields in JSON format.
|
||||
|
||||
By default, when writing CSV files, Gam uses a quote character of double quote `"`. The quote character is used to enclose columns that contain
|
||||
the quote character itself, the column delimiter (comma by default) and new-line characters. Any quote characters within the column are doubled.
|
||||
When using the `formatjson` option, double quotes are used extensively in the data resulting in hard to read/process output.
|
||||
The `quotechar <Character>` option allows you to choose an alternate quote character, single quote for instance, that makes for readable/processable output.
|
||||
`quotechar` defaults to `gam.cfg/csv_output_quote_char`. When uploading CSV files to Google, double quote `"` should be used.
|
||||
|
||||
## Manage Chat Members
|
||||
### Add members to a chat space
|
||||
```
|
||||
|
||||
@@ -92,6 +92,7 @@ gam <UserTypeEntity> copy drivefile <DriveFileEntity>
|
||||
[excludepermissionsfromdomains|includepermissionsfromdomains <DomainNameList>]
|
||||
(mappermissionsdomain <DomainName> <DomainName>)*
|
||||
[sendemailifrequired [<Boolean>]]
|
||||
[verifyorganizer [<Boolean>]]
|
||||
```
|
||||
The files/folders specified by `<DriveFileEntity>` are referred to as `source`, `target` refers to where those files are being copied.
|
||||
The files/folders specified by `<DriveFileEntity>` are referred to as `top`; when a folder is being copied recursively, the files/folders that it contains are referred as `sub`.
|
||||
@@ -100,6 +101,10 @@ At its simplest, you copy files/folders by giving the copy a new name and parent
|
||||
|
||||
By default, files/folders in the Trash are copied; use `excludetrashed` to prevent these files/folders from being copied.
|
||||
|
||||
When a copy operation involves a Shared Drive, GAM verifies that the user is an organizer. Unfortunatley, this fails
|
||||
when the user is not a direct organizer but is a member of a group that is an organizer. Specifying
|
||||
`verifyorganizer false` suppresses the verification.
|
||||
|
||||
When copying folders, you have three modes of operation:
|
||||
|
||||
### Copy the top folder but none of its sub files/folders
|
||||
@@ -443,12 +448,17 @@ gam <UserTypeEntity> move drivefile <DriveFileEntity> [newfilename <DriveFileNam
|
||||
[updatefilepermissions [<Boolean>]]
|
||||
[retainsourcefolders [<Boolean>]]
|
||||
[sendemailifrequired [<Boolean>]]
|
||||
[verifyorganizer [<Boolean>]]
|
||||
```
|
||||
The files/folders specified by `<DriveFileEntity>` are referred to as `source`, `target` refers to where those files are being moved.
|
||||
The files/folders specified by `<DriveFileEntity>` are referred to as `top`; when a folder is being moved, the files/folders that it contains are referred as `sub`.
|
||||
|
||||
At its simplest, you move files/folders by giving them a new name and parent location.
|
||||
|
||||
When a move operation involves a Shared Drive, GAM verifies that the user is an organizer. Unfortunatley, this fails
|
||||
when the user is not a direct organizer but is a member of a group that is an organizer. Specifying
|
||||
`verifyorganizer false` suppresses the verification.
|
||||
|
||||
When moving folders, you have two modes of operation:
|
||||
|
||||
### Move the top folder and its sub files/folders
|
||||
|
||||
@@ -80,6 +80,9 @@ queries "\"orgUnitPath='/Students/Middle School'\",\"orgUnitPath='/Students/Low
|
||||
none|nomail
|
||||
|
||||
<DomainName> ::= <String>(.<String>)+
|
||||
<DomainNameList> ::= "<DomainName>(,<DomainName>)*"
|
||||
<DomainNameEntity> ::=
|
||||
<DomainNameList> | <FileSelector> | <CSVFileSelector>
|
||||
<EmailAddress> ::=
|
||||
<String>@<DomainName> |
|
||||
<String> <<String>@<DomainName>> # The outer <> around <String>@<DomainName> are literal, e.g., IT Group<group@domain.com>
|
||||
@@ -953,8 +956,8 @@ gam print users
|
||||
See: https://developers.google.com/admin-sdk/directory/v1/guides/search-users
|
||||
```
|
||||
gam print users [todrive <ToDriveAttribute>*]
|
||||
([domain <DomainName>] [(query <QueryUser>)|(queries <QueryUserList>)]
|
||||
[limittoou <OrgUnitPath>|<OrgUnitID>] [deleted_only|only_deleted])
|
||||
([domain|domains <DomainNameEntity>] [(query <QueryUser>)|(queries <QueryUserList>)]
|
||||
[limittoou <OrgUnitItem>] [deleted_only|only_deleted])
|
||||
[orderby <UserOrderByFieldName> [ascending|descending]]
|
||||
[groups|groupsincolumns] [license|licenses|licence|licences]
|
||||
[schemas|custom|customschemas all|<SchemaNameList>]
|
||||
@@ -966,9 +969,9 @@ gam print users [todrive <ToDriveAttribute>*]
|
||||
```
|
||||
|
||||
By default, users in all domains in the account are selected; these options allow selection of subsets of users:
|
||||
* `domain <DomainName>` - Limit users to those in `<DomainName>`
|
||||
* `(query <QueryUser>)|(queries <QueryUserList>)` - Limit users to those that match a query
|
||||
* `limittoou <OrgUnitPath>|<OrgUnitID>` - Limit users to those in the specified `<OrgUnitPath>|<OrgUnitID>`
|
||||
* `domain|domains <DomainNameEntity>` - Limit users to those in the domains specified by `<DomainNameEntity>`
|
||||
* `(query <QueryUser>)|(queries <QueryUserList>)` - Limit users to those that match a query; each query is run against each domain
|
||||
* `limittoou <OrgUnitPath>|<OrgUnitID>` - Limit users to those in the specified `<OrgUnitItem>>`
|
||||
* `deleted_only|only_deleted` - Only display deleted users
|
||||
* `issuspended <Boolean>` - Limit users based on their status
|
||||
|
||||
@@ -1060,15 +1063,15 @@ Print a CSV file with headers `domain,count` that gives the number of users in e
|
||||
### Print domain counts for users in a specific domain and/or selected by a query
|
||||
```
|
||||
gam print users [todrive <ToDriveAttribute>*]
|
||||
([domain <DomainName>] [(query <QueryUser>)|(queries <QueryUserList>)]
|
||||
[limittoou <OrgUnitPath>|<OrgUnitID>] [deleted_only|only_deleted])
|
||||
([domain|domains <DomainNameEntity>] [(query <QueryUser>)|(queries <QueryUserList>)]
|
||||
[limittoou <OrgUnitItem>] [deleted_only|only_deleted])
|
||||
[formatjson [quotechar <Character>]] [countonly]
|
||||
[issuspended <Boolean>]
|
||||
```
|
||||
By default, users in all domains in the account are selected; these options allow selection of subsets of users:
|
||||
* `domain <DomainName>` - Limit users to those in `<DomainName>`
|
||||
* `(query <QueryUser>)|(queries <QueryUserList>)` - Limit users to those that match a query
|
||||
* `limittoou <OrgUnitPath>|<OrgUnitID>` - Limit users to those in the specified `<OrgUnitPath>|<OrgUnitID>`
|
||||
* `domain|domains <DomainNameEntity>` - Limit users to those in the domains specified by `<DomainNameEntity>`
|
||||
* `(query <QueryUser>)|(queries <QueryUserList>)` - Limit users to those that match a query; each query is run against each domain
|
||||
* `limittoou <OrgUnitPath>|<OrgUnitID>` - Limit users to those in the specified `<OrgUnitItem>>`
|
||||
* `deleted_only|only_deleted` - Only display deleted users
|
||||
* `issuspended <Boolean>` - Limit users based on their status
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Print the current version of Gam with details
|
||||
```
|
||||
gam version
|
||||
GAMADV-XTD3 6.63.13 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
GAMADV-XTD3 6.63.14 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
Ross Scroggs <ross.scroggs@gmail.com>
|
||||
Python 3.11.5 64-bit final
|
||||
MacOS Monterey 12.6.6 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
|
||||
GAMADV-XTD3 6.63.13 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
GAMADV-XTD3 6.63.14 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
Ross Scroggs <ross.scroggs@gmail.com>
|
||||
Python 3.11.5 64-bit final
|
||||
MacOS Monterey 12.6.6 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
|
||||
GAMADV-XTD3 6.63.13 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
GAMADV-XTD3 6.63.14 - https://github.com/taers232c/GAMADV-XTD3 - pythonsource
|
||||
Ross Scroggs <ross.scroggs@gmail.com>
|
||||
Python 3.11.5 64-bit final
|
||||
MacOS Monterey 12.6.6 x86_64
|
||||
@@ -64,7 +64,7 @@ MacOS High Sierra 10.13.6 x86_64
|
||||
Path: /Users/Admin/bin/gamadv-xtd3
|
||||
Version Check:
|
||||
Current: 5.35.08
|
||||
Latest: 6.63.13
|
||||
Latest: 6.63.14
|
||||
echo $?
|
||||
1
|
||||
```
|
||||
@@ -72,7 +72,7 @@ echo $?
|
||||
Print the current version number without details
|
||||
```
|
||||
gam version simple
|
||||
6.63.13
|
||||
6.63.14
|
||||
```
|
||||
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 6.63.13 - https://github.com/taers232c/GAMADV-XTD3
|
||||
GAM 6.63.14 - https://github.com/taers232c/GAMADV-XTD3
|
||||
Ross Scroggs <ross.scroggs@gmail.com>
|
||||
Python 3.11.5 64-bit final
|
||||
MacOS Monterey 12.6.6 x86_64
|
||||
|
||||
@@ -401,6 +401,13 @@ people_max_results
|
||||
how many should be retrieved in each API call
|
||||
Default: 100
|
||||
Range: 1 - 1000
|
||||
print_agu_domains
|
||||
A comma separated list of domain names that are used in these commands:
|
||||
gam print aliases
|
||||
gam print groups
|
||||
gam print|show group-members
|
||||
gam print users
|
||||
Default: Blank
|
||||
process_wait_limit
|
||||
When processing batch/CSV files, how long (in seconds) GAM should wait for all batch|csv processes to complete
|
||||
after all have been started. If the limit is reached, GAM terminates any remaining processes.
|
||||
@@ -633,6 +640,7 @@ Section: DEFAULT
|
||||
output_dateformat = ''
|
||||
output_timeformat = ''
|
||||
people_max_results = 100
|
||||
print_agu_domains = ''
|
||||
process_wait_limit = 0
|
||||
quick_cros_move = false
|
||||
quick_info_user = false
|
||||
@@ -814,6 +822,7 @@ oauth2service_json = oauth2service.json
|
||||
output_dateformat = ''
|
||||
output_timeformat = ''
|
||||
people_max_results = 100
|
||||
print_agu_domains = ''
|
||||
process_wait_limit = 0
|
||||
quick_cros_move = False
|
||||
quick_info_user = False
|
||||
|
||||
@@ -700,6 +700,7 @@ If an item contains spaces, it should be surrounded by ".
|
||||
<QueryBrowserList> ::= "<QueryBrowser>(,<QueryBrowser>)*"
|
||||
<QueryCrOSList> ::= "<QueryCrOS>(,<QueryCrOS>)*"
|
||||
<QueryDeviceList> ::= "<QueryDevice>(,<QueryDevice>)*"
|
||||
<QueryGroupList> ::= "<QueryGroup>(,<QueryGroup>)*"
|
||||
<QueryMobileList> ::= "<QueryMobile>(,<QueryMobile>)*"
|
||||
<QueryUserList> ::= "<QueryUser>(,<QueryUser>)*"
|
||||
<ResourceIDList> ::= "<ResourceID>(,<ResourceID>)*"
|
||||
@@ -1453,7 +1454,8 @@ gam <UserTypeEntity> delete alias|aliases
|
||||
gam info alias|aliases <EmailAddressEntity>
|
||||
|
||||
gam print alias|aliases [todrive <ToDriveAttribute>*]
|
||||
[domain <DomainName>] [(query <QueryUser>)|(queries <QueryUserList>)]
|
||||
([domain|domains <DomainNameEntity>] [(query <QueryUser>)|(queries <QueryUserList>)]
|
||||
[limittoou <OrgUnitItem>])
|
||||
[user|users <EmailAddressList>] [group|groups <EmailAddressList>]
|
||||
[select <UserTypeEntity>]
|
||||
[aliasmatchpattern <RegularExpression>]
|
||||
@@ -3555,7 +3557,7 @@ gam info group|groups <GroupEntity>
|
||||
[memberemaildisplaypattern|memberemailskippattern <RegularExpression>]
|
||||
[formatjson]
|
||||
gam print groups [todrive <ToDriveAttribute>*]
|
||||
[([domain <DomainName>] ([member|showownedby <EmailItem>]|[query <QueryGroup>]))|
|
||||
[([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryGroupList>)]))|
|
||||
(select <GroupEntity>)]
|
||||
[emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
|
||||
[descriptionmatchpattern [not] <RegularExpression>] (matchsetting [not] <GroupAttribute>)*
|
||||
@@ -3591,7 +3593,7 @@ gam show grouptree <GroupEntity>
|
||||
gam <UserTypeEntity> info member|group-members <GroupEntity>
|
||||
gam info member|group-members <UserItem>|<UserTypeEntity> <GroupEntity>
|
||||
gam print group-members [todrive <ToDriveAttribute>*]
|
||||
[([domain <DomainName>] ([member|showownedby <EmailItem>]|[query <QueryGroup>]))|
|
||||
[([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryGroupList>)]))|
|
||||
(group|group_ns|group_susp <GroupItem>)|
|
||||
(select <GroupEntity>)]
|
||||
[emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
|
||||
@@ -3608,7 +3610,7 @@ gam print group-members [todrive <ToDriveAttribute>*]
|
||||
[peoplelookup|(peoplelookupuser <EmailAddress>)]
|
||||
[formatjson [quotechar <Character>]]
|
||||
gam show group-members
|
||||
[([domain <DomainName>] ([member|showownedby <EmailItem>]|[query <QueryGroup>]))|
|
||||
[([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryGroupList>)]))|
|
||||
(group|group_ns|group_susp <GroupItem>)|
|
||||
(select <GroupEntity>)]
|
||||
[emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
|
||||
@@ -5262,7 +5264,7 @@ Print fields for selected users; use these options to select users:
|
||||
If none of these options are chosen, all users are selected.
|
||||
|
||||
gam print users [todrive <ToDriveAttribute>*]
|
||||
([domain <DomainName>] [(query <QueryUser>)|(queries <QueryUserList>)]
|
||||
([domain|domains <DomainNameEntity>] [(query <QueryUser>)|(queries <QueryUserList>)]
|
||||
[limittoou <OrgUnitItem>] [deleted_only|only_deleted])
|
||||
[orderby <UserOrderByFieldName> [ascending|descending]]
|
||||
[groups|groupsincolumns]
|
||||
@@ -5310,12 +5312,12 @@ gam <UserTypeEntity> print
|
||||
gam <UserTypeEntity> print users
|
||||
|
||||
Print user domain counts for selected users; use these options to select users:
|
||||
([domain <DomainName>] [(query <QueryUser>)|(queries <QueryUserList>)]
|
||||
([domain|domains <DomainNameEntity>] [(query <QueryUser>)|(queries <QueryUserList>)]
|
||||
[limittoou <OrgUnitItem>] [deleted_only|only_deleted])
|
||||
If none of these options are chosen, all users are selected.
|
||||
|
||||
gam print users [todrive <ToDriveAttribute>*]
|
||||
([domain <DomainName>] [(query <QueryUser>)|(queries <QueryUserList>)]
|
||||
([domain|domains <DomainNameEntity>] [(query <QueryUser>)|(queries <QueryUserList>)]
|
||||
[limittoou <OrgUnitItem>] [deleted_only|only_deleted])
|
||||
[formatjson [quotechar <Character>]] [countsonly|countonly]
|
||||
[issuspended <Boolean>]
|
||||
@@ -6015,6 +6017,7 @@ gam <UserTypeEntity> copy drivefile <DriveFileEntity>
|
||||
[copysheetprotectedrangesnoninheritedpermissions [<Boolean>]]
|
||||
[sendemailifrequired [<Boolean>]]
|
||||
[suppressnotselectedmessages [<Boolean>]]
|
||||
[verifyorganizer [<Boolean>]]
|
||||
|
||||
gam <UserTypeEntity> move drivefile <DriveFileEntity> [newfilename <DriveFileName>]
|
||||
[summary [<Boolean>]] [showpermissionmessages [<Boolean>]]
|
||||
@@ -6037,6 +6040,7 @@ gam <UserTypeEntity> move drivefile <DriveFileEntity> [newfilename <DriveFileNam
|
||||
[updatefilepermissions [<Boolean>]]
|
||||
[retainsourcefolders [<Boolean>]]
|
||||
[sendemailifrequired [<Boolean>]]
|
||||
[verifyorganizer [<Boolean>]]
|
||||
|
||||
gam <UserTypeEntity> get document <DriveFileEntity>
|
||||
[viewmode default|suggestions_inline|preview_suggestions_accepted|preview_without_suggestions]
|
||||
|
||||
@@ -2,6 +2,37 @@
|
||||
|
||||
Merged GAM-Team version
|
||||
|
||||
6.63.14
|
||||
|
||||
Added option `verifyorganizer [<Boolean>]` to `gam <UserTypeEntity> copy|move drivefile`. When a copy/move
|
||||
operation involves a Shared Drive, GAM verifies that the user is an organizer. Unfortunatley, this fails
|
||||
when the user is not a direct organizer but is a member of a group that is an organizer. Specifying
|
||||
`verifyorganizer false` suppresses the verification.
|
||||
|
||||
Updated the following commands to be able to specify a list of domains rather than a single domain:
|
||||
```
|
||||
gam print alias|aliases
|
||||
gam print groups
|
||||
gam print|show group-members
|
||||
gam print users
|
||||
```
|
||||
Added `print_agu_domains` variable to `gam.cfg` that provides a default list of domains for these commands.
|
||||
|
||||
When multiple domains are specified and a query/queries are specified, an API call is made for each domain/query combination.
|
||||
```
|
||||
$ gam print users domains school.org,students.school.org queries "'email:admin*','email:test*'"
|
||||
Getting all Users that match query (domain=school.org, query="email:admin*"), may take some time on a large Google Workspace Account...
|
||||
Got 3 Users: admin@school.org - admindirector@school.org
|
||||
Getting all Users that match query (domain=school.org, query="email:test*"), may take some time on a large Google Workspace Account...
|
||||
Got 20 Users: testusera@school.org - testuserx@school.org
|
||||
Getting all Users that match query (domain=students.school.org, query="email:admin*"), may take some time on a large Google Workspace Account...
|
||||
Got 1 User: admin@students.school.org - admin@students.school.org
|
||||
Getting all Users that match query (domain=students.school.org, query="email:test*"), may take some time on a large Google Workspace Account...
|
||||
Got 1 User: testuser1@students.school.org - testuser1@students.school.org
|
||||
primaryEmail
|
||||
...
|
||||
```
|
||||
|
||||
6.63.13
|
||||
|
||||
Updated `gam <UserTypeEntity> print filelist ... showdrivename` and `gam <UserTypeEntity> show fileinfo <DriveFileEntity> ... showdrivename`
|
||||
|
||||
@@ -2375,12 +2375,9 @@ def emptyQuery(query, entityType):
|
||||
def invalidQuery(query):
|
||||
return f'{Ent.Singular(Ent.QUERY)} ({query}) {Msg.INVALID}'
|
||||
|
||||
def invalidMember(kwargs):
|
||||
if 'userKey' in kwargs:
|
||||
badRequestWarning(Ent.GROUP, Ent.MEMBER, kwargs['userKey'])
|
||||
return True
|
||||
if 'query' in kwargs:
|
||||
badRequestWarning(Ent.GROUP, Ent.QUERY, invalidQuery(kwargs['query']))
|
||||
def invalidMember(query):
|
||||
if query:
|
||||
badRequestWarning(Ent.GROUP, Ent.QUERY, invalidQuery(query))
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -4121,6 +4118,8 @@ def SetGlobalVariables():
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_DROP_FILTER_MODE] = GM.Globals[GM.CSV_OUTPUT_ROW_DROP_FILTER_MODE]
|
||||
if not GC.Values[GC.CSV_OUTPUT_ROW_LIMIT]:
|
||||
GC.Values[GC.CSV_OUTPUT_ROW_LIMIT] = GM.Globals[GM.CSV_OUTPUT_ROW_LIMIT]
|
||||
if not GC.Values[GC.PRINT_AGU_DOMAINS]:
|
||||
GC.Values[GC.PRINT_AGU_DOMAINS] = GM.Globals[GM.PRINT_AGU_DOMAINS]
|
||||
# customer_id, domain and admin_email must be set when enable_dasa = true
|
||||
if GC.Values[GC.ENABLE_DASA]:
|
||||
errors = 0
|
||||
@@ -9300,7 +9299,7 @@ def terminateStdQueueHandler(mpQueue, mpQueueHandler):
|
||||
mpQueueHandler.join()
|
||||
|
||||
def ProcessGAMCommandMulti(pid, numItems, logCmd, mpQueueCSVFile, mpQueueStdout, mpQueueStderr,
|
||||
debugLevel, todrive,
|
||||
debugLevel, todrive, printAguDomains,
|
||||
output_dateformat, output_timeformat,
|
||||
csvColumnDelimiter, csvQuoteChar,
|
||||
csvTimestampColumn,
|
||||
@@ -9339,6 +9338,7 @@ 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.SAVED_STDOUT] = None
|
||||
GM.Globals[GM.SYSEXITRC] = 0
|
||||
if mpQueueCSVFile:
|
||||
@@ -9508,6 +9508,7 @@ def MultiprocessGAMCommands(items, showCmds):
|
||||
poolProcessResults[pid] = pool.apply_async(ProcessGAMCommandMulti,
|
||||
[pid, numItems, logCmd, mpQueueCSVFile, mpQueueStdout, mpQueueStderr,
|
||||
GC.Values[GC.DEBUG_LEVEL], GM.Globals[GM.CSV_TODRIVE],
|
||||
GC.Values[GC.PRINT_AGU_DOMAINS],
|
||||
GC.Values[GC.OUTPUT_DATEFORMAT], GC.Values[GC.OUTPUT_TIMEFORMAT],
|
||||
GC.Values[GC.CSV_OUTPUT_COLUMN_DELIMITER],
|
||||
GC.Values[GC.CSV_OUTPUT_QUOTE_CHAR],
|
||||
@@ -17452,11 +17453,58 @@ def infoAliases(entityList):
|
||||
def doInfoAliases():
|
||||
infoAliases(getEntityList(Cmd.OB_EMAIL_ADDRESS_ENTITY))
|
||||
|
||||
def initUserGroupDomainQueryFilters():
|
||||
if not GC.Values[GC.PRINT_AGU_DOMAINS]:
|
||||
return {'list': [{'customer': GC.Values[GC.CUSTOMER_ID]}], 'queries': [None]}
|
||||
return {'list': [{'domain': domain.lower()} for domain in GC.Values[GC.PRINT_AGU_DOMAINS].replace(',', ' ').split()], 'queries': [None]}
|
||||
|
||||
def getUserGroupDomainQueryFilters(myarg, kwargsDict):
|
||||
if myarg in {'domain', 'domains'}:
|
||||
kwargsDict['list'] = [{'domain': domain.lower()} for domain in getEntityList(Cmd.OB_DOMAIN_NAME_ENTITY)]
|
||||
elif myarg in {'query', 'queries'}:
|
||||
kwargsDict['queries'] = getQueries(myarg)
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
def makeUserGroupDomainQueryFilters(kwargsDict):
|
||||
kwargsQueries = []
|
||||
for kwargs in kwargsDict['list']:
|
||||
for query in kwargsDict['queries']:
|
||||
kwargsQueries.append((kwargs, query))
|
||||
return kwargsQueries
|
||||
|
||||
def userFilters(kwargs, query, orgUnitPath, isSuspended):
|
||||
queryTitle = ''
|
||||
if kwargs.get('domain'):
|
||||
queryTitle += f'domain={kwargs["domain"]}, '
|
||||
if orgUnitPath is not None:
|
||||
if query is not None and query.find(orgUnitPath) == -1:
|
||||
query += f" orgUnitPath='{orgUnitPath}'"
|
||||
else:
|
||||
if query is None:
|
||||
query = ''
|
||||
else:
|
||||
query += ' '
|
||||
query += f"orgUnitPath='{orgUnitPath}'"
|
||||
if isSuspended is not None:
|
||||
if query is None:
|
||||
query = ''
|
||||
else:
|
||||
query += ' '
|
||||
query += f'isSuspended={isSuspended}'
|
||||
if query is not None:
|
||||
queryTitle += f'query="{query}", '
|
||||
if queryTitle:
|
||||
return query, queryTitle[:-2]
|
||||
return query, queryTitle
|
||||
|
||||
# gam print aliases|nicknames [todrive <ToDriveAttribute>*]
|
||||
# [domain <DomainName>] [(query <QueryUser>)|(queries <QueryUserList>)]
|
||||
# ([domain|domains <DomainNameEntity>] [(query <QueryUser>)|(queries <QueryUserList>)]
|
||||
# [limittoou <OrgUnitItem>])
|
||||
# [user|users <EmailAddressList>] [group|groups <EmailAddressList>]
|
||||
# [select <UserTypeEntity>]
|
||||
# [aliasmatchpattern <RegularExpression>]
|
||||
# [issuspended <Boolean>] [aliasmatchpattern <RegularExpression>]
|
||||
# [shownoneditable] [nogroups] [nousers]
|
||||
# [onerowpertarget] [delimiter <Character>]
|
||||
# [suppressnoaliasrows]
|
||||
@@ -17497,12 +17545,12 @@ def doPrintAliases():
|
||||
userFields = ['primaryEmail', 'aliases']
|
||||
groupFields = ['email', 'aliases']
|
||||
oneRowPerTarget = showNonEditable = suppressNoAliasRows = False
|
||||
kwargs = {'customer': GC.Values[GC.CUSTOMER_ID]}
|
||||
queries = [None]
|
||||
kwargsDict = initUserGroupDomainQueryFilters()
|
||||
getGroups = getUsers = True
|
||||
groups = []
|
||||
users = []
|
||||
aliasMatchPattern = re.compile(r'^.*$')
|
||||
isSuspended = orgUnitPath = None
|
||||
addCSVData = {}
|
||||
delimiter = GC.Values[GC.CSV_OUTPUT_FIELD_DELIMITER]
|
||||
while Cmd.ArgumentsRemaining():
|
||||
@@ -17517,15 +17565,17 @@ def doPrintAliases():
|
||||
getGroups = False
|
||||
elif myarg == 'nousers':
|
||||
getUsers = False
|
||||
elif myarg == 'domain':
|
||||
kwargs['domain'] = getString(Cmd.OB_DOMAIN_NAME).lower()
|
||||
kwargs.pop('customer', None)
|
||||
elif myarg in {'query', 'queries'}:
|
||||
queries = getQueries(myarg)
|
||||
elif myarg == 'limittoou':
|
||||
orgUnitPath = getOrgUnitItem(pathOnly=True, cd=cd)
|
||||
orgUnitPathLower = orgUnitPath.lower()
|
||||
userFields.append('orgUnitPath')
|
||||
getGroups = False
|
||||
getUsers = True
|
||||
elif getUserGroupDomainQueryFilters(myarg, kwargsDict):
|
||||
pass
|
||||
elif myarg == 'select':
|
||||
_, users = getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS)
|
||||
elif myarg == 'issuspended':
|
||||
isSuspended = getBoolean()
|
||||
elif myarg in {'user','users'}:
|
||||
users.extend(convertEntityToList(getString(Cmd.OB_EMAIL_ADDRESS_LIST, minLen=0)))
|
||||
elif myarg in {'group', 'groups'}:
|
||||
@@ -17543,7 +17593,7 @@ def doPrintAliases():
|
||||
delimiter = getCharacter()
|
||||
else:
|
||||
unknownArgumentExit()
|
||||
if users or groups and queries[0] is None:
|
||||
if (users or groups) and kwargsDict['queries'][0] is None:
|
||||
getUsers = getGroups = False
|
||||
if not oneRowPerTarget:
|
||||
titlesList = ['Alias', 'Target', 'TargetType']
|
||||
@@ -17557,8 +17607,11 @@ def doPrintAliases():
|
||||
if addCSVData:
|
||||
csvPF.AddTitles(sorted(addCSVData.keys()))
|
||||
if getUsers:
|
||||
for query in queries:
|
||||
printGettingAllAccountEntities(Ent.USER, query)
|
||||
for kwargsQuery in makeUserGroupDomainQueryFilters(kwargsDict):
|
||||
kwargs = kwargsQuery[0]
|
||||
query = kwargsQuery[1]
|
||||
query, pquery = userFilters(kwargs, query, orgUnitPath, isSuspended)
|
||||
printGettingAllAccountEntities(Ent.USER, pquery)
|
||||
try:
|
||||
entityList = callGAPIpages(cd.users(), 'list', 'users',
|
||||
pageMessage=getPageMessage(showFirstLastItems=True), messageAttribute='primaryEmail',
|
||||
@@ -17568,13 +17621,14 @@ def doPrintAliases():
|
||||
fields=f'nextPageToken,users({",".join(userFields)})',
|
||||
maxResults=GC.Values[GC.USER_MAX_RESULTS], **kwargs)
|
||||
for user in entityList:
|
||||
writeAliases(user, user['primaryEmail'], 'User')
|
||||
if orgUnitPath is None or orgUnitPathLower == user.get('orgUnitPath', '').lower():
|
||||
writeAliases(user, user['primaryEmail'], 'User')
|
||||
except (GAPI.invalidOrgunit, GAPI.invalidInput):
|
||||
entityActionFailedWarning([Ent.ALIAS, None], invalidQuery(query))
|
||||
return
|
||||
continue
|
||||
except GAPI.domainNotFound as e :
|
||||
entityActionFailedWarning([Ent.ALIAS, None, Ent.DOMAIN, kwargs['domain']], str(e))
|
||||
return
|
||||
continue
|
||||
except (GAPI.resourceNotFound, GAPI.forbidden, GAPI.badRequest):
|
||||
accessErrorExit(cd)
|
||||
count = len(users)
|
||||
@@ -17593,19 +17647,24 @@ def doPrintAliases():
|
||||
except (GAPI.userNotFound, GAPI.badRequest, GAPI.invalid, GAPI.forbidden, GAPI.invalidResource, GAPI.conditionNotMet) as e:
|
||||
entityActionFailedWarning([Ent.USER, user], str(e), i, count)
|
||||
if getGroups:
|
||||
printGettingAllAccountEntities(Ent.GROUP)
|
||||
try:
|
||||
entityList = callGAPIpages(cd.groups(), 'list', 'groups',
|
||||
pageMessage=getPageMessage(showFirstLastItems=True), messageAttribute='email',
|
||||
throwReasons=GAPI.GROUP_LIST_THROW_REASONS,
|
||||
orderBy='email', fields=f'nextPageToken,groups({",".join(groupFields)})', **kwargs)
|
||||
for group in entityList:
|
||||
writeAliases(group, group['email'], 'Group')
|
||||
except GAPI.domainNotFound as e :
|
||||
entityActionFailedWarning([Ent.ALIAS, None, Ent.DOMAIN, kwargs['domain']], str(e))
|
||||
return
|
||||
except (GAPI.resourceNotFound, GAPI.forbidden, GAPI.badRequest):
|
||||
accessErrorExit(cd)
|
||||
for kwargsQuery in makeUserGroupDomainQueryFilters(kwargsDict):
|
||||
kwargs = kwargsQuery[0]
|
||||
query = kwargsQuery[1]
|
||||
query, pquery = groupFilters(kwargs, query)
|
||||
printGettingAllAccountEntities(Ent.GROUP, pquery)
|
||||
try:
|
||||
entityList = callGAPIpages(cd.groups(), 'list', 'groups',
|
||||
pageMessage=getPageMessage(showFirstLastItems=True), messageAttribute='email',
|
||||
throwReasons=GAPI.GROUP_LIST_THROW_REASONS,
|
||||
query=query, orderBy='email',
|
||||
fields=f'nextPageToken,groups({",".join(groupFields)})', **kwargs)
|
||||
for group in entityList:
|
||||
writeAliases(group, group['email'], 'Group')
|
||||
except GAPI.domainNotFound as e :
|
||||
entityActionFailedWarning([Ent.ALIAS, None, Ent.DOMAIN, kwargs['domain']], str(e))
|
||||
continue
|
||||
except (GAPI.resourceNotFound, GAPI.forbidden, GAPI.badRequest):
|
||||
accessErrorExit(cd)
|
||||
count = len(groups)
|
||||
i = 0
|
||||
for group in groups:
|
||||
@@ -30442,40 +30501,32 @@ def infoGroups(entityList):
|
||||
def doInfoGroups():
|
||||
infoGroups(getEntityList(Cmd.OB_GROUP_ENTITY))
|
||||
|
||||
def groupFilters(kwargs):
|
||||
def groupFilters(kwargs, query):
|
||||
queryTitle = ''
|
||||
if kwargs.get('domain'):
|
||||
queryTitle += f'{Ent.Singular(Ent.DOMAIN)}={kwargs["domain"]}, '
|
||||
if kwargs.get('userKey'):
|
||||
queryTitle += f'{Ent.Singular(Ent.MEMBER)}={kwargs["userKey"]}, '
|
||||
if kwargs.get('query'):
|
||||
queryTitle += f'query="{kwargs["query"]}", '
|
||||
queryTitle += f'domain={kwargs["domain"]}, '
|
||||
if query is not None:
|
||||
queryTitle += f'query="{query}", '
|
||||
if queryTitle:
|
||||
return queryTitle[:-2]
|
||||
return queryTitle
|
||||
return query, queryTitle[:-2]
|
||||
return query, queryTitle
|
||||
|
||||
def getGroupFilters(myarg, kwargs, showOwnedBy):
|
||||
if myarg == 'domain':
|
||||
kwargs['domain'] = getString(Cmd.OB_DOMAIN_NAME).lower()
|
||||
kwargs.pop('customer', None)
|
||||
def getGroupFilters(myarg, kwargsDict, showOwnedBy):
|
||||
if getUserGroupDomainQueryFilters(myarg, kwargsDict):
|
||||
pass
|
||||
elif myarg in {'member', 'showownedby'}:
|
||||
emailAddressOrUID = getEmailAddress()
|
||||
if emailAddressOrUID != GC.Values[GC.CUSTOMER_ID].lower():
|
||||
kwargs['userKey'] = emailAddressOrUID
|
||||
kwargs.pop('customer', None)
|
||||
kwargsDict['queries'] = [f'memberKey={emailAddressOrUID}']
|
||||
key = 'email' if emailAddressOrUID.find('@') != -1 else 'id'
|
||||
else:
|
||||
kwargs['query'] = f'memberKey={GC.Values[GC.CUSTOMER_ID]}'
|
||||
kwargsDict['queries'] = [f'memberKey={GC.Values[GC.CUSTOMER_ID]}']
|
||||
key = 'id'
|
||||
if myarg == 'showownedby':
|
||||
showOwnedBy['key'] = key
|
||||
showOwnedBy['value'] = emailAddressOrUID
|
||||
elif myarg == 'query':
|
||||
kwargs['query'] = getString(Cmd.OB_QUERY)
|
||||
else:
|
||||
return False
|
||||
if kwargs.get('userKey') and kwargs.get('query'):
|
||||
usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format('member', 'query'))
|
||||
return True
|
||||
|
||||
def checkGroupShowOwnedBy(showOwnedBy, members):
|
||||
@@ -30669,7 +30720,7 @@ def addMemberInfoToRow(row, groupMembers, typesSet, memberOptions, memberDisplay
|
||||
PRINT_GROUPS_JSON_TITLES = ['email', 'JSON']
|
||||
|
||||
# gam print groups [todrive <ToDriveAttribute>*]
|
||||
# [([domain <DomainName>] ([member|showownedby <EmailItem>]|[query <QueryGroup>]))|
|
||||
# [([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryUserList>)]))|
|
||||
# (select <GroupEntity>)]
|
||||
# [emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
|
||||
# [descriptionmatchpattern [not] <RegularExpression>] (matchsetting [not] <GroupAttribute>)*
|
||||
@@ -30849,7 +30900,7 @@ def doPrintGroups():
|
||||
|
||||
cd = buildGAPIObject(API.DIRECTORY)
|
||||
ci = None
|
||||
kwargs = {'customer': GC.Values[GC.CUSTOMER_ID]}
|
||||
kwargsDict = initUserGroupDomainQueryFilters()
|
||||
convertCRNL = GC.Values[GC.CSV_OUTPUT_CONVERT_CR_NL]
|
||||
delimiter = GC.Values[GC.CSV_OUTPUT_FIELD_DELIMITER]
|
||||
getCloudIdentity = getSettings = showCIgroupKey = sortHeaders = False
|
||||
@@ -30871,7 +30922,7 @@ def doPrintGroups():
|
||||
myarg = getArgument()
|
||||
if myarg == 'todrive':
|
||||
csvPF.GetTodriveParameters()
|
||||
elif getGroupFilters(myarg, kwargs, showOwnedBy):
|
||||
elif getGroupFilters(myarg, kwargsDict, showOwnedBy):
|
||||
pass
|
||||
elif getGroupMatchPatterns(myarg, matchPatterns, False):
|
||||
pass
|
||||
@@ -30992,40 +31043,43 @@ def doPrintGroups():
|
||||
if getCloudIdentity:
|
||||
csvPF.AddJSONTitle('JSON-cloudIdentity')
|
||||
if entitySelection is None:
|
||||
printGettingAllAccountEntities(Ent.GROUP, groupFilters(kwargs))
|
||||
try:
|
||||
entityList = callGAPIpages(cd.groups(), 'list', 'groups',
|
||||
pageMessage=getPageMessage(showFirstLastItems=True), messageAttribute='email',
|
||||
throwReasons=GAPI.GROUP_LIST_USERKEY_THROW_REASONS,
|
||||
orderBy='email', fields=cdfieldsnp, maxResults=maxResults, **kwargs)
|
||||
except (GAPI.invalidMember, GAPI.invalidInput) as e:
|
||||
if not invalidMember(kwargs):
|
||||
entityActionFailedExit([Ent.GROUP, None], str(e))
|
||||
entityList = []
|
||||
except (GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.forbidden, GAPI.badRequest):
|
||||
if kwargs.get('domain'):
|
||||
badRequestWarning(Ent.GROUP, Ent.DOMAIN, kwargs['domain'])
|
||||
entityList = []
|
||||
else:
|
||||
accessErrorExit(cd)
|
||||
if getCloudIdentity:
|
||||
printGettingAllAccountEntities(Ent.CLOUD_IDENTITY_GROUP)
|
||||
entityList = []
|
||||
for kwargsQuery in makeUserGroupDomainQueryFilters(kwargsDict):
|
||||
kwargs = kwargsQuery[0]
|
||||
query = kwargsQuery[1]
|
||||
query, pquery = groupFilters(kwargs, query)
|
||||
printGettingAllAccountEntities(Ent.GROUP, pquery)
|
||||
try:
|
||||
ciGroupList = callGAPIpages(ci.groups(), 'list', 'groups',
|
||||
pageMessage=getPageMessage(showFirstLastItems=True), messageAttribute=['groupKey', 'id'],
|
||||
throwReasons=GAPI.CIGROUP_LIST_THROW_REASONS, retryReasons=GAPI.CIGROUP_RETRY_REASONS,
|
||||
parent=f'customers/{GC.Values[GC.CUSTOMER_ID]}', view='FULL',
|
||||
fields=cifieldsnp, pageSize=500)
|
||||
except (GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.domainCannotUseApis,
|
||||
GAPI.forbidden, GAPI.badRequest, GAPI.invalid, GAPI.invalidArgument,
|
||||
GAPI.systemError, GAPI.permissionDenied, GAPI.serviceNotAvailable) as e:
|
||||
accessErrorExitNonDirectory(API.CLOUDIDENTITY_GROUPS, str(e))
|
||||
for ciGroup in ciGroupList:
|
||||
key = ciGroup['groupKey']['id']
|
||||
if not showCIgroupKey:
|
||||
ciGroup.pop('groupKey')
|
||||
ciGroups[key] = ciGroup.copy()
|
||||
del ciGroupList
|
||||
entityList.extend(callGAPIpages(cd.groups(), 'list', 'groups',
|
||||
pageMessage=getPageMessage(showFirstLastItems=True), messageAttribute='email',
|
||||
throwReasons=GAPI.GROUP_LIST_USERKEY_THROW_REASONS,
|
||||
orderBy='email', query=query, fields=cdfieldsnp, maxResults=maxResults, **kwargs))
|
||||
except (GAPI.invalidMember, GAPI.invalidInput) as e:
|
||||
if not invalidMember(query):
|
||||
entityActionFailedExit([Ent.GROUP, None], str(e))
|
||||
except (GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.forbidden, GAPI.badRequest):
|
||||
if kwargs.get('domain'):
|
||||
badRequestWarning(Ent.GROUP, Ent.DOMAIN, kwargs['domain'])
|
||||
else:
|
||||
accessErrorExit(cd)
|
||||
if getCloudIdentity:
|
||||
printGettingAllAccountEntities(Ent.CLOUD_IDENTITY_GROUP)
|
||||
try:
|
||||
ciGroupList = callGAPIpages(ci.groups(), 'list', 'groups',
|
||||
pageMessage=getPageMessage(showFirstLastItems=True), messageAttribute=['groupKey', 'id'],
|
||||
throwReasons=GAPI.CIGROUP_LIST_THROW_REASONS, retryReasons=GAPI.CIGROUP_RETRY_REASONS,
|
||||
parent=f'customers/{GC.Values[GC.CUSTOMER_ID]}', view='FULL',
|
||||
fields=cifieldsnp, pageSize=500)
|
||||
except (GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.domainCannotUseApis,
|
||||
GAPI.forbidden, GAPI.badRequest, GAPI.invalid, GAPI.invalidArgument,
|
||||
GAPI.systemError, GAPI.permissionDenied, GAPI.serviceNotAvailable) as e:
|
||||
accessErrorExitNonDirectory(API.CLOUDIDENTITY_GROUPS, str(e))
|
||||
for ciGroup in ciGroupList:
|
||||
key = ciGroup['groupKey']['id']
|
||||
if not showCIgroupKey:
|
||||
ciGroup.pop('groupKey')
|
||||
ciGroups[key] = ciGroup.copy()
|
||||
del ciGroupList
|
||||
else:
|
||||
svcargs = dict([('groupKey', None), ('fields', cdfields)]+GM.Globals[GM.EXTRA_ARGS_LIST])
|
||||
cdmethod = getattr(cd.groups(), 'get')
|
||||
@@ -31219,26 +31273,28 @@ def infoGroupMembers(entityList, ciGroupsAPI=False):
|
||||
def doInfoGroupMembers():
|
||||
infoGroupMembers(getEntityToModify(defaultEntityType=Cmd.ENTITY_USERS)[1], False)
|
||||
|
||||
def getGroupMembersEntityList(cd, entityList, matchPatterns, fieldsList, kwargs):
|
||||
def getGroupMembersEntityList(cd, entityList, matchPatterns, fieldsList, kwargsDict):
|
||||
if entityList is None:
|
||||
updateFieldsForGroupMatchPatterns(matchPatterns, fieldsList)
|
||||
subTitle = groupFilters(kwargs)
|
||||
printGettingAllAccountEntities(Ent.GROUP, subTitle)
|
||||
try:
|
||||
entityList = callGAPIpages(cd.groups(), 'list', 'groups',
|
||||
pageMessage=getPageMessage(showFirstLastItems=True), messageAttribute='email',
|
||||
throwReasons=GAPI.GROUP_LIST_USERKEY_THROW_REASONS,
|
||||
orderBy='email', fields=f'nextPageToken,groups({",".join(set(fieldsList))})', **kwargs)
|
||||
except (GAPI.invalidMember, GAPI.invalidInput) as e:
|
||||
if not invalidMember(kwargs):
|
||||
entityActionFailedExit([Ent.GROUP, None], str(e))
|
||||
entityList = []
|
||||
except (GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.forbidden, GAPI.badRequest):
|
||||
if kwargs.get('domain'):
|
||||
badRequestWarning(Ent.GROUP, Ent.DOMAIN, kwargs['domain'])
|
||||
entityList = []
|
||||
else:
|
||||
accessErrorExit(cd)
|
||||
entityList = []
|
||||
for kwargsQuery in makeUserGroupDomainQueryFilters(kwargsDict):
|
||||
kwargs = kwargsQuery[0]
|
||||
query = kwargsQuery[1]
|
||||
query, pquery = groupFilters(kwargs, query)
|
||||
printGettingAllAccountEntities(Ent.GROUP, pquery)
|
||||
try:
|
||||
entityList.extend(callGAPIpages(cd.groups(), 'list', 'groups',
|
||||
pageMessage=getPageMessage(showFirstLastItems=True), messageAttribute='email',
|
||||
throwReasons=GAPI.GROUP_LIST_USERKEY_THROW_REASONS,
|
||||
orderBy='email', query=query, fields=f'nextPageToken,groups({",".join(set(fieldsList))})', **kwargs))
|
||||
except (GAPI.invalidMember, GAPI.invalidInput) as e:
|
||||
if not invalidMember(query):
|
||||
entityActionFailedExit([Ent.GROUP, None], str(e))
|
||||
except (GAPI.resourceNotFound, GAPI.domainNotFound, GAPI.forbidden, GAPI.badRequest):
|
||||
if kwargs.get('domain'):
|
||||
badRequestWarning(Ent.GROUP, Ent.DOMAIN, kwargs['domain'])
|
||||
else:
|
||||
accessErrorExit(cd)
|
||||
else:
|
||||
clearUnneededGroupMatchPatterns(matchPatterns)
|
||||
return entityList
|
||||
@@ -31347,7 +31403,7 @@ GROUPMEMBERS_FIELDS_CHOICE_MAP = {
|
||||
GROUPMEMBERS_DEFAULT_FIELDS = ['group', 'type', 'role', 'id', 'status', 'email']
|
||||
|
||||
# gam print group-members [todrive <ToDriveAttribute>*]
|
||||
# [([domain <DomainName>] ([member|showownedby <EmailItem>]|[query <QueryGroup>]))|
|
||||
# [([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryUserList>)]))|
|
||||
# (group|group_ns|group_susp <GroupItem>)|
|
||||
# (select <GroupEntity>)]
|
||||
# [emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
|
||||
@@ -31384,7 +31440,8 @@ def doPrintGroupMembers():
|
||||
peopleNames = {}
|
||||
memberOptions = initMemberOptions()
|
||||
groupColumn = True
|
||||
kwargs = {'customer': GC.Values[GC.CUSTOMER_ID]}
|
||||
customerKey = GC.Values[GC.CUSTOMER_ID]
|
||||
kwargsDict = initUserGroupDomainQueryFilters()
|
||||
subTitle = f'{Msg.ALL} {Ent.Plural(Ent.GROUP)}'
|
||||
fieldsList = []
|
||||
csvPF = CSVPrintFile('group')
|
||||
@@ -31401,7 +31458,7 @@ def doPrintGroupMembers():
|
||||
myarg = getArgument()
|
||||
if myarg == 'todrive':
|
||||
csvPF.GetTodriveParameters()
|
||||
elif getGroupFilters(myarg, kwargs, showOwnedBy):
|
||||
elif getGroupFilters(myarg, kwargsDict, showOwnedBy):
|
||||
pass
|
||||
elif getGroupMatchPatterns(myarg, matchPatterns, False):
|
||||
pass
|
||||
@@ -31457,7 +31514,7 @@ def doPrintGroupMembers():
|
||||
FJQC.GetFormatJSONQuoteChar(myarg, False)
|
||||
if not typesSet:
|
||||
typesSet = {Ent.TYPE_USER} if memberOptions[MEMBEROPTION_RECURSIVE] else ALL_GROUP_TYPES
|
||||
entityList = getGroupMembersEntityList(cd, entityList, matchPatterns, cdfieldsList, kwargs)
|
||||
entityList = getGroupMembersEntityList(cd, entityList, matchPatterns, cdfieldsList, kwargsDict)
|
||||
if not fieldsList:
|
||||
for field in GROUPMEMBERS_DEFAULT_FIELDS:
|
||||
csvPF.AddField(field, {field: field}, fieldsList)
|
||||
@@ -31493,7 +31550,6 @@ def doPrintGroupMembers():
|
||||
getRolesSet.add(Ent.ROLE_OWNER)
|
||||
getRoles = ','.join(sorted(getRolesSet))
|
||||
level = 0
|
||||
customerKey = GC.Values[GC.CUSTOMER_ID]
|
||||
setCustomerMemberEmail = 'email' in fieldsList
|
||||
i = 0
|
||||
count = len(entityList)
|
||||
@@ -31595,7 +31651,7 @@ def doPrintGroupMembers():
|
||||
csvPF.writeCSVfile(f'Group Members ({subTitle})')
|
||||
|
||||
# gam show group-members
|
||||
# [([domain <DomainName>] ([member|showownedby <EmailItem>]|[query <QueryGroup>]))|
|
||||
# [([domain|domains <DomainNameEntity>] ([member|showownedby <EmailItem>]|[(query <QueryGroup>)|(queries <QueryUserList>)]))|
|
||||
# (group|group_ns|group_susp <GroupItem>)|
|
||||
# (select <GroupEntity>)]
|
||||
# [emailmatchpattern [not] <RegularExpression>] [namematchpattern [not] <RegularExpression>]
|
||||
@@ -31642,8 +31698,7 @@ def doShowGroupMembers():
|
||||
|
||||
cd = buildGAPIObject(API.DIRECTORY)
|
||||
ci = None
|
||||
customerKey = GC.Values[GC.CUSTOMER_ID]
|
||||
kwargs = {'customer': customerKey}
|
||||
kwargsDict = initUserGroupDomainQueryFilters()
|
||||
entityList = None
|
||||
showOwnedBy = {}
|
||||
cdfieldsList = ['email']
|
||||
@@ -31655,7 +31710,7 @@ def doShowGroupMembers():
|
||||
includeDerivedMembership = False
|
||||
while Cmd.ArgumentsRemaining():
|
||||
myarg = getArgument()
|
||||
if getGroupFilters(myarg, kwargs, showOwnedBy):
|
||||
if getGroupFilters(myarg, kwargsDict, showOwnedBy):
|
||||
pass
|
||||
elif getGroupMatchPatterns(myarg, matchPatterns, False):
|
||||
pass
|
||||
@@ -31687,7 +31742,7 @@ def doShowGroupMembers():
|
||||
rolesSet = ALL_GROUP_ROLES
|
||||
if not typesSet:
|
||||
typesSet = ALL_GROUP_TYPES
|
||||
entityList = getGroupMembersEntityList(cd, entityList, matchPatterns, cdfieldsList, kwargs)
|
||||
entityList = getGroupMembersEntityList(cd, entityList, matchPatterns, cdfieldsList, kwargsDict)
|
||||
i = 0
|
||||
count = len(entityList)
|
||||
for group in entityList:
|
||||
@@ -41662,7 +41717,7 @@ USERS_INDEXED_TITLES = ['addresses', 'aliases', 'nonEditableAliases', 'emails',
|
||||
'phones', 'posixAccounts', 'relations', 'sshPublicKeys', 'websites']
|
||||
|
||||
# gam print users [todrive <ToDriveAttribute>*]
|
||||
# ([domain <DomainName>] [(query <QueryUser>)|(queries <QueryUserList>)]
|
||||
# ([domain|domains <DomainNameEntity>] [(query <QueryUser>)|(queries <QueryUserList>)]
|
||||
# [limittoou <OrgUnitItem>] [deleted_only|only_deleted])|[select <UserTypeEntity>]
|
||||
# [groups|groupsincolumns]
|
||||
# [license|licenses|licence|licences|licensebyuser|licensesbyuser|licencebyuser|licencesbyuser]
|
||||
@@ -41804,8 +41859,7 @@ def doPrintUsers(entityList=None):
|
||||
'sortHeaders': False,
|
||||
'maxGroups': 0
|
||||
}
|
||||
kwargs = {'customer': GC.Values[GC.CUSTOMER_ID]}
|
||||
queries = [None]
|
||||
kwargsDict = initUserGroupDomainQueryFilters()
|
||||
licenses = {}
|
||||
lic = None
|
||||
skus = None
|
||||
@@ -41824,11 +41878,8 @@ def doPrintUsers(entityList=None):
|
||||
elif entityList is None and myarg == 'limittoou':
|
||||
orgUnitPath = getOrgUnitItem(pathOnly=True, cd=cd)
|
||||
orgUnitPathLower = orgUnitPath.lower()
|
||||
elif myarg == 'domain':
|
||||
kwargs['domain'] = getString(Cmd.OB_DOMAIN_NAME).lower()
|
||||
kwargs.pop('customer', None)
|
||||
elif entityList is None and myarg in {'query', 'queries'}:
|
||||
queries = getQueries(myarg)
|
||||
elif entityList is None and getUserGroupDomainQueryFilters(myarg, kwargsDict):
|
||||
pass
|
||||
elif entityList is None and myarg in {'deletedonly', 'onlydeleted'}:
|
||||
showDeleted = True
|
||||
elif entityList is None and myarg == 'select':
|
||||
@@ -41927,23 +41978,11 @@ def doPrintUsers(entityList=None):
|
||||
if orgUnitPath is not None and fieldsList:
|
||||
fieldsList.append('orgUnitPath')
|
||||
fields = getItemFieldsFromFieldsList('users', fieldsList)
|
||||
for query in queries:
|
||||
if orgUnitPath is not None:
|
||||
if query is not None and query.find(orgUnitPath) == -1:
|
||||
query += f" orgUnitPath='{orgUnitPath}'"
|
||||
else:
|
||||
if query is None:
|
||||
query = ''
|
||||
else:
|
||||
query += ' '
|
||||
query += f"orgUnitPath='{orgUnitPath}'"
|
||||
if isSuspended is not None:
|
||||
if query is None:
|
||||
query = ''
|
||||
else:
|
||||
query += ' '
|
||||
query += f'isSuspended={isSuspended}'
|
||||
printGettingAllAccountEntities(Ent.USER, query)
|
||||
for kwargsQuery in makeUserGroupDomainQueryFilters(kwargsDict):
|
||||
kwargs = kwargsQuery[0]
|
||||
query = kwargsQuery[1]
|
||||
query, pquery = userFilters(kwargs, query, orgUnitPath, isSuspended)
|
||||
printGettingAllAccountEntities(Ent.USER, pquery)
|
||||
pageMessage = getPageMessage(showFirstLastItems=True)
|
||||
try:
|
||||
feed = yieldGAPIpages(cd.users(), 'list', 'users',
|
||||
@@ -41972,7 +42011,7 @@ def doPrintUsers(entityList=None):
|
||||
_updateDomainCounts(user['primaryEmail'])
|
||||
except GAPI.domainNotFound:
|
||||
entityActionFailedWarning([Ent.USER, None, Ent.DOMAIN, kwargs['domain']], Msg.NOT_FOUND)
|
||||
return
|
||||
continue
|
||||
except (GAPI.invalidOrgunit, GAPI.invalidInput) as e:
|
||||
if query and not customFieldMask:
|
||||
entityActionFailedWarning([Ent.USER, None], invalidQuery(query))
|
||||
@@ -41982,7 +42021,7 @@ def doPrintUsers(entityList=None):
|
||||
entityActionFailedWarning([Ent.USER, None], f'{invalidQuery(query)} or {invalidUserSchema(customFieldMask)}')
|
||||
else:
|
||||
entityActionFailedWarning([Ent.USER, None], str(e))
|
||||
return
|
||||
continue
|
||||
except (GAPI.badRequest, GAPI.resourceNotFound, GAPI.forbidden):
|
||||
accessErrorExit(cd)
|
||||
else:
|
||||
@@ -50235,8 +50274,6 @@ class DriveFileFields():
|
||||
self.fieldsList = []
|
||||
self.includeLabels = []
|
||||
self.parentsSubFields = {'id': False, 'isRoot': False, 'rootFolderId': None}
|
||||
self.sharedDriveNames = {}
|
||||
self.drive = None
|
||||
|
||||
def SetAllParentsSubFields(self):
|
||||
self.parentsSubFields['id'] = self.parentsSubFields['isRoot'] = True
|
||||
@@ -50286,16 +50323,6 @@ class DriveFileFields():
|
||||
def orderBy(self):
|
||||
return self.OBY.orderBy
|
||||
|
||||
def SharedDriveName(self, drive, driveId):
|
||||
if driveId not in self.sharedDriveNames:
|
||||
try:
|
||||
self.sharedDriveNames[driveId] = callGAPI(drive.drives(), 'get',
|
||||
throwReasons=GAPI.DRIVE_USER_THROW_REASONS+[GAPI.NOT_FOUND],
|
||||
useDomainAdminAccess=False, driveId=driveId, fields='name')['name']
|
||||
except (GAPI.notFound, GAPI.serviceNotAvailable, GAPI.authError, GAPI.domainPolicy):
|
||||
self.sharedDriveNames[driveId] = TEAM_DRIVE
|
||||
return self.sharedDriveNames[driveId]
|
||||
|
||||
def _setSkipObjects(skipObjects, skipTitles, fieldsList):
|
||||
for field in skipTitles:
|
||||
if field != 'parents':
|
||||
@@ -50453,9 +50480,9 @@ def showFileInfo(users):
|
||||
driveId = result.get('driveId')
|
||||
if driveId:
|
||||
if result['mimeType'] == MIMETYPE_GA_FOLDER and result['name'] == TEAM_DRIVE:
|
||||
result['name'] = DFF.SharedDriveName(drive, driveId)
|
||||
result['name'] = _getSharedDriveNameFromId(drive, driveId)
|
||||
if DFF.showSharedDriveNames:
|
||||
result['driveName'] = DFF.SharedDriveName(drive, driveId)
|
||||
result['driveName'] = _getSharedDriveNameFromId(drive, driveId)
|
||||
if showNoParents:
|
||||
result.setdefault('parents', [])
|
||||
if getPermissionsForSharedDrives and driveId and 'permissions' not in result:
|
||||
@@ -51791,7 +51818,7 @@ def printFileList(users):
|
||||
if not pmselect and 'permissions' in fileInfo:
|
||||
fileInfo['permissions'] = DLP.GetFileMatchingPermission(fileInfo)
|
||||
if DFF.showSharedDriveNames and driveId:
|
||||
fileInfo['driveName'] = DFF.SharedDriveName(drive, driveId)
|
||||
fileInfo['driveName'] = _getSharedDriveNameFromId(drive, driveId)
|
||||
if filepath:
|
||||
if not FJQC.formatJSON or not addPathsToJSON:
|
||||
addFilePathsToRow(drive, fileTree, fileInfo, filePathInfo, csvPF, row, fullpath=fullpath, showDepth=showDepth)
|
||||
@@ -54904,6 +54931,7 @@ copyReturnItemMap = {
|
||||
# [copysheetprotectedrangesnoninheritedpermissions [<Boolean>]]
|
||||
# [sendemailifrequired [<Boolean>]]
|
||||
# [suppressnotselectedmessages [<Boolean>]]
|
||||
# [verifyorganizer [<Boolean>]]
|
||||
def copyDriveFile(users):
|
||||
def _writeCSVData(user, oldName, oldId, newName, newId, mimeType):
|
||||
row = {'User': user, fileNameTitle: oldName, 'id': oldId,
|
||||
@@ -55240,6 +55268,7 @@ def copyDriveFile(users):
|
||||
copyMoveOptions = initCopyMoveOptions(True)
|
||||
excludeTrashed = newParentsSpecified = recursive = suppressNotSelectedMessages = False
|
||||
maxdepth = -1
|
||||
verifyOrganizer = True
|
||||
while Cmd.ArgumentsRemaining():
|
||||
myarg = getArgument()
|
||||
if getCopyMoveOptions(myarg, copyMoveOptions):
|
||||
@@ -55267,6 +55296,8 @@ def copyDriveFile(users):
|
||||
suppressNotSelectedMessages = getBoolean()
|
||||
elif getDriveFileCopyAttribute(myarg, copyBody, copyParameters):
|
||||
pass
|
||||
elif myarg == 'verifyorganizer':
|
||||
verifyOrganizer = getBoolean()
|
||||
else:
|
||||
unknownArgumentExit()
|
||||
if csvPF:
|
||||
@@ -55319,7 +55350,7 @@ def copyDriveFile(users):
|
||||
continue
|
||||
if copyMoveOptions['sourceDriveId']:
|
||||
# If copying from a Shared Drive, user has to be an organizer
|
||||
if not _verifyUserIsOrganizer(drive, user, i, count, copyMoveOptions['sourceDriveId']):
|
||||
if verifyOrganizer and not _verifyUserIsOrganizer(drive, user, i, count, copyMoveOptions['sourceDriveId']):
|
||||
_incrStatistic(statistics, STAT_USER_NOT_ORGANIZER)
|
||||
continue
|
||||
sourceSearchArgs = {'driveId': copyMoveOptions['sourceDriveId'], 'corpora': 'drive', 'includeItemsFromAllDrives': True, 'supportsAllDrives': True}
|
||||
@@ -55347,7 +55378,7 @@ def copyDriveFile(users):
|
||||
copyMoveOptions['destParentType'] = dest['destParentType']
|
||||
if copyMoveOptions['destDriveId']:
|
||||
# If copying to a Shared Drive, user has to be an organizer
|
||||
if not _verifyUserIsOrganizer(drive, user, i, count, copyMoveOptions['destDriveId']):
|
||||
if verifyOrganizer and not _verifyUserIsOrganizer(drive, user, i, count, copyMoveOptions['destDriveId']):
|
||||
_incrStatistic(statistics, STAT_USER_NOT_ORGANIZER)
|
||||
continue
|
||||
if not parentParms[DFA_SEARCHARGS]:
|
||||
@@ -55680,6 +55711,7 @@ def _updateMoveFilePermissions(drive, user, i, count,
|
||||
# [updatefilepermissions [<Boolean>]]
|
||||
# [retainsourcefolders [<Boolean>]]
|
||||
# [sendemailifrequired [<Boolean>]]
|
||||
# [verifyorganizer [<Boolean>]]
|
||||
def moveDriveFile(users):
|
||||
def _cloneFolderMove(drive, user, i, count, j, jcount,
|
||||
source, targetChildren, newFolderName, newParentId, newParentName, mergeParentModifiedTime,
|
||||
@@ -56014,6 +56046,7 @@ def moveDriveFile(users):
|
||||
newParentsSpecified = False
|
||||
movedFiles = {}
|
||||
updateFilePermissions = False
|
||||
verifyOrganizer = True
|
||||
while Cmd.ArgumentsRemaining():
|
||||
myarg = getArgument()
|
||||
if getCopyMoveOptions(myarg, copyMoveOptions):
|
||||
@@ -56022,6 +56055,8 @@ def moveDriveFile(users):
|
||||
newParentsSpecified = True
|
||||
elif myarg == 'updatefilepermissions':
|
||||
updateFilePermissions = getBoolean()
|
||||
elif myarg == 'verifyorganizer':
|
||||
verifyOrganizer = getBoolean()
|
||||
else:
|
||||
unknownArgumentExit()
|
||||
i, count, users = getEntityArgument(users)
|
||||
@@ -56054,7 +56089,7 @@ def moveDriveFile(users):
|
||||
copyMoveOptions['sourceDriveId'] = source.get('driveId')
|
||||
if copyMoveOptions['sourceDriveId']:
|
||||
# If moving from a Shared Drive, user has to be an organizer
|
||||
if not _verifyUserIsOrganizer(drive, user, i, count, copyMoveOptions['sourceDriveId']):
|
||||
if verifyOrganizer and not _verifyUserIsOrganizer(drive, user, i, count, copyMoveOptions['sourceDriveId']):
|
||||
_incrStatistic(statistics, STAT_USER_NOT_ORGANIZER)
|
||||
continue
|
||||
if source['trashed']:
|
||||
@@ -56099,7 +56134,7 @@ def moveDriveFile(users):
|
||||
continue
|
||||
if copyMoveOptions['destDriveId']:
|
||||
# If moving to a Shared Drive, user has to be an organizer
|
||||
if not _verifyUserIsOrganizer(drive, user, i, count, copyMoveOptions['destDriveId']):
|
||||
if verifyOrganizer and not _verifyUserIsOrganizer(drive, user, i, count, copyMoveOptions['destDriveId']):
|
||||
_incrStatistic(statistics, STAT_USER_NOT_ORGANIZER)
|
||||
continue
|
||||
# 3rd party shortcuts can't be moved to Shared Drives
|
||||
|
||||
@@ -198,6 +198,8 @@ OUTPUT_DATEFORMAT = 'output_dateformat'
|
||||
OUTPUT_TIMEFORMAT = 'output_timeformat'
|
||||
# When retrieving lists of people from API, how many should be retrieved in each chunk
|
||||
PEOPLE_MAX_RESULTS = 'people_max_results'
|
||||
# Domains for print alises|groups|users
|
||||
PRINT_AGU_DOMAINS = 'print_agu_domains'
|
||||
# Number of seconds to wait for batch/csv processes to complete
|
||||
PROCESS_WAIT_LIMIT = 'process_wait_limit'
|
||||
# Use quick method to move Chromebooks to OU
|
||||
@@ -363,6 +365,7 @@ Defaults = {
|
||||
OUTPUT_DATEFORMAT: '',
|
||||
OUTPUT_TIMEFORMAT: '',
|
||||
PEOPLE_MAX_RESULTS: '100',
|
||||
PRINT_AGU_DOMAINS: '',
|
||||
PROCESS_WAIT_LIMIT: '0',
|
||||
QUICK_CROS_MOVE: FALSE,
|
||||
QUICK_INFO_USER: FALSE,
|
||||
@@ -511,6 +514,7 @@ VAR_INFO = {
|
||||
OUTPUT_DATEFORMAT: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
|
||||
OUTPUT_TIMEFORMAT: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
|
||||
PEOPLE_MAX_RESULTS: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (0, 1000)},
|
||||
PRINT_AGU_DOMAINS: {VAR_TYPE: TYPE_STRING, VAR_LIMITS: (0, None)},
|
||||
PROCESS_WAIT_LIMIT: {VAR_TYPE: TYPE_INTEGER, VAR_LIMITS: (0, None)},
|
||||
QUICK_CROS_MOVE: {VAR_TYPE: TYPE_BOOLEAN},
|
||||
QUICK_INFO_USER: {VAR_TYPE: TYPE_BOOLEAN},
|
||||
|
||||
@@ -156,6 +156,8 @@ OUTPUT_TIMEFORMAT = 'outf'
|
||||
PARSER = 'pars'
|
||||
# Process ID
|
||||
PID = 'pid '
|
||||
# Domains for print alises|groups|users
|
||||
PRINT_AGU_DOMAINS = 'pagu'
|
||||
# Check API calls rate
|
||||
RATE_CHECK_COUNT = 'rccn'
|
||||
RATE_CHECK_START = 'rcst'
|
||||
@@ -269,6 +271,7 @@ Globals = {
|
||||
OUTPUT_TIMEFORMAT: '',
|
||||
PARSER: None,
|
||||
PID: 0,
|
||||
PRINT_AGU_DOMAINS: '',
|
||||
RATE_CHECK_COUNT: 0,
|
||||
RATE_CHECK_START: 0,
|
||||
SECTION: None,
|
||||
|
||||
Reference in New Issue
Block a user