From 8091e23e00bfd3e139bf01542fca64ff060e8744 Mon Sep 17 00:00:00 2001 From: Ross Scroggs Date: Fri, 2 Apr 2021 11:44:58 -0700 Subject: [PATCH] Implement Chrome Management API calls (#1350) * Implement Chrome Management API calls * User start/end in print chromeappdevices * Handle a Chrome version without a version field --- src/GamCommands.txt | 15 ++ src/gam/__init__.py | 12 ++ src/gam/gapi/chromemanagement.py | 265 +++++++++++++++++++++++++++++++ src/gam/var.py | 1 + src/project-apis.txt | 1 + 5 files changed, 294 insertions(+) create mode 100644 src/gam/gapi/chromemanagement.py diff --git a/src/GamCommands.txt b/src/GamCommands.txt index 1b0d8ac1..00f30f86 100644 --- a/src/GamCommands.txt +++ b/src/GamCommands.txt @@ -1231,6 +1231,21 @@ The listlimit argument limits the number of recent users, time ranges a The start and end arguments filter the time ranges. Delimiter defaults to comma. +gam print chromeapps [todrive] + [ou|org|orgunit ] + [filter ] + [orderby appname|apptype|installtype|numberofpermissions|totalinstallcount] + +gam print chromeappdevices [todrive] + appid apptype extension|app|theme|hostedapp|androidapp + [ou|org|orgunit ] + [start ] [end ] + [orderby deviceid|machine] + +gam print chromeversions [todrive] + [ou|org|orgunit ] + [start ] [end ] [recentfirst] + gam delete chromepolicy + ou|org|orgunit [(printerid )|(appid )] gam update chromepolicy ( ( )+)+ ou|org|orgunit [(printerid )|(appid )] gam show chromepolicy ou|org|orgunit [(printerid )|(appid )] diff --git a/src/gam/__init__.py b/src/gam/__init__.py index 6fee9a99..f61958a5 100755 --- a/src/gam/__init__.py +++ b/src/gam/__init__.py @@ -53,6 +53,7 @@ from gam import fileutils from gam.gapi import calendar as gapi_calendar from gam.gapi import cloudidentity as gapi_cloudidentity from gam.gapi import cbcm as gapi_cbcm +from gam.gapi import chromemanagement as gapi_chromemanagement from gam.gapi import chromepolicy as gapi_chromepolicy from gam.gapi.cloudidentity import devices as gapi_cloudidentity_devices from gam.gapi.cloudidentity import groups as gapi_cloudidentity_groups @@ -10248,6 +10249,11 @@ OAUTH2_SCOPES = [ 'subscopes': ['readonly'], 'scopes': 'https://www.googleapis.com/auth/admin.directory.device.chromebrowsers', }, + { + 'name': 'Chrome Management API - read only', + 'subscope': [], + 'scopes': ['https://www.googleapis.com/auth/chrome.management.reports.readonly'], + }, { 'name': 'Chrome Policy API', 'subscope': ['readonly'], @@ -11493,6 +11499,12 @@ def ProcessGAMCommand(args): gapi_directory_printers.print_models() elif argument in ['printers']: gapi_directory_printers.print_() + elif argument in ['chromeapps']: + gapi_chromemanagement.printApps() + elif argument in ['chromeappdevices']: + gapi_chromemanagement.printAppDevices() + elif argument in ['chromeversions']: + gapi_chromemanagement.printVersions() else: controlflow.invalid_argument_exit(argument, 'gam print') sys.exit(0) diff --git a/src/gam/gapi/chromemanagement.py b/src/gam/gapi/chromemanagement.py new file mode 100644 index 00000000..e1537c59 --- /dev/null +++ b/src/gam/gapi/chromemanagement.py @@ -0,0 +1,265 @@ +"""Chrome Management API calls""" + +import sys + +import gam +from gam.var import GC_CUSTOMER_ID, GC_Values, MY_CUSTOMER +from gam.var import CROS_START_ARGUMENTS, CROS_END_ARGUMENTS +from gam.var import YYYYMMDD_FORMAT +from gam import controlflow +from gam import display +from gam import gapi +from gam.gapi.directory import orgunits as gapi_directory_orgunits +from gam.gapi.directory.cros import _getFilterDate + + +def _get_customerid(): + customer = GC_Values[GC_CUSTOMER_ID] + if customer != MY_CUSTOMER and customer[0] != 'C': + customer = 'C' + customer + return f'customers/{customer}' + + +def _get_orgunit(orgunit): + if orgunit.startswith('orgunits/'): + return orgunit + _, orgunitid = gapi_directory_orgunits.getOrgUnitId(orgunit) + return f'{orgunitid[3:]}' + + +def build(): + return gam.buildGAPIObject('chromemanagement') + + +CHROME_APPS_ORDERBY_CHOICE_MAP = { + 'appname': 'app_name', + 'apptype': 'appType', + 'installtype': 'install_type', + 'numberofpermissions': 'number_of_permissions', + 'totalinstallcount': 'total_install_count', + } +CHROME_APPS_TITLES = [ + 'appId', 'displayName', + 'browserDeviceCount', 'osUserCount', + 'appType', 'description', + 'appInstallType', 'appSource', + 'disabled', 'homepageUri', + 'permissions' + ] + +def printApps(): + cm = build() + customer = _get_customerid() + todrive = False + titles = CHROME_APPS_TITLES + csvRows = [] + orgunit = None + pfilter = None + orderBy = None + i = 3 + while i < len(sys.argv): + myarg = sys.argv[i].lower().replace('_', '') + if myarg == 'todrive': + todrive = True + i += 1 + elif myarg in ['ou', 'org', 'orgunit']: + orgunit = _get_orgunit(sys.argv[i+1]) + i += 2 + elif myarg == 'filter': + pfilter = sys.argv[i + 1] + i += 2 + elif myarg == 'orderby': + orderBy = sys.argv[i + 1].lower().replace('_', '') + if orderBy not in CHROME_APPS_ORDERBY_CHOICE_MAP: + controlflow.expected_argument_exit('orderby', + ', '.join(CHROME_APPS_ORDERBY_CHOICE_MAP), + orderBy) + orderBy = CHROME_APPS_ORDERBY_CHOICE_MAP[orderBy] + i += 2 + else: + msg = f'{myarg} is not a valid argument to "gam print chromeapps"' + controlflow.system_error_exit(3, msg) + if orgunit: + orgUnitPath = gapi_directory_orgunits.orgunit_from_orgunitid(orgunit, None) + titles.append('orgUnitPath') + else: + orgUnitPath = '/' + gam.printGettingAllItems('Chrome Installed Applications', pfilter) + page_message = gapi.got_total_items_msg('Chrome Installed Applications', '...\n') + apps = gapi.get_all_pages(cm.customers().reports(), + 'countInstalledApps', + 'installedApps', + page_message=page_message, + customer=customer, orgUnitId=orgunit, + filter=pfilter, orderBy=orderBy) + for app in apps: + if orgunit: + app['orgUnitPath'] = orgUnitPath + if 'permissions'in app: + app['permissions'] = ' '.join(app['permissions']) + csvRows.append(app) + display.write_csv_file(csvRows, titles, 'Chrome Installed Applications', todrive) + + +CHROME_APP_DEVICES_APPTYPE_CHOICE_MAP = { + 'extension': 'EXTENSION', + 'app': 'APP', + 'theme': 'THEME', + 'hostedapp': 'HOSTED_APP', + 'androidapp': 'ANDROID_APP', + } +CHROME_APP_DEVICES_ORDERBY_CHOICE_MAP = { + 'deviceid': 'deviceId', + 'machine': 'machine', + } +CHROME_APP_DEVICES_TITLES = [ + 'appId', 'appType', 'deviceId', 'machine' + ] + +def printAppDevices(): + cm = build() + customer = _get_customerid() + todrive = False + titles = CHROME_APP_DEVICES_TITLES + csvRows = [] + orgunit = None + appId = None + appType = None + startDate = None + endDate = None + pfilter = None + orderBy = None + i = 3 + while i < len(sys.argv): + myarg = sys.argv[i].lower().replace('_', '') + if myarg == 'todrive': + todrive = True + i += 1 + elif myarg in ['ou', 'org', 'orgunit']: + orgunit = _get_orgunit(sys.argv[i+1]) + i += 2 + elif myarg == 'appid': + appId = sys.argv[i + 1] + i += 2 + elif myarg == 'apptype': + appType = sys.argv[i + 1].lower().replace('_', '') + if appType not in CHROME_APP_DEVICES_APPTYPE_CHOICE_MAP: + controlflow.expected_argument_exit('orderby', + ', '.join(CHROME_APP_DEVICES_APPTYPE_CHOICE_MAP), + appType) + appType = CHROME_APP_DEVICES_APPTYPE_CHOICE_MAP[appType] + i += 2 + elif myarg in CROS_START_ARGUMENTS: + startDate = _getFilterDate(sys.argv[i + 1]).strftime(YYYYMMDD_FORMAT) + i += 2 + elif myarg in CROS_END_ARGUMENTS: + endDate = _getFilterDate(sys.argv[i + 1]).strftime(YYYYMMDD_FORMAT) + i += 2 + elif myarg == 'orderby': + orderBy = sys.argv[i + 1].lower().replace('_', '') + if orderBy not in CHROME_APP_DEVICES_ORDERBY_CHOICE_MAP: + controlflow.expected_argument_exit('orderby', + ', '.join(CHROME_APP_DEVICES_ORDERBY_CHOICE_MAP), + orderBy) + orderBy = CHROME_APP_DEVICES_ORDERBY_CHOICE_MAP[orderBy] + i += 2 + else: + msg = f'{myarg} is not a valid argument to "gam print chromeappdevices"' + controlflow.system_error_exit(3, msg) + if not appId: + controlflow.system_error_exit(3, 'You must specify an appid') + if not appType: + controlflow.system_error_exit(3, 'You must specify an apptype') + if endDate: + pfilter = f'last_active_date<={endDate}' + if startDate: + if pfilter: + pfilter += ' AND ' + else: + pfilter = '' + pfilter += f'last_active_date>={startDate}' + if orgunit: + orgUnitPath = gapi_directory_orgunits.orgunit_from_orgunitid(orgunit, None) + titles.append('orgUnitPath') + else: + orgUnitPath = '/' + gam.printGettingAllItems('Chrome Installed Application Devices', pfilter) + page_message = gapi.got_total_items_msg('Chrome Installed Application Devices', '...\n') + devices = gapi.get_all_pages(cm.customers().reports(), + 'findInstalledAppDevices', + 'devices', + page_message=page_message, + appId=appId, appType=appType, + customer=customer, orgUnitId=orgunit, + filter=pfilter, orderBy=orderBy) + for device in devices: + if orgunit: + device['orgUnitPath'] = orgUnitPath + device['appId'] = appId + device['appType'] = appType + csvRows.append(device) + display.write_csv_file(csvRows, titles, 'Chrome Installed Application Devices', todrive) + + +CHROME_VERSIONS_TITLES = [ + 'version', 'count', 'channel', 'deviceOsVersion', 'system' + ] +def printVersions(): + cm = build() + customer = _get_customerid() + todrive = False + titles = CHROME_VERSIONS_TITLES + csvRows = [] + orgunit = None + startDate = None + endDate = None + pfilter = None + reverse = False + i = 3 + while i < len(sys.argv): + myarg = sys.argv[i].lower().replace('_', '') + if myarg == 'todrive': + todrive = True + i += 1 + elif myarg in ['ou', 'org', 'orgunit']: + orgunit = _get_orgunit(sys.argv[i+1]) + i += 2 + elif myarg in CROS_START_ARGUMENTS: + startDate = _getFilterDate(sys.argv[i + 1]).strftime(YYYYMMDD_FORMAT) + i += 2 + elif myarg in CROS_END_ARGUMENTS: + endDate = _getFilterDate(sys.argv[i + 1]).strftime(YYYYMMDD_FORMAT) + i += 2 + elif myarg == 'recentfirst': + reverse = True + i += 1 + else: + msg = f'{myarg} is not a valid argument to "gam print chromeversions"' + controlflow.system_error_exit(3, msg) + if endDate: + pfilter = f'last_active_date<={endDate}' + if startDate: + if pfilter: + pfilter += ' AND ' + else: + pfilter = '' + pfilter += f'last_active_date>={startDate}' + if orgunit: + orgUnitPath = gapi_directory_orgunits.orgunit_from_orgunitid(orgunit, None) + titles.append('orgUnitPath') + else: + orgUnitPath = '/' + gam.printGettingAllItems('Chrome Versions', pfilter) + page_message = gapi.got_total_items_msg('Chrome Versions', '...\n') + versions = gapi.get_all_pages(cm.customers().reports(), + 'countChromeVersions', + 'browserVersions', + page_message=page_message, + customer=customer, orgUnitId=orgunit, filter=pfilter) + for version in sorted(versions, key=lambda k: k.get('version', 'Unknown'), reverse=reverse): + if orgunit: + version['orgUnitPath'] = orgUnitPath + if 'version' not in version: + version['version'] = 'Unknown' + csvRows.append(version) + display.write_csv_file(csvRows, titles, 'Chrome Versions', todrive) diff --git a/src/gam/var.py b/src/gam/var.py index 0c6073fe..1760022b 100644 --- a/src/gam/var.py +++ b/src/gam/var.py @@ -297,6 +297,7 @@ API_VER_MAPPING = { 'driveactivity': 'v2', 'calendar': 'v3', 'cbcm': 'v1.1beta1', + 'chromemanagement': 'v1', 'chromepolicy': 'v1', 'classroom': 'v1', 'cloudidentity': 'v1', diff --git a/src/project-apis.txt b/src/project-apis.txt index 95805b33..ac1677e6 100644 --- a/src/project-apis.txt +++ b/src/project-apis.txt @@ -2,6 +2,7 @@ admin.googleapis.com alertcenter.googleapis.com calendar-json.googleapis.com chat.googleapis.com +chromemanagement.googleapis.com chromepolicy.googleapis.com classroom.googleapis.com cloudidentity.googleapis.com