From 40899de9899a4fa126be8cb0bc48ce3bee555432 Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Fri, 25 Oct 2024 16:01:15 -0700 Subject: [PATCH] Added command to display selected Cloud Identity policies. --- docs/Cloud-Identity-Policies.md | 28 +++- docs/Collections-of-Items.md | 4 +- docs/GamUpdates.md | 11 ++ docs/How-to-Upgrade-Legacy-GAM-to-GAM7.md | 4 +- docs/List-Items.md | 1 + docs/Version-and-Help.md | 12 +- src/GamCommands.txt | 11 +- src/GamUpdate.txt | 13 ++ src/gam/__init__.py | 166 ++++++++++++---------- src/gam/gamlib/glclargs.py | 2 + 10 files changed, 164 insertions(+), 88 deletions(-) diff --git a/docs/Cloud-Identity-Policies.md b/docs/Cloud-Identity-Policies.md index c09c744be..bac6154be 100644 --- a/docs/Cloud-Identity-Policies.md +++ b/docs/Cloud-Identity-Policies.md @@ -14,12 +14,15 @@ To use these commands you must update your client access authentication. ``` gam oauth create ... -[*] 19) Cloud Identity - Policy +[R] 19) Cloud Identity - Policy ``` ## Definitions ``` ::= policies/ + ::= "(,)*" + ::= + | | ``` ## Policies @@ -305,26 +308,41 @@ workspace_marketplace.apps_allowlist apps ``` ## Display Cloud Identity Policies +Display selected policies. +``` +gam info policies + [nowarnings] + [formatjson] +``` + +By default, policy warnings are displayed, use the 'nowarnings` option to suppress their display. + +By default, Gam displays the information as an indented list of keys and values. +* `formatjson` - Display the fields in JSON format. + +Display all or filtered policies. ``` gam show policies - [(filter )|(name )] [nowarnings] + [filter ] [nowarnings] [formatjson] ``` By default, all policies are displayed. * `filter ` - Display filtered policies, See https://github.com/taers232c/GAMADV-XTD3/wiki/Cloud-Identity-Policies -* `name ` - Display a specfic policy + +By default, policy warnings are displayed, use the 'nowarnings` option to suppress their display. By default, Gam displays the information as an indented list of keys and values. * `formatjson` - Display the fields in JSON format. ``` gam print policies [todrive *] - [(filter )|(name )] [nowarnings] + [filter ] [nowarnings] [formatjson [quotechar ]] ``` By default, all policies are displayed: * `filter ` - Display filtered policies, See https://github.com/taers232c/GAMADV-XTD3/wiki/Cloud-Identity-Policies -* `name ` - Display a specfic policy + +By default, policy warnings are displayed, use the 'nowarnings` option to suppress their display. 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. diff --git a/docs/Collections-of-Items.md b/docs/Collections-of-Items.md index 771e91f78..f35562fd6 100644 --- a/docs/Collections-of-Items.md +++ b/docs/Collections-of-Items.md @@ -1,4 +1,4 @@ -!# Collections of Items +# Collections of Items - [Python Regular Expressions](Python-Regular-Expressions) Match function - [Definitions](#definitions) - [ListSelector](#listselector) @@ -144,6 +144,8 @@ Data fields identified in a `csvkmd` argument. | | | | ::= | | | | + ::= + | | ::= | | | | ::= diff --git a/docs/GamUpdates.md b/docs/GamUpdates.md index 1ad719135..354d67602 100644 --- a/docs/GamUpdates.md +++ b/docs/GamUpdates.md @@ -10,6 +10,17 @@ Add the `-s` option to the end of the above commands to suppress creating the `g See [Downloads-Installs-GAM7](https://github.com/GAM-team/GAM/wiki/Downloads-Installs) for Windows or other options, including manual installation +### 7.00.30 + +Added command to display selected Cloud Identity policies. +``` +gam info policies + [nowarnings] + [formatjson] +``` + +Removed option `name ` from `gam print|show policies`; use `info policies`. + ### 7.00.29 Added option `name ` to `gam print|show policies` that displays diff --git a/docs/How-to-Upgrade-Legacy-GAM-to-GAM7.md b/docs/How-to-Upgrade-Legacy-GAM-to-GAM7.md index 33d4d9ed1..c7ddc4b24 100644 --- a/docs/How-to-Upgrade-Legacy-GAM-to-GAM7.md +++ b/docs/How-to-Upgrade-Legacy-GAM-to-GAM7.md @@ -251,7 +251,7 @@ writes the credentials into the file oauth2.txt. admin@server:/Users/admin$ rm -f /Users/admin/GAMConfig/oauth2.txt admin@server:/Users/admin$ gam version WARNING: Config File: /Users/admin/GAMConfig/gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: /Users/admin/GAMConfig/oauth2.txt, Not Found -GAM 7.00.29 - https://github.com/GAM-team/GAM - pyinstaller +GAM 7.00.30 - https://github.com/GAM-team/GAM - pyinstaller GAM Team Python 3.13.0 64-bit final MacOS Sonoma 14.5 x86_64 @@ -923,7 +923,7 @@ writes the credentials into the file oauth2.txt. C:\>del C:\GAMConfig\oauth2.txt C:\>gam version WARNING: Config File: C:\GAMConfig\gam.cfg, Section: DEFAULT, Item: oauth2_txt, Value: C:\GAMConfig\oauth2.txt, Not Found -GAM7 7.00.29 - https://github.com/GAM-team/GAM - pythonsource +GAM7 7.00.30 - https://github.com/GAM-team/GAM - pythonsource GAM Team Python 3.13.0 64-bit final Windows-10-10.0.17134 AMD64 diff --git a/docs/List-Items.md b/docs/List-Items.md index 436145fd1..92dbd1bdd 100644 --- a/docs/List-Items.md +++ b/docs/List-Items.md @@ -13,6 +13,7 @@ ::= "(,)*" ::= "(,)*" ::= "(,)*" + ::= "(,)*" ::= "(,)*" ::= "(,)*" ::= "(,)*" diff --git a/docs/Version-and-Help.md b/docs/Version-and-Help.md index 100fa1acf..ada2772bf 100644 --- a/docs/Version-and-Help.md +++ b/docs/Version-and-Help.md @@ -3,7 +3,7 @@ Print the current version of Gam with details ``` gam version -GAM 7.00.29 - https://github.com/GAM-team/GAM - pyinstaller +GAM 7.00.30 - https://github.com/GAM-team/GAM - pyinstaller GAM Team Python 3.13.0 64-bit final MacOS Sonoma 14.5 x86_64 @@ -15,7 +15,7 @@ Time: 2023-06-02T21:10:00-07:00 Print the current version of Gam with details and time offset information ``` gam version timeoffset -GAM 7.00.29 - https://github.com/GAM-team/GAM - pyinstaller +GAM 7.00.30 - https://github.com/GAM-team/GAM - pyinstaller GAM Team Python 3.13.0 64-bit final MacOS Sonoma 14.5 x86_64 @@ -27,7 +27,7 @@ Your system time differs from www.googleapis.com by less than 1 second Print the current version of Gam with extended details and SSL information ``` gam version extended -GAM 7.00.29 - https://github.com/GAM-team/GAM - pyinstaller +GAM 7.00.30 - https://github.com/GAM-team/GAM - pyinstaller GAM Team Python 3.13.0 64-bit final MacOS Sonoma 14.5 x86_64 @@ -64,7 +64,7 @@ MacOS High Sierra 10.13.6 x86_64 Path: /Users/Admin/bin/gam7 Version Check: Current: 5.35.08 - Latest: 7.00.29 + Latest: 7.00.30 echo $? 1 ``` @@ -72,7 +72,7 @@ echo $? Print the current version number without details ``` gam version simple -7.00.29 +7.00.30 ``` In Linux/MacOS you can do: ``` @@ -82,7 +82,7 @@ echo $VER Print the current version of Gam and address of this Wiki ``` gam help -GAM 7.00.29 - https://github.com/GAM-team/GAM +GAM 7.00.30 - https://github.com/GAM-team/GAM GAM Team Python 3.13.0 64-bit final MacOS Sonoma 14.5 x86_64 diff --git a/src/GamCommands.txt b/src/GamCommands.txt index 091a6259f..2f748eef4 100644 --- a/src/GamCommands.txt +++ b/src/GamCommands.txt @@ -665,6 +665,7 @@ If an item contains spaces, it should be surrounded by ". ::= "(,)*" ::= "(,)*" ::= "(,)*" + ::= "(,)*" ::= "(,)*" ::= "(,)*" ::= "(,)*" @@ -1001,6 +1002,8 @@ Specify a collection of items by directly specifying them; the item type is dete | | | | ::= | | | | + ::= + | | ::= | | | | ::= @@ -4072,11 +4075,15 @@ gam update deviceuserstate [clientid ] # Cloud Identity Policies +gam info policies + [nowarnings] + [formatjson] + gam print policies [todrive *] - [(filter )|(name )] [nowarnings] + [filter ] [nowarnings] [formatjson [quotechar ]] gam show policies - [(filter )|(name )] [nowarnings] + [filter ] [nowarnings] [formatjson] # Inbound SSO diff --git a/src/GamUpdate.txt b/src/GamUpdate.txt index 83da5daa4..e81ec68dd 100644 --- a/src/GamUpdate.txt +++ b/src/GamUpdate.txt @@ -1,3 +1,14 @@ +7.00.30 + +Added command to display selected Cloud Identity policies. +``` +gam info policies + [nowarnings] + [formatjson] +``` + +Removed option `name ` from `gam print|show policies`; use `info policies`. + 7.00.29 Added option `name ` to `gam print|show policies` that displays @@ -94,6 +105,8 @@ as files/folders are being identified for processing. Added option `` to `gam create|update caalevel`. +Updated to Python 3.13.0. + 7.00.15 Added options `timestamp []` and `timeformat ` to `gam create|update drivefile` that allow diff --git a/src/gam/__init__.py b/src/gam/__init__.py index 13232cea6..487238348 100755 --- a/src/gam/__init__.py +++ b/src/gam/__init__.py @@ -25,7 +25,7 @@ """ __author__ = 'GAM Team ' -__version__ = '7.00.29' +__version__ = '7.00.30' __license__ = 'Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)' #pylint: disable=wrong-import-position @@ -35089,28 +35089,82 @@ def updateFieldsForCIGroupMatchPatterns(matchPatterns, fieldsList, csvPF=None): CIPOLICY_TIME_OBJECTS = {'createTime', 'updateTime'} +# Policies where GAM should offer additional guidance and information +CIPOLICY_ADDITIONAL_WARNINGS = { + 'settings/drive_and_docs.external_sharing': { + 'warningType': 'SUPERSEDED_POLICY', + 'warningMessage': 'CAUTION: Drive Sharing settings are superseded by Drive Trust Rules if Trust Rules has been enabled for your domain. Drive Trust Rule settings are not available in the Policy API today so GAM is not able to check if Trust Rules is enabled and if the settings/drive_and_docs.external_sharing policies are actually in effect for your domain. If Drive Trust Rules is enabled for your domain then this settings/drive_and_docs.external_sharing policy does not accurately reflect your current Drive sharing settings.' + } +} + +def _cleanPolicy(policy, add_warnings, cd, groups_ci): + # convert any wordlists into spaced strings to reduce output complexity + if policy['setting']['type'] == 'settings/detector.word_list': + policy['setting']['value']['wordList'] = ' '.join(policy['setting']['value']['wordList']['words']) + # add any warnings to applicable policies + if add_warnings and policy['setting']['type'] in CIPOLICY_ADDITIONAL_WARNINGS: + policy['warning'] = CIPOLICY_ADDITIONAL_WARNINGS[policy['setting']['type']] + if groupId := policy['policyQuery'].get('group'): + _, _, policy['policyQuery']['groupEmail'] = convertGroupCloudIDToEmail(groups_ci, groupId) + # all groups are in the root OU so the orgUnit attribute is useless + policy['policyQuery'].pop('orgUnit', None) + elif orgId := policy['policyQuery'].get('orgUnit'): + policy['policyQuery']['orgUnitPath'] = convertOrgUnitIDtoPath(cd, orgId) + +def _showPolicy(policy, FJQC, i=0, count=0): + if FJQC is not None and FJQC.formatJSON: + printLine(json.dumps(cleanJSON(policy, timeObjects=CIPOLICY_TIME_OBJECTS), + ensure_ascii=False, + sort_keys=True)) + return + printEntity([Ent.POLICY, policy['name']], i, count) + Ind.Increment() + policy.pop('name') + showJSON(None, policy, timeObjects=CIPOLICY_TIME_OBJECTS) + printBlankLine() + Ind.Decrement() + +# gam info policies +# [nowarnings] [formatjson] +def doInfoCIPolicies(): + groups_ci = buildGAPIObject(API.CLOUDIDENTITY_GROUPS) + ci = buildGAPIObject(API.CLOUDIDENTITY_POLICY) + cd = buildGAPIObject(API.DIRECTORY) + entityList = getEntityList(Cmd.OB_CIPOLICY_NAME_ENTITY) + FJQC = FormatJSONQuoteChar() + add_warnings = True + while Cmd.ArgumentsRemaining(): + myarg = getArgument() + if myarg == 'nowarnings': + add_warnings = False + else: + FJQC.GetFormatJSON(myarg) + i = 0 + count = len(entityList) + for pname in entityList: + i += 1 + if not pname.startswith('policies/'): + pname = 'policies/'+pname + try: + policy = callGAPI(ci.policies(), 'get', + bailOnInternalError=True, + throwReasons=[GAPI.INVALID, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.INTERNAL_ERROR], + name=pname, + fields='name,policyQuery(group,orgUnit,sortOrder),type,setting') + _cleanPolicy(policy, add_warnings, cd, groups_ci) + _showPolicy(policy, FJQC, i, count) + except (GAPI.invalid, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.internalError) as e: + entityActionFailedWarning([Ent.POLICY, pname], str(e), i, count) + continue + # gam print policies [todrive *] -# [(filter )|(name )] [nowarnings] +# [filter ] [nowarnings] # [formatjson [quotechar ]] # gam show policies -# [(filter )|(name )] [nowarnings] +# [filter ] [nowarnings] # [formatjson] def doPrintShowCIPolicies(): - def _showPolicy(policy, FJQC, i=0, count=0): - if FJQC is not None and FJQC.formatJSON: - printLine(json.dumps(cleanJSON(policy, timeObjects=CIPOLICY_TIME_OBJECTS), - ensure_ascii=False, - sort_keys=True)) - return - printEntity([Ent.POLICY, policy['name']], i, count) - Ind.Increment() - policy.pop('name') - showJSON(None, policy, timeObjects=CIPOLICY_TIME_OBJECTS) - if not pname: - printBlankLine() - Ind.Decrement() - def _printPolicy(policy): row = flattenJSON(policy, timeObjects=CIPOLICY_TIME_OBJECTS) if not FJQC.formatJSON: @@ -35121,19 +35175,12 @@ def _printPolicy(policy): ensure_ascii=False, sort_keys=True)}) - # Policies where GAM should offer additional guidance and information - warnings = { - 'settings/drive_and_docs.external_sharing': { - 'warningType': 'SUPERSEDED_POLICY', - 'warningMessage': 'CAUTION: Drive Sharing settings are superseded by Drive Trust Rules if Trust Rules has been enabled for your domain. Drive Trust Rule settings are not available in the Policy API today so GAM is not able to check if Trust Rules is enabled and if the settings/drive_and_docs.external_sharing policies are actually in effect for your domain. If Drive Trust Rules is enabled for your domain then this settings/drive_and_docs.external_sharing policy does not accurately reflect your current Drive sharing settings.' - } - } groups_ci = buildGAPIObject(API.CLOUDIDENTITY_GROUPS) ci = buildGAPIObject(API.CLOUDIDENTITY_POLICY) cd = buildGAPIObject(API.DIRECTORY) csvPF = CSVPrintFile(['name']) if Act.csvFormat() else None FJQC = FormatJSONQuoteChar(csvPF) - ifilter = pname = None + ifilter = None add_warnings = True while Cmd.ArgumentsRemaining(): myarg = getArgument() @@ -35141,63 +35188,36 @@ def _printPolicy(policy): csvPF.GetTodriveParameters() elif myarg == 'filter': ifilter = getString(Cmd.OB_STRING) - elif myarg == 'name': - pname = getString(Cmd.OB_STRING) elif myarg == 'nowarnings': add_warnings = False else: FJQC.GetFormatJSONQuoteChar(myarg, True) - if ifilter and pname: - usageErrorExit(Msg.ARE_MUTUALLY_EXCLUSIVE.format('filter', 'name')) - throwReasons = [GAPI.INVALID, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED, GAPI.INTERNAL_ERROR] - fields = 'name,policyQuery(group,orgUnit,sortOrder),type,setting' - if not pname: - printGettingAllAccountEntities(Ent.POLICY, ifilter) - pageMessage = getPageMessage() - try: - policies = callGAPIpages(ci.policies(), 'list', 'policies', - throwReasons=throwReasons, - pageMessage=pageMessage, - filter=ifilter, - fields=f'nextPageToken,policies({fields})', - pageSize=100) - except (GAPI.invalid, GAPI.invalidArgument, GAPI.permissionDenied) as e: - entityActionFailedExit([Ent.POLICY, None], str(e)) - else: - try: - policies = [callGAPI(ci.policies(), 'get', - bailOnInternalError=True, - throwReasons=throwReasons, - name=pname, - fields=fields)] - except (GAPI.invalid, GAPI.invalidArgument, GAPI.permissionDenied, GAPI.internalError) as e: - entityActionFailedExit([Ent.POLICY, pname], str(e)) + printGettingAllAccountEntities(Ent.POLICY, ifilter) + pageMessage = getPageMessage() + try: + policies = callGAPIpages(ci.policies(), 'list', 'policies', + pageMessage=pageMessage, + throwReasons=[GAPI.INVALID, GAPI.INVALID_ARGUMENT, GAPI.PERMISSION_DENIED], + filter=ifilter, + fields='nextPageToken,policies(name,policyQuery(group,orgUnit,sortOrder),type,setting)', + pageSize=100) + except (GAPI.invalid, GAPI.invalidArgument, GAPI.permissionDenied) as e: + entityActionFailedExit([Ent.POLICY, None], str(e)) # Google returns unordered results, sort them by setting type policies = sorted(policies, key=lambda p: p.get('setting', {}).get('type', '')) - for policy in policies: - # convert any wordlists into spaced strings to reduce output complexity - if policy['setting']['type'] == 'settings/detector.word_list': - policy['setting']['value']['wordList'] = ' '.join(policy['setting']['value']['wordList']['words']) - # add any warnings to applicable policies - if add_warnings and policy['setting']['type'] in warnings: - policy['warning'] = warnings[policy['setting']['type']] - if groupId := policy['policyQuery'].get('group'): - _, _, policy['policyQuery']['groupEmail'] = convertGroupCloudIDToEmail(groups_ci, groupId) - # all groups are in the root OU so the orgUnit attribute is useless - policy['policyQuery'].pop('orgUnit', None) - elif orgId := policy['policyQuery'].get('orgUnit'): - policy['policyQuery']['orgUnitPath'] = convertOrgUnitIDtoPath(cd, orgId) if not csvPF: - jcount = len(policies) - performActionNumItems(jcount, Ent.POLICY) + count = len(policies) + performActionNumItems(count, Ent.POLICY) Ind.Increment() - j = 0 + i = 0 for policy in policies: - j += 1 - _showPolicy(policy, FJQC, j, jcount) + i += 1 + _cleanPolicy(policy, add_warnings, cd, groups_ci) + _showPolicy(policy, FJQC, i, count) Ind.Decrement() else: for policy in policies: + _cleanPolicy(policy, add_warnings, cd, groups_ci) _printPolicy(policy) if csvPF: csvPF.writeCSVfile('Policies') @@ -75125,6 +75145,7 @@ def doPrintShowCAALevels(): Cmd.ARG_CHROMESCHEMA: doInfoChromePolicySchemas, Cmd.ARG_CIGROUP: doInfoCIGroups, Cmd.ARG_CIGROUPMEMBERS: doInfoCIGroupMembers, + Cmd.ARG_CIPOLICY: doInfoCIPolicies, Cmd.ARG_CONTACT: doInfoDomainContacts, Cmd.ARG_COURSE: doInfoCourse, Cmd.ARG_COURSES: doInfoCourses, @@ -75212,7 +75233,7 @@ def doPrintShowCAALevels(): Cmd.ARG_CHROMEVERSIONS: doPrintShowChromeVersions, Cmd.ARG_CIGROUP: doPrintCIGroups, Cmd.ARG_CIGROUPMEMBERS: doPrintCIGroupMembers, - Cmd.ARG_CIPOLICIES: doPrintShowCIPolicies, + Cmd.ARG_CIPOLICY: doPrintShowCIPolicies, Cmd.ARG_CLASSROOMINVITATION: doPrintShowClassroomInvitations, Cmd.ARG_CONTACT: doPrintShowDomainContacts, Cmd.ARG_COURSE: doPrintCourses, @@ -75341,7 +75362,7 @@ def doPrintShowCAALevels(): Cmd.ARG_CHROMESCHEMA: doPrintShowChromeSchemas, Cmd.ARG_CHROMEVERSIONS: doPrintShowChromeVersions, Cmd.ARG_CIGROUPMEMBERS: doShowCIGroupMembers, - Cmd.ARG_CIPOLICIES: doPrintShowCIPolicies, + Cmd.ARG_CIPOLICY: doPrintShowCIPolicies, Cmd.ARG_CLASSROOMINVITATION: doPrintShowClassroomInvitations, Cmd.ARG_CONTACT: doPrintShowDomainContacts, Cmd.ARG_CROSTELEMETRY: doInfoPrintShowCrOSTelemetry, @@ -75525,6 +75546,7 @@ def doPrintShowCAALevels(): Cmd.ARG_CIGROUPSMEMBERS: Cmd.ARG_CIGROUPMEMBERS, Cmd.ARG_CIMEMBER: Cmd.ARG_CIGROUPMEMBERS, Cmd.ARG_CIMEMBERS: Cmd.ARG_CIGROUPMEMBERS, + Cmd.ARG_CIPOLICIES: Cmd.ARG_CIPOLICY, Cmd.ARG_CLASS: Cmd.ARG_COURSE, Cmd.ARG_CLASSES: Cmd.ARG_COURSES, Cmd.ARG_CLASSPARTICIPANTS: Cmd.ARG_COURSEPARTICIPANTS, diff --git a/src/gam/gamlib/glclargs.py b/src/gam/gamlib/glclargs.py index 1629a1fb4..1f9267674 100644 --- a/src/gam/gamlib/glclargs.py +++ b/src/gam/gamlib/glclargs.py @@ -493,6 +493,7 @@ class GamCLArgs(): ARG_CIGROUPSMEMBERS = 'cigroupsmembers' ARG_CIMEMBER = 'cimember' ARG_CIMEMBERS = 'cimembers' + ARG_CIPOLICY = 'policy' ARG_CIPOLICIES = 'policies' ARG_CLASS = 'class' ARG_CLASSES = 'classes' @@ -844,6 +845,7 @@ class GamCLArgs(): OB_CHROME_VERSION = 'ChromeVersion' OB_CIDR_NETMASK = 'CIDRnetmask' OB_CIGROUP_ALIAS_LIST = "CIGroupAliasList" + OB_CIPOLICY_NAME_ENTITY = 'CIPolicyNameEntity' OB_CLASSROOM_INVITATION_ID_ENTITY = 'ClassroomInvitationIDEntity' OB_CLIENT_ID = 'ClientID' OB_COLLABORATOR_ITEM = 'CollaboratorItem'