Compare commits

..

43 Commits
v6.18 ... v6.20

Author SHA1 Message Date
Jay Lee
be4403331f Update var.py 2022-04-11 08:44:08 -04:00
Ross Scroggs
b84025debf Use new CI orgUnits.memberships/move (#1514)
Old method never seemed to work; commented code can probably be dropped
2022-04-11 02:33:45 -04:00
Jay Lee
4685f29aba Handle Cloud identity OrgUnits update 500 errors 2022-04-10 19:50:06 -04:00
Ross Scroggs
a832698366 Update CAA documentation (#1513) 2022-04-10 10:41:23 -04:00
Ross Scroggs
cf894fd0bd Code cleanup (#1512)
* Code cleanup

* Two fixes
2022-04-10 08:16:10 -04:00
Ross Scroggs
fd6c04bd94 Fix indentaton (#1511) 2022-04-09 14:20:22 -04:00
Ross Scroggs
49a2352d6d Standardize getting Shared Drive ID or Name (#1510) 2022-04-09 07:26:46 -04:00
Ross Scroggs
291871ec45 Update CAA osType in documentation (#1509) 2022-04-08 22:45:19 -04:00
Jay Lee
73c21a1156 Update caa.py 2022-04-08 19:42:20 -04:00
Ross Scroggs
27eed06617 Document CAA, minimumVersion is optional in constraint (#1508) 2022-04-08 19:39:29 -04:00
Ross Scroggs
a440cbbbdc Make print|show oushareddrives consistent with other commands (#1507) 2022-04-08 15:13:04 -04:00
Jay Lee
eb9ca5eb1d Update build.yml 2022-04-08 15:11:15 -04:00
Ross Scroggs
5eb1277691 Do print|show for caalevels and oushareddrives (#1506) 2022-04-08 12:28:11 -04:00
Ross Scroggs
6de424b185 Two updates (#1504) 2022-04-08 10:43:47 -04:00
Jay Lee
f89491d801 Update build.yml 2022-04-08 10:18:36 -04:00
Jay Lee
154de4818e Update build.yml 2022-04-08 09:57:02 -04:00
Jay Lee
c5895a3082 Update build.yml 2022-04-08 09:40:58 -04:00
Jay Lee
e085257a51 Update var.py 2022-04-08 09:37:22 -04:00
Ross Scroggs
d8e84cf045 Delete extraneous code, custom <String> is operative (#1503) 2022-04-07 21:12:37 -04:00
Jay Lee
a5eb61421d Update build.yml 2022-04-07 20:23:22 -04:00
Jay Lee
cddbea2718 don't use dasa for printer cmds 2022-04-08 00:10:38 +00:00
Jay Lee
c9bf5158e4 capture shared drive id 2022-04-07 23:58:32 +00:00
Jay Lee
7822b36f97 enable commands now that scopes should be present 2022-04-07 23:31:41 +00:00
Jay Lee
20d541ca8e juggle command order to give time for propogation 2022-04-07 23:26:50 +00:00
Jay Lee
72af9fb4a9 Update creds.tar.gpg 2022-04-07 23:17:24 +00:00
Jay Lee
a2972a3329 disable shared drive OU update for now, need to Actions 2022-04-07 20:31:22 +00:00
Jay Lee
97b74c0c8f fix shareddrive name 2022-04-07 20:20:02 +00:00
Jay Lee
332519e5d4 fix gam_user 2022-04-07 20:06:54 +00:00
Jay Lee
881641e2b4 gapi_drive 2022-04-07 19:53:11 +00:00
Jay Lee
82bfe74175 orgunits.py 2022-04-07 19:51:15 +00:00
Jay Lee
bac3451c21 OrgUnit Support for Shared Drives 2022-04-07 19:48:23 +00:00
Ross Scroggs
c97495ab05 Fix bug (#1502) 2022-04-07 15:36:56 -04:00
Jay Lee
aa3dad1e07 Merge branch 'main' of https://github.com/GAM-team/GAM 2022-04-07 17:00:05 +00:00
Jay Lee
2d4e15504c GAM 6.19 2022-04-07 16:59:58 +00:00
Jay Lee
3cd41e3d0f Update build.yml 2022-04-07 11:10:03 -04:00
Jay Lee
a94518c48d Update build.yml 2022-04-07 09:39:18 -04:00
Jay Lee
2837671ed7 Merge branch 'main' of https://github.com/GAM-team/GAM 2022-04-07 12:52:34 +00:00
Jay Lee
e49eed2a24 Improve error instructions for CAA 2022-04-07 12:52:21 +00:00
Jay Lee
edfc27c960 Update project-apis.txt 2022-04-07 08:29:25 -04:00
Jay Lee
41a10932cb handle >1 access policies in org, action tests 2022-04-07 01:21:15 +00:00
Jay Lee
119538c10c Support for CAA levels 2022-04-07 00:48:40 +00:00
Ross Scroggs
0a03fbb82e Fix add license message (#1497) 2022-03-31 13:38:18 -04:00
Ross Scroggs
b838054e2f Update pip install version (#1496) 2022-03-30 11:59:23 -04:00
15 changed files with 667 additions and 102 deletions

Binary file not shown.

View File

@@ -544,7 +544,7 @@ jobs:
$gam info user $gam info user
#$gam info user $gam_user grouptree #$gam info user $gam_user grouptree
export tstamp=$($PYTHON -c "import time; print(time.time_ns())") export tstamp=$($PYTHON -c "import time; print(time.time_ns())")
export newbase=gha-test-$JID-$tstamp export newbase=gha_test_$JID_$tstamp
export newuser=$newbase@pdl.jaylee.us export newuser=$newbase@pdl.jaylee.us
export newgroup=$newbase-group@pdl.jaylee.us export newgroup=$newbase-group@pdl.jaylee.us
export newalias=$newbase-alias@pdl.jaylee.us export newalias=$newbase-alias@pdl.jaylee.us
@@ -552,7 +552,7 @@ jobs:
export newresource=$newbase-resource export newresource=$newbase-resource
export GAM_THREADS=5 export GAM_THREADS=5
echo email > sample.csv; echo email > sample.csv;
for i in {01..10}; do for i in {1..10}; do
echo "${newbase}-bulkuser-$i" >> sample.csv; echo "${newbase}-bulkuser-$i" >> sample.csv;
done done
$gam create user $newuser firstname GHA lastname $JID password random recoveryphone 12125121110 recoveryemail jay0lee@gmail.com gha.jid $JID languages en+,en-GB- $gam create user $newuser firstname GHA lastname $JID password random recoveryphone 12125121110 recoveryemail jay0lee@gmail.com gha.jid $JID languages en+,en-GB-
@@ -562,12 +562,12 @@ jobs:
$gam create group $newgroup name "GHA $JID group" description "This is a description" isarchived true $gam create group $newgroup name "GHA $JID group" description "This is a description" isarchived true
$gam user $gam_user sendemail recipient $newuser subject "test message $newbase" message "GHA test message" $gam user $gam_user sendemail recipient $newuser subject "test message $newbase" message "GHA test message"
$gam user $gam_user sendemail recipient exchange@pdl.jaylee.us subject "test ${tstamp}" message "test message" $gam user $gam_user sendemail recipient exchange@pdl.jaylee.us subject "test ${tstamp}" message "test message"
$gam user $newuser add license workspaceenterpriseplus
$gam print privileges
$gam update cigroup $newgroup memberrestriction 'member.type == 1 || member.customer_id == groupCustomerId()' $gam update cigroup $newgroup memberrestriction 'member.type == 1 || member.customer_id == groupCustomerId()'
$gam info cigroup $newgroup $gam info cigroup $newgroup
$gam user $newuser add license workspaceenterpriseplus
$gam update group $newgroup add owner $gam_user $gam update group $newgroup add owner $gam_user
$gam update group $newgroup add member $newuser $gam update group $newgroup add member $newuser
$gam print privileges
$gam create admin $newuser _GROUPS_EDITOR_ROLE CUSTOMER condition nonsecuritygroup $gam create admin $newuser _GROUPS_EDITOR_ROLE CUSTOMER condition nonsecuritygroup
$gam csv sample.csv gam create user ~~email~~ firstname "GHA Bulk" lastname ~~email~~ gha.jid $JID $gam csv sample.csv gam create user ~~email~~ firstname "GHA Bulk" lastname ~~email~~ gha.jid $JID
$gam csv sample.csv gam update user ~~email~~ recoveryphone 12125121110 recoveryemail jay0lee@gmail.com password random $gam csv sample.csv gam update user ~~email~~ recoveryphone 12125121110 recoveryemail jay0lee@gmail.com password random
@@ -583,8 +583,8 @@ jobs:
$gam user $newuser imap on $gam user $newuser imap on
$gam user $newuser show imap $gam user $newuser show imap
$gam user $newuser show delegates $gam user $newuser show delegates
#$gam user $newuser add contactdelegate "${newbase}-bulkuser-01" $gam user $newuser add contactdelegate "${newbase}-bulkuser-1"
#$gam user $newuser print contactdelegates $gam user $newuser print contactdelegates
export biohazard=$(echo -e '\xe2\x98\xa3') export biohazard=$(echo -e '\xe2\x98\xa3')
$gam user $newuser label "$biohazard unicode biohazard $biohazard" $gam user $newuser label "$biohazard unicode biohazard $biohazard"
$gam user $newuser show labels $gam user $newuser show labels
@@ -594,10 +594,10 @@ jobs:
$gam user $gam_user sendemail subject "GHA send $gam_user $newbase" file gam.py recipient admin@pdl.jaylee.us $gam user $gam_user sendemail subject "GHA send $gam_user $newbase" file gam.py recipient admin@pdl.jaylee.us
$gam user $gam_user draftemail subject "GHA draft $newbase" message "Draft message test" $gam user $gam_user draftemail subject "GHA draft $newbase" message "Draft message test"
$gam csvfile sample.csv:email waitformailbox $gam csvfile sample.csv:email waitformailbox
$gam user $newuser delegate to "${newbase}-bulkuser-01" $gam user $newuser delegate to "${newbase}-bulkuser-1"
$gam users "$gam_user $newbase-bulkuser-01 $newbase-bulkuser-02 $newbase-bulkuser-03" delete messages query in:anywhere maxtodelete 99999 doit $gam users "$gam_user $newbase-bulkuser-1 $newbase-bulkuser-2 $newbase-bulkuser-3" delete messages query in:anywhere maxtodelete 99999 doit
$gam users "$newbase-bulkuser-04 $newbase-bulkuser-05 $newbase-bulkuser-06" trash messages query in:anywhere maxtotrash 99999 doit $gam users "$newbase-bulkuser-4 $newbase-bulkuser-5 $newbase-bulkuser-6" trash messages query in:anywhere maxtotrash 99999 doit
$gam users "$newbase-bulkuser-07 $newbase-bulkuser-08 $newbase-bulkuser-09" modify messages query in:anywhere maxtomodify 99999 addlabel IMPORTANT addlabel STARRED doit $gam users "$newbase-bulkuser-7 $newbase-bulkuser-8 $newbase-bulkuser-9" modify messages query in:anywhere maxtomodify 99999 addlabel IMPORTANT addlabel STARRED doit
$gam user $newuser delete label --ALL_LABELS-- $gam user $newuser delete label --ALL_LABELS--
GAM_CSV_ROW_FILTER="name:regex:gha-test-${JID}" $gam print features | $gam csv - gam delete feature ~name GAM_CSV_ROW_FILTER="name:regex:gha-test-${JID}" $gam print features | $gam csv - gam delete feature ~name
$gam create feature name Whiteboard-$newbase $gam create feature name Whiteboard-$newbase
@@ -647,7 +647,7 @@ jobs:
export sn="$JID$JID$JID$JID-$(openssl rand -base64 32 | sed 's/[^a-zA-Z0-9]//g')" export sn="$JID$JID$JID$JID-$(openssl rand -base64 32 | sed 's/[^a-zA-Z0-9]//g')"
$gam create device serialnumber $sn devicetype android $gam create device serialnumber $sn devicetype android
$gam print cros allfields orderby serialnumber $gam print cros allfields orderby serialnumber
#$gam show crostelemetry storagepercentonly $gam show crostelemetry storagepercentonly
$gam report usageparameters customer $gam report usageparameters customer
$gam report usage customer parameters gmail:num_emails_sent,accounts:num_1day_logins $gam report usage customer parameters gmail:num_emails_sent,accounts:num_1day_logins
$gam report customer todrive $gam report customer todrive
@@ -656,14 +656,23 @@ jobs:
$gam print devices nopersonaldevices nodeviceusers filter "serial:$JID$JID$JID$JID-" | $gam csv - gam delete device id ~name $gam print devices nopersonaldevices nodeviceusers filter "serial:$JID$JID$JID$JID-" | $gam csv - gam delete device id ~name
$gam print userinvitations $gam print userinvitations
$gam print userinvitations | $gam csv - gam send userinvitation ~name $gam print userinvitations | $gam csv - gam send userinvitation ~name
export CUSTOMER_ID="C01wfv983" $gam create caalevel "zzz_${newbase}" basic condition ipsubnetworks 1.1.1.1/32,2.2.2.2/32 endcondition
export GA_DOMAIN="pdl.jaylee.us" $gam print caalevels
touch $gampath/enabledasa.txt $gam delete caalevel "zzz_${newbase}"
driveid=$($gam user $gam_user add shareddrive "${newbase}" | awk '{print $NF}')
echo "Created shared drive ${driveid}"
$gam user $gam_user update shareddrive "${driveid}" ou "id:03ph8a2z1t2ph5z"
$gam user $gam_user show shareddrives asadmin
$gam user $gam_user delete shareddrive "${driveid}"
echo "printer model count:" echo "printer model count:"
$gam print printermodels | wc -l $gam print printermodels | wc -l
#$gam print printers #$gam print printers
#$gam create printer displayname "${newbase}" uri ipp://localhost:631 driverless description "made by $(gam_user)" #$gam create printer displayname "${newbase}" uri ipp://localhost:631 driverless description "made by $(gam_user)" ou /
rm -f -v $gampath/enabledasa.txt #export CUSTOMER_ID="C01wfv983"
#export GA_DOMAIN="pdl.jaylee.us"
#touch $gampath/enabledasa.txt
#echo "using delegated admin service account"
#$gam print users
# - name: Upload to Google Drive, build only. # - name: Upload to Google Drive, build only.
# if: github.event_name == 'push' && matrix.goal != 'test' # if: github.event_name == 'push' && matrix.goal != 'test'

View File

@@ -243,6 +243,7 @@ If an item contains spaces, it should be surrounded by ".
<SMTPHostName> ::= <String> <SMTPHostName> ::= <String>
<StudentItem> ::= <EmailAddress>|<UniqueID>|<String> <StudentItem> ::= <EmailAddress>|<UniqueID>|<String>
<TeamDriveID> ::= <String> <TeamDriveID> ::= <String>
<TeamDriveName> ::= <String>
<Timezone> ::= <String> <Timezone> ::= <String>
<Title> ::= <String> <Title> ::= <String>
<URI> ::= <String> <URI> ::= <String>
@@ -632,6 +633,7 @@ Items, separated by spaces, with spaces, commas or single quotes in the items th
<SchemaNameList> ::= "<SchemaName>(,<SchemaName>)*" <SchemaNameList> ::= "<SchemaName>(,<SchemaName>)*"
<SerialNumberList> ::= "<SerialNumber>(,<SerialNumber>)*" <SerialNumberList> ::= "<SerialNumber>(,<SerialNumber>)*"
<ServiceAccountKeyList> ::= "<ServiceAccountKey>(,<ServiceAccountKey>)*" <ServiceAccountKeyList> ::= "<ServiceAccountKey>(,<ServiceAccountKey>)*"
<StringList> ::= "<String>(,<String>)*"
<TeamDriveIDList> ::= "<TeamDriveID>(,<TeamDriveID>)*" <TeamDriveIDList> ::= "<TeamDriveID>(,<TeamDriveID>)*"
<UserFieldNameList> ::= "<UserFieldName>(,<UserFieldName>)*" <UserFieldNameList> ::= "<UserFieldName>(,<UserFieldName>)*"
<UserList> ::= "<UserItem>(,<UserItem>)*" <UserList> ::= "<UserItem>(,<UserItem>)*"
@@ -1210,6 +1212,76 @@ gam print browsertokens [todrive]
[fields <BrowserTokenFieldNameList>] [fields <BrowserTokenFieldNameList>]
[sortheaders] [sortheaders]
<CAAAllowedEncryptionStatus> ::=
encryption_unsupported |
encrypted |
unencrypted
<CAAAllowedEncryptionStatusList> ::= "<CAAAllowedEncryptionStatus>(,<CAAAllowedEncryptionStatus>)"
<CAAAllowedDeviceManagementLevel> ::=
basic |
advanced|complete |
none
<CAAAllowedDeviceManagementLevelList> ::= "<CAAAllowedDeviceManagementLevel>(,<CAAAllowedDeviceManagementLevel>)"
<CAACombiningFunction> ::=
and |
or
<CAAIPSubNetwork> ::=
<CIDRnetmask>
<CAAIPSubNetworkList> ::= "<CAAIPSubNetwork>(,<CAAIPSubNetwork>)"
<CAAMember> ::=
user:<EmailAddress> |
serviceAccount:<EmailAddress>
<CAAMemberList> ::= "<CAAMember>(,<CAAMember>)"
<CAAOsType> ::=
DESKTOP_MAC |
DESKTOP_WINDOWS |
DESKTOP_LINUX |
DESKTOP_CHROME_OS |
VERIFIED_DESKTOP_CHROME_OS |
ANDROID |
IOS
<CAAOsConstraint> ::=
<CAAOsType> |
<CAAOsType>:<String>.<String>.<String>
<CAAOsConstraintList> ::= "<CAAOsConstraint>(,<CAAOsConstraint>)"
<CAARegion> ::=
<Character><Character>
<CAARegionList> ::= "<CAARegion>(,<CAARegion>)"
See: https://www.iso.org/obp/ui/#search
<CAADevicePolicyAttribute> ::=
(requirescreenlock <Boolean>) |
(allowedencryptionstatuses <CAAAllowedEncryptionStatusList>) |
(osconstraints <CAAOsConstraintList>) |
(alloweddevicemanagementlevels <CAAAllowedDeviceManagementLevelList>) |
(requireadminapproval <Boolean>) |
(requirecorpowned <Boolean>)
<CAAConditionAttribute> ::=
(ipsubnetworks <CAAIPSubNetworkList>) |
(devicepolicy <CAADevicePolicyAttribute> enddevicepolicy) |
(requiredaccesslevels <StringList>) |
(negate <Boolean>) |
(members <CAARegionList>) |
(regions <CAAMemberList>)
<CAABasicAttribute> ::+
(combiningfunction <CAACombiningFunction>) |
(condition <CAAConditionAttribute>+ endcondition)
gam create caalevel <String> (basic <CAABasicAttribute>+)|(custom <String>)
gam update caalevel <CAALevelName> (basic <CAABasicAttribute>+)|(custom <String>)
gam delete caalevel <CAALevelName>
gam show caalevels
gam print caalevels [todrive]
gam print chatspaces [todrive] gam print chatspaces [todrive]
gam print chatmembers space <ChatSpace> [todrive] gam print chatmembers space <ChatSpace> [todrive]
gam create chatmessage space <ChatSpace> [thread <String>] gam create chatmessage space <ChatSpace> [thread <String>]
@@ -1350,11 +1422,11 @@ gam print chromehistory channels [todrive]
gam print chromehistory versions [todrive] gam print chromehistory versions [todrive]
[platform <ChromePlatformType>] [channel <ChromeChannelType>] [platform <ChromePlatformType>] [channel <ChromeChannelType>]
[filter <String>] [filter <String>]
(orderby <ChromeVersionsOrderByFieldName> [ascending|descending])* (orderby <ChromeVersionsOrderByFieldName> [ascending|descending])*
gam print chromehistory releases [todrive] gam print chromehistory releases [todrive]
[platform <ChromePlatformType>] [channel <ChromeChannelType>] [version <String>] [platform <ChromePlatformType>] [channel <ChromeChannelType>] [version <String>]
[filter <String>] [filter <String>]
(orderby <ChromeReleasessOrderByFieldName> [ascending|descending])* (orderby <ChromeReleasessOrderByFieldName> [ascending|descending])*
gam delete chromepolicy <SchemaName>+ ou|org|orgunit <OrgUnitItem> [(printerid <PrinterID>)|(appid <AppID>)] gam delete chromepolicy <SchemaName>+ ou|org|orgunit <OrgUnitItem> [(printerid <PrinterID>)|(appid <AppID>)]
gam update chromepolicy (<SchemaName> (<Field> <Value>)+)+ ou|org|orgunit <OrgUnitItem> [(printerid <PrinterID>)|(appid <AppID>)] gam update chromepolicy (<SchemaName> (<Field> <Value>)+)+ ou|org|orgunit <OrgUnitItem> [(printerid <PrinterID>)|(appid <AppID>)]
@@ -1488,6 +1560,9 @@ gam update feature <Name> name <Name>
gam delete feature <Name> gam delete feature <Name>
gam print features [todrive] gam print features [todrive]
gam show oushareddrives|orgunitshareddrives [ou|org|orgunit <OrgUnitItem>]
gam print oushareddrives|orgunitshareddrives [todrive] [ou|org|orgunit <OrgUnitItem>]
gam create resource <ResourceID> <Name> <ResourceAttribute>* gam create resource <ResourceID> <Name> <ResourceAttribute>*
gam update resource <ResourceID> <ResourceAttribute>* gam update resource <ResourceID> <ResourceAttribute>*
gam delete resource <ResourceID> gam delete resource <ResourceID>
@@ -1760,12 +1835,12 @@ gam <UserTypeEntity> show signature|sig [format]
teammembersonly teammembersonly
gam <UserTypeEntity> create|add teamdrive <Name> gam <UserTypeEntity> create|add teamdrive <Name>
gam <UserTypeEntity> update teamdrive <TeamDriveID> [asadmin] [name <Name>] gam <UserTypeEntity> update teamdrive <TeamDriveID>|(name <TeamDriveName>) [asadmin] [name <Name>]
[(theme|themeid <String>) | ([customtheme <DriveFileID> <Float> <Float> <Float>] [color <ColorValue>])] [(theme|themeid <String>) | ([customtheme <DriveFileID> <Float> <Float> <Float>] [color <ColorValue>])]
(<TeamDriveRestrictionsSubfieldName> <Boolean>)* (<TeamDriveRestrictionsSubfieldName> <Boolean>)*
[hidden <Boolean>] [hidden <Boolean>]
gam <UserTypeEntity> delete teamdrive <TeamDriveID> gam <UserTypeEntity> delete teamdrive <TeamDriveID>|(name <TeamDriveName>)
gam <UserTypeEntity> show teamdriveinfo <TeamDriveID> [asadmin] gam <UserTypeEntity> show teamdriveinfo <TeamDriveID>|(name <TeamDriveName>) [asadmin]
gam <UserTypeEntity> show teamdrives [query <QueryTeamDrive>] [asadmin] gam <UserTypeEntity> show teamdrives [query <QueryTeamDrive>] [asadmin]
gam <UserTypeEntity> print teamdrives [query <QueryTeamDrive>] [todrive] [asadmin] gam <UserTypeEntity> print teamdrives [query <QueryTeamDrive>] [todrive] [asadmin]
gam <UserTypeEntity> show teamdrivethemes gam <UserTypeEntity> show teamdrivethemes

View File

@@ -55,6 +55,7 @@ from gam import auth
from gam import controlflow from gam import controlflow
from gam import display from gam import display
from gam import fileutils from gam import fileutils
from gam.gapi import caa as gapi_caa
from gam.gapi import calendar as gapi_calendar from gam.gapi import calendar as gapi_calendar
from gam.gapi import cloudidentity as gapi_cloudidentity from gam.gapi import cloudidentity as gapi_cloudidentity
from gam.gapi import cbcm as gapi_cbcm from gam.gapi import cbcm as gapi_cbcm
@@ -64,6 +65,7 @@ from gam.gapi import chromemanagement as gapi_chromemanagement
from gam.gapi import chromepolicy as gapi_chromepolicy from gam.gapi import chromepolicy as gapi_chromepolicy
from gam.gapi.cloudidentity import devices as gapi_cloudidentity_devices from gam.gapi.cloudidentity import devices as gapi_cloudidentity_devices
from gam.gapi.cloudidentity import groups as gapi_cloudidentity_groups from gam.gapi.cloudidentity import groups as gapi_cloudidentity_groups
from gam.gapi.cloudidentity import orgunits as gapi_cloudidentity_orgunits
from gam.gapi.cloudidentity import userinvitations as gapi_cloudidentity_userinvitations from gam.gapi.cloudidentity import userinvitations as gapi_cloudidentity_userinvitations
from gam.gapi import contactdelegation as gapi_contactdelegation from gam.gapi import contactdelegation as gapi_contactdelegation
from gam.gapi.directory import asps as gapi_directory_asps from gam.gapi.directory import asps as gapi_directory_asps
@@ -80,6 +82,7 @@ from gam.gapi.directory import resource as gapi_directory_resource
from gam.gapi.directory import roles as gapi_directory_roles from gam.gapi.directory import roles as gapi_directory_roles
from gam.gapi.directory import roleassignments as gapi_directory_roleassignments from gam.gapi.directory import roleassignments as gapi_directory_roleassignments
from gam.gapi.directory import users as gapi_directory_users from gam.gapi.directory import users as gapi_directory_users
from gam.gapi.drive import drives as gapi_drive_drives
from gam.gapi import licensing as gapi_licensing from gam.gapi import licensing as gapi_licensing
from gam.gapi import siteverification as gapi_siteverification from gam.gapi import siteverification as gapi_siteverification
from gam.gapi import errors as gapi_errors from gam.gapi import errors as gapi_errors
@@ -848,7 +851,9 @@ def _getSvcAcctData():
controlflow.system_error_exit(6, None) controlflow.system_error_exit(6, None)
GM_Globals[GM_OAUTH2SERVICE_JSON_DATA] = json.loads(json_string) GM_Globals[GM_OAUTH2SERVICE_JSON_DATA] = json.loads(json_string)
jwt_apis = ['chat'] # APIs which can handle OAuthless JWT tokens jwt_apis = ['chat',
'cloudresourcemanager',
'accesscontextmanager'] # APIs which can handle OAuthless JWT tokens
def getSvcAcctCredentials(scopes, act_as, api=None): def getSvcAcctCredentials(scopes, act_as, api=None):
try: try:
_getSvcAcctData() _getSvcAcctData()
@@ -2730,7 +2735,7 @@ def printDriveSettings(users):
display.write_csv_file(csvRows, titles, 'User Drive Settings', todrive) display.write_csv_file(csvRows, titles, 'User Drive Settings', todrive)
def getTeamDriveThemes(users): def getSharedDriveThemes(users):
for user in users: for user in users:
user, drive = buildDrive3GAPIObject(user) user, drive = buildDrive3GAPIObject(user)
if not drive: if not drive:
@@ -8014,10 +8019,17 @@ def doPrintShowProjects(csvFormat):
display.write_csv_file(csvRows, titles, 'Projects', todrive) display.write_csv_file(csvRows, titles, 'Projects', todrive)
def doGetTeamDriveInfo(users): def getSharedDriveId(i):
teamDriveId = sys.argv[5] driveId = sys.argv[i]
if driveId.lower() == 'name':
i += 1
driveId = gapi_drive_drives.drive_name_to_id(sys.argv[i])
return (i+1, driveId)
def doGetSharedDriveInfo(users):
i, driveId = getSharedDriveId(5)
useDomainAdminAccess = False useDomainAdminAccess = False
i = 6
while i < len(sys.argv): while i < len(sys.argv):
myarg = sys.argv[i].lower().replace('_', '') myarg = sys.argv[i].lower().replace('_', '')
if myarg == 'asadmin': if myarg == 'asadmin':
@@ -8025,7 +8037,7 @@ def doGetTeamDriveInfo(users):
i += 1 i += 1
else: else:
controlflow.invalid_argument_exit(myarg, controlflow.invalid_argument_exit(myarg,
'gam <users> show teamdrive') 'gam <users> show shareddrive')
for user in users: for user in users:
drive = buildGAPIServiceObject('drive3', user) drive = buildGAPIServiceObject('drive3', user)
if not drive: if not drive:
@@ -8033,13 +8045,15 @@ def doGetTeamDriveInfo(users):
continue continue
result = gapi.call(drive.drives(), result = gapi.call(drive.drives(),
'get', 'get',
driveId=teamDriveId, driveId=driveId,
useDomainAdminAccess=useDomainAdminAccess, useDomainAdminAccess=useDomainAdminAccess,
fields='*') fields='*')
if useDomainAdminAccess and 'orgUnitId' in result:
result['orgUnit'] = gapi_directory_orgunits.orgunit_from_orgunitid(f'id:{result["orgUnitId"]}')
display.print_json(result) display.print_json(result)
def doCreateTeamDrive(users): def doCreateSharedDrive(users):
body = {'name': sys.argv[5]} body = {'name': sys.argv[5]}
i = 6 i = 6
while i < len(sys.argv): while i < len(sys.argv):
@@ -8049,7 +8063,7 @@ def doCreateTeamDrive(users):
i += 2 i += 2
else: else:
controlflow.invalid_argument_exit(sys.argv[i], controlflow.invalid_argument_exit(sys.argv[i],
'gam <users> create teamdrive') 'gam <users> create shareddrive')
for user in users: for user in users:
drive = buildGAPIServiceObject('drive3', user) drive = buildGAPIServiceObject('drive3', user)
if not drive: if not drive:
@@ -8061,7 +8075,7 @@ def doCreateTeamDrive(users):
requestId=requestId, requestId=requestId,
body=body, body=body,
fields='id') fields='id')
print(f'Created Team Drive {body["name"]} with id {result["id"]}') print(f'Created Shared Drive {body["name"]} with id {result["id"]}')
TEAMDRIVE_RESTRICTIONS_MAP = { TEAMDRIVE_RESTRICTIONS_MAP = {
@@ -8072,17 +8086,20 @@ TEAMDRIVE_RESTRICTIONS_MAP = {
} }
def doUpdateTeamDrive(users): def doUpdateSharedDrive(users):
teamDriveId = sys.argv[5] i, driveId = getSharedDriveId(5)
body = {} body = {}
useDomainAdminAccess = False useDomainAdminAccess = False
change_hide = None change_hide = None
i = 6 orgUnit = None
while i < len(sys.argv): while i < len(sys.argv):
myarg = sys.argv[i].lower().replace('_', '') myarg = sys.argv[i].lower().replace('_', '')
if myarg == 'name': if myarg == 'name':
body['name'] = sys.argv[i + 1] body['name'] = sys.argv[i + 1]
i += 2 i += 2
elif myarg in ['ou', 'org', 'orgunit']:
orgUnit = sys.argv[i+1]
i += 2
elif myarg == 'theme': elif myarg == 'theme':
body['themeId'] = sys.argv[i + 1] body['themeId'] = sys.argv[i + 1]
i += 2 i += 2
@@ -8100,9 +8117,9 @@ def doUpdateTeamDrive(users):
elif myarg == 'asadmin': elif myarg == 'asadmin':
useDomainAdminAccess = True useDomainAdminAccess = True
i += 1 i += 1
elif myarg in ['ou', 'org', 'orgunit']: # elif myarg in ['ou', 'org', 'orgunit']:
body['orgUnitId'] = gapi_directory_orgunits.getOrgUnitId(sys.argv[i + 1]) # body['orgUnitId'] = gapi_directory_orgunits.getOrgUnitId(sys.argv[i + 1])
i += 2 # i += 2
elif myarg in ['hidden']: elif myarg in ['hidden']:
if getBoolean(sys.argv[i+1], myarg): if getBoolean(sys.argv[i+1], myarg):
change_hide = 'hide' change_hide = 'hide'
@@ -8117,8 +8134,8 @@ def doUpdateTeamDrive(users):
i += 2 i += 2
else: else:
controlflow.invalid_argument_exit(sys.argv[i], controlflow.invalid_argument_exit(sys.argv[i],
'gam <users> update teamdrive') 'gam <users> update shareddrive')
if not body and not change_hide: if not body and not change_hide and not orgUnit:
controlflow.system_error_exit( controlflow.system_error_exit(
4, 'nothing to update. Need at least a name argument.') 4, 'nothing to update. Need at least a name argument.')
for user in users: for user in users:
@@ -8130,7 +8147,7 @@ def doUpdateTeamDrive(users):
'update', 'update',
useDomainAdminAccess=useDomainAdminAccess, useDomainAdminAccess=useDomainAdminAccess,
body=body, body=body,
driveId=teamDriveId, driveId=driveId,
fields='id', fields='id',
soft_errors=True) soft_errors=True)
if not result: if not result:
@@ -8138,15 +8155,19 @@ def doUpdateTeamDrive(users):
if change_hide: if change_hide:
ch_result = gapi.call(drive.drives(), ch_result = gapi.call(drive.drives(),
change_hide, change_hide,
driveId=teamDriveId, driveId=driveId,
fields='id', fields='id',
soft_errors=True) soft_errors=True)
print(f'Updated Team Drive {teamDriveId}') if orgUnit:
gapi_cloudidentity_orgunits.move_shared_drive(driveId,
orgUnit)
print(f'Updated Shared Drive {driveId}')
def printShowTeamDrives(users, csvFormat): def printShowSharedDrives(users, csvFormat):
todrive = False todrive = False
useDomainAdminAccess = False useDomainAdminAccess = False
q = None q = None
get_orgunits = True
i = 5 i = 5
while i < len(sys.argv): while i < len(sys.argv):
myarg = sys.argv[i].lower().replace('_', '') myarg = sys.argv[i].lower().replace('_', '')
@@ -8159,13 +8180,18 @@ def printShowTeamDrives(users, csvFormat):
elif myarg == 'query': elif myarg == 'query':
q = sys.argv[i + 1] q = sys.argv[i + 1]
i += 2 i += 2
elif myarg == 'noorgunits':
get_orgunits = False
i += 1
else: else:
controlflow.invalid_argument_exit( controlflow.invalid_argument_exit(
myarg, f"gam {['show', 'print'][csvFormat]} teamdrives") myarg, f"gam {['show', 'print'][csvFormat]} shareddrives")
tds = [] tds = []
titles = [] titles = []
if get_orgunits and useDomainAdminAccess:
ou_map = gapi_directory_orgunits.orgid_to_org_map()
for user in users: for user in users:
sys.stderr.write(f'Getting Team Drives for {user}\n') sys.stderr.write(f'Getting Shared Drives for {user}\n')
user, drive = buildDrive3GAPIObject(user) user, drive = buildDrive3GAPIObject(user)
if not drive: if not drive:
continue continue
@@ -8180,12 +8206,16 @@ def printShowTeamDrives(users, csvFormat):
continue continue
for td in results: for td in results:
td = utils.flatten_json(td) td = utils.flatten_json(td)
if get_orgunits and useDomainAdminAccess:
td_ouid = td.get('orgUnitId')
if td_ouid:
td['orgUnit'] = ou_map.get(f'id:{td_ouid}', 'Unknown')
for key in td: for key in td:
if key not in titles: if key not in titles:
titles.append(key) titles.append(key)
tds.append(td) tds.append(td)
if csvFormat: if csvFormat:
display.write_csv_file(tds, titles, 'Team Drives', todrive) display.write_csv_file(tds, titles, 'Shared Drives', todrive)
else: else:
for td in tds: for td in tds:
name = td.pop('name') name = td.pop('name')
@@ -8195,17 +8225,16 @@ def printShowTeamDrives(users, csvFormat):
print() print()
def doDeleteSharedDrive(users):
def doDeleteTeamDrive(users): _, driveId = getSharedDriveId(5)
teamDriveId = sys.argv[5]
for user in users: for user in users:
user, drive = buildDrive3GAPIObject(user) user, drive = buildDrive3GAPIObject(user)
if not drive: if not drive:
continue continue
print(f'Deleting Team Drive {teamDriveId}') print(f'Deleting Shared Drive {driveId}')
gapi.call(drive.drives(), gapi.call(drive.drives(),
'delete', 'delete',
driveId=teamDriveId, driveId=driveId,
soft_errors=True) soft_errors=True)
@@ -10453,6 +10482,11 @@ OAUTH2_SCOPES = [
'subscopes': ['readonly'], 'subscopes': ['readonly'],
'scopes': 'https://www.googleapis.com/auth/cloud-identity.groups' 'scopes': 'https://www.googleapis.com/auth/cloud-identity.groups'
}, },
{
'name': 'Cloud Identity - OrgUnits',
'subscopes': ['readonly'],
'scopes': 'https://www.googleapis.com/auth/cloud-identity.orgunits',
},
{ {
'name': 'Cloud Identity - User Invitations', 'name': 'Cloud Identity - User Invitations',
'subscopes': ['readonly'], 'subscopes': ['readonly'],
@@ -11374,6 +11408,8 @@ def ProcessGAMCommand(args):
gapi_directory_printers.create() gapi_directory_printers.create()
elif argument in ['chatmessage']: elif argument in ['chatmessage']:
gapi_chat.create_message() gapi_chat.create_message()
elif argument in ['caalevel']:
gapi_caa.create_access_level()
else: else:
controlflow.invalid_argument_exit(argument, 'gam create') controlflow.invalid_argument_exit(argument, 'gam create')
sys.exit(0) sys.exit(0)
@@ -11438,6 +11474,8 @@ def ProcessGAMCommand(args):
gapi_directory_printers.update() gapi_directory_printers.update()
elif argument in ['chatmessage']: elif argument in ['chatmessage']:
gapi_chat.update_message() gapi_chat.update_message()
elif argument in ['caalevel']:
gapi_caa.update_access_level()
else: else:
controlflow.invalid_argument_exit(argument, 'gam update') controlflow.invalid_argument_exit(argument, 'gam update')
sys.exit(0) sys.exit(0)
@@ -11578,6 +11616,8 @@ def ProcessGAMCommand(args):
gapi_chromepolicy.delete_policy() gapi_chromepolicy.delete_policy()
elif argument == 'chatmessage': elif argument == 'chatmessage':
gapi_chat.delete_message() gapi_chat.delete_message()
elif argument == 'caalevel':
gapi_caa.delete_access_level()
else: else:
controlflow.invalid_argument_exit(argument, 'gam delete') controlflow.invalid_argument_exit(argument, 'gam delete')
sys.exit(0) sys.exit(0)
@@ -11699,6 +11739,10 @@ def ProcessGAMCommand(args):
gapi_chat.print_spaces() gapi_chat.print_spaces()
elif argument in ['chatmembers']: elif argument in ['chatmembers']:
gapi_chat.print_members() gapi_chat.print_members()
elif argument in ['caalevels']:
gapi_caa.printshow_access_levels(True)
elif argument in ['oushareddrives', 'orgunitshareddrives']:
gapi_cloudidentity_orgunits.printshow_orgunit_shared_drives(True)
else: else:
controlflow.invalid_argument_exit(argument, 'gam print') controlflow.invalid_argument_exit(argument, 'gam print')
sys.exit(0) sys.exit(0)
@@ -11729,6 +11773,10 @@ def ProcessGAMCommand(args):
gapi_chromepolicy.printshow_policies() gapi_chromepolicy.printshow_policies()
elif argument == 'crostelemetry': elif argument == 'crostelemetry':
gapi_chromemanagement.printShowCrosTelemetry('show') gapi_chromemanagement.printShowCrosTelemetry('show')
elif argument in ['caalevels']:
gapi_caa.printshow_access_levels(False)
elif argument in ['oushareddrives', 'orgunitshareddrives']:
gapi_cloudidentity_orgunits.printshow_orgunit_shared_drives(False)
else: else:
controlflow.invalid_argument_exit(argument, 'gam show') controlflow.invalid_argument_exit(argument, 'gam show')
sys.exit(0) sys.exit(0)
@@ -11887,8 +11935,8 @@ def ProcessGAMCommand(args):
gapi_calendar.showCalSettings(users) gapi_calendar.showCalSettings(users)
elif showWhat == 'drivesettings': elif showWhat == 'drivesettings':
printDriveSettings(users) printDriveSettings(users)
elif showWhat == 'teamdrivethemes': elif showWhat in ['teamdrivethemes', 'shareddrivethemes']:
getTeamDriveThemes(users) getSharedDriveThemes(users)
elif showWhat == 'drivefileacl': elif showWhat == 'drivefileacl':
showDriveFileACL(users) showDriveFileACL(users)
elif showWhat == 'filelist': elif showWhat == 'filelist':
@@ -11931,10 +11979,10 @@ def ProcessGAMCommand(args):
printShowFilters(users, False) printShowFilters(users, False)
elif showWhat in ['forwardingaddress', 'forwardingaddresses']: elif showWhat in ['forwardingaddress', 'forwardingaddresses']:
printShowForwardingAddresses(users, False) printShowForwardingAddresses(users, False)
elif showWhat in ['teamdrive', 'teamdrives']: elif showWhat in shared_drive_values:
printShowTeamDrives(users, False) printShowSharedDrives(users, False)
elif showWhat in ['teamdriveinfo']: elif showWhat in ['shareddriveinfo', 'teamdriveinfo']:
doGetTeamDriveInfo(users) doGetSharedDriveInfo(users)
elif showWhat in ['contactdelegate', 'contactdelegates']: elif showWhat in ['contactdelegate', 'contactdelegates']:
gapi_contactdelegation.print_(users, False) gapi_contactdelegation.print_(users, False)
elif showWhat in ['holds', 'vaultholds']: elif showWhat in ['holds', 'vaultholds']:
@@ -11965,8 +12013,8 @@ def ProcessGAMCommand(args):
printShowSmime(users, True) printShowSmime(users, True)
elif printWhat in ['token', 'tokens', 'oauth', '3lo']: elif printWhat in ['token', 'tokens', 'oauth', '3lo']:
printShowTokens(5, 'users', users, True) printShowTokens(5, 'users', users, True)
elif printWhat in ['teamdrive', 'teamdrives']: elif printWhat in shared_drive_values:
printShowTeamDrives(users, True) printShowSharedDrives(users, True)
elif printWhat in ['contactdelegate', 'contactdelegates']: elif printWhat in ['contactdelegate', 'contactdelegates']:
gapi_contactdelegation.print_(users, True) gapi_contactdelegation.print_(users, True)
elif printWhat in ['labels']: elif printWhat in ['labels']:
@@ -12049,8 +12097,8 @@ def ProcessGAMCommand(args):
deleteSendAs(users) deleteSendAs(users)
elif delWhat == 'smime': elif delWhat == 'smime':
deleteSmime(users) deleteSmime(users)
elif delWhat == 'teamdrive': elif delWhat in shared_drive_values:
doDeleteTeamDrive(users) doDeleteSharedDrive(users)
elif delWhat == 'contactdelegate': elif delWhat == 'contactdelegate':
gapi_contactdelegation.delete(users) gapi_contactdelegation.delete(users)
else: else:
@@ -12083,8 +12131,8 @@ def ProcessGAMCommand(args):
addUpdateSendAs(users, 5, True) addUpdateSendAs(users, 5, True)
elif addWhat == 'smime': elif addWhat == 'smime':
addSmime(users) addSmime(users)
elif addWhat == 'teamdrive': elif addWhat in shared_drive_values:
doCreateTeamDrive(users) doCreateSharedDrive(users)
elif addWhat == 'contactdelegate': elif addWhat == 'contactdelegate':
gapi_contactdelegation.create(users) gapi_contactdelegation.create(users)
else: else:
@@ -12125,8 +12173,8 @@ def ProcessGAMCommand(args):
addUpdateSendAs(users, 5, False) addUpdateSendAs(users, 5, False)
elif updateWhat == 'smime': elif updateWhat == 'smime':
updateSmime(users) updateSmime(users)
elif updateWhat == 'teamdrive': elif updateWhat in shared_drive_values:
doUpdateTeamDrive(users) doUpdateSharedDrive(users)
else: else:
controlflow.invalid_argument_exit(updateWhat, controlflow.invalid_argument_exit(updateWhat,
'gam <users> update') 'gam <users> update')

274
src/gam/gapi/caa.py Normal file
View File

@@ -0,0 +1,274 @@
import string
import sys
import googleapiclient.errors
import gam
from gam.var import *
from gam import controlflow
from gam import display
from gam import gapi
from gam import utils
from gam.gapi import errors as gapi_errors
from gam.gapi import cloudresourcemanager as gapi_crm
THROW_REASONS = [gapi_errors.ErrorReason.FOUR_O_THREE]
def _gen_role_error(caa):
sa_email = caa._http.credentials.signer_email
role_error = f'Please grant service account {sa_email} the Access Context Manager Editor role to your GCP organization.'
controlflow.system_error_exit(2, role_error)
def build():
return gam.buildGAPIServiceObject('accesscontextmanager',
act_as=None)
def get_access_policy(caa=None):
if not caa:
caa = build()
parent = gapi_crm.get_org_id()
if not parent:
_gen_role_error(caa)
try:
aps = gapi.get_all_pages(caa.accessPolicies(),
'list',
'accessPolicies',
throw_reasons=THROW_REASONS,
parent=parent,
fields='accessPolicies(name,title)')
except googleapiclient.errors.HttpError:
_gen_role_error(caa)
if not aps:
controlflow.system_error_exit(2, 'You don\'t seem to have any access policies. That is odd.')
elif len(aps) == 1:
return aps[0]['name']
for ap in aps:
if ap.get('title') == 'Access policy created in Cloud Identity Console':
return ap['name']
controlflow.system_error_exit(2, ' Could not find a org level access policy. That is odd.')
def printshow_access_levels(csvFormat):
caa = build()
ap_name = get_access_policy(caa)
if csvFormat:
todrive = False
csvRows = []
titles = ['name', 'title']
i = 3
while i < len(sys.argv):
myarg = sys.argv[i].lower()
if csvFormat and myarg == 'todrive':
todrive = True
i += 1
else:
controlflow.invalid_argument_exit(sys.argv[i],
f"gam {['show', 'print'][csvFormat]} caalevels")
try:
levels = gapi.get_all_pages(caa.accessPolicies().accessLevels(),
'list',
'accessLevels',
throw_reasons=THROW_REASONS,
parent=ap_name,
accessLevelFormat='CEL', fields='*')
except googleapiclient.errors.HttpError:
_gen_role_error(caa)
if not csvFormat:
for level in levels:
display.print_json(level)
print()
else:
for level in levels:
display.add_row_titles_to_csv_file(
utils.flatten_json(level),
csvRows, titles)
display.write_csv_file(csvRows, titles, 'CAA Levels', todrive)
def build_os_constraints(constraints):
consts_obj = []
constraints = constraints.upper().split(',')
valid_os_types = ['DESKTOP_MAC', 'DESKTOP_WINDOWS', 'DESKTOP_LINUX',
'DESKTOP_CHROME_OS', 'VERIFIED_DESKTOP_CHROME_OS', 'ANDROID', 'IOS']
for constraint in constraints:
new_const = {}
if ':' in constraint:
new_const['osType'], new_const['minimumVersion'] = constraint.split(':')
else:
new_const['osType'] = constraint
if new_const['osType'] not in valid_os_types:
controlflow.system_error_exit(2, f'expected os type of {", ".join(valid_os_types)} got {new_const["osType"]}')
if new_const['osType'] == 'VERIFIED_DESKTOP_CHROME_OS':
new_const['osType'] = 'DESKTOP_CHROME_OS'
new_const['requireVerifiedChromeOs'] = True
consts_obj.append(new_const)
return consts_obj
def build_device_policy(i, schemas):
device_policy = {}
while True:
myarg = sys.argv[i].replace('_', '').lower()
if myarg == 'requirescreenlock':
device_policy['requireScreenLock'] = gam.getBoolean(sys.argv[i+1], myarg)
i += 2
elif myarg == 'allowedencryptionstatuses':
allowed_statuses = gapi.get_enum_values_minus_unspecified(schemas["DevicePolicy"]["properties"]["allowedEncryptionStatuses"]["items"]["enum"])
device_policy['allowedEncryptionStatuses'] = sys.argv[i+1].upper().split(',')
for status in device_policy['allowedEncryptionStatuses']:
if status not in allowed_statuses:
controlflow.system_error_exit(2, f'expected encryption status of {", ".join(allowed_statuses)} got {status}')
i += 2
elif myarg == 'osconstraints':
device_policy['osConstraints'] = build_os_constraints(sys.argv[i+1])
i += 2
elif myarg == 'alloweddevicemanagementlevels':
allowed_levels = gapi.get_enum_values_minus_unspecified(schemas["DevicePolicy"]["properties"]["allowedDeviceManagementLevels"]["items"]["enum"])
device_policy['allowedDeviceManagementLevels'] = sys.argv[i+1].upper().split(',')
for level in device_policy['allowedDeviceManagementLevels']:
if level == 'ADVANCED':
level = 'COMPLETE'
if level not in allowed_levels:
controlflow.system_error_exit(2, f'expected device management level of {", ".join(allowed_levels)} got {level}')
i += 2
elif myarg == 'requireadminapproval':
device_policy['requireAdminApproval'] = gam.getBoolean(sys.argv[i+1], myarg)
i += 2
elif myarg == 'requirecorpowned':
device_policy['requireCorpOwned'] = gam.getBoolean(sys.argv[i+1], myarg)
i += 2
elif myarg == 'enddevicepolicy':
i += 1
break
else:
controlflow.invalid_argument_exit(myarg, 'gam create/update caalevel')
return i, device_policy
def build_condition(i, schemas):
condition = {}
while True:
myarg = sys.argv[i].replace('_', '').lower()
if myarg == 'ipsubnetworks':
condition['ipSubnetworks'] = sys.argv[i+1].split(',')
i += 2
elif myarg == 'devicepolicy':
i += 1
i, condition['devicePolicy'] = build_device_policy(i, schemas)
elif myarg == 'requiredaccesslevels':
condition['requiredAccessLevels'] = sys.argv[i+1].split(',')
i += 2
elif myarg == 'negate':
condition['negate'] = gam.getBoolean(sys.argv[i+1], myarg)
i += 2
elif myarg == 'members':
condition['members'] = sys.argv[i+1].split(',')
i += 2
elif myarg == 'regions':
condition['regions'] = sys.argv[i+1].upper().split(',')
i += 2
elif myarg == 'endcondition':
i += 1
break
else:
controlflow.invalid_argument_exit(myarg, 'gam create/update caalevel')
return i, condition
def build_basic_level(i, schemas):
basic_level = {'conditions': []}
valid_functions = gapi.get_enum_values_minus_unspecified(schemas['BasicLevel']['properties']['combiningFunction']['enum'])
while i < len(sys.argv):
myarg = sys.argv[i].replace('_', '').lower()
if myarg == 'combiningfunction':
combiningFunction = sys.argv[i+1].upper()
if combiningFunction not in valid_functions:
controlflow.system_error_exit(2, f'expected combining function of {",".join(valid_functions)} got {combiningFunction}')
basic_level['combiningFunction'] = combiningFunction
i += 2
elif myarg == 'condition':
i += 1
i, condition = build_condition(i, schemas)
basic_level['conditions'].append(condition)
else:
controlflow.invalid_argument_exit(myarg, 'gam create/update caalevel')
return i, basic_level
def build_caa_level(i, caa, body):
while i < len(sys.argv):
myarg = sys.argv[i].lower().replace('_', '')
if myarg == 'basic':
schemas = caa._rootDesc['schemas']
i += 1
i, body['basic'] = build_basic_level(i, schemas)
elif myarg == 'custom':
body['custom'] = {'expr': {'expression': sys.argv[i+1], 'title': 'expr'}}
i += 2
elif myarg == 'description':
body['description'] = sys.argv[i+1]
i += 2
else:
controlflow.invalid_argument_exit(myarg, 'gam create/update caalevel')
def create_access_level():
caa = build()
ap_name = get_access_policy(caa)
title = sys.argv[3].replace(' ', '_')
allowed_title_chars = string.ascii_letters + string.digits + '_'
name = ''.join([c for c in title if c in allowed_title_chars])[:50]
name = f'{ap_name}/accessLevels/{name}'
body = {
'name': name,
'title': title,
}
build_caa_level(4, caa, body)
print(f'Creating access level {name}...')
try:
gapi.call(caa.accessPolicies().accessLevels(),
'create',
throw_reasons=THROW_REASONS,
parent=ap_name,
body=body)
except googleapiclient.errors.HttpError:
_gen_role_error(caa)
def get_access_level_name(i, caa):
name = sys.argv[i]
if not name.startswith('accessPolicies/'):
ap_name = get_access_policy(caa)
name = f'{ap_name}/accessLevels/{name}'
return name
def update_access_level():
caa = build()
name = get_access_level_name(3, caa)
body = {}
build_caa_level(4, caa, body)
updateMask = ','.join(body.keys())
print(f'Updating access level {name}...')
try:
gapi.call(caa.accessPolicies().accessLevels(),
'patch',
throw_reasons=THROW_REASONS,
name=name,
updateMask=updateMask,
body=body)
except googleapiclient.errors.HttpError:
_gen_role_error(caa)
def delete_access_level():
caa = build()
name = get_access_level_name(3, caa)
print(f'Deleting access level {name}...')
try:
gapi.call(caa.accessPolicies().accessLevels(),
'delete',
name=name)
except googleapiclient.errors.HttpError:
_gen_role_error(caa)

View File

@@ -0,0 +1,72 @@
import sys
import googleapiclient
import gam
from gam.var import * # pylint: disable=unused-wildcard-import
from gam import controlflow
from gam import display
from gam import gapi
from gam import utils
from gam.gapi import errors as gapi_errors
from gam.gapi import cloudidentity as gapi_cloudidentity
from gam.gapi.directory import orgunits as gapi_directory_orgunits
def _get_orgunit_customerid():
customer = GC_Values[GC_CUSTOMER_ID]
if customer != MY_CUSTOMER and not customer.startswith('C'):
customer = f'C{customer}'
return f'customers/{customer}'
def move_shared_drive(driveId, orgUnit):
_, orgUnitId = gapi_directory_orgunits.getOrgUnitId(orgUnit)
orgUnitId = f'orgUnits/{orgUnitId[3:]}'
name = f'orgUnits/-/memberships/shared_drive;{driveId}'
ci = gapi_cloudidentity.build('cloudidentity_beta')
body = {
'customer': _get_orgunit_customerid(),
'destinationOrgUnit': orgUnitId,
}
return gapi.call(ci.orgUnits().memberships(),
'move',
name=name,
body=body)
def printshow_orgunit_shared_drives(csvFormat):
orgunit = '/'
if csvFormat:
todrive = False
csvRows = []
titles = ['name']
i = 3
while i < len(sys.argv):
myarg = sys.argv[i].lower()
if csvFormat and myarg == 'todrive':
todrive = True
i += 1
elif myarg in ['ou', 'org', 'orgunit']:
orgunit = sys.argv[i + 1]
i += 2
else:
controlflow.invalid_argument_exit(sys.argv[i],
f"gam {['show', 'print'][csvFormat]} oushareddrives")
ci = gapi_cloudidentity.build('cloudidentity_beta')
_, orgUnitId = gapi_directory_orgunits.getOrgUnitId(orgunit)
parent = f'orgUnits/{orgUnitId[3:]}'
filter_ = "type == 'shared_drive'"
sds = gapi.get_all_pages(ci.orgUnits().memberships(),
'list',
'orgMemberships',
parent=parent,
customer=_get_orgunit_customerid(),
filter=filter_)
if not csvFormat:
for sd in sds:
display.print_json(sd)
print()
else:
for sd in sds:
display.add_row_titles_to_csv_file(
utils.flatten_json(sd),
csvRows, titles)
display.write_csv_file(csvRows, titles, f'OrgUnit {orgunit} Shared Drives', todrive)

View File

@@ -0,0 +1,24 @@
import gam
from gam.var import GC_Values, GC_CUSTOMER_ID
from gam import controlflow
from gam import gapi
from gam.gapi.directory import customer as gapi_directory_customer
def build():
return gam.buildGAPIServiceObject('cloudresourcemanager',
act_as=None)
def get_org_id():
gapi_directory_customer.setTrueCustomerId()
crm = build()
query = f'directorycustomerid:{GC_Values[GC_CUSTOMER_ID]}'
orgs = gapi.get_all_pages(crm.organizations(),
'search',
'organizations',
query=query)
if len(orgs) < 1:
# return nothing and let calling API deal with it
# since caller knows what GCP role would serve best
return
return orgs[0]['name']

View File

@@ -160,42 +160,15 @@ def info(name=None, return_attrib=None):
print('') print('')
def print_(): def list_orgunits(listType='all', orgUnitPath=None, fields=None):
print_order = [
'orgUnitPath', 'orgUnitId', 'name', 'description', 'parentOrgUnitPath',
'parentOrgUnitId', 'blockInheritance'
]
cd = gapi_directory.build()
listType = 'all'
orgUnitPath = '/'
todrive = False
fields = ['orgUnitPath', 'name', 'orgUnitId', 'parentOrgUnitId']
titles = []
csvRows = []
parentOrgIds = []
retrievedOrgIds = [] retrievedOrgIds = []
i = 3 parentOrgIds = []
while i < len(sys.argv): cd = gapi_directory.build()
myarg = sys.argv[i].lower().replace('_', '')
if myarg == 'todrive':
todrive = True
i += 1
elif myarg == 'toplevelonly':
listType = 'children'
i += 1
elif myarg == 'fromparent':
orgUnitPath = getOrgUnitItem(sys.argv[i + 1])
i += 2
elif myarg == 'allfields':
fields = None
i += 1
elif myarg == 'fields':
fields += sys.argv[i + 1].split(',')
i += 2
else:
controlflow.invalid_argument_exit(sys.argv[i], 'gam print orgs')
gam.printGettingAllItems('Organizational Units', None)
if fields: if fields:
# Always get parentOrgUnitId so we can
# find missing parents
if 'parentOrgUnitId' not in fields:
fields.append('parentOrgUnitId')
get_fields = ','.join(fields) get_fields = ','.join(fields)
list_fields = f'organizationUnits({get_fields})' list_fields = f'organizationUnits({get_fields})'
else: else:
@@ -230,6 +203,44 @@ def print_():
orgunits.append(result) orgunits.append(result)
except: except:
pass pass
return orgunits
def print_():
print_order = [
'orgUnitPath', 'orgUnitId', 'name', 'description', 'parentOrgUnitPath',
'parentOrgUnitId', 'blockInheritance'
]
listType = 'all'
orgUnitPath = '/'
todrive = False
fields = ['orgUnitPath', 'name', 'orgUnitId', 'parentOrgUnitId']
titles = []
csvRows = []
i = 3
while i < len(sys.argv):
myarg = sys.argv[i].lower().replace('_', '')
if myarg == 'todrive':
todrive = True
i += 1
elif myarg == 'toplevelonly':
listType = 'children'
i += 1
elif myarg == 'fromparent':
orgUnitPath = getOrgUnitItem(sys.argv[i + 1])
i += 2
elif myarg == 'allfields':
fields = None
i += 1
elif myarg == 'fields':
fields += sys.argv[i + 1].split(',')
i += 2
else:
controlflow.invalid_argument_exit(sys.argv[i], 'gam print orgs')
gam.printGettingAllItems('Organizational Units', None)
orgunits = list_orgunits(listType=listType,
orgUnitPath=orgUnitPath,
fields=fields)
for row in orgunits: for row in orgunits:
orgEntity = {} orgEntity = {}
for key, value in list(row.items()): for key, value in list(row.items()):
@@ -248,6 +259,11 @@ def print_():
display.write_csv_file(csvRows, titles, 'Orgs', todrive) display.write_csv_file(csvRows, titles, 'Orgs', todrive)
def orgid_to_org_map():
orgunits = list_orgunits(fields=['orgUnitPath', 'orgUnitId'])
result = {ou['orgUnitId']:ou['orgUnitPath'] for ou in orgunits}
return result
def update(): def update():
cd = gapi_directory.build() cd = gapi_directory.build()
orgUnitPath = getOrgUnitItem(sys.argv[3]) orgUnitPath = getOrgUnitItem(sys.argv[3])

View File

@@ -0,0 +1,8 @@
import gam
def build(user=None):
if not user:
user = gam._get_admin_email()
userEmail = gam.convertUIDtoEmailAddress(user)
return (userEmail, gam.buildGAPIServiceObject('drive3', userEmail))

View File

@@ -0,0 +1,26 @@
"""Methods related to Drive API Shared Drives"""
import sys
import gam
from gam.var import GC_CUSTOMER_ID, GC_Values, MY_CUSTOMER, SORTORDER_CHOICES_MAP
from gam import controlflow
from gam import display
from gam import gapi
from gam.gapi import errors as gapi_errors
from gam.gapi import drive as gapi_drive
def drive_name_to_id(name, drive=None):
if not drive:
_, drive = gapi_drive.build()
q = f"name = '{name}'"
sds = gapi.get_all_pages(drive.drives(),
'list',
'drives',
q=q,
useDomainAdminAccess=True)
if len(sds) == 0:
controlflow.system_error_exit(3, f'Could not find shared drive named "{name}"')
elif len(sds) > 1:
controlflow.system_error_exit(3, f'Got more than one shared drive named "{name}"')
return sds[0]['id']

View File

@@ -120,6 +120,7 @@ class ErrorReason(Enum):
FAILED_PRECONDITION = 'failedPrecondition' FAILED_PRECONDITION = 'failedPrecondition'
FORBIDDEN = 'forbidden' FORBIDDEN = 'forbidden'
FIVE_O_THREE = '503' FIVE_O_THREE = '503'
FIVE_O_O = '500'
FOUR_O_NINE = '409' FOUR_O_NINE = '409'
FOUR_O_O = '400' FOUR_O_O = '400'
FOUR_O_FOUR = '404' FOUR_O_FOUR = '404'
@@ -159,6 +160,7 @@ DEFAULT_RETRY_REASONS = [
ErrorReason.GATEWAY_TIMEOUT, ErrorReason.GATEWAY_TIMEOUT,
ErrorReason.INTERNAL_ERROR, ErrorReason.INTERNAL_ERROR,
ErrorReason.FOUR_TWO_NINE, ErrorReason.FOUR_TWO_NINE,
ErrorReason.FIVE_O_O,
ErrorReason.FIVE_O_THREE, ErrorReason.FIVE_O_THREE,
] ]
GMAIL_THROW_REASONS = [ErrorReason.SERVICE_NOT_AVAILABLE] GMAIL_THROW_REASONS = [ErrorReason.SERVICE_NOT_AVAILABLE]

View File

@@ -56,7 +56,7 @@ def create(users, sku=None):
productId = sys.argv[i+1] productId = sys.argv[i+1]
i += 2 i += 2
for user in users: for user in users:
print(f'Adding license {sku_name} from to {user}') print(f'Adding license {sku_name} to {user}')
gapi.call(lic.licenseAssignments(), gapi.call(lic.licenseAssignments(),
'insert', 'insert',
soft_errors=True, soft_errors=True,

View File

@@ -8,7 +8,7 @@ import platform
import re import re
GAM_AUTHOR = 'Jay Lee <jay0lee@gmail.com>' GAM_AUTHOR = 'Jay Lee <jay0lee@gmail.com>'
GAM_VERSION = '6.18' GAM_VERSION = '6.20'
GAM_LICENSE = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)' GAM_LICENSE = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)'
GAM_URL = 'https://git.io/gam' GAM_URL = 'https://git.io/gam'
@@ -193,6 +193,11 @@ SKUS = {
'wsentplus', 'workspaceenterpriseplus'], 'wsentplus', 'workspaceenterpriseplus'],
'displayName': 'Workspace Enterprise Plus' 'displayName': 'Workspace Enterprise Plus'
}, },
'1010020029': {
'product': 'Google-Apps',
'aliases': ['wes', 'workspaceenterprisestarter'],
'displayName': 'Workspace Enterprise Starter'
},
'1010020030': { '1010020030': {
'product': 'Google-Apps', 'product': 'Google-Apps',
'aliases': ['workspacefrontline', 'workspacefrontlineworker'], 'aliases': ['workspacefrontline', 'workspacefrontlineworker'],
@@ -301,6 +306,7 @@ API_NAME_MAPPING = {
} }
API_VER_MAPPING = { API_VER_MAPPING = {
'accesscontextmanager': 'v1',
'alertcenter': 'v1beta1', 'alertcenter': 'v1beta1',
'driveactivity': 'v2', 'driveactivity': 'v2',
'calendar': 'v3', 'calendar': 'v3',
@@ -1524,6 +1530,9 @@ MESSAGE_UPDATE_GAM_TO_64BIT = 'You\'re running a 32-bit version of GAM on a' \
MESSAGE_YOUR_SYSTEM_TIME_DIFFERS_FROM_GOOGLE_BY = 'Your system time differs' \ MESSAGE_YOUR_SYSTEM_TIME_DIFFERS_FROM_GOOGLE_BY = 'Your system time differs' \
' from %s by %s' ' from %s by %s'
shared_drive_values = ['teamdrive', 'teamdrives',
'shareddrive', 'shareddrives']
USER_ADDRESS_TYPES = ['home', 'work', 'other'] USER_ADDRESS_TYPES = ['home', 'work', 'other']
USER_EMAIL_TYPES = ['home', 'work', 'other'] USER_EMAIL_TYPES = ['home', 'work', 'other']
USER_EXTERNALID_TYPES = [ USER_EXTERNALID_TYPES = [

View File

@@ -1,3 +1,4 @@
accesscontextmanager.googleapis.com
admin.googleapis.com admin.googleapis.com
alertcenter.googleapis.com alertcenter.googleapis.com
calendar-json.googleapis.com calendar-json.googleapis.com
@@ -6,6 +7,7 @@ chromemanagement.googleapis.com
chromepolicy.googleapis.com chromepolicy.googleapis.com
classroom.googleapis.com classroom.googleapis.com
cloudidentity.googleapis.com cloudidentity.googleapis.com
cloudresourcemanager.googleapis.com
contacts.googleapis.com contacts.googleapis.com
drive.googleapis.com drive.googleapis.com
driveactivity.googleapis.com driveactivity.googleapis.com

View File

@@ -1,6 +1,6 @@
[metadata] [metadata]
name = GAM for Google Workspace name = GAM for Google Workspace
version = 6.0.14 version = 6.0.20
description = Command line management for Google Workspaces description = Command line management for Google Workspaces
long_description = file: readme.md long_description = file: readme.md
long_description_content_type = text/markdown long_description_content_type = text/markdown