summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2016-11-07 20:30:55 -0500
committerEli Collins <elic@assurancetechnologies.com>2016-11-07 20:30:55 -0500
commitd5566625e9ef818665c5a7f73ce7c5872e2b2879 (patch)
tree88f7a0cbfd0ce556c8df780f215b24642f036d1b
parent63f952a7c9b0e306a8116e21cab9d8da6b811d6a (diff)
downloadpasslib-d5566625e9ef818665c5a7f73ce7c5872e2b2879.tar.gz
totp: stripped out BaseOTP subtype resolution
In preparation for merging BaseOTP & TOTP: * removed BaseOTP.type attr & ._type_map, * hardcoded TOTP class a few places since _type_map is gone * removed 'type' parameter from dict/json format (not needed since HTOP gone) * replaced refs to BaseOTP.from_*()/to_*() with TOTP.from_*()/to_*(), since the base class methods no longer work.
-rw-r--r--docs/lib/passlib.totp-tutorial.rst20
-rw-r--r--docs/lib/passlib.totp.rst12
-rw-r--r--passlib/totp.py75
3 files changed, 44 insertions, 63 deletions
diff --git a/docs/lib/passlib.totp-tutorial.rst b/docs/lib/passlib.totp-tutorial.rst
index 4a56088..a840d08 100644
--- a/docs/lib/passlib.totp-tutorial.rst
+++ b/docs/lib/passlib.totp-tutorial.rst
@@ -83,7 +83,7 @@ TOTP applications support the user grabbing this configuration off the screen.
When transferring things this way, you will need to provide identifiers
for your application and the user, in order for the TOTP client to distinguish
this key from the others in it's database. This is done via the "issue" and "label"
-parameters of the :meth:`~passlib.totp.BaseOTP.to_uri` method:
+parameters of the :meth:`~passlib.totp.TOTP.to_uri` method:
>>> # assume an existing TOTP instance has been created
>>> from passlib import totp
@@ -117,8 +117,8 @@ URI. This can be useful for testing URI encoding & output:
.. seealso::
- For more details, see the :meth:`~passlib.totp.BaseOTP.from_uri` constructor,
- and the :meth:`~passlib.totp.BaseOTP.to_uri` method.
+ For more details, see the :meth:`~passlib.totp.TOTP.from_uri` constructor,
+ and the :meth:`~passlib.totp.TOTP.to_uri` method.
Storing TOTP instances
----------------------
@@ -126,7 +126,7 @@ One disadvantage of :meth:`~TOTP.to_uri` and :func:`!from_uri` (above)
is that they're oriented towards helping a server configure a client device.
Server applications will still need to persist this information
to disk (whether a database, flat file, etc). To help with this, passlib offers
-a way to serialize OTP tokens to and from JSON: the :meth:`BaseOTP.to_json` method,
+a way to serialize OTP tokens to and from JSON: the :meth:`TOTP.to_json` method,
and the :meth:`passlib.totp.from_uri` constructor::
>>> # assume an existing TOTP instance has been created
@@ -153,15 +153,15 @@ they can include stateful data (see :ref:`totp-stateful-usage` below),
and they support storing the keys in an encrypted fashion (see :ref:`totp-context-usage` below).
For cases where a python dictionary is more useful than a json string,
-the :meth:`BaseOTP.to_dict` method returns a python dict identical to parsing
-the output of :meth:`BaseOTP.to_json`. This value can be reconstructed
+the :meth:`TOTP.to_dict` method returns a python dict identical to parsing
+the output of :meth:`TOTP.to_json`. This value can be reconstructed
via :func:`from_json`, which will autodetect whether the input
is a dictionary vs string.
.. seealso::
- For more details, see the :meth:`~passlib.totp.BaseOTP.from_json` constructor,
- and the :meth:`~passlib.totp.BaseOTP.to_json` method;
+ For more details, see the :meth:`~passlib.totp.TOTP.from_json` constructor,
+ and the :meth:`~passlib.totp.TOTP.to_json` method;
as well as the stateful usage & OTPContext usage tutorials below.
Generating Tokens
@@ -337,7 +337,7 @@ if an attacker then comes along and attempts to re-use this token within :meth:`
.. seealso::
- The :meth:`TOTP.consume` and :meth:`BaseOTP.to_json` methods,
+ The :meth:`TOTP.consume` and :meth:`TOTP.to_json` methods,
the :attr:`TOTP.last_counter` attribute, and the :func:`~totp.from_json` constructor.
..
@@ -363,7 +363,7 @@ application, using passlib's highlevel :class:`OTPContext` helper class.
Why Rate Limiting is Critical
=============================
-The :meth:`HOTP.verify` and :meth:`TOTP.verify` methods both offer a ``window``
+The :meth:`TOTP.verify` methods offers a ``window``
parameter, expanding the search range to account for the client getting
slightly out of sync.
diff --git a/docs/lib/passlib.totp.rst b/docs/lib/passlib.totp.rst
index 8e5c9d6..ef29572 100644
--- a/docs/lib/passlib.totp.rst
+++ b/docs/lib/passlib.totp.rst
@@ -56,7 +56,7 @@ can be performed with the following methods:
Boolean flag set by all BaseOTP subclass methods which modify the internal state.
if true, then something has changed in the object since it was created / loaded
- via :meth:`~BaseOTP.from_json`, and needs re-persisting via :meth:`~BaseOTP.to_json`.
+ via :meth:`~TOTP.from_json`, and needs re-persisting via :meth:`~TOTP.to_json`.
After which, your application may clear the flag, or discard the object, as appropriate.
.. automethod:: BaseOTP.to_json
@@ -75,7 +75,7 @@ BaseOTP – Configuration Attributes
----------------------------------
All the OTP objects offer the following attributes,
which correspond to the constructor options (above).
-Most of this information will be serialized by :meth:`~BaseOTP.to_uri` and :meth:`~BaseOTP.to_json`:
+Most of this information will be serialized by :meth:`~TOTP.to_uri` and :meth:`~TOTP.to_json`:
.. autoattribute:: BaseOTP.key
.. autoattribute:: BaseOTP.hex_key
@@ -136,7 +136,7 @@ this class also offers the following extra attrs (which correspond to the extra
TOTP – Internal State Attributes
--------------------------------
The following attributes are used to track the internal state of this generator,
-and will be included in the output of :meth:`~BaseOTP.to_json`:
+and will be included in the output of :meth:`~TOTP.to_json`:
.. autoattribute:: TOTP.last_counter
@@ -166,7 +166,7 @@ Support Functions
.. function:: from_uri(uri)
Create an TOTP instance from a provisioning URI,
- such as generated by :meth:`BaseOTP.to_uri`.
+ such as generated by :meth:`TOTP.to_uri`.
:returns:
:class:`TOTP` instance
@@ -174,9 +174,9 @@ Support Functions
.. function:: from_json(json)
Create an TOTP instance from a JSON string,
- such as generated by :meth:`BaseOTP.to_json`.
+ such as generated by :meth:`TOTP.to_json`.
Also accepts a dict object with the same format,
- such as returned by :meth:`BaseOTP.to_dict`.
+ such as returned by :meth:`TOTP.to_dict`.
:returns:
:class:`TOTP` instance
diff --git a/passlib/totp.py b/passlib/totp.py
index 6e6270a..8e93223 100644
--- a/passlib/totp.py
+++ b/passlib/totp.py
@@ -386,55 +386,51 @@ class OTPContext(object):
#========================================================================
# frontend wrappers
#========================================================================
- def new(self, type="totp", **kwds):
+ def new(self, **kwds):
"""
Create new OTP instance from scratch,
generating a new key.
- :param type:
- "totp" (the default)
-
:param \*\*kwds:
All remaining keywords passed to the :class:`TOTP` constructor.
:return:
:class:`!TOTP` instance.
"""
- cls = BaseOTP._type_map[type]
- return cls(new=True, context=self, **kwds)
+ return TOTP(new=True, context=self, **kwds)
def from_uri(self, uri):
"""
Create OTP instance from configuration uri.
- This is just a wrapper for :meth:`BaseOTP.from_uri`
+ This is just a wrapper for :meth:`TOTP.from_uri`
which returns an OTP object tied to this context
(and will thus use any application secrets to encrypt the key for storage).
:param uri:
URI to parse.
This URI may come externally (e.g. from a scanned qrcode),
- or from the :meth:`BaseOTP.to_uri` method.
+ or from the :meth:`TOTP.to_uri` method.
:return:
:class:`TOTP` instance.
"""
- return BaseOTP.from_uri(uri, context=self)
+ return TOTP.from_uri(uri, context=self)
def from_json(self, source):
"""
Create OTP instance from serialized json state.
- This is just a wrapper for :class:`BaseOTP.from_json`,
+ This is just a wrapper for :meth:`TOTP.from_json`,
and returns an OTP object tied to this context.
:param source:
- json string as returned by :class:`BaseOTP.to_json`.
+ json string as returned by :meth:`TOTP.to_json`.
:return:
:class:`TOTP` instance.
"""
- return BaseOTP.from_json(source, context=self)
+ return TOTP.from_json(source, context=self)
#========================================================================
# encrypted key helpers -- used internally by BaseOTP
@@ -644,17 +640,10 @@ class BaseOTP(object):
# class attrs
#=============================================================================
- #: otpauth uri type that subclass implements ('totp' or 'hotp')
- #: (used by uri & serialization code)
- type = None
-
#: minimum number of bytes to allow in key, enforced by passlib.
# XXX: see if spec says anything relevant to this.
_min_key_size = 10
- #: dict used by from_uri() to lookup subclass based on otpauth type
- _type_map = {}
-
#: minimum & current serialization version (may be set independently by subclasses)
min_json_version = json_version = 1
@@ -969,12 +958,17 @@ class BaseOTP(object):
if result.scheme != "otpauth":
raise cls._uri_error("wrong uri scheme")
- # lookup factory to handle OTP type, and hand things off to it.
- try:
- subcls = cls._type_map[result.netloc]
- except KeyError:
- raise cls._uri_error("unknown OTP type")
- return subcls._from_parsed_uri(result, context)
+ # validate netloc, and hand off to helper
+ cls._check_otp_type(result.netloc)
+ return cls._from_parsed_uri(result, context)
+
+ @classmethod
+ def _check_otp_type(cls, type):
+ if type == "totp":
+ return True
+ if type == "hotp":
+ raise NotImplementedError("HOTP not supported")
+ raise ValueError("unknown otp type: %r" % type)
@classmethod
def _from_parsed_uri(cls, result, context):
@@ -1050,7 +1044,7 @@ class BaseOTP(object):
@classmethod
def _uri_error(cls, reason):
"""uri parsing helper -- creates preformatted error message"""
- prefix = cls.__name__ + ": " if cls.type else ""
+ prefix = cls.__name__ + ": "
return ValueError("%sInvalid otpauth uri: %s" % (prefix, reason))
@classmethod
@@ -1139,7 +1133,7 @@ class BaseOTP(object):
assert argstr, "argstr should never be empty"
# render uri
- return u("otpauth://%s/%s?%s") % (self.type, label, argstr)
+ return u("otpauth://totp/%s?%s") % (label, argstr)
def _to_uri_params(self):
"""return list of (key, param) entries for URI"""
@@ -1184,16 +1178,12 @@ class BaseOTP(object):
return cls.from_uri(source, context=context)
else:
source = json.loads(source)
- if not (isinstance(source, dict) and "type" in source):
+ if not isinstance(source, dict):
raise cls._json_error("unrecognized json data")
- try:
- subcls = cls._type_map[source.pop('type')]
- except KeyError:
- raise cls._json_error("unknown OTP type")
- return subcls(context=context, **subcls._adapt_json_dict(**source))
+ return cls(context=context, **cls._adapt_json_dict(**source))
@classmethod
- def _adapt_json_dict(cls, **kwds):
+ def _adapt_json_dict(cls, type="totp", **kwds):
"""
Internal helper for .from_json() --
Adapts serialized json dict into constructor keywords.
@@ -1201,6 +1191,7 @@ class BaseOTP(object):
# default json format is just serialization of constructor kwds.
# XXX: just pass all this through to _from_json / constructor?
# go ahead and mark as changed (needs re-saving) if the version is too old
+ assert cls._check_otp_type(type), "check legacy type parameter"
ver = kwds.pop("v", None)
if not ver or ver < cls.min_json_version or ver > cls.json_version:
raise cls._json_error("missing/unsupported version (%r)" % (ver,))
@@ -1221,8 +1212,8 @@ class BaseOTP(object):
@classmethod
def _json_error(cls, reason):
"""json parsing helper -- creates preformatted error message"""
- prefix = cls.__name__ + ": " if cls.type else ""
- return ValueError("%sInvalid otp json string: %s" % (prefix, reason))
+ prefix = cls.__name__ + ": "
+ return ValueError("%sInvalid totp json data: %s" % (prefix, reason))
#=============================================================================
# json rendering
@@ -1256,7 +1247,7 @@ class BaseOTP(object):
:returns:
dictionary, containing basic (json serializable) datatypes.
"""
- state = dict(type=self.type, v=self.json_version)
+ state = dict(v=self.json_version)
if self.alg != "sha1":
state['alg'] = self.alg
if self.digits != 6:
@@ -1434,13 +1425,6 @@ class TOTP(BaseOTP):
See the passlib documentation for a full list of attributes & methods.
"""
#=============================================================================
- # class attrs
- #=============================================================================
-
- #: otpauth type this class implements
- type = "totp"
-
- #=============================================================================
# instance attrs
#=============================================================================
@@ -1899,9 +1883,6 @@ class TOTP(BaseOTP):
# eoc
#=============================================================================
-# register subclass with from_uri() helper
-BaseOTP._type_map[TOTP.type] = TOTP
-
#=============================================================================
# convenience helpers
#=============================================================================