From 8379f6229687912045c5fa6d70b74d03af58fe69 Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Tue, 11 Sep 2018 10:39:31 -0400 Subject: [PATCH 01/28] starting mam --- pyairwatch/mam/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 pyairwatch/mam/__init__.py diff --git a/pyairwatch/mam/__init__.py b/pyairwatch/mam/__init__.py new file mode 100644 index 0000000..e69de29 From 8570fff645531b4a3d35ae5cde1f6672c5d737e9 Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Wed, 12 Sep 2018 17:28:24 -0400 Subject: [PATCH 02/28] adding support for mam and feature flags --- pyairwatch/client.py | 4 ++++ pyairwatch/mam/apps.py | 7 +++++++ pyairwatch/mam/blobs.py | 16 ++++++++++++++++ pyairwatch/mam/internalapps.py | 16 ++++++++++++++++ pyairwatch/system/featureflag.py | 21 +++++++++++++++++++++ pyairwatch/system/groups.py | 5 +++++ setup.py | 2 +- 7 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 pyairwatch/mam/apps.py create mode 100644 pyairwatch/mam/blobs.py create mode 100644 pyairwatch/mam/internalapps.py create mode 100644 pyairwatch/system/featureflag.py diff --git a/pyairwatch/client.py b/pyairwatch/client.py index 7349084..3bb517d 100644 --- a/pyairwatch/client.py +++ b/pyairwatch/client.py @@ -2,6 +2,9 @@ import json import logging import requests +from .mam.apps import Apps +from .mam.blobs import Blobs +from .mam.internalapps import InternalApps from .mdm.devices import Devices from .mdm.profiles import Profiles from .mdm.smartgroups import SmartGroups @@ -9,6 +12,7 @@ from .system.admins import Admins from .system.groups import Groups from .system.users import Users +from .system.featureflag import FeatureFlag # Enabling debugging at http.client level (requests->urllib3->http.client) diff --git a/pyairwatch/mam/apps.py b/pyairwatch/mam/apps.py new file mode 100644 index 0000000..928cc50 --- /dev/null +++ b/pyairwatch/mam/apps.py @@ -0,0 +1,7 @@ +class Apps(object): + """ + apps + """ + + def __init__(self, client): + self.client = client diff --git a/pyairwatch/mam/blobs.py b/pyairwatch/mam/blobs.py new file mode 100644 index 0000000..0c3471f --- /dev/null +++ b/pyairwatch/mam/blobs.py @@ -0,0 +1,16 @@ +class Blobs(object): + """ + A class to manage Blobs + """ + + def __init__(self, client): + self.client = client + + def _get(self, module='mam', path=None, version=None, params=None, header=None): + """GET requests for the /MAM/blobs module.""" + response = self.client.get(module=module, path=path, version=version, params=params, header=header) + return response + + def _post(self, module='mam', path=None, version=None, params=None, data=None, json=None, header=None): + """Post requests for the /MAM/blobs module.""" + response = self.client.post(module=module, path=path, version=version, params=params, data=data, json=json, header=header) diff --git a/pyairwatch/mam/internalapps.py b/pyairwatch/mam/internalapps.py new file mode 100644 index 0000000..35a582f --- /dev/null +++ b/pyairwatch/mam/internalapps.py @@ -0,0 +1,16 @@ +class InternalApps(object): + """ + A class to manage Internal Applications + """ + + def __init__(self, client): + self.client = client + + def _get(self, module='mam/apps', path=None, version=None, params=None, header=None): + """GET requests for the /MAM/apps/internal module.""" + response = self.client.get(module=module, path=path, version=version, params=params, header=header) + return response + + def _post(self, module='mam/apps', path=None, version=None, params=None, data=None, json=None, header=None): + """Post requests for the /MAM/apps/internal module.""" + response = self.client.post(module=module, path=path, version=version, params=params, data=data, json=json, header=header) diff --git a/pyairwatch/system/featureflag.py b/pyairwatch/system/featureflag.py new file mode 100644 index 0000000..7780214 --- /dev/null +++ b/pyairwatch/system/featureflag.py @@ -0,0 +1,21 @@ +class FeatureFlag(object): + """ + For Feature Flags + """ + + def __init__(self, client): + self.client = client + + def feature_flag_update(self, feature_flag, org_group_uuid, override_value): + response = self._post(path='/featureFlag/{}/{}/{}'.format(feature_flag, org_group_uuid, override_value)) + return response + + def _get(self, module='system', path=None, version=None, params=None, header=None): + """GET requests for the /System/featureFlag module.""" + response = self.client.get(module=module, path=path, version=version, params=params, header=header) + return response + + def _post(self, module='system', path=None, version=None, params=None, data=None, json=None, header=None): + """POST requests for the /System/featureFlag module.""" + response = self.client.post(module=module, path=path, version=version, params=params, data=data, json=json, header=header) + return response diff --git a/pyairwatch/system/groups.py b/pyairwatch/system/groups.py index 868d775..ea565c3 100644 --- a/pyairwatch/system/groups.py +++ b/pyairwatch/system/groups.py @@ -17,6 +17,11 @@ def get_id_from_groupid(self, groupid): response = self.search(groupid=str(groupid)) return response['LocationGroups'][0]['Id']['Value'] + def get_uuid_from_groupid(self, groupid): + """Returns the OG ID for a given Group ID""" + response = self.search(groupid=str(groupid)) + return response['LocationGroups'][0]['Uuid'] + def create(self, parent_id, ogdata): """Creates a Group and returns the new ID.""" response = self._post(path='/groups/{}'.format(parent_id), data=ogdata, header=self.jheader) diff --git a/setup.py b/setup.py index 849ab8b..3f4759b 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup setup(name='PyVMwareAirWatch', - version='1.0', + version='1.0.1', description='PyVMwareAirWatch is a Python API library for [VMware AirWatch](https://www.air-watch.com/) 9.1+', url='https://github.com/jprichards/PyVMwareAirWatch', author='jprichards', From 0ba2d7593cdf15a37778e567b6c95977a6a1b6ff Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Wed, 12 Sep 2018 21:12:24 -0400 Subject: [PATCH 03/28] - added new modules to client - modified groups.py to retrive UUID of an OG via groupid --- pyairwatch/client.py | 1 + pyairwatch/system/groups.py | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pyairwatch/client.py b/pyairwatch/client.py index 3bb517d..ab67217 100644 --- a/pyairwatch/client.py +++ b/pyairwatch/client.py @@ -61,6 +61,7 @@ def __init__(self, env, apikey, username, password): self.tags = Tags(self) self.admins = Admins(self) self.users = Users(self) + self.featureflag = FeatureFlag(self) def get(self, module, path, version=None, params=None, header=None, timeout=30): """Sends a GET request to the API. Returns the response object.""" diff --git a/pyairwatch/system/groups.py b/pyairwatch/system/groups.py index ea565c3..509324c 100644 --- a/pyairwatch/system/groups.py +++ b/pyairwatch/system/groups.py @@ -1,3 +1,5 @@ +import json + class Groups(object): """ A class to manage all core functionalities for AirWatch Organization Groups. @@ -18,9 +20,9 @@ def get_id_from_groupid(self, groupid): return response['LocationGroups'][0]['Id']['Value'] def get_uuid_from_groupid(self, groupid): - """Returns the OG ID for a given Group ID""" - response = self.search(groupid=str(groupid)) - return response['LocationGroups'][0]['Uuid'] + """Returns the OG UUID for a given Group ID""" + response = self._get(path='/groups/{}'.format(groupid)) + return response['Uuid'] def create(self, parent_id, ogdata): """Creates a Group and returns the new ID.""" From 730f261c02bbbcc6b75f5d1ef08db5030c13aff7 Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Thu, 13 Sep 2018 12:21:42 -0400 Subject: [PATCH 04/28] removed unused code and simplefierd featureflag.py --- pyairwatch/system/featureflag.py | 20 +++++++++++++------- pyairwatch/system/groups.py | 2 -- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/pyairwatch/system/featureflag.py b/pyairwatch/system/featureflag.py index 7780214..c0b0250 100644 --- a/pyairwatch/system/featureflag.py +++ b/pyairwatch/system/featureflag.py @@ -6,16 +6,22 @@ class FeatureFlag(object): def __init__(self, client): self.client = client - def feature_flag_update(self, feature_flag, org_group_uuid, override_value): - response = self._post(path='/featureFlag/{}/{}/{}'.format(feature_flag, org_group_uuid, override_value)) - return response + def set_flag(self, feature_flag, og_uuid, override): + """Set the Feature Flag""" + return self._post(path='/featureFlag/{}/{}/{}'.format(feature_flag, og_uuid, override)) + + def get_status(self, feature_flag, og_uuid): + """GET a specific Feature Flag status""" + return self._get(path='/featureFlag/{}/{}'.format(feature_flag, og_uuid)) + + def og_status(self, og_uuid): + """GET all Feature Flags for a particular OG need UUID""" + return self._get(path='/featureFlag/{}'.format(og_uuid)) def _get(self, module='system', path=None, version=None, params=None, header=None): """GET requests for the /System/featureFlag module.""" - response = self.client.get(module=module, path=path, version=version, params=params, header=header) - return response + return self.client.get(module=module, path=path, version=version, params=params, header=header) def _post(self, module='system', path=None, version=None, params=None, data=None, json=None, header=None): """POST requests for the /System/featureFlag module.""" - response = self.client.post(module=module, path=path, version=version, params=params, data=data, json=json, header=header) - return response + return self.client.post(module=module, path=path, version=version, params=params, data=data, json=json, header=header) diff --git a/pyairwatch/system/groups.py b/pyairwatch/system/groups.py index 509324c..0be8450 100644 --- a/pyairwatch/system/groups.py +++ b/pyairwatch/system/groups.py @@ -1,5 +1,3 @@ -import json - class Groups(object): """ A class to manage all core functionalities for AirWatch Organization Groups. From ed48140fba6b7c7cac3224552cb521f0e3c279f9 Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Wed, 19 Sep 2018 16:03:43 -0400 Subject: [PATCH 05/28] added ldap.py for adding LDAP to an Envrioment. Structure of API means that the endpoint is in MDM so that a stupid thing. updated client.py and setup.py for some of the changes removed jheader from create method in groups.py --- pyairwatch/client.py | 5 ++++- pyairwatch/mdm/ldap.py | 14 ++++++++++++++ pyairwatch/system/groups.py | 2 +- setup.py | 2 +- 4 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 pyairwatch/mdm/ldap.py diff --git a/pyairwatch/client.py b/pyairwatch/client.py index ab67217..7da48c6 100644 --- a/pyairwatch/client.py +++ b/pyairwatch/client.py @@ -13,10 +13,12 @@ from .system.groups import Groups from .system.users import Users from .system.featureflag import FeatureFlag +from .mdm.ldap import LDAP # Enabling debugging at http.client level (requests->urllib3->http.client) -# you will see the REQUEST, including HEADERS and DATA, and RESPONSE with HEADERS but without DATA. +# you will see the REQUEST, including HEADERS and DATA, and RESPONSE with +# HEADERS but without DATA. # the only thing missing will be the response.body which is not logged. try: from http.client import HTTPConnection @@ -62,6 +64,7 @@ def __init__(self, env, apikey, username, password): self.admins = Admins(self) self.users = Users(self) self.featureflag = FeatureFlag(self) + self.ldap = LDAP(self) def get(self, module, path, version=None, params=None, header=None, timeout=30): """Sends a GET request to the API. Returns the response object.""" diff --git a/pyairwatch/mdm/ldap.py b/pyairwatch/mdm/ldap.py new file mode 100644 index 0000000..788b643 --- /dev/null +++ b/pyairwatch/mdm/ldap.py @@ -0,0 +1,14 @@ +class LDAP(object): + """ + A class to manage functionalities of LDAP Definition. + """ + + def __init__(self, client): + self.client = client + + def _post(self, module='mdm', path=None, version=None, params=None, data=None, json=None, header=None): + """POST requests for the /MDM/Devices/EnterpriseIntegration/LDAP module.""" + return self.client.post(module=module, path=path, version=version, params=params, data=data, json=json, header=header) + + def create_ldap(self, ldap_data): + return self._post(path='/enterpriseintegration/ldap', data=ldap_data) diff --git a/pyairwatch/system/groups.py b/pyairwatch/system/groups.py index 0be8450..75de354 100644 --- a/pyairwatch/system/groups.py +++ b/pyairwatch/system/groups.py @@ -24,7 +24,7 @@ def get_uuid_from_groupid(self, groupid): def create(self, parent_id, ogdata): """Creates a Group and returns the new ID.""" - response = self._post(path='/groups/{}'.format(parent_id), data=ogdata, header=self.jheader) + response = self._post(path='/groups/{}'.format(parent_id), data=ogdata) return response def create_customer_og(self, groupid, name=None): diff --git a/setup.py b/setup.py index 3f4759b..90982a5 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup setup(name='PyVMwareAirWatch', - version='1.0.1', + version='1.0.2', description='PyVMwareAirWatch is a Python API library for [VMware AirWatch](https://www.air-watch.com/) 9.1+', url='https://github.com/jprichards/PyVMwareAirWatch', author='jprichards', From 4040387c31bc5b7716d498b41114499c9cbfb556 Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Thu, 20 Sep 2018 15:08:26 -0400 Subject: [PATCH 06/28] worked on ldap.py added methods to groups.py to help with ldap configuration --- pyairwatch/mdm/ldap.py | 3 ++- pyairwatch/system/groups.py | 11 ++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/pyairwatch/mdm/ldap.py b/pyairwatch/mdm/ldap.py index 788b643..3738776 100644 --- a/pyairwatch/mdm/ldap.py +++ b/pyairwatch/mdm/ldap.py @@ -2,6 +2,7 @@ class LDAP(object): """ A class to manage functionalities of LDAP Definition. """ + jheader = {'Content-Type': 'application/json'} def __init__(self, client): self.client = client @@ -11,4 +12,4 @@ def _post(self, module='mdm', path=None, version=None, params=None, data=None, j return self.client.post(module=module, path=path, version=version, params=params, data=data, json=json, header=header) def create_ldap(self, ldap_data): - return self._post(path='/enterpriseintegration/ldap', data=ldap_data) + return self._post(path='/enterpriseintegration/ldap', json=ldap_data, header=self.jheader) diff --git a/pyairwatch/system/groups.py b/pyairwatch/system/groups.py index 75de354..97b41fc 100644 --- a/pyairwatch/system/groups.py +++ b/pyairwatch/system/groups.py @@ -17,14 +17,19 @@ def get_id_from_groupid(self, groupid): response = self.search(groupid=str(groupid)) return response['LocationGroups'][0]['Id']['Value'] - def get_uuid_from_groupid(self, groupid): + def get_groupid_from_id(self, id): + """Returns the Group ID for a given ID""" + response = self._get(path='/groups/{}'.format(id)) + return response['GroupId'] + + def get_uuid_from_groupid(self, id): """Returns the OG UUID for a given Group ID""" - response = self._get(path='/groups/{}'.format(groupid)) + response = self._get(path='/groups/{}'.format(id)) return response['Uuid'] def create(self, parent_id, ogdata): """Creates a Group and returns the new ID.""" - response = self._post(path='/groups/{}'.format(parent_id), data=ogdata) + response = self._post(path='/groups/{}'.format(parent_id), data=ogdata, header=self.jheader) return response def create_customer_og(self, groupid, name=None): From d7c9a6848084fd7869d8cea5a77524c2a23c33da Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Sun, 23 Sep 2018 11:16:23 -0400 Subject: [PATCH 07/28] creating api end point class structure --- pyairwatch/system/featureflag.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/pyairwatch/system/featureflag.py b/pyairwatch/system/featureflag.py index c0b0250..44a78b0 100644 --- a/pyairwatch/system/featureflag.py +++ b/pyairwatch/system/featureflag.py @@ -1,27 +1,24 @@ -class FeatureFlag(object): +from .system import System + + +class FeatureFlag(System): """ For Feature Flags """ def __init__(self, client): - self.client = client + super().__init__(client) def set_flag(self, feature_flag, og_uuid, override): """Set the Feature Flag""" - return self._post(path='/featureFlag/{}/{}/{}'.format(feature_flag, og_uuid, override)) + path = '/featureFlag/{}/{}/{}'.format(feature_flag, og_uuid, override) + return super._post(path=path) def get_status(self, feature_flag, og_uuid): """GET a specific Feature Flag status""" - return self._get(path='/featureFlag/{}/{}'.format(feature_flag, og_uuid)) + path = '/featureFlag/{}/{}'.format(feature_flag, og_uuid) + return super._get(path=path) def og_status(self, og_uuid): """GET all Feature Flags for a particular OG need UUID""" - return self._get(path='/featureFlag/{}'.format(og_uuid)) - - def _get(self, module='system', path=None, version=None, params=None, header=None): - """GET requests for the /System/featureFlag module.""" - return self.client.get(module=module, path=path, version=version, params=params, header=header) - - def _post(self, module='system', path=None, version=None, params=None, data=None, json=None, header=None): - """POST requests for the /System/featureFlag module.""" - return self.client.post(module=module, path=path, version=version, params=params, data=data, json=json, header=header) + return super._get(path='/featureFlag/{}'.format(og_uuid)) From 3f4d67f6ea045b171de630e8cdbf151cc5290ed0 Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Tue, 23 Oct 2018 15:15:28 -0400 Subject: [PATCH 08/28] Fixed issues with class structure and added some more end points --- .gitignore | 2 ++ pyairwatch/client.py | 28 +++++++++++++++++++++------- pyairwatch/mdm/ldap.py | 17 ++++++++--------- pyairwatch/system/featureflag.py | 19 +++++++++---------- 4 files changed, 40 insertions(+), 26 deletions(-) diff --git a/.gitignore b/.gitignore index 7fdea58..90433b3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ *.pyc *.egg-info +.idea +venv diff --git a/pyairwatch/client.py b/pyairwatch/client.py index 7da48c6..880610b 100644 --- a/pyairwatch/client.py +++ b/pyairwatch/client.py @@ -13,7 +13,9 @@ from .system.groups import Groups from .system.users import Users from .system.featureflag import FeatureFlag +from .system.info import Info from .mdm.ldap import LDAP +from .mdm.network import Network # Enabling debugging at http.client level (requests->urllib3->http.client) @@ -33,7 +35,6 @@ requests_log.propagate = True - class AirWatchAPIError(Exception): def __init__(self, json_response=None): if json_response is None: @@ -65,6 +66,8 @@ def __init__(self, env, apikey, username, password): self.users = Users(self) self.featureflag = FeatureFlag(self) self.ldap = LDAP(self) + self.info = Info(self) + self.network = Network(self) def get(self, module, path, version=None, params=None, header=None, timeout=30): """Sends a GET request to the API. Returns the response object.""" @@ -94,7 +97,9 @@ def post(self, module, path, version=None, params=None, data=None, json=None, he raise e def put(self, module, path, version=None, params=None, data=None, json=None, header=None, timeout=30): - """Sends a PUT request to the API. Returns the response object.""" + """ + Sends a PUT request to the API. Returns the response object. + """ if header is None: header = {} header.update(self._build_header(self.username, self.password, self.apikey)) @@ -108,7 +113,9 @@ def put(self, module, path, version=None, params=None, data=None, json=None, hea #NOQA def delete(self, module, path, version=None, params=None, header=None, timeout=30): - """Sends a DELETE request to the API. Returns the response object.""" + """ + Sends a DELETE request to the API. Returns the response object. + """ if header is None: header = {} header.update(self._build_header(self.username, self.password, self.apikey)) @@ -122,7 +129,10 @@ def delete(self, module, path, version=None, params=None, header=None, timeout=3 @staticmethod def _check_for_error(response): - """Checks the response for json data, then for an error, then for a status code""" + """ + Checks the response for json data, then for an error, then for + a status code + """ if response.headers.get('Content-Type') in ('application/json', 'application/json; charset=utf-8'): json = response.json() if json.get('errorCode'): @@ -134,7 +144,9 @@ def _check_for_error(response): @staticmethod def _build_endpoint(base_url, module, path=None, version=None): - """Builds the full url endpoint for the API request""" + """ + Builds the full url endpoint for the API request + """ if not base_url.startswith('https://'): base_url = 'https://' + base_url if base_url.endswith('/'): @@ -150,10 +162,12 @@ def _build_endpoint(base_url, module, path=None, version=None): return url + '/{}'.format(path) return url - @staticmethod def _build_header(username, password, token, accept='application/json'): - """Build the header with base64 login, AW API token, and accept a json response""" + """ + Build the header with base64 login, AW API token, + and accept a json response + """ hashed_auth = base64.b64encode('{}:{}'.format(username, password)) header = { 'Authorization': 'Basic {}'.format(hashed_auth.encode('utf-8')), diff --git a/pyairwatch/mdm/ldap.py b/pyairwatch/mdm/ldap.py index 3738776..3b37060 100644 --- a/pyairwatch/mdm/ldap.py +++ b/pyairwatch/mdm/ldap.py @@ -1,15 +1,14 @@ -class LDAP(object): +from .mdm import MDM + + +class LDAP(MDM): """ A class to manage functionalities of LDAP Definition. """ - jheader = {'Content-Type': 'application/json'} - def __init__(self, client): - self.client = client - - def _post(self, module='mdm', path=None, version=None, params=None, data=None, json=None, header=None): - """POST requests for the /MDM/Devices/EnterpriseIntegration/LDAP module.""" - return self.client.post(module=module, path=path, version=version, params=params, data=data, json=json, header=header) + MDM.__init__(self, client) + self.jheader = {'Content-Type': 'application/json'} def create_ldap(self, ldap_data): - return self._post(path='/enterpriseintegration/ldap', json=ldap_data, header=self.jheader) + path = '/enterpriseintegration/ldap' + return MDM._post(self, path=path, json=ldap_data, header=self.jheader) diff --git a/pyairwatch/system/featureflag.py b/pyairwatch/system/featureflag.py index 44a78b0..caf7fcc 100644 --- a/pyairwatch/system/featureflag.py +++ b/pyairwatch/system/featureflag.py @@ -2,23 +2,22 @@ class FeatureFlag(System): - """ + ''' For Feature Flags - """ - + ''' def __init__(self, client): - super().__init__(client) + System.__init__(self, client) def set_flag(self, feature_flag, og_uuid, override): - """Set the Feature Flag""" + '''Set the Feature Flag''' path = '/featureFlag/{}/{}/{}'.format(feature_flag, og_uuid, override) - return super._post(path=path) + return System._post(self, path=path) def get_status(self, feature_flag, og_uuid): - """GET a specific Feature Flag status""" + '''GET a specific Feature Flag status''' path = '/featureFlag/{}/{}'.format(feature_flag, og_uuid) - return super._get(path=path) + return System._get(self, path=path) def og_status(self, og_uuid): - """GET all Feature Flags for a particular OG need UUID""" - return super._get(path='/featureFlag/{}'.format(og_uuid)) + '''GET all Feature Flags for a particular OG need UUID''' + return System._get(self, path='/featureFlag/{}'.format(og_uuid)) From f79798e4c6b5697ba712be23dc6a938015ba8967 Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Thu, 24 Jan 2019 10:55:59 -0500 Subject: [PATCH 09/28] Making some inheritance changes and impovements --- pyairwatch/mdm/devices.py | 45 ++++++++++++++++++++++++++++---------- pyairwatch/mdm/profiles.py | 32 +++++++++++++++------------ pyairwatch/system/users.py | 22 ++++++------------- tests/__init__.py | 0 tests/test_first.py | 13 +++++++++++ 5 files changed, 71 insertions(+), 41 deletions(-) create mode 100644 tests/__init__.py create mode 100644 tests/test_first.py diff --git a/pyairwatch/mdm/devices.py b/pyairwatch/mdm/devices.py index 6af1f10..640fbfa 100644 --- a/pyairwatch/mdm/devices.py +++ b/pyairwatch/mdm/devices.py @@ -1,15 +1,17 @@ -class Devices(object): +from .mdm import MDM + + +class Devices(MDM): """ A class to manage functionalities of Mobile Device Management (MDM). """ def __init__(self, client): - self.client = client + MDM.__init__(self, client) def search(self, **kwargs): """Returns the Device information matching the search parameters.""" - response = self._get(path='/devices', params=kwargs) - return response + return MDM._get(path='/devices', params=kwargs) def get_details_by_alt_id(self, serialnumber=None, macaddress=None, udid=None, imeinumber=None, easid=None): """Returns the Device information matching the search parameters.""" @@ -43,12 +45,31 @@ def get_id_by_alt_id(self, serialnumber=None, macaddress=None, udid=None, imeinu return None return response['Id']['Value'] - def _get(self, module='mdm', path=None, version=None, params=None, header=None): - """GET requests for the /MDM/Devices module.""" - response = self.client.get(module=module, path=path, version=version, params=params, header=header) - return response + def clear_device_passcode(self, device_id): + """ + Clear the passcode on a device + """ + return MDM._post(self, path='/devices/{}/clearpasscode'.format(device_id)) - def _post(self, module='mdm', path=None, version=None, params=None, data=None, json=None, header=None): - """POST requests for the /MDM/Devices module.""" - response = self.client.post(module=module, path=path, version=version, params=params, data=data, json=json, header=header) - return response + def commands_for_device_id(self, command, device_id): + """ + Commands for devices selecting device based on id + """ + path = '/devices/{}/commands'.format(device_id) + command = 'command=' + command + return MDM._post(self, path=path, params=command) + + def send_commands_by_id(self, command, searchby, id): + """ + Commands for devices selecting device based on id + """ + path = '/devices/commands' + query = 'command=' + str(command) + '&searchBy=' + str(searchby) + query = query + '&id=' + str(id) + return MDM._post(self, path=path, params=query) + + def get_details_by_device_id(self, device_id): + """ + device detals by device id + """ + return MDM._get(self, path='/devices/{}'.format(device_id)) diff --git a/pyairwatch/mdm/profiles.py b/pyairwatch/mdm/profiles.py index e2e2d08..b3991f2 100644 --- a/pyairwatch/mdm/profiles.py +++ b/pyairwatch/mdm/profiles.py @@ -1,10 +1,13 @@ -class Profiles(object): +from .mdm import MDM + + +class Profiles(MDM): """ A class to manage V2 API's for AirWatch Profiles Management """ def __init__(self, client): - self.client = client + MDM.__init__(self, client) def search(self, **kwargs): """ @@ -20,15 +23,16 @@ def search(self, **kwargs): status={status} ownership={ownership} """ - response = self._get(path='/profiles/search', params=kwargs) - return response - - def _get(self, module='mdm', path=None, version=None, params=None, header=None): - """GET requests for the /MDM/Profiles module.""" - response = self.client.get(module=module, path=path, version=version, params=params, header=header) - return response - - def _post(self, module='mdm', path=None, version=None, params=None, data=None, json=None, header=None): - """POST requests for the /MDM/Profiles module.""" - response = self.client.post(module=module, path=path, version=version, params=params, data=data, json=json, header=header) - return response + return MDM._get(self, path='/profiles/search', params=kwargs) + + def install_profile(self, device_id, profile_id, payloads=None): + """ + Queues up installation commands for interactive + profiles for a device by overriding payload settings. + """ + path = '/devices/{}/commands/installprofile'.format(device_id) + query = 'profileid=' + str(profile_id) + return MDM._post(self, path=path, params=query) + + def get_profile_by_id(self, profile_id): + return MDM._get(self, path='/profiles/{}'.format(profile_id), version=2) diff --git a/pyairwatch/system/users.py b/pyairwatch/system/users.py index f72f191..246ed9b 100644 --- a/pyairwatch/system/users.py +++ b/pyairwatch/system/users.py @@ -1,9 +1,12 @@ -class Users(object): +from .system import System + + +class Users(System): def __init__(self, client): - self.client = client + System.__init__(self, client) - #UNTESTED + # UNTESTED def search(self, **kwargs): """ Returns the Enrollment User's details matching the search parameters @@ -18,15 +21,4 @@ def search(self, **kwargs): organizationgroupid={locationgroupid} role={role} """ - response = self._get(path='/users/search', params=kwargs) - return response - - def _get(self, module='system', path=None, version=None, params=None, header=None): - """GET requests for the /System/Users module.""" - response = self.client.get(module=module, path=path, version=version, params=params, header=header) - return response - - def _post(self, module='system', path=None, version=None, params=None, data=None, json=None, header=None): - """POST requests for the /System/Users module.""" - response = self.client.post(module=module, path=path, version=version, params=params, data=data, json=json, header=header) - return response + return System._get(path='/users/search', params=kwargs) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_first.py b/tests/test_first.py new file mode 100644 index 0000000..daa71c1 --- /dev/null +++ b/tests/test_first.py @@ -0,0 +1,13 @@ +import pyairwatch.client + + +def setup(): + print "SETUP!" + + +def teardown(): + print "TEAR DOWN!" + + +def test_basic(): + print "I RAN!" From f7251e632a1ea63411624328ce14503f9c485258 Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Thu, 24 Jan 2019 10:56:48 -0500 Subject: [PATCH 10/28] adding base cases for API --- pyairwatch/mdm/mdm.py | 26 +++++++++++++++++++++ pyairwatch/mdm/network.py | 14 ++++++++++++ pyairwatch/mdm/scheduleosupdate.py | 36 ++++++++++++++++++++++++++++++ pyairwatch/system/info.py | 13 +++++++++++ pyairwatch/system/system.py | 35 +++++++++++++++++++++++++++++ 5 files changed, 124 insertions(+) create mode 100644 pyairwatch/mdm/mdm.py create mode 100644 pyairwatch/mdm/network.py create mode 100644 pyairwatch/mdm/scheduleosupdate.py create mode 100644 pyairwatch/system/info.py create mode 100644 pyairwatch/system/system.py diff --git a/pyairwatch/mdm/mdm.py b/pyairwatch/mdm/mdm.py new file mode 100644 index 0000000..1a075b2 --- /dev/null +++ b/pyairwatch/mdm/mdm.py @@ -0,0 +1,26 @@ +class MDM(object): + """ + Base MDM class + """ + def __init__(self, client): + self.client = client + + def _get(self, module='mdm', path=None, + version=None, params=None, header=None): + """GET requests for base mdm endpoints""" + return self.client.get(module=module, path=path, + version=version, params=params, header=header) + + def _post(self, module='mdm', path=None, + version=None, params=None, data=None, json=None, header=None): + """POST requests for base mdm endpoints""" + return self.client.post(module=module, path=path, version=version, + params=params, data=data, + json=json, header=header) + + def _put(self, module='mdm', path=None, + version=None, params=None, data=None, json=None, header=None): + """PUT requests for base mdm endpoints""" + return self.client.put(module=module, path=path, version=version, + params=params, data=data, + json=json, header=header) diff --git a/pyairwatch/mdm/network.py b/pyairwatch/mdm/network.py new file mode 100644 index 0000000..ad80eab --- /dev/null +++ b/pyairwatch/mdm/network.py @@ -0,0 +1,14 @@ +from .mdm import MDM + + +class Network(MDM): + """ + Get network sample information + version 1 + """ + def __init__(self, client): + MDM.__init__(self, client) + + def get_network_by_device_id(self, device_id): + """get network sample information based with Device ID""" + return MDM._get(self, path='/devices/{}/network'.format(device_id)) diff --git a/pyairwatch/mdm/scheduleosupdate.py b/pyairwatch/mdm/scheduleosupdate.py new file mode 100644 index 0000000..7aa66bc --- /dev/null +++ b/pyairwatch/mdm/scheduleosupdate.py @@ -0,0 +1,36 @@ +from .mdm import MDM + + +class ScheduleOSUpdate(MDM): + """ + For Schedule OS Update + """ + def __init__(self, client): + MDM.__init__(self, client) + self.jheader = {'Content-Type': 'application/json'} + + def update_device(self, os_update_product_keys, install_action, + serialnumber=None, macaddress=None, udid=None, + imeinumber=None, easid=None): + + """ + Needs work + """ + path = '/devices/commands/scheduleosupdate' + params = {} + if serialnumber: + params['searchby'] = 'Serialnumber' + params['id'] = str(serialnumber) + # elif macaddress: + # response = self.search(searchby='Macaddress', id=str(macaddress)) + # elif udid: + # response = self.search(searchby='Udid', id=str(udid)) + # elif imeinumber: + # response = self.search(searchby='ImeiNumber', id=str(imeinumber)) + # elif easid: + # response = self.search(searchby='EasId', id=str(easid)) + else: + return None + params['installaction'] = 'default' + body = os_update_product_keys + return MDM._get(path=path, params=params,) diff --git a/pyairwatch/system/info.py b/pyairwatch/system/info.py new file mode 100644 index 0000000..08cbf3a --- /dev/null +++ b/pyairwatch/system/info.py @@ -0,0 +1,13 @@ +from .system import System + + +class Info(System): + """ + Get enverioment information + """ + def __init__(self, client): + System.__init__(self, client) + + def get_enviroment_info(self): + """get enviroment information""" + return System._get(self, path='/info') diff --git a/pyairwatch/system/system.py b/pyairwatch/system/system.py new file mode 100644 index 0000000..a18734c --- /dev/null +++ b/pyairwatch/system/system.py @@ -0,0 +1,35 @@ +class System(object): + """ + Base System class + """ + def __init__(self, client): + self.client = client + + def _get(self, module='system', path=None, + version=None, params=None, header=None): + """GET requests for base system endpoints""" + return self.client.get(module=module, path=path, + version=version, params=params, header=header) + + def _post(self, module='system', path=None, + version=None, params=None, data=None, json=None, header=None): + """POST requests""" + return self.client.post(module=module, path=path, version=version, + params=params, data=data, + json=json, header=header) + + def _post_no_error_check(self, module='system', path=None, + version=None, params=None, data=None, + json=None, header=None): + """POST requests with no error check when none json is returned""" + return self.client.post_no_error_check(module=module, path=path, + version=version, params=params, + data=data, + json=json, header=header) + + def _put(self, module='system', path=None, + version=None, params=None, data=None, json=None, header=None): + """PUT requests""" + return self.client.put(module=module, path=path, version=version, + params=params, data=data, + json=json, header=header) From ed3ec82e7c4f7110ff9af8374248778261c9a463 Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Mon, 24 Jun 2019 11:33:24 -0400 Subject: [PATCH 11/28] - Fixed a bug with search function - renamed a function to be more consistant - reworked some string manipulation ot be consistant - added 3 function for getting security infor sample info - added function to retrevie jsut FIleVault Key --- pyairwatch/mdm/devices.py | 46 ++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/pyairwatch/mdm/devices.py b/pyairwatch/mdm/devices.py index 640fbfa..0c43a34 100644 --- a/pyairwatch/mdm/devices.py +++ b/pyairwatch/mdm/devices.py @@ -11,7 +11,7 @@ def __init__(self, client): def search(self, **kwargs): """Returns the Device information matching the search parameters.""" - return MDM._get(path='/devices', params=kwargs) + return MDM._get(self, path='/devices', params=kwargs) def get_details_by_alt_id(self, serialnumber=None, macaddress=None, udid=None, imeinumber=None, easid=None): """Returns the Device information matching the search parameters.""" @@ -51,12 +51,12 @@ def clear_device_passcode(self, device_id): """ return MDM._post(self, path='/devices/{}/clearpasscode'.format(device_id)) - def commands_for_device_id(self, command, device_id): + def send_commands_for_device_id(self, command, device_id): """ Commands for devices selecting device based on id """ path = '/devices/{}/commands'.format(device_id) - command = 'command=' + command + command = 'command={}'.format(command) return MDM._post(self, path=path, params=command) def send_commands_by_id(self, command, searchby, id): @@ -64,8 +64,9 @@ def send_commands_by_id(self, command, searchby, id): Commands for devices selecting device based on id """ path = '/devices/commands' - query = 'command=' + str(command) + '&searchBy=' + str(searchby) - query = query + '&id=' + str(id) + query = 'command={}&searchBy={}&id={}'.format(str(command), + str(searchby), + str(id)) return MDM._post(self, path=path, params=query) def get_details_by_device_id(self, device_id): @@ -73,3 +74,38 @@ def get_details_by_device_id(self, device_id): device detals by device id """ return MDM._get(self, path='/devices/{}'.format(device_id)) + + def get_device_filevualt_recovery_key(self, device_uuid): + """ + Gets a maccOS device's FileVualt Recovery Key + """ + _path = '/devices/{}/security/recovery-key'.format(device_uuid) + return MDM._get(self, path=_path) + + def get_security_info_by_id(self, device_id): + """ + Processes the device ID to retrieve the security + information sample related info + """ + _path = '/devices/{}/security'.format(device_id) + return MDM._get(self, path=_path) + + def get_security_info_by_atlternat_id(self, searchby, id): + """ + Processes the device ID to retrieve the security + information sample related info by Alternater ID + """ + _path = '/devices/security' + _params = 'searchby={}&id={}'.format(searchby, id) + return MDM._get(self, path=_path, params=_params) + + def get_bulk_security_info(self, organization_group_id, user_name, params=None): + """ + Processes the information like organizationgroup ID, user name, model, + platform, last seen, ownership, compliant status, seen since parameters + and fetches the security information for the same. + """ + _path = '/devices/securityinfosearch' + _query = 'organizationgroupid={}&user={}'.format(organization_group_id, + user_name) + return MDM._get(self, path=_path, params=_query) From 8b8d2b19d697185245f961db8d2f8469d8f2c50a Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Wed, 18 Sep 2019 09:53:22 -0400 Subject: [PATCH 12/28] Added some APIs - Stagging flow for none AD - Added Patch to MDM - Added Patch to client --- pyairwatch/client.py | 56 ++++++++++++++++++++++++++++++--------- pyairwatch/mdm/devices.py | 19 ++++++++++--- pyairwatch/mdm/mdm.py | 20 +++++++++----- 3 files changed, 72 insertions(+), 23 deletions(-) diff --git a/pyairwatch/client.py b/pyairwatch/client.py index 880610b..6b18f29 100644 --- a/pyairwatch/client.py +++ b/pyairwatch/client.py @@ -69,59 +69,88 @@ def __init__(self, env, apikey, username, password): self.info = Info(self) self.network = Network(self) - def get(self, module, path, version=None, params=None, header=None, timeout=30): + def get(self, module, path, version=None, params=None, header=None, + timeout=30): """Sends a GET request to the API. Returns the response object.""" if header is None: header = {} - header.update(self._build_header(self.username, self.password, self.apikey)) + header.update(self._build_header(self.username, self.password, + self.apikey)) header.update({'Content-Type': 'application/json'}) endpoint = self._build_endpoint(self.env, module, path, version) try: - r = requests.get(endpoint, params=params, headers=header, timeout=timeout) + r = requests.get(endpoint, params=params, headers=header, + timeout=timeout) r = self._check_for_error(r) return r except AirWatchAPIError as e: raise e - def post(self, module, path, version=None, params=None, data=None, json=None, header=None, timeout=30): + def post(self, module, path, version=None, params=None, data=None, + json=None, header=None, timeout=30): """Sends a POST request to the API. Returns the response object.""" if header is None: header = {} - header.update(self._build_header(self.username, self.password, self.apikey)) + header.update(self._build_header(self.username, self.password, + self.apikey)) endpoint = self._build_endpoint(self.env, module, path, version) try: - r = requests.post(endpoint, params=params, data=data, json=json, headers=header, timeout=timeout) + r = requests.post(endpoint, params=params, data=data, json=json, + headers=header, timeout=timeout) r = self._check_for_error(r) return r except AirWatchAPIError as e: raise e - def put(self, module, path, version=None, params=None, data=None, json=None, header=None, timeout=30): + def put(self, module, path, version=None, params=None, data=None, + json=None, header=None, timeout=30): """ Sends a PUT request to the API. Returns the response object. """ if header is None: header = {} - header.update(self._build_header(self.username, self.password, self.apikey)) + header.update(self._build_header(self.username, self.password, + self.apikey)) endpoint = self._build_endpoint(self.env, module, path, version) try: - r = requests.put(endpoint, params=params, data=data, json=json, headers=header, timeout=timeout) + r = requests.put(endpoint, params=params, data=data, json=json, + headers=header, timeout=timeout) r = self._check_for_error(r) return r except AirWatchAPIError as e: raise e + def patch(self, module, path, version=None, params=None, data=None, + json=None, header=None, timeout=30): + """ + Sends a Patch request to the API. Returns the response object. + """ + if header is None: + header = {} + header.update(self._build_header(self.username, self.password, + self.apikey)) + endpoint = self._build_endpoint(self.env, module, path, version) + try: + r = requests.patch(endpoint, params=params, data=data, json=json, + headers=header, timeout=timeout) + r = self._check_for_error(r) + return r + except AirWatchAPIError as e: + raise e #NOQA - def delete(self, module, path, version=None, params=None, header=None, timeout=30): + def delete(self, module, path, version=None, params=None, header=None, + timeout=30): """ Sends a DELETE request to the API. Returns the response object. """ if header is None: header = {} - header.update(self._build_header(self.username, self.password, self.apikey)) + header.update(self._build_header(self.username, self.password, + self.apikey)) endpoint = self._build_endpoint(self.env, module, path, version) try: - r = requests.delete(endpoint, params=params, headers=header, timeout=timeout) + r = requests.delete(endpoint, params=params, headers=header, + timeout=timeout) r = self._check_for_error(r) return r except AirWatchAPIError as e: @@ -133,7 +162,8 @@ def _check_for_error(response): Checks the response for json data, then for an error, then for a status code """ - if response.headers.get('Content-Type') in ('application/json', 'application/json; charset=utf-8'): + if response.headers.get('Content-Type') in ('application/json', + 'application/json; charset=utf-8'): json = response.json() if json.get('errorCode'): raise AirWatchAPIError(json_response=json) diff --git a/pyairwatch/mdm/devices.py b/pyairwatch/mdm/devices.py index 0c43a34..fba310a 100644 --- a/pyairwatch/mdm/devices.py +++ b/pyairwatch/mdm/devices.py @@ -13,7 +13,8 @@ def search(self, **kwargs): """Returns the Device information matching the search parameters.""" return MDM._get(self, path='/devices', params=kwargs) - def get_details_by_alt_id(self, serialnumber=None, macaddress=None, udid=None, imeinumber=None, easid=None): + def get_details_by_alt_id(self, serialnumber=None, macaddress=None, + udid=None, imeinumber=None, easid=None): """Returns the Device information matching the search parameters.""" params = {} if serialnumber: @@ -30,7 +31,8 @@ def get_details_by_alt_id(self, serialnumber=None, macaddress=None, udid=None, i return None return response - def get_id_by_alt_id(self, serialnumber=None, macaddress=None, udid=None, imeinumber=None, easid=None): + def get_id_by_alt_id(self, serialnumber=None, macaddress=None, udid=None, + imeinumber=None, easid=None): if serialnumber: response = self.search(searchby='Serialnumber', id=str(serialnumber)) elif macaddress: @@ -49,7 +51,8 @@ def clear_device_passcode(self, device_id): """ Clear the passcode on a device """ - return MDM._post(self, path='/devices/{}/clearpasscode'.format(device_id)) + return MDM._post(self, + path='/devices/{}/clearpasscode'.format(device_id)) def send_commands_for_device_id(self, command, device_id): """ @@ -99,7 +102,8 @@ def get_security_info_by_atlternat_id(self, searchby, id): _params = 'searchby={}&id={}'.format(searchby, id) return MDM._get(self, path=_path, params=_params) - def get_bulk_security_info(self, organization_group_id, user_name, params=None): + def get_bulk_security_info(self, organization_group_id, user_name, + params=None): """ Processes the information like organizationgroup ID, user name, model, platform, last seen, ownership, compliant status, seen since parameters @@ -109,3 +113,10 @@ def get_bulk_security_info(self, organization_group_id, user_name, params=None): _query = 'organizationgroupid={}&user={}'.format(organization_group_id, user_name) return MDM._get(self, path=_path, params=_query) + + def switch_device_from_staging_to_user(self, device_id, user_id): + """ + API for Single Staging switch to directory or basic user + """ + _path = "/devices/{}/enrollmentuser/{}".format(device_id, user_id) + return MDM._patch(self, path=_path) diff --git a/pyairwatch/mdm/mdm.py b/pyairwatch/mdm/mdm.py index 1a075b2..fe2e157 100644 --- a/pyairwatch/mdm/mdm.py +++ b/pyairwatch/mdm/mdm.py @@ -5,22 +5,30 @@ class MDM(object): def __init__(self, client): self.client = client - def _get(self, module='mdm', path=None, - version=None, params=None, header=None): + def _get(self, module='mdm', path=None, version=None, params=None, + header=None): """GET requests for base mdm endpoints""" return self.client.get(module=module, path=path, version=version, params=params, header=header) - def _post(self, module='mdm', path=None, - version=None, params=None, data=None, json=None, header=None): + def _post(self, module='mdm', path=None, version=None, params=None, + data=None, json=None, header=None): """POST requests for base mdm endpoints""" return self.client.post(module=module, path=path, version=version, params=params, data=data, json=json, header=header) - def _put(self, module='mdm', path=None, - version=None, params=None, data=None, json=None, header=None): + def _put(self, module='mdm', path=None, version=None, params=None, + data=None, json=None, header=None): """PUT requests for base mdm endpoints""" return self.client.put(module=module, path=path, version=version, params=params, data=data, json=json, header=header) + + + def _patch(self, module='mdm', path=None, version=None, params=None, + data=None, json=None, header=None): + """Patch requests for base mdm endpoints""" + return self.client.patch(module=module, path=path, version=version, + params=params, data=data, + json=json, header=header) From 2c116b5623a1e7a55822530d87c4a3a879c869ee Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Wed, 18 Sep 2019 13:55:13 -0400 Subject: [PATCH 13/28] finishing framework changes I made a long time ago --- pyairwatch/mam/apps.py | 7 ------ pyairwatch/mam/blobs.py | 14 ++++-------- pyairwatch/mam/internalapps.py | 16 ++++--------- pyairwatch/mam/mam.py | 21 +++++++++++++++++ pyairwatch/mdm/smartgroups.py | 35 +++++++++++----------------- pyairwatch/mdm/tags.py | 22 +++++++----------- pyairwatch/system/admins.py | 18 ++++----------- pyairwatch/system/featureflag.py | 10 ++++---- pyairwatch/system/groups.py | 39 ++++++++++++++++---------------- pyairwatch/system/system.py | 4 +++- pyairwatch/system/usergroups.py | 22 +++++++----------- 11 files changed, 91 insertions(+), 117 deletions(-) delete mode 100644 pyairwatch/mam/apps.py create mode 100644 pyairwatch/mam/mam.py diff --git a/pyairwatch/mam/apps.py b/pyairwatch/mam/apps.py deleted file mode 100644 index 928cc50..0000000 --- a/pyairwatch/mam/apps.py +++ /dev/null @@ -1,7 +0,0 @@ -class Apps(object): - """ - apps - """ - - def __init__(self, client): - self.client = client diff --git a/pyairwatch/mam/blobs.py b/pyairwatch/mam/blobs.py index 0c3471f..136dbcc 100644 --- a/pyairwatch/mam/blobs.py +++ b/pyairwatch/mam/blobs.py @@ -1,16 +1,10 @@ +from .mam import MAM + + class Blobs(object): """ A class to manage Blobs """ def __init__(self, client): - self.client = client - - def _get(self, module='mam', path=None, version=None, params=None, header=None): - """GET requests for the /MAM/blobs module.""" - response = self.client.get(module=module, path=path, version=version, params=params, header=header) - return response - - def _post(self, module='mam', path=None, version=None, params=None, data=None, json=None, header=None): - """Post requests for the /MAM/blobs module.""" - response = self.client.post(module=module, path=path, version=version, params=params, data=data, json=json, header=header) + MAM.__init__(self, client) diff --git a/pyairwatch/mam/internalapps.py b/pyairwatch/mam/internalapps.py index 35a582f..16b2f52 100644 --- a/pyairwatch/mam/internalapps.py +++ b/pyairwatch/mam/internalapps.py @@ -1,16 +1,10 @@ -class InternalApps(object): +from .mam import MAM + + +class InternalApps(MAM): """ A class to manage Internal Applications """ def __init__(self, client): - self.client = client - - def _get(self, module='mam/apps', path=None, version=None, params=None, header=None): - """GET requests for the /MAM/apps/internal module.""" - response = self.client.get(module=module, path=path, version=version, params=params, header=header) - return response - - def _post(self, module='mam/apps', path=None, version=None, params=None, data=None, json=None, header=None): - """Post requests for the /MAM/apps/internal module.""" - response = self.client.post(module=module, path=path, version=version, params=params, data=data, json=json, header=header) + MAM.__init__(self, client) diff --git a/pyairwatch/mam/mam.py b/pyairwatch/mam/mam.py new file mode 100644 index 0000000..76e114a --- /dev/null +++ b/pyairwatch/mam/mam.py @@ -0,0 +1,21 @@ +class MAM(object): + """ + Application manamgment + """ + + def __init__(self, client): + self.client = client + + def _get(self, module='mam', path=None, version=None, params=None, + header=None): + """GET requests for the /MAM/blobs module.""" + response = self.client.get(module=module, path=path, version=version, + params=params, header=header) + return response + + def _post(self, module='mam', path=None, version=None, params=None, + data=None, json=None, header=None): + """Post requests for the /MAM/blobs module.""" + response = self.client.post(module=module, path=path, version=version, + params=params, data=data, json=json, + header=header) diff --git a/pyairwatch/mdm/smartgroups.py b/pyairwatch/mdm/smartgroups.py index 0ec3b07..d6522d6 100644 --- a/pyairwatch/mdm/smartgroups.py +++ b/pyairwatch/mdm/smartgroups.py @@ -1,10 +1,13 @@ +from .mdm import MDM + + class SmartGroups(object): """ A class to manage AirWatch Smart Groups. """ def __init__(self, client): - self.client = client + MDM.__init__(self, client) def search(self, **kwargs): """ @@ -17,22 +20,23 @@ def search(self, **kwargs): organizationgroupid={organizationgroupid} managedbyorganizationgroupid={managedbyorganizationgroupid} """ - response = self._get(path='/smartgroups/search', params=kwargs) + response = MDM._get(self, path='/smartgroups/search', params=kwargs) return response def get_details(self, id): """Retrieves the Smart Group details created in an Organization Group""" - response = self._get(path='/smartgroups/{}'.format(id)) + response = MDM._get(self, path='/smartgroups/{}'.format(id)) return response def get_devices(self, id): """Retrieves all devices from Smart Group""" - devices = self._get(path='/smartgroups/{}/devices'.format(id)) + devices = MDM._get(self, path='/smartgroups/{}/devices'.format(id)) return devices def get_id_from_og_id(self, og_id, sg_name): """Returns the Smart Group ID for a given SG Name & OG ID""" - response = self.search(managedbyorganizationgroupid=str(og_id), orderby='smartgroupid') + response = self.search(managedbyorganizationgroupid=str(og_id), + orderby='smartgroupid') for keys in response['SmartGroups']: if keys['Name'] == sg_name: sg_id = keys.get('SmartGroupID') @@ -43,31 +47,18 @@ def move_device_to_sg(self, sg_id, device_id, device_name): # sg_details = self.get_details(sg_id) # print type(sg_details) sg_details = {} - sg_details[u'DeviceAdditions'] = [{u'Id': str(device_id).decode(), u'Name': str(device_name).decode()}] + sg_details[u'DeviceAdditions'] = [{u'Id': str(device_id).decode(), + u'Name': str(device_name).decode()}] print(sg_details) # device = {'DeviceAdditions':[{ 'Id':'{}'.format(device_id)}]} - response = self._post(path='/smartgroups/{}/update'.format(str(sg_id)), data=sg_details) + path = '/smartgroups/{}/update'.format(str(sg_id)) + response = MDM._post(self, path=path, data=sg_details) d = self.get_details(sg_id) print(d) return response - def _get(self, module='mdm', path=None, version=None, params=None, header=None): - """GET requests for the /MDM/SmartGroups module.""" - response = self.client.get(module=module, path=path, version=version, params=params, header=header) - return response - - def _post(self, module='mdm', path=None, version=None, params=None, data=None, json=None, header=None): - """POST requests for the /MDM/SmartGroups module.""" - response = self.client.post(module=module, path=path, version=version, params=params, data=data, json=json, header=header) - return response - - def _put(self, module='mdm', path=None, version=None, params=None, data=None, json=None, header=None): - """PUT requests for the /MDM/SmartGroups module.""" - response = self.client.put(module=module, path=path, version=version, params=params, data=data, json=json, header=header) - return response - # Inconsistent behaviors during testing, commenting out these methods for now: # # def remove_device_from_sg(self, sg_id, device_id): diff --git a/pyairwatch/mdm/tags.py b/pyairwatch/mdm/tags.py index 6c60d1e..b5a1e9a 100644 --- a/pyairwatch/mdm/tags.py +++ b/pyairwatch/mdm/tags.py @@ -1,20 +1,14 @@ -class Tags(object): +from .mdm import MDM + + +class Tags(MDM): """A class to manage various AirWatch device tag functionalities""" def __init__(self, client): - self.client = client + MDM.__init__(self, client) def get_id_by_name(self, name, og_id): - # mdm/tags/search?name={name} - response = self._get(path='/tags/search', params={'name':str(name), 'organizationgroupid':str(og_id)}) - return response - - def _get(self, module='mdm', path=None, version=None, params=None, header=None): - """GET requests for the /MDM/Tags module.""" - response = self.client.get(module=module, path=path, version=version, params=params, header=header) - return response - - def _post(self, module='mdm', path=None, version=None, params=None, data=None, json=None, header=None): - """POST requests for the /MDM/Tags module.""" - response = self.client.post(module=module, path=path, version=version, params=params, data=data, json=json, header=header) + """mdm/tags/search?name={name}""" + params = {'name': str(name), 'organizationgroupid': str(og_id)} + response = MDM._get(self, path='/tags/search', params=params) return response diff --git a/pyairwatch/system/admins.py b/pyairwatch/system/admins.py index 66d9c72..c29e6e3 100644 --- a/pyairwatch/system/admins.py +++ b/pyairwatch/system/admins.py @@ -1,7 +1,9 @@ -class Admins(object): +from .system import System + +class Admins(System): def __init__(self, client): - self.client = client + System.__init__(self, client) def search(self, **kwargs): """ @@ -17,15 +19,5 @@ def search(self, **kwargs): organizationgroupid={locationgroupid} role={role} """ - response = self._get(path='/admins/search', params=kwargs) - return response - - def _get(self, module='system', path=None, version=None, params=None, header=None): - """GET requests for the /System/Admins module.""" - response = self.client.get(module=module, path=path, version=version, params=params, header=header) - return response - - def _post(self, module='system', path=None, version=None, params=None, data=None, json=None, header=None): - """POST requests for the /System/Admins module.""" - response = self.client.post(module=module, path=path, version=version, params=params, data=data, json=json, header=header) + response = System._get(self, path='/admins/search', params=kwargs) return response diff --git a/pyairwatch/system/featureflag.py b/pyairwatch/system/featureflag.py index caf7fcc..9621d08 100644 --- a/pyairwatch/system/featureflag.py +++ b/pyairwatch/system/featureflag.py @@ -2,22 +2,22 @@ class FeatureFlag(System): - ''' + """ For Feature Flags - ''' + """ def __init__(self, client): System.__init__(self, client) def set_flag(self, feature_flag, og_uuid, override): - '''Set the Feature Flag''' + """Set the Feature Flag""" path = '/featureFlag/{}/{}/{}'.format(feature_flag, og_uuid, override) return System._post(self, path=path) def get_status(self, feature_flag, og_uuid): - '''GET a specific Feature Flag status''' + """GET a specific Feature Flag status""" path = '/featureFlag/{}/{}'.format(feature_flag, og_uuid) return System._get(self, path=path) def og_status(self, og_uuid): - '''GET all Feature Flags for a particular OG need UUID''' + """GET all Feature Flags for a particular OG need UUID""" return System._get(self, path='/featureFlag/{}'.format(og_uuid)) diff --git a/pyairwatch/system/groups.py b/pyairwatch/system/groups.py index 97b41fc..ec01566 100644 --- a/pyairwatch/system/groups.py +++ b/pyairwatch/system/groups.py @@ -1,15 +1,19 @@ -class Groups(object): +from .system import System +import json + + +class Groups(System): """ A class to manage all core functionalities for AirWatch Organization Groups. """ jheader = {'Content-Type': 'application/json'} def __init__(self, client): - self.client = client + System.__init__(self, client) def search(self, **kwargs): """Returns the Groups matching the search parameters.""" - response = self._get(path='/groups/search', params=kwargs) + response = System._get(self, path='/groups/search', params=kwargs) return response def get_id_from_groupid(self, groupid): @@ -19,22 +23,25 @@ def get_id_from_groupid(self, groupid): def get_groupid_from_id(self, id): """Returns the Group ID for a given ID""" - response = self._get(path='/groups/{}'.format(id)) + response = System._get(self, path='/groups/{}'.format(id)) return response['GroupId'] def get_uuid_from_groupid(self, id): """Returns the OG UUID for a given Group ID""" - response = self._get(path='/groups/{}'.format(id)) + response = System._get(self, path='/groups/{}'.format(id)) return response['Uuid'] def create(self, parent_id, ogdata): """Creates a Group and returns the new ID.""" - response = self._post(path='/groups/{}'.format(parent_id), data=ogdata, header=self.jheader) + response = System._post(self, path='/groups/{}'.format(parent_id), + data=ogdata, header=self.jheader) return response def create_customer_og(self, groupid, name=None): - """Creates a Customer type OG, with a given Group ID and Name, and returns the new ID""" - import json + """ + Creates a Customer type OG, with a given Group ID and Name, and returns + the new ID + """ new_og = {'GroupId': str(groupid), 'Name': str(name), 'LocationGroupType': 'Customer'} @@ -44,8 +51,10 @@ def create_customer_og(self, groupid, name=None): return response.get('Value') def create_child_og(self, parent_groupid, groupid, og_type=None, name=None): - """Creates a Child OG for a given Parent Group ID, with a given Type, Group ID, and Name, and returns the new ID""" - import json + """ + Creates a Child OG for a given Parent Group ID, with a given Type, + Group ID, and Name, and returns the new ID + """ pid = self.get_id_from_groupid(parent_groupid) new_og = {'GroupId': str(groupid), 'Name': str(name), @@ -56,13 +65,3 @@ def create_child_og(self, parent_groupid, groupid, og_type=None, name=None): new_og['LocationGroupType'] = 'Container' response = self.create(parent_id=pid, ogdata=json.dumps(new_og)) return response.get('Value') - - def _get(self, module='system', path=None, version=None, params=None, header=None): - """GET requests for the /System/Groups module.""" - response = self.client.get(module=module, path=path, version=version, params=params, header=header) - return response - - def _post(self, module='system', path=None, version=None, params=None, data=None, json=None, header=None): - """POST requests for the /System/Groups module.""" - response = self.client.post(module=module, path=path, version=version, params=params, data=data, json=json, header=header) - return response diff --git a/pyairwatch/system/system.py b/pyairwatch/system/system.py index a18734c..7ccc160 100644 --- a/pyairwatch/system/system.py +++ b/pyairwatch/system/system.py @@ -7,7 +7,9 @@ def __init__(self, client): def _get(self, module='system', path=None, version=None, params=None, header=None): - """GET requests for base system endpoints""" + """ + GET requests for base system endpoints + """ return self.client.get(module=module, path=path, version=version, params=params, header=header) diff --git a/pyairwatch/system/usergroups.py b/pyairwatch/system/usergroups.py index 60acb4e..66734ec 100644 --- a/pyairwatch/system/usergroups.py +++ b/pyairwatch/system/usergroups.py @@ -1,28 +1,22 @@ -class UserGroups(object): +from .system import System + + +class UserGroups(System): """ A class to manage all core functionalities for AirWatch Organization Groups. """ jheader = {'Content-Type': 'application/json'} def __init__(self, client): - self.client = client + System.__init__(self, client) def search(self, **kwargs): """Returns the Users Groups matching the search parameters.""" - response = self._get(path='/usergroups/search', params=kwargs) + response = System._get(self, path='/usergroups/search', params=kwargs) return response def search_users(self, id, **kwargs): """Retrieves list of users from the provided user group id.""" - response = self._get(path='/usergroups/{}/users'.format(id), params=kwargs) - return response - - def _get(self, module='system', path=None, version=None, params=None, header=None): - """GET requests for the /System/UsersGroups module.""" - response = self.client.get(module=module, path=path, version=version, params=params, header=header) - return response - - def _post(self, module='system', path=None, version=None, params=None, data=None, json=None, header=None): - """POST requests for the /System/UsersGroups module.""" - response = self.client.post(module=module, path=path, version=version, params=params, data=data, json=json, header=header) + response = System._get(self, path='/usergroups/{}/users'.format(id), + params=kwargs) return response From e1bdbc1c275f15131812869825d5b8dba9da5a27 Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Wed, 18 Sep 2019 14:53:28 -0400 Subject: [PATCH 14/28] Some formatting and fixed a miss for inheritance --- pyairwatch/mdm/smartgroups.py | 2 +- setup.py | 21 ++++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/pyairwatch/mdm/smartgroups.py b/pyairwatch/mdm/smartgroups.py index d6522d6..ac79d80 100644 --- a/pyairwatch/mdm/smartgroups.py +++ b/pyairwatch/mdm/smartgroups.py @@ -1,7 +1,7 @@ from .mdm import MDM -class SmartGroups(object): +class SmartGroups(MDM): """ A class to manage AirWatch Smart Groups. """ diff --git a/setup.py b/setup.py index 90982a5..bd48f67 100644 --- a/setup.py +++ b/setup.py @@ -1,11 +1,14 @@ from setuptools import setup -setup(name='PyVMwareAirWatch', - version='1.0.2', - description='PyVMwareAirWatch is a Python API library for [VMware AirWatch](https://www.air-watch.com/) 9.1+', - url='https://github.com/jprichards/PyVMwareAirWatch', - author='jprichards', - author_email='jprichards@example.com', - license='MIT', - packages=['pyairwatch'], - zip_safe=False) +setup( + name='PyVMwareAirWatch', + version='1.0.3', + description=('PyVMwareAirWatch is a Python API library for ' + '[VMware AirWatch] (https://www.air-watch.com/) 9.1+'), + url='https://github.com/jprichards/PyVMwareAirWatch', + author='jprichards', + author_email='jprichards@example.com', + license='MIT', + packages=['pyairwatch'], + zip_safe=False +) From 2f057b7dfb4344459561405f48ee15935031910c Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Wed, 18 Sep 2019 14:58:47 -0400 Subject: [PATCH 15/28] Removed commented code that had not value --- pyairwatch/mdm/smartgroups.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pyairwatch/mdm/smartgroups.py b/pyairwatch/mdm/smartgroups.py index ac79d80..bc92080 100644 --- a/pyairwatch/mdm/smartgroups.py +++ b/pyairwatch/mdm/smartgroups.py @@ -44,13 +44,10 @@ def get_id_from_og_id(self, og_id, sg_name): def move_device_to_sg(self, sg_id, device_id, device_name): """Move Device to a Smart Group by Device ID""" - # sg_details = self.get_details(sg_id) - # print type(sg_details) sg_details = {} sg_details[u'DeviceAdditions'] = [{u'Id': str(device_id).decode(), u'Name': str(device_name).decode()}] print(sg_details) - # device = {'DeviceAdditions':[{ 'Id':'{}'.format(device_id)}]} path = '/smartgroups/{}/update'.format(str(sg_id)) response = MDM._post(self, path=path, data=sg_details) From 740f64762f7e86f2e3d85cebcbb470828cc6afea Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Wed, 18 Sep 2019 15:05:34 -0400 Subject: [PATCH 16/28] deleted commented out code --- pyairwatch/client.py | 61 ++++++++++++++++-------------- pyairwatch/mdm/scheduleosupdate.py | 10 +---- 2 files changed, 33 insertions(+), 38 deletions(-) diff --git a/pyairwatch/client.py b/pyairwatch/client.py index 993cfa4..eedd7bd 100644 --- a/pyairwatch/client.py +++ b/pyairwatch/client.py @@ -55,22 +55,22 @@ def __str__(self): class AirWatchAPI(object): def __init__(self, env, apikey, username, password): - self.env = env - self.apikey = apikey - self.username = username - self.password = password - self.groups = Groups(self) - self.smartgroups = SmartGroups(self) - self.devices = Devices(self) - self.profiles = Profiles(self) - self.tags = Tags(self) - self.admins = Admins(self) - self.users = Users(self) - self.usergroups = UserGroups(self) - self.featureflag = FeatureFlag(self) - self.ldap = LDAP(self) - self.info = Info(self) - self.network = Network(self) + self.env = env + self.apikey = apikey + self.username = username + self.password = password + self.groups = Groups(self) + self.smartgroups = SmartGroups(self) + self.devices = Devices(self) + self.profiles = Profiles(self) + self.tags = Tags(self) + self.admins = Admins(self) + self.users = Users(self) + self.usergroups = UserGroups(self) + self.featureflag = FeatureFlag(self) + self.ldap = LDAP(self) + self.info = Info(self) + self.network = Network(self) def get(self, module, path, version=None, params=None, header=None, timeout=30): @@ -78,7 +78,7 @@ def get(self, module, path, version=None, params=None, header=None, if header is None: header = {} header.update(self._build_header(self.username, self.password, - self.apikey)) + self.apikey)) header.update({'Content-Type': 'application/json'}) endpoint = self._build_endpoint(self.env, module, path, version) try: @@ -91,11 +91,13 @@ def get(self, module, path, version=None, params=None, header=None, def post(self, module, path, version=None, params=None, data=None, json=None, header=None, timeout=30): - """Sends a POST request to the API. Returns the response object.""" + """ + Sends a POST request to the API. Returns the response object. + """ if header is None: header = {} header.update(self._build_header(self.username, self.password, - self.apikey)) + self.apikey)) endpoint = self._build_endpoint(self.env, module, path, version) try: r = requests.post(endpoint, params=params, data=data, json=json, @@ -113,7 +115,7 @@ def put(self, module, path, version=None, params=None, data=None, if header is None: header = {} header.update(self._build_header(self.username, self.password, - self.apikey)) + self.apikey)) endpoint = self._build_endpoint(self.env, module, path, version) try: r = requests.put(endpoint, params=params, data=data, json=json, @@ -131,7 +133,7 @@ def patch(self, module, path, version=None, params=None, data=None, if header is None: header = {} header.update(self._build_header(self.username, self.password, - self.apikey)) + self.apikey)) endpoint = self._build_endpoint(self.env, module, path, version) try: r = requests.patch(endpoint, params=params, data=data, json=json, @@ -140,7 +142,8 @@ def patch(self, module, path, version=None, params=None, data=None, return r except AirWatchAPIError as e: raise e - #NOQA + # NOQA + def delete(self, module, path, version=None, params=None, header=None, timeout=30): """ @@ -149,7 +152,7 @@ def delete(self, module, path, version=None, params=None, header=None, if header is None: header = {} header.update(self._build_header(self.username, self.password, - self.apikey)) + self.apikey)) endpoint = self._build_endpoint(self.env, module, path, version) try: r = requests.delete(endpoint, params=params, headers=header, @@ -166,7 +169,7 @@ def _check_for_error(response): a status code """ if response.headers.get('Content-Type') in ('application/json', - 'application/json; charset=utf-8'): + 'application/json; charset=utf-8'): json = response.json() if json.get('errorCode'): raise AirWatchAPIError(json_response=json) @@ -198,13 +201,13 @@ def _build_endpoint(base_url, module, path=None, version=None): @staticmethod def _build_header(username, password, token, accept='application/json'): """ - Build the header with base64 login, AW API token, + Build the header with base64 login, AW API token, and accept a json response """ hashed_auth = base64.b64encode((username + ':' + password).encode('utf8')).decode("utf-8") header = { - 'Authorization': 'Basic {}'.format(hashed_auth), - 'aw-tenant-code': token, - 'Accept': accept - } + 'Authorization': 'Basic {}'.format(hashed_auth), + 'aw-tenant-code': token, + 'Accept': accept + } return header diff --git a/pyairwatch/mdm/scheduleosupdate.py b/pyairwatch/mdm/scheduleosupdate.py index 7aa66bc..4c064db 100644 --- a/pyairwatch/mdm/scheduleosupdate.py +++ b/pyairwatch/mdm/scheduleosupdate.py @@ -5,6 +5,7 @@ class ScheduleOSUpdate(MDM): """ For Schedule OS Update """ + def __init__(self, client): MDM.__init__(self, client) self.jheader = {'Content-Type': 'application/json'} @@ -12,7 +13,6 @@ def __init__(self, client): def update_device(self, os_update_product_keys, install_action, serialnumber=None, macaddress=None, udid=None, imeinumber=None, easid=None): - """ Needs work """ @@ -21,14 +21,6 @@ def update_device(self, os_update_product_keys, install_action, if serialnumber: params['searchby'] = 'Serialnumber' params['id'] = str(serialnumber) - # elif macaddress: - # response = self.search(searchby='Macaddress', id=str(macaddress)) - # elif udid: - # response = self.search(searchby='Udid', id=str(udid)) - # elif imeinumber: - # response = self.search(searchby='ImeiNumber', id=str(imeinumber)) - # elif easid: - # response = self.search(searchby='EasId', id=str(easid)) else: return None params['installaction'] = 'default' From eaa203fa79b7ea6a852b22cd53e37411d3242222 Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Wed, 18 Sep 2019 15:24:19 -0400 Subject: [PATCH 17/28] Adding Tests as I made lots of changes --- tests/test_first.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/test_first.py b/tests/test_first.py index daa71c1..a0636e8 100644 --- a/tests/test_first.py +++ b/tests/test_first.py @@ -1,13 +1,17 @@ import pyairwatch.client +import unittest -def setup(): - print "SETUP!" +class TestBasicTests(unittest.TestCase): + def setUP(self): + print("SETUP!") + def teardown(self): + print("TEAR DOWN!") -def teardown(): - print "TEAR DOWN!" + def test_basic(self): + self.assert_(True, "I RAN!") -def test_basic(): - print "I RAN!" +if __name__ == '__main__': + unittest.main() From 7466d10570ef29fe58e282a5ce94580d92938bfe Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Thu, 19 Sep 2019 10:54:45 -0400 Subject: [PATCH 18/28] Added Tests and a template for environment as I don't want to expose any environment details. --- tests/enviroments_template.json | 8 ++++++++ tests/testing_is_important.py | 26 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 tests/enviroments_template.json create mode 100644 tests/testing_is_important.py diff --git a/tests/enviroments_template.json b/tests/enviroments_template.json new file mode 100644 index 0000000..ed9a947 --- /dev/null +++ b/tests/enviroments_template.json @@ -0,0 +1,8 @@ +{ + "enviroment_name" : { + "server" : "apitest.awmdmd.com", + "apikey" : "apikey", + "username" : "admin_user", + "password" : "password" + } +} diff --git a/tests/testing_is_important.py b/tests/testing_is_important.py new file mode 100644 index 0000000..71d656d --- /dev/null +++ b/tests/testing_is_important.py @@ -0,0 +1,26 @@ +from pyairwatch.client import AirWatchAPI +import unittest +import json + + +class TestBasicTests(unittest.TestCase): + def setUp(self): + input = "cnaapp" + with open("enviroments.json") as json_file: + server_details = json.load(json_file) + self.enviroment = AirWatchAPI(env=server_details[input]["server"], + apikey=server_details[input]["apikey"], + username=server_details[input]["username"], + password=server_details[input]["password"]) + + def teardown(self): + print("TEAR DOWN!") + + def test_basic(self): + response = self.enviroment.info.get_enviroment_info() + self.assertEqual(response["ProductName"], "AirWatch Platform Service", + "Info API ran") + + +if __name__ == '__main__': + unittest.main() From 047a78190d5d5ffdcbd2e02e6bed09ed9b3e2e75 Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Thu, 19 Sep 2019 10:55:47 -0400 Subject: [PATCH 19/28] removed file --- tests/test_first.py | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 tests/test_first.py diff --git a/tests/test_first.py b/tests/test_first.py deleted file mode 100644 index a0636e8..0000000 --- a/tests/test_first.py +++ /dev/null @@ -1,17 +0,0 @@ -import pyairwatch.client -import unittest - - -class TestBasicTests(unittest.TestCase): - def setUP(self): - print("SETUP!") - - def teardown(self): - print("TEAR DOWN!") - - def test_basic(self): - self.assert_(True, "I RAN!") - - -if __name__ == '__main__': - unittest.main() From 30e45537a50e3c513e22d992d0b5a2e7aa5d6d47 Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Thu, 19 Sep 2019 10:58:25 -0400 Subject: [PATCH 20/28] Ignoring my environments.json and fixed a spelling mistake. --- .gitignore | 2 ++ tests/{enviroments_template.json => environments_template.json} | 0 tests/testing_is_important.py | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) rename tests/{enviroments_template.json => environments_template.json} (100%) diff --git a/.gitignore b/.gitignore index 90433b3..d876e25 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ *.egg-info .idea venv +enviroments.json +environments.json diff --git a/tests/enviroments_template.json b/tests/environments_template.json similarity index 100% rename from tests/enviroments_template.json rename to tests/environments_template.json diff --git a/tests/testing_is_important.py b/tests/testing_is_important.py index 71d656d..3776fc3 100644 --- a/tests/testing_is_important.py +++ b/tests/testing_is_important.py @@ -6,7 +6,7 @@ class TestBasicTests(unittest.TestCase): def setUp(self): input = "cnaapp" - with open("enviroments.json") as json_file: + with open("environments.json") as json_file: server_details = json.load(json_file) self.enviroment = AirWatchAPI(env=server_details[input]["server"], apikey=server_details[input]["apikey"], From 073b33fe02268efcb74062491ee331fdf6088ee0 Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Thu, 19 Sep 2019 11:31:36 -0400 Subject: [PATCH 21/28] - Updated README - renamed some functions for read ability - made small changes for conventions i created elsewhere --- README.md | 20 ++++++++++++++++++++ pyairwatch/mdm/devices.py | 10 +++++----- pyairwatch/system/featureflag.py | 15 ++++++++------- tests/testing_is_important.py | 2 +- 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 4481452..4c7a2b5 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,13 @@ Supported Functionality * Devices * Get Device Details by Alt ID (Macaddress, Udid, Serialnumber, ImeiNumber, EasId) * Get Device ID by Alt ID (Macaddress, Udid, Serialnumber, ImeiNumber, EasId) + * Clear Device Passcode + * Send Commands To devices via Device ID or by Alt ID + * Get Device FileVualt Recover Key + * Get Security Info Sample by Device ID or Alt ID + * GET Bulk Security Info Sample + * Switch device From Staging User to End User + * Get Network info Sample by Device ID * Users * Search for users by Username, Firstname, Lastname, Email, OrganizationGroupID, or Role @@ -38,8 +45,12 @@ Supported Functionality * Get OG ID from Group ID * Create Customer type OG (On-Prem only) * Create Child OG + * Get UUID from OG ID * Smart Groups * Get SG ID by Name and OG ID + * Get SG Details + * Get Devices that are included in SG + * Add Device to SG Device Additions * Admins * Search for admins by Username, Firstname, Lastname, Email, OrganizationGroupID, or Role @@ -50,6 +61,15 @@ Supported Functionality * Get Tag ID from Tag Name * Profiles * Search for profiles by Type, Name, OG ID, Platform, Status, or Ownership + * Request Profile Install for a device +* LDAP + * Create LDAP configurations +* Feature Flags + * Set Feature Flag Status + * Get Feature Flag Status + * List all Feature Flags for a particular OG by UUID +* Info + * Get Environment Information Requirements --- diff --git a/pyairwatch/mdm/devices.py b/pyairwatch/mdm/devices.py index 7f2c6b5..efa9058 100644 --- a/pyairwatch/mdm/devices.py +++ b/pyairwatch/mdm/devices.py @@ -71,11 +71,11 @@ def send_commands_by_id(self, command, searchby, id): """ Commands for devices selecting device based on id """ - path = '/devices/commands' - query = 'command={}&searchBy={}&id={}'.format(str(command), - str(searchby), - str(id)) - return MDM._post(self, path=path, params=query) + _path = '/devices/commands' + _query = 'command={}&searchBy={}&id={}'.format(str(command), + str(searchby), + str(id)) + return MDM._post(self, path=_path, params=_query) def get_details_by_device_id(self, device_id): """ diff --git a/pyairwatch/system/featureflag.py b/pyairwatch/system/featureflag.py index 9621d08..6a1ab96 100644 --- a/pyairwatch/system/featureflag.py +++ b/pyairwatch/system/featureflag.py @@ -5,19 +5,20 @@ class FeatureFlag(System): """ For Feature Flags """ + def __init__(self, client): System.__init__(self, client) - def set_flag(self, feature_flag, og_uuid, override): + def set_feature_flag(self, feature_flag, og_uuid, override): """Set the Feature Flag""" - path = '/featureFlag/{}/{}/{}'.format(feature_flag, og_uuid, override) - return System._post(self, path=path) + _path = '/featureFlag/{}/{}/{}'.format(feature_flag, og_uuid, override) + return System._post(self, path=_path) - def get_status(self, feature_flag, og_uuid): + def get_feature_flag_status(self, feature_flag, og_uuid): """GET a specific Feature Flag status""" - path = '/featureFlag/{}/{}'.format(feature_flag, og_uuid) - return System._get(self, path=path) + _path = '/featureFlag/{}/{}'.format(feature_flag, og_uuid) + return System._get(self, path=_path) - def og_status(self, og_uuid): + def list_feature_flags_by_og(self, og_uuid): """GET all Feature Flags for a particular OG need UUID""" return System._get(self, path='/featureFlag/{}'.format(og_uuid)) diff --git a/tests/testing_is_important.py b/tests/testing_is_important.py index 3776fc3..3fb1ce3 100644 --- a/tests/testing_is_important.py +++ b/tests/testing_is_important.py @@ -16,7 +16,7 @@ def setUp(self): def teardown(self): print("TEAR DOWN!") - def test_basic(self): + def test_is_this_AirWatch(self): response = self.enviroment.info.get_enviroment_info() self.assertEqual(response["ProductName"], "AirWatch Platform Service", "Info API ran") From a77ffbfecfd4b084e586a6e21d17f4790744d4b8 Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Thu, 19 Sep 2019 12:06:17 -0400 Subject: [PATCH 22/28] usergroups.py - removed jheader that wasn't used tags.py - following convention --- pyairwatch/mdm/tags.py | 4 ++-- pyairwatch/system/usergroups.py | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/pyairwatch/mdm/tags.py b/pyairwatch/mdm/tags.py index b5a1e9a..44b7ee3 100644 --- a/pyairwatch/mdm/tags.py +++ b/pyairwatch/mdm/tags.py @@ -9,6 +9,6 @@ def __init__(self, client): def get_id_by_name(self, name, og_id): """mdm/tags/search?name={name}""" - params = {'name': str(name), 'organizationgroupid': str(og_id)} - response = MDM._get(self, path='/tags/search', params=params) + _params = {'name': str(name), 'organizationgroupid': str(og_id)} + response = MDM._get(self, path='/tags/search', params=_params) return response diff --git a/pyairwatch/system/usergroups.py b/pyairwatch/system/usergroups.py index 66734ec..6c45993 100644 --- a/pyairwatch/system/usergroups.py +++ b/pyairwatch/system/usergroups.py @@ -5,7 +5,6 @@ class UserGroups(System): """ A class to manage all core functionalities for AirWatch Organization Groups. """ - jheader = {'Content-Type': 'application/json'} def __init__(self, client): System.__init__(self, client) @@ -17,6 +16,6 @@ def search(self, **kwargs): def search_users(self, id, **kwargs): """Retrieves list of users from the provided user group id.""" - response = System._get(self, path='/usergroups/{}/users'.format(id), - params=kwargs) + _path = '/usergroups/{}/users'.format(id) + response = System._get(self, path=_path, params=kwargs) return response From 1aa93ad1193331f9b2d05441e235b2685b4ae05c Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Thu, 23 Apr 2020 20:12:28 -0400 Subject: [PATCH 23/28] made some python3 changes moved the error class out to it's own file --- pyairwatch/client.py | 55 ++++++++++++++--------------------- pyairwatch/error.py | 14 +++++++++ pyairwatch/mdm/devices.py | 8 +++++ pyairwatch/system/admins.py | 9 ++++++ pyairwatch/system/users.py | 7 ++++- requirements.txt | 2 ++ tests/testing_is_important.py | 3 +- 7 files changed, 63 insertions(+), 35 deletions(-) create mode 100644 pyairwatch/error.py create mode 100644 requirements.txt diff --git a/pyairwatch/client.py b/pyairwatch/client.py index eedd7bd..3120ced 100644 --- a/pyairwatch/client.py +++ b/pyairwatch/client.py @@ -1,23 +1,21 @@ -from __future__ import print_function +from __future__ import print_function, absolute_import import base64 -import json import logging import requests -from .mam.apps import Apps -from .mam.blobs import Blobs -from .mam.internalapps import InternalApps -from .mdm.devices import Devices -from .mdm.profiles import Profiles -from .mdm.smartgroups import SmartGroups -from .mdm.tags import Tags -from .system.admins import Admins -from .system.groups import Groups -from .system.usergroups import UserGroups -from .system.users import Users -from .system.featureflag import FeatureFlag -from .system.info import Info -from .mdm.ldap import LDAP -from .mdm.network import Network +from pyairwatch.error import AirWatchAPIError +from pyairwatch.mdm.devices import Devices +from pyairwatch.mdm.profiles import Profiles +from pyairwatch.mdm.smartgroups import SmartGroups +from pyairwatch.mdm.tags import Tags +from pyairwatch.mdm.ldap import LDAP +from pyairwatch.mdm.network import Network +from pyairwatch.system.admins import Admins +from pyairwatch.system.groups import Groups +from pyairwatch.system.usergroups import UserGroups +from pyairwatch.system.users import Users +from pyairwatch.system.featureflag import FeatureFlag +from pyairwatch.system.info import Info + # Enabling debugging at http.client level (requests->urllib3->http.client) @@ -30,6 +28,9 @@ from httplib import HTTPConnection HTTPConnection.debuglevel = 0 +#todo: programing using library should be able to set logging level +#todo: Implement logging to using config https://docs.python.org/3/howto/logging.html#configuring-logging +#todo: sett logging correclty for a library https://docs.python.org/3/howto/logging.html#configuring-logging-for-a-library logging.basicConfig() logging.getLogger().setLevel(logging.DEBUG) requests_log = logging.getLogger("requests.packages.urllib3") @@ -37,21 +38,6 @@ requests_log.propagate = True -class AirWatchAPIError(Exception): - def __init__(self, json_response=None): - if json_response is None: - pass - else: - self.response = json_response - self.error_code = json_response.get('errorCode') - self.error_msg = str(json_response.get('message')) - if self.error_code is None: - self.error_code = 0 - self.error_msg = 'Unknown API error occurred' - - def __str__(self): - return 'Error #{}: {}'.format(self.error_code, self.error_msg) - class AirWatchAPI(object): def __init__(self, env, apikey, username, password): @@ -74,7 +60,9 @@ def __init__(self, env, apikey, username, password): def get(self, module, path, version=None, params=None, header=None, timeout=30): - """Sends a GET request to the API. Returns the response object.""" + """ + Sends a GET request to the API. Returns the response object. + """ if header is None: header = {} header.update(self._build_header(self.username, self.password, @@ -142,6 +130,7 @@ def patch(self, module, path, version=None, params=None, data=None, return r except AirWatchAPIError as e: raise e + # NOQA def delete(self, module, path, version=None, params=None, header=None, diff --git a/pyairwatch/error.py b/pyairwatch/error.py new file mode 100644 index 0000000..84c1f36 --- /dev/null +++ b/pyairwatch/error.py @@ -0,0 +1,14 @@ +class AirWatchAPIError(Exception): + def __init__(self, json_response=None): + if json_response is None: + pass + else: + self.response = json_response + self.error_code = json_response.get('errorCode') + self.error_msg = str(json_response.get('message')) + if self.error_code is None: + self.error_code = 0 + self.error_msg = 'Unknown API error occurred' + + def __str__(self): + return 'Error #{}: {}'.format(self.error_code, self.error_msg) diff --git a/pyairwatch/mdm/devices.py b/pyairwatch/mdm/devices.py index efa9058..c7bce94 100644 --- a/pyairwatch/mdm/devices.py +++ b/pyairwatch/mdm/devices.py @@ -125,3 +125,11 @@ def switch_device_from_staging_to_user(self, device_id, user_id): """ _path = "/devices/{}/enrollmentuser/{}".format(device_id, user_id) return MDM._patch(self, path=_path) + + def get_managed_admin_account_by_uuid(self, device_id): + """ + Get information of the administrator account configured on a macOS + device via device enrollment program (DEP). + """ + _path = "/devices/{}/security/managed-admin-information".format(device_id) + return MDM._get(self, path=_path) diff --git a/pyairwatch/system/admins.py b/pyairwatch/system/admins.py index c29e6e3..1692942 100644 --- a/pyairwatch/system/admins.py +++ b/pyairwatch/system/admins.py @@ -1,5 +1,6 @@ from .system import System + class Admins(System): def __init__(self, client): @@ -21,3 +22,11 @@ def search(self, **kwargs): """ response = System._get(self, path='/admins/search', params=kwargs) return response + + def create_adim_v1(self, user_data): + """ + Performs necessary checks and Create a new basic Admin user. + """ + path = '/admins/addadminuser' + response = System._post(self, path=path, data=user_data) + return response diff --git a/pyairwatch/system/users.py b/pyairwatch/system/users.py index 246ed9b..a1692aa 100644 --- a/pyairwatch/system/users.py +++ b/pyairwatch/system/users.py @@ -21,4 +21,9 @@ def search(self, **kwargs): organizationgroupid={locationgroupid} role={role} """ - return System._get(path='/users/search', params=kwargs) + return System._get(self, path='/users/search', params=kwargs) + + def create_device_regestration_to_user(self, user_id, register_device_details): + path = '/users{}/registerdevice'.format(user_id) + responce = System._post(path=path, data=register_device_details) + return responce diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..26d8f57 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +setuptools +requests \ No newline at end of file diff --git a/tests/testing_is_important.py b/tests/testing_is_important.py index 3fb1ce3..0ad8b6f 100644 --- a/tests/testing_is_important.py +++ b/tests/testing_is_important.py @@ -2,6 +2,7 @@ import unittest import json +#convert to pytest class TestBasicTests(unittest.TestCase): def setUp(self): @@ -20,7 +21,7 @@ def test_is_this_AirWatch(self): response = self.enviroment.info.get_enviroment_info() self.assertEqual(response["ProductName"], "AirWatch Platform Service", "Info API ran") - +#todo: more tests if __name__ == '__main__': unittest.main() From d10140e146b1cf8132c9a4d48cc035625a2d274e Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Fri, 24 Apr 2020 09:36:26 -0400 Subject: [PATCH 24/28] fixed spelling mistakes --- pyairwatch/mam/blobs.py | 2 +- pyairwatch/system/admins.py | 2 +- pyairwatch/system/info.py | 6 +++--- pyairwatch/system/users.py | 6 +++--- tests/testing_is_important.py | 15 ++++++++------- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/pyairwatch/mam/blobs.py b/pyairwatch/mam/blobs.py index 136dbcc..ea153cc 100644 --- a/pyairwatch/mam/blobs.py +++ b/pyairwatch/mam/blobs.py @@ -1,7 +1,7 @@ from .mam import MAM -class Blobs(object): +class Blobs(MAM): """ A class to manage Blobs """ diff --git a/pyairwatch/system/admins.py b/pyairwatch/system/admins.py index 1692942..8b93c05 100644 --- a/pyairwatch/system/admins.py +++ b/pyairwatch/system/admins.py @@ -23,7 +23,7 @@ def search(self, **kwargs): response = System._get(self, path='/admins/search', params=kwargs) return response - def create_adim_v1(self, user_data): + def create_admin_v1(self, user_data): """ Performs necessary checks and Create a new basic Admin user. """ diff --git a/pyairwatch/system/info.py b/pyairwatch/system/info.py index 08cbf3a..eec6ab3 100644 --- a/pyairwatch/system/info.py +++ b/pyairwatch/system/info.py @@ -3,11 +3,11 @@ class Info(System): """ - Get enverioment information + Get environment information """ def __init__(self, client): System.__init__(self, client) - def get_enviroment_info(self): - """get enviroment information""" + def get_environment_info(self): + """get environment information""" return System._get(self, path='/info') diff --git a/pyairwatch/system/users.py b/pyairwatch/system/users.py index a1692aa..c6c84e5 100644 --- a/pyairwatch/system/users.py +++ b/pyairwatch/system/users.py @@ -23,7 +23,7 @@ def search(self, **kwargs): """ return System._get(self, path='/users/search', params=kwargs) - def create_device_regestration_to_user(self, user_id, register_device_details): + def create_device_registration_to_user(self, user_id, register_device_details): path = '/users{}/registerdevice'.format(user_id) - responce = System._post(path=path, data=register_device_details) - return responce + response = System._post(path=path, data=register_device_details) + return response diff --git a/tests/testing_is_important.py b/tests/testing_is_important.py index 0ad8b6f..64ec4ee 100644 --- a/tests/testing_is_important.py +++ b/tests/testing_is_important.py @@ -2,26 +2,27 @@ import unittest import json -#convert to pytest +# convert to pytest class TestBasicTests(unittest.TestCase): def setUp(self): input = "cnaapp" with open("environments.json") as json_file: server_details = json.load(json_file) - self.enviroment = AirWatchAPI(env=server_details[input]["server"], - apikey=server_details[input]["apikey"], - username=server_details[input]["username"], - password=server_details[input]["password"]) + self.environment = AirWatchAPI(env=server_details[input]["server"], + apikey=server_details[input]["apikey"], + username=server_details[input]["username"], + password=server_details[input]["password"]) def teardown(self): print("TEAR DOWN!") def test_is_this_AirWatch(self): - response = self.enviroment.info.get_enviroment_info() + response = self.environment.info.get_enviroment_info() self.assertEqual(response["ProductName"], "AirWatch Platform Service", "Info API ran") -#todo: more tests +# todo: more tests + if __name__ == '__main__': unittest.main() From 993d772b24cc91cb8410f041939008ca3e2b6e29 Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Fri, 24 Apr 2020 11:42:10 -0400 Subject: [PATCH 25/28] fixed a few more typos --- pyairwatch/mdm/devices.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pyairwatch/mdm/devices.py b/pyairwatch/mdm/devices.py index c7bce94..9ef3bef 100644 --- a/pyairwatch/mdm/devices.py +++ b/pyairwatch/mdm/devices.py @@ -79,13 +79,13 @@ def send_commands_by_id(self, command, searchby, id): def get_details_by_device_id(self, device_id): """ - device detals by device id + device details by device id """ return MDM._get(self, path='/devices/{}'.format(device_id)) - def get_device_filevualt_recovery_key(self, device_uuid): + def get_device_filevault_recovery_key(self, device_uuid): """ - Gets a maccOS device's FileVualt Recovery Key + Gets a macOS device's FileVault Recovery Key """ _path = '/devices/{}/security/recovery-key'.format(device_uuid) return MDM._get(self, path=_path) @@ -98,10 +98,10 @@ def get_security_info_by_id(self, device_id): _path = '/devices/{}/security'.format(device_id) return MDM._get(self, path=_path) - def get_security_info_by_atlternat_id(self, searchby, id): + def get_security_info_by_alternate_id(self, searchby, id): """ Processes the device ID to retrieve the security - information sample related info by Alternater ID + information sample related info by Alternate ID """ _path = '/devices/security' _params = 'searchby={}&id={}'.format(searchby, id) @@ -110,7 +110,7 @@ def get_security_info_by_atlternat_id(self, searchby, id): def get_bulk_security_info(self, organization_group_id, user_name, params=None): """ - Processes the information like organizationgroup ID, user name, model, + Processes the information like organization group ID, user name, model, platform, last seen, ownership, compliant status, seen since parameters and fetches the security information for the same. """ From 9c295621771658e341c44256b061f64a7f4564d8 Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Thu, 30 Apr 2020 11:09:58 -0400 Subject: [PATCH 26/28] - formating changes - Device Delete added - removed some duplicate code --- pyairwatch/client.py | 2 -- pyairwatch/mdm/devices.py | 21 ++++++++------------- pyairwatch/mdm/mdm.py | 4 +++- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/pyairwatch/client.py b/pyairwatch/client.py index 3120ced..f98f24a 100644 --- a/pyairwatch/client.py +++ b/pyairwatch/client.py @@ -17,7 +17,6 @@ from pyairwatch.system.info import Info - # Enabling debugging at http.client level (requests->urllib3->http.client) # you will see the REQUEST, including HEADERS and DATA, and RESPONSE with # HEADERS but without DATA. @@ -38,7 +37,6 @@ requests_log.propagate = True - class AirWatchAPI(object): def __init__(self, env, apikey, username, password): self.env = env diff --git a/pyairwatch/mdm/devices.py b/pyairwatch/mdm/devices.py index 9ef3bef..3873ca9 100644 --- a/pyairwatch/mdm/devices.py +++ b/pyairwatch/mdm/devices.py @@ -21,7 +21,6 @@ def search_all(self, **kwargs): def get_details_by_alt_id(self, serialnumber=None, macaddress=None, udid=None, imeinumber=None, easid=None): """Returns the Device information matching the search parameters.""" - params = {} if serialnumber: response = self.search(searchby='Serialnumber', id=str(serialnumber)) elif macaddress: @@ -38,18 +37,7 @@ def get_details_by_alt_id(self, serialnumber=None, macaddress=None, def get_id_by_alt_id(self, serialnumber=None, macaddress=None, udid=None, imeinumber=None, easid=None): - if serialnumber: - response = self.search(searchby='Serialnumber', id=str(serialnumber)) - elif macaddress: - response = self.search(searchby='Macaddress', id=str(macaddress)) - elif udid: - response = self.search(searchby='Udid', id=str(udid)) - elif imeinumber: - response = self.search(searchby='ImeiNumber', id=str(imeinumber)) - elif easid: - response = self.search(searchby='EasId', id=str(easid)) - else: - return None + response = self.get_details_by_alt_id(serialnumber, macaddress, udid, imeinumber, easid) return response['Id']['Value'] def clear_device_passcode(self, device_id): @@ -133,3 +121,10 @@ def get_managed_admin_account_by_uuid(self, device_id): """ _path = "/devices/{}/security/managed-admin-information".format(device_id) return MDM._get(self, path=_path) + + def delete_device_by_id(self, device_id): + """ + :param device_id: + :return: API response + """ + return MDM._delete(self, path='/devices/{}'.format(device_id)) diff --git a/pyairwatch/mdm/mdm.py b/pyairwatch/mdm/mdm.py index fe2e157..3d7bd3f 100644 --- a/pyairwatch/mdm/mdm.py +++ b/pyairwatch/mdm/mdm.py @@ -25,10 +25,12 @@ def _put(self, module='mdm', path=None, version=None, params=None, params=params, data=data, json=json, header=header) - def _patch(self, module='mdm', path=None, version=None, params=None, data=None, json=None, header=None): """Patch requests for base mdm endpoints""" return self.client.patch(module=module, path=path, version=version, params=params, data=data, json=json, header=header) + + def _delete(self, module='MDM', path=None, version=None, params=None, header=None): + return self.client.delete(module=module, path=path, version=version, params=params, header=header) From a6b539322c38520f64c27b0d37487775255f589c Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Thu, 11 Feb 2021 16:43:33 -0500 Subject: [PATCH 27/28] Some updates to the client and modeles i've been working on --- pyairwatch/client.py | 4 ++++ pyairwatch/mam/application.py | 22 ++++++++++++++++++++ pyairwatch/mam/mam.py | 38 +++++++++++++++++++++++++---------- pyairwatch/mam/vpp.py | 26 ++++++++++++++++++++++++ pyairwatch/mdm/profiles.py | 4 ++++ 5 files changed, 83 insertions(+), 11 deletions(-) create mode 100644 pyairwatch/mam/application.py create mode 100644 pyairwatch/mam/vpp.py diff --git a/pyairwatch/client.py b/pyairwatch/client.py index f98f24a..3a88d72 100644 --- a/pyairwatch/client.py +++ b/pyairwatch/client.py @@ -15,6 +15,8 @@ from pyairwatch.system.users import Users from pyairwatch.system.featureflag import FeatureFlag from pyairwatch.system.info import Info +from pyairwatch.mam.application import Application +from pyairwatch.mam.vpp import VPP # Enabling debugging at http.client level (requests->urllib3->http.client) @@ -55,6 +57,8 @@ def __init__(self, env, apikey, username, password): self.ldap = LDAP(self) self.info = Info(self) self.network = Network(self) + self.application = Application(self) + self.vpp = VPP(self) def get(self, module, path, version=None, params=None, header=None, timeout=30): diff --git a/pyairwatch/mam/application.py b/pyairwatch/mam/application.py new file mode 100644 index 0000000..e294ffe --- /dev/null +++ b/pyairwatch/mam/application.py @@ -0,0 +1,22 @@ +from .mam import MAM + + +class Application(MAM): + """ + A class to manage Internal Applications + """ + + def __init__(self, client): + MAM.__init__(self, client) + + def get_app_assignment(self, application_uuid): + path = '/apps/{}/assignment-rule'.format(application_uuid) + return MAM._get(self, path=path) + + def create_app_assignment(self, application_uuid, assignment_data, action): + path = '/apps/{}/assignment-rule'.format(application_uuid) + return MAM._post(self, path=path, json=assignment_data) + + def update_app_assignment(self, application_uuid, assignment_data): + path = '/apps/{}/assignment-rule'.format(application_uuid) + return MAM._put(self, path=path, json=assignment_data) \ No newline at end of file diff --git a/pyairwatch/mam/mam.py b/pyairwatch/mam/mam.py index 76e114a..f17c6d3 100644 --- a/pyairwatch/mam/mam.py +++ b/pyairwatch/mam/mam.py @@ -1,21 +1,37 @@ class MAM(object): """ - Application manamgment + Application management """ def __init__(self, client): self.client = client - def _get(self, module='mam', path=None, version=None, params=None, + def _get(self, path=None, version=None, params=None, header=None): - """GET requests for the /MAM/blobs module.""" - response = self.client.get(module=module, path=path, version=version, - params=params, header=header) - return response + """GET requests for base mam endpoints""" + return self.client.get(module='mam', path=path, + version=version, params=params, header=header) - def _post(self, module='mam', path=None, version=None, params=None, + def _post(self, path=None, version=None, params=None, data=None, json=None, header=None): - """Post requests for the /MAM/blobs module.""" - response = self.client.post(module=module, path=path, version=version, - params=params, data=data, json=json, - header=header) + """POST requests for base mam endpoints""" + return self.client.post(module='mam', path=path, version=version, + params=params, data=data, + json=json, header=header) + + def _put(self, path=None, version=None, params=None, + data=None, json=None, header=None): + """PUT requests for base mam endpoints""" + return self.client.put(module='mam', path=path, version=version, + params=params, data=data, + json=json, header=header) + + def _patch(self, path=None, version=None, params=None, + data=None, json=None, header=None): + """Patch requests for base mam endpoints""" + return self.client.patch(module='mam', path=path, version=version, + params=params, data=data, + json=json, header=header) + + def _delete(self, path=None, version=None, params=None, header=None): + return self.client.delete(module='mam', path=path, version=version, params=params, header=header) \ No newline at end of file diff --git a/pyairwatch/mam/vpp.py b/pyairwatch/mam/vpp.py new file mode 100644 index 0000000..4a0569a --- /dev/null +++ b/pyairwatch/mam/vpp.py @@ -0,0 +1,26 @@ +from .mam import MAM + + +class VPP(MAM): + """ + A class to manage Internal Applications + """ + + def __init__(self, client): + MAM.__init__(self, client) + + def get_vpp_details(self, application_id): + path = '/apps/purchased/{}'.format(application_id) + header = {'Content-Type': 'application/json;version=2'} + return MAM._get(self, path=path, header=header , version=2) + + def search(self, **kwargs): + """ + Search for VPP application details, its assignments, and deployment parameters. + :param kwargs: + :return: + """ + return MAM._get(self, path='/apps/purchased/search', params=kwargs) + + # def search_by_atl_id(self, search_by, value): + # return self.search(search_by, str(value)) diff --git a/pyairwatch/mdm/profiles.py b/pyairwatch/mdm/profiles.py index b3991f2..38a0a9f 100644 --- a/pyairwatch/mdm/profiles.py +++ b/pyairwatch/mdm/profiles.py @@ -36,3 +36,7 @@ def install_profile(self, device_id, profile_id, payloads=None): def get_profile_by_id(self, profile_id): return MDM._get(self, path='/profiles/{}'.format(profile_id), version=2) + + def get_payload_keys(self, platform, payload): + path = '/profiles/platforms/{}/payloads/{}/getpayloadkeys'.format(platform, payload) + return MDM._get(self, path=path, version=2) From 2a4d53174178500cd33fd1f27ce918235c82bc08 Mon Sep 17 00:00:00 2001 From: Jacob Postema Date: Fri, 12 Feb 2021 13:24:24 -0500 Subject: [PATCH 28/28] Small changes to commits and documentation --- pyairwatch/client.py | 4 +--- pyairwatch/system/users.py | 3 +++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pyairwatch/client.py b/pyairwatch/client.py index 3a88d72..1bafdc4 100644 --- a/pyairwatch/client.py +++ b/pyairwatch/client.py @@ -31,7 +31,7 @@ #todo: programing using library should be able to set logging level #todo: Implement logging to using config https://docs.python.org/3/howto/logging.html#configuring-logging -#todo: sett logging correclty for a library https://docs.python.org/3/howto/logging.html#configuring-logging-for-a-library +#todo: set logging correclty for a library https://docs.python.org/3/howto/logging.html#configuring-logging-for-a-library logging.basicConfig() logging.getLogger().setLevel(logging.DEBUG) requests_log = logging.getLogger("requests.packages.urllib3") @@ -133,8 +133,6 @@ def patch(self, module, path, version=None, params=None, data=None, except AirWatchAPIError as e: raise e - # NOQA - def delete(self, module, path, version=None, params=None, header=None, timeout=30): """ diff --git a/pyairwatch/system/users.py b/pyairwatch/system/users.py index c6c84e5..a84dd53 100644 --- a/pyairwatch/system/users.py +++ b/pyairwatch/system/users.py @@ -24,6 +24,9 @@ def search(self, **kwargs): return System._get(self, path='/users/search', params=kwargs) def create_device_registration_to_user(self, user_id, register_device_details): + """ + Creates a registration record for a user with the provided device details + """ path = '/users{}/registerdevice'.format(user_id) response = System._post(path=path, data=register_device_details) return response