diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8dcbcafe..141104b9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -321,6 +321,14 @@ jobs: $gam report users fields accounts:is_less_secure_apps_access_allowed,gmail:last_imap_time,gmail:last_pop_time filters "accounts:last_login_time>2019-01-01T00:00:00.000Z" todrive $gam report admin start -3d todrive $gam print devices nopersonaldevices nodeviceusers filter "serial:$JID$JID$JID$JID-" | $gam csv - gam delete device id ~name + $gam print userinvitations + $gam print userinvitations filter | $gam csv - gam create userinvitation ~name + export CUSTOMER_ID="C01wfv983" + export GA_DOMAIN="pdl.jaylee.us" + touch $gampath/enabledasa.txt + $gam print printermodels + $gam print printers + $gam create printer displayname "${newbase}" uri ipp://localhost:631 driverless description "made by $(date)" - name: Upload to Google Drive, build only. if: github.event_name == 'push' && matrix.goal != 'test' diff --git a/src/gam/__init__.py b/src/gam/__init__.py index 12522a21..de22ad6c 100755 --- a/src/gam/__init__.py +++ b/src/gam/__init__.py @@ -65,6 +65,7 @@ from gam.gapi.directory import domains as gapi_directory_domains from gam.gapi.directory import groups as gapi_directory_groups from gam.gapi.directory import mobiledevices as gapi_directory_mobiledevices from gam.gapi.directory import orgunits as gapi_directory_orgunits +from gam.gapi.directory import printers as gapi_directory_printers from gam.gapi.directory import privileges as gapi_directory_privileges from gam.gapi.directory import resource as gapi_directory_resource from gam.gapi.directory import roles as gapi_directory_roles @@ -10300,12 +10301,17 @@ OAUTH2_SCOPES = [ 'scopes': 'https://www.googleapis.com/auth/admin.directory.group' }, { - 'name': - 'Directory API - Mobile Devices', + 'name': 'Directory API - Mobile Devices', 'subscopes': ['readonly', 'action'], 'scopes': 'https://www.googleapis.com/auth/admin.directory.device.mobile' }, + { + 'name': 'Directory API - Printers', + 'subscopes': ['readonly'], + # note - currently DASA only but admin credentials should work soon + 'scopes': 'https://www.googleapis.com/auth/admin.chrome.printers' + }, { 'name': 'Directory API - Organizational Units', 'subscopes': ['readonly'], @@ -11176,6 +11182,8 @@ def ProcessGAMCommand(args): gapi_cbcm.createtoken() elif argument in ['userinvitation', 'userinvitations']: gapi_cloudidentity_userinvitations.send() + elif argument in ['printer']: + gapi_directory_printers.create() else: controlflow.invalid_argument_exit(argument, 'gam create') sys.exit(0) @@ -11234,6 +11242,8 @@ def ProcessGAMCommand(args): gapi_cloudidentity_devices.update_state() elif argument in ['browser', 'browsers']: gapi_cbcm.update() + elif argument in ['printer']: + gapi_directory_printers.update() else: controlflow.invalid_argument_exit(argument, 'gam update') sys.exit(0) @@ -11364,6 +11374,8 @@ def ProcessGAMCommand(args): gapi_directory_roles.delete() elif argument in ['browser', 'browsers']: gapi_cbcm.delete() + elif argument in ['printer']: + gapi_directory_printers.delete() else: controlflow.invalid_argument_exit(argument, 'gam delete') sys.exit(0) @@ -11467,6 +11479,10 @@ def ProcessGAMCommand(args): gapi_vault.print_count() elif argument in ['userinvitations']: gapi_cloudidentity_userinvitations.print_() + elif argument in ['printermodels']: + gapi_directory_printers.print_models() + elif argument in ['printers']: + gapi_directory_printers.print_() else: controlflow.invalid_argument_exit(argument, 'gam print') sys.exit(0) @@ -11960,6 +11976,8 @@ def ProcessGAMCommand(args): gapi_directory_users.turn_off_2sv(users) elif command == 'waitformailbox': gapi_directory_users.wait_for_mailbox(users) + elif command == 'deleteprinters': + gapi_directory_printers.batch_delete(users) else: controlflow.invalid_argument_exit(command, 'gam') except IndexError: diff --git a/src/gam/gapi/directory/printers.py b/src/gam/gapi/directory/printers.py new file mode 100644 index 00000000..804f530d --- /dev/null +++ b/src/gam/gapi/directory/printers.py @@ -0,0 +1,171 @@ +'''Commands to manage directory printers.''' +# pylint: disable=unused-wildcard-import wildcard-import + +from gam import controlflow +from gam import display +from gam import gapi +from gam.var import * +from gam.gapi import directory as gapi_directory +from gam.gapi.directory import orgunits as gapi_directory_orgunits + + +def _get_customerid(): + ''' returns customer in format needed for this API''' + customer = GC_Values[GC_CUSTOMER_ID] + return f'customers/{customer}' + +def _get_printer_attributes(i, cdapi=None): + '''get printer attributes for create/update commands''' + body = {} + while i < len(sys.argv): + myarg = sys.argv[i].lower().replace('_', '') + if myarg == 'description': + body['description'] = sys.argv[i+1] + i += 2 + elif myarg == 'displayname': + body['displayName'] = sys.argv[i+1] + i += 2 + elif myarg == 'makeandmodel': + body['makeAndModel'] = sys.argv[i+1] + i += 2 + elif myarg in ['ou', 'orgunit']: + _, body['orgUnitId'] = gapi_directory_orgunits.getOrgUnitId(sys.argv[i+1], cdapi) + body['orgUnitId'] = body['orgUnitId'][3:] + i += 2 + elif myarg == 'uri': + body['uri'] = sys.argv[i+1] + i += 2 + elif myarg == 'driverless': + body['useDriverlessConfig'] = True + i += 1 + return body + + +def batch_delete(printer_ids): + '''gam croscsvfile file:column deleteprinters''' + cdapi = gapi_directory.build() + parent = _get_customerid() + # max 50 per API call + batch_size = 50 + for chunk in range(0, len(printer_ids), batch_size): + body = { + 'printerIds': printer_ids[chunk:chunk + batch_size] + } + result = gapi.call(cdapi.customers().chrome().printers(), + 'batchDeletePrinters', + parent=parent, + body=body) + for printer_id in result.get('printerIds', []): + print(f'Deleted printer {printer_id}') + for printer_id in result.get('failedPrinters', []): + print(f'ERROR: failed to delete {printer_id.get("printerIds")}') + + +def create(): + '''gam create printer''' + cdapi = gapi_directory.build() + parent = _get_customerid() + body = _get_printer_attributes(3, cdapi) + result = gapi.call(cdapi.customers().chrome().printers(), + 'create', + parent=parent, + body=body) + display.print_json(result) + + +def delete(): + '''gam delete printer''' + cdapi = gapi_directory.build() + customer_id = _get_customerid() + printer_id = sys.argv[3] + name = f'{customer_id}/chrome/printers/{printer_id}' + gapi.call(cdapi.customers().chrome().printers(), + 'delete', + name=name) + print(f'Deleted printer {printer_id}') + +def print_(): + '''gam print printers''' + cdapi = gapi_directory.build() + parent = _get_customerid() + filter_ = None + todrive = False + titles = [] + rows = [] + i = 3 + while i < len(sys.argv): + myarg = sys.argv[i].lower() + if myarg == 'filter': + filter_ = sys.argv[i+1] + i += 2 + elif myarg == 'todrive': + todrive = True + i += 1 + else: + controlflow.invalid_argument_exit(sys.argv[i], 'gam print printermodels') + printers = gapi.get_all_pages(cdapi.customers().chrome().printers(), + 'list', + items='printers', + parent=parent, + pageSize=10000, + filter=filter_) + for printer in printers: + row = {} + for key, val in printer.items(): + if key not in titles: + titles.append(key) + row[key] = val + rows.append(row) + display.write_csv_file(rows, titles, 'Printer', todrive) + + +def print_models(): + '''gam print printermodels''' + cdapi = gapi_directory.build() + parent = _get_customerid() + filter_ = None + todrive = False + titles = [] + rows = [] + i = 3 + while i < len(sys.argv): + myarg = sys.argv[i].lower() + if myarg == 'filter': + filter_ = sys.argv[i+1] + i += 2 + elif myarg == 'todrive': + todrive = True + i += 1 + else: + controlflow.invalid_argument_exit(sys.argv[i], 'gam print printermodels') + models = gapi.get_all_pages(cdapi.customers().chrome().printers(), + 'listPrinterModels', + items='printerModels', + parent=parent, + pageSize=10000, + filter=filter_) + for model in models: + row = {} + for key, val in model.items(): + if key not in titles: + titles.append(key) + row[key] = val + rows.append(row) + display.write_csv_file(rows, titles, 'Printer Models', todrive) + + +def update(): + '''gam update printer''' + cdapi = gapi_directory.build() + customer = _get_customerid() + printer_id = sys.argv[3] + name = f'{customer}/chrome/printers/{printer_id}' + body = _get_printer_attributes(4, cdapi) + update_mask = ','.join(body) + # note clearMask seems unnecessary. Updating field to '' clears it. + result = gapi.call(cdapi.customers().chrome().printers(), + 'patch', + name=name, + updateMask=update_mask, + body=body) + display.print_json(result) diff --git a/src/gam/gapi/errors.py b/src/gam/gapi/errors.py index e940bf11..bbbc5185 100644 --- a/src/gam/gapi/errors.py +++ b/src/gam/gapi/errors.py @@ -116,6 +116,7 @@ class ErrorReason(Enum): DUPLICATE = 'duplicate' FAILED_PRECONDITION = 'failedPrecondition' FORBIDDEN = 'forbidden' + FIVE_O_THREE = '503' FOUR_O_NINE = '409' FOUR_O_O = '400' FOUR_O_THREE = '403' @@ -153,6 +154,7 @@ DEFAULT_RETRY_REASONS = [ ErrorReason.GATEWAY_TIMEOUT, ErrorReason.INTERNAL_ERROR, ErrorReason.FOUR_TWO_NINE, + ErrorReason.FIVE_O_THREE, ] GMAIL_THROW_REASONS = [ErrorReason.SERVICE_NOT_AVAILABLE] GROUP_GET_THROW_REASONS = [