Last active
June 19, 2019 14:43
-
-
Save turtlemonvh/229cd5ffaa5b9486e481e233519c4863 to your computer and use it in GitHub Desktop.
Custom device profile persistor implementations for the Ionic Python SDK
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
from __future__ import division, print_function | |
""" | |
Install the Ionic python sdk with: `pip install ionicsdk` | |
""" | |
import binascii | |
import calendar | |
import datetime | |
import json | |
import os | |
import ionicsdk | |
## Profile helpers | |
def load_from_dict(d): | |
""" | |
Create a device profile from a dictionary | |
""" | |
creationtimestampsecs = int(d.get('creationTimestamp', -1)) | |
if creationtimestampsecs <= 0: | |
# This is a bug in the sdk; 0 results in an exception | |
# Set to current time in this case | |
creationtimestampsecs = calendar.timegm(datetime.datetime.utcnow().timetuple()) | |
profargs = dict( | |
name=d.get('name', d['deviceId']), | |
deviceid=d['deviceId'], | |
keyspace=d['deviceId'].split(".")[0], | |
server=d['server'], | |
creationtimestampsecs=creationtimestampsecs, | |
aesCdIdcProfileKey=binascii.unhexlify(d['aesCdIdcKey']), | |
aesCdEiProfileKey=binascii.unhexlify(d['aesCdEiKey']), | |
) | |
return ionicsdk.DeviceProfile(**profargs) | |
## Custom exceptions | |
class IonicLoadProfilesNoPersistorException(Exception): | |
"""persistor must be passed to enable loading profiles | |
""" | |
pass | |
class IonicSaveProfilesNoPersistorException(Exception): | |
"""persistor must be passed to enable saving profiles | |
""" | |
pass | |
class IonicPersistorBadCaseClassException(Exception): | |
"""profilepersistor must be an instance of the DeviceProfilePersistorBase class from ionic_sdk_ext | |
""" | |
pass | |
## Customized persistors | |
class DeviceProfilePersistorBase(object): | |
""" | |
A base class that is used in the modified agent to make custom persistors simpler. | |
""" | |
def loadprofiles(self, *args, **kwargs): | |
""" | |
Should return a list of DeviceProfile objects. | |
""" | |
raise NotImplementedError | |
def saveprofiles(self, profiles, *args, **kwargs): | |
""" | |
Should raise an exception on failure. | |
""" | |
raise NotImplementedError | |
class PlainTextPersistor(DeviceProfilePersistorBase): | |
""" | |
An example subclass. | |
Just like the normal plain text persistor except | |
* it creates the path you ask for on init | |
* it doesn't include any facility to marking a profile as active | |
The default path is '~/.ionicsecurity/profiles.pt' | |
""" | |
def __init__(self, path=None): | |
if path is None: | |
path = os.path.expanduser("~/.ionicsecurity/profiles.pt") | |
self.path = path | |
# Create directory if DNE | |
dirname = os.path.dirname(self.path) | |
if not os.path.exists(dirname): | |
os.makedirs(dirname) | |
# Create file if DNE | |
if not os.path.exists(self.path): | |
with open(self.path, "w+") as pp: | |
json.dump([], pp) | |
def loadprofiles(self, *args, **kwargs): | |
loaded_profiles = [] | |
with open(self.path) as pp: | |
for p in json.load(pp): | |
loaded_profiles.append(load_from_dict(p)) | |
return loaded_profiles | |
def saveprofiles(self, profiles, *args, **kwargs): | |
with open(self.path, "w+") as pp: | |
json.dump(profiles, pp) | |
## Agent subclass which accepts custom persistors derived from DeviceProfilePersistorBase | |
class Agent(ionicsdk.Agent): | |
""" | |
A version of the Ionic Agent with all persistor functionality changed to use subclasses of the persistor base class defined in this module. | |
We have to subclass all persistor related methods because the code calls methods on ctypes directly to | |
work with profiles, making overriding functionality in pure python challenging. | |
""" | |
def __init__(self, agentconfig = None, profilepersistor = None, loadprofiles = True): | |
# Call base constructor with no persistor and with loadprofiles set to false | |
ionicsdk.Agent.__init__(self, agentconfig, None, False) | |
if profilepersistor: | |
if not isinstance(profilepersistor, DeviceProfilePersistorBase): | |
# Assert that the persistor is subclassed from this type | |
raise IonicPersistorBadCaseClassException() | |
# Set it as a property to make it accessible | |
# The user can also set this property manually after creating the agent | |
self.persistor = profilepersistor | |
# Load the profiles if requested and set an active profile | |
if loadprofiles: | |
if not self.persistor: | |
raise IonicLoadProfilesNoPersistorException() | |
self.loadprofiles(self.persistor) | |
profiles = self.getallprofiles() | |
if len(profiles) > 0: | |
self.setactiveprofile(profiles[0]) | |
def loadprofiles(self, persistor=None, *args, **kwargs): | |
""" | |
Load profiles from the custom persistor. | |
Note that this method will not do anything to set a profile as active. | |
""" | |
persistor = persistor or self.persistor | |
if persistor is None: | |
raise IonicLoadProfilesNoPersistorException() | |
if not isinstance(persistor, DeviceProfilePersistorBase): | |
raise IonicPersistorBadCaseClassException() | |
for deviceprofile in persistor.loadprofiles(*args, **kwargs): | |
self.addprofile(deviceprofile) | |
def saveprofiles(self, persistor=None, *args, **kwargs): | |
""" | |
Save profiles into the custom persistor. | |
""" | |
persistor = persistor or self.persistor | |
if persistor is None: | |
raise IonicSaveProfilesNoPersistorException() | |
if not isinstance(persistor, DeviceProfilePersistorBase): | |
raise IonicPersistorBadCaseClassException() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment