5. Advanced Topics¶
This section is a collection of advanced topics for users who intend to contribute and maintain this library.
5.1. Sessions¶
Sessions in this library are designed for ease of use by front-end users. However, this section is dedicated to a deeper understanding of Sessions for advanced users and contributors to this library.
5.1.1. Parent Class¶
Both dyn.tm.session.DynectSession
and dyn.mm.session.MMSession
are subclasses of dyn.core.SessionEngine
. The dyn.core.SessionEngine
provides a simple internal API for preparing, sending, and processing outbound
API calls. This class was added in v1.0.0 and reduced the amount of logic
and duplicated code that made understanding these Sessions difficult.
5.1.2. Parent Type¶
Since v0.4.0, Sessions have been implemented as a Singleton type. This made it easier for end users to use the SDK and to utilize the API. By internally implementing Sessions as a Singleton, it allows the user discard their Session objects, unless they wish to keep them. It also doesn’t require users to share their Session information with other classes in this library to make API calls. (EXAMPLE):
>>> from dyn.tm.session import DynectSession
>>> from dyn.tm.zones import get_all_zones
>>> DynectSession(**my_credentials)
>>> zones = get_all_zones()
as opposed to something like this:
>>> from dyn.tm.session import DynectSession
>>> from dyn.tm.zones import get_all_zones
>>> my_session = DynectSession(**my_credentials)
>>> zones = get_all_zones(my_session)
Or, even worse:
>>> from dyn.tm.session import DynectSession
>>> my_session = DynectSession(**my_credentials)
>>> zones = my_session.get_all_zones(my_session)
In these examples, the changes may not seem significant but gain more relevance when creating multiple types of records, adding or editing Traffic Director and other complex services. Not needing to share your Session with other classes, or use it as a point of entry to other functionality, makes using this SDK much simpler.
5.1.3. What We Used to Do¶
From a backend perspective, the following is an example of how Session types were handled before v0.4.0:
def session():
"""Accessor for the current Singleton DynectSession"""
try:
return globals()['SESSION']
except KeyError:
return None
class DynectSession(object):
"""Base object representing a DynectSession Session"""
def __init__(self, customer, username, password, host='api.dynect.net',
port=443, ssl=True, api_version='current', auto_auth=True):
# __init__ logic here
def __new__(cls, *args, **kwargs):
try:
if globals()['SESSION'] is None:
globals()['SESSION'] = super(DynectSession, cls).__new__(cls,
*args,
**kwargs)
except KeyError:
globals()['SESSION'] = super(DynectSession, cls).__new__(cls, *args)
return globals()['SESSION']
- While this worked for a short while, it had its flaws:
- Once Message Management support was added, the code needed to be duplicated to rename the ‘SESSION’ key to ‘MM_SESSION’. This was inefficient.
- This allowed you to only have one active Session, even in shared memory space, i.e. threads.
- Sessions were only truly “global” in the scope of the dyn.tm module. It could still be accessed externally, but it was less than ideal.
5.1.4. What We Do Now¶
As of v1.0.0, Session types remain Singletons but are implemented differently.
Sessions are now implemented as dyn.core.SessionEngine
objects
and dyn.core.Singleton
type objects. EXAMPLE:
class Singleton(type):
"""A :class:`Singleton` type for implementing a true Singleton design
pattern, cleanly, using metaclasses
"""
_instances = {}
def __call__(cls, *args, **kwargs):
cur_thread = threading.current_thread()
key = getattr(cls, '__metakey__')
if key not in cls._instances:
cls._instances[key] = {
# super(Singleton, cls) evaluates to type; *args/**kwargs get
# passed to class __init__ method via type.__call__
cur_thread: super(_Singleton, cls).__call__(*args, **kwargs)
}
return cls._instances[key][cur_thread]
The Singleton type is applied as a __metaclass__ in each of the two Session types. This allows for a much cleaner implementation of Singletons. Every time one is accessed, it will globally have knowledge of other instances, as those instances are tied to the classes themselves instead of held in the globals of the session modules. In addition, this allows users to have multiple active sessions across multiple threads, which was not possible in the prior implementation.
5.2. Password Encryption¶
The Managed DNS REST API only accepts passwords in plain text. The
passwords stored in DynectSession
objects only
live in memory, reducing the security risk of plain text passwords in this instance.
However, for users looking to do more advanced things, such as serialize and store
their session objects in something less secure, such as a database, these
plain text passwords are not ideal. In response to this, Dyn added optional AES-256
password encryption for all DynectSession
instances in
version 1.1.0. To enable password encryption, install
PyCrypto.
5.2.1. Key Generation¶
In version 1.1.0, an optional key field parameter was added to the
DynectSession
__init__ method. This field will allow
you to specify the key that your encrypted password will be using. You can also
let the Dyn module handle the key generation in addition to using
the generate_key()
function, which generates a random
50 character key that can be easily consumed by the AESCipher
class (the class responsible for performing the encryption and decryption).
5.2.2. Encrypt Module¶
:: .. autofunction:: dyn.encrypt.generate_key
-
class
dyn.encrypt.
AESCipher
(key=None)[source]¶ An AES-256 password hasher
-
__init__
(key=None)[source]¶ Create a new AES-256 Cipher instance
Parameters: key – The secret key used to generate the password hashes
-