summaryrefslogtreecommitdiff
path: root/passlib/exc.py
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2016-07-12 14:27:07 -0400
committerEli Collins <elic@assurancetechnologies.com>2016-07-12 14:27:07 -0400
commit36e14926d6b102e8d905fb031bbb214600c1fef3 (patch)
tree36a3766f528b627139fb347b0fc9b202dd3bad21 /passlib/exc.py
parent5722f82fa8e28a7a3e164db880fb23181321af94 (diff)
downloadpasslib-36e14926d6b102e8d905fb031bbb214600c1fef3.tar.gz
passlib.totp: large refactoring of API, added support for migration application secrets
This reworks a large portion of the totp module's API, to make it fit better with the needs of the applications it's been integrated into so far. * Key encryption encapsulated in new OTPContext() class, which not only handles encryption of keys, but supports multiple application secrets, allowing migration to new secrets (whether periodic, or after a breach). This makes workflow of OTP object serialization *much* simpler. * encryption format changed to use a simple dict, which gets embedded into overall json data; eliminates need for custom binary format. * BaseOTP.generate_next() has been renamed to .advance(), to make it distinct from .generate(), and give better hinting that it modifies the internal state BaseOTP.verify_next() has been renamed to .consume() for similar reasons. * All .verify() and .verify_next() methods have been modified so they throw an InvalidTokenError if the token doesn't match, instead of returning False. This reduces the boilerplate needed to implement them, as code already had to catch ValueErrors for malformed tokens & reused tokens. - the HotpMatch / TotpMatch objects were adjusted to account for fact that they're only used when token matches successfully. * better exception hierarchy: added base TokenError, as well as subclasses for specific cases (MalformedTokenError, InvalidTokenError, UsedTokenError). * renamed BaseOTP.dirty -> BaseOTP.changed * BaseOTP now detects if encryption is old, and flags that re-encryption + re-serialization is needed. * .from_string() / .to_string() renamed to .from_json() / .to_json() to disambiguate with .from_uri() / .to_uri(), which also returns a string.
Diffstat (limited to 'passlib/exc.py')
-rw-r--r--passlib/exc.py47
1 files changed, 43 insertions, 4 deletions
diff --git a/passlib/exc.py b/passlib/exc.py
index 3e4f0c1..2aaaccb 100644
--- a/passlib/exc.py
+++ b/passlib/exc.py
@@ -78,12 +78,51 @@ class PasslibSecurityError(RuntimeError):
.. versionadded:: 1.6.3
"""
-class TokenReuseError(ValueError):
- """Error raised by various methods in :mod:`passlib.totp` if a token is reused.
- This exception derives from :exc:`!ValueError`.
+
+class TokenError(ValueError):
+ """
+ Base error raised by v:mod:`passlib.totp` when
+ a token can't be parsed / isn't valid / etc.
+ Derives from :exc:`!ValueError`.
+
+ Usually one of the more specific subclasses below will be raised:
+ :class:`InvalidTokenError`, :class:`TokenMatchError`, :class:`UsedTokenError`.
+
+ .. versionadded:: 1.7
+ """
+ _default_message = None
+
+ def __init__(self, msg=None, *args, **kwds):
+ if msg is None:
+ msg = self._default_message
+ assert msg # external code should be able to rely on str(err) always being True
+ ValueError.__init__(self, msg, *args, **kwds)
+
+
+class MalformedTokenError(TokenError):
+ """
+ Error raised by :mod:`passlib.totp` when a token isn't formatted correctly
+ (contains invalid characters, wrong number of digits, etc)
+ """
+ _default_message = "Unrecognized token"
+
+
+class InvalidTokenError(TokenError):
+ """
+ Error raised by :mod:`passlib.totp` when a token is formatted correctly,
+ but doesn't match any tokens within valid range.
+ """
+ _default_message = "Token did not match"
+
+
+class UsedTokenError(TokenError):
+ """
+ Error raised by :mod:`passlib.totp` if a token is reused.
+ Derives from :exc:`TokenError`.
.. versionadded:: 1.7
"""
+ _default_message = ""
#: optional value indicating when current counter period will end,
#: and a new token can be generated.
@@ -91,7 +130,7 @@ class TokenReuseError(ValueError):
def __init__(self, *args, **kwds):
self.expire_time = kwds.pop("expire_time", None)
- ValueError.__init__(self, *args, **kwds)
+ TokenError.__init__(self, *args, **kwds)
class UnknownHashError(ValueError):