Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
08c0b97
Merge pull request #1 from deltakhan/setup
deltakhan Apr 3, 2018
8379f62
starting mam
Sep 11, 2018
8570fff
adding support for mam and feature flags
Sep 12, 2018
0ba2d75
- added new modules to client
Sep 13, 2018
730f261
removed unused code and simplefierd featureflag.py
Sep 13, 2018
ed48140
added ldap.py for adding LDAP to an Envrioment. Structure of API mean…
Sep 19, 2018
4040387
worked on ldap.py
Sep 20, 2018
d7c9a68
creating api end point class structure
Sep 23, 2018
3f4d67f
Fixed issues with class structure and added some more end points
Oct 23, 2018
f79798e
Making some inheritance changes and impovements
Jan 24, 2019
f7251e6
adding base cases for API
Jan 24, 2019
ed3ec82
- Fixed a bug with search function
Jun 24, 2019
8b8d2b1
Added some APIs
Sep 18, 2019
fbe6779
Merge remote-tracking branch 'upstream/master'
Sep 18, 2019
2c116b5
finishing framework changes I made a long time ago
Sep 18, 2019
e1bdbc1
Some formatting and fixed a miss for inheritance
Sep 18, 2019
2f057b7
Removed commented code that had not value
Sep 18, 2019
740f647
deleted commented out code
Sep 18, 2019
eaa203f
Adding Tests as I made lots of changes
Sep 18, 2019
7466d10
Added Tests and a template for environment as I don't want to expose …
Sep 19, 2019
047a781
removed file
Sep 19, 2019
30e4553
Ignoring my environments.json and fixed a spelling mistake.
Sep 19, 2019
073b33f
- Updated README
Sep 19, 2019
a77ffbf
usergroups.py
Sep 19, 2019
1aa93ad
made some python3 changes
Apr 24, 2020
d10140e
fixed spelling mistakes
Apr 24, 2020
993d772
fixed a few more typos
Apr 24, 2020
9c29562
- formating changes
Apr 30, 2020
a6b5393
Some updates to the client and modeles i've been working on
Feb 11, 2021
2a4d531
Small changes to commits and documentation
Feb 12, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
*.pyc
*.egg-info
.idea
venv
enviroments.json
environments.json
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,26 @@ 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
* Groups
* 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
Expand All @@ -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
---
Expand Down
178 changes: 111 additions & 67 deletions pyairwatch/client.py
Original file line number Diff line number Diff line change
@@ -1,124 +1,164 @@
from __future__ import print_function
from __future__ import print_function, absolute_import
import base64
import json
import logging
import requests
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 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
from pyairwatch.mam.application import Application
from pyairwatch.mam.vpp import VPP


# 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
except ImportError:
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: 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")
requests_log.setLevel(logging.DEBUG)
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):
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)

def get(self, module, path, version=None, params=None, header=None, timeout=30):
"""Sends a GET request to the API. Returns the response object."""
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.application = Application(self)
self.vpp = VPP(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.
"""
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):
"""
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))
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 = 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):
"""Sends a POST request to the API. Returns the response object."""
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.post(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 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."""
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))
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.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):
"""Sends a DELETE request to the API. Returns the response object."""
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:
raise e

@staticmethod
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'):
"""
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'):
raise AirWatchAPIError(json_response=json)
Expand All @@ -129,7 +169,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('/'):
Expand All @@ -145,14 +187,16 @@ 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((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
14 changes: 14 additions & 0 deletions pyairwatch/error.py
Original file line number Diff line number Diff line change
@@ -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)
Empty file added pyairwatch/mam/__init__.py
Empty file.
22 changes: 22 additions & 0 deletions pyairwatch/mam/application.py
Original file line number Diff line number Diff line change
@@ -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)
10 changes: 10 additions & 0 deletions pyairwatch/mam/blobs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from .mam import MAM


class Blobs(MAM):
"""
A class to manage Blobs
"""

def __init__(self, client):
MAM.__init__(self, client)
10 changes: 10 additions & 0 deletions pyairwatch/mam/internalapps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from .mam import MAM


class InternalApps(MAM):
"""
A class to manage Internal Applications
"""

def __init__(self, client):
MAM.__init__(self, client)
Loading