diff options
| author | Eli Collins <elic@assurancetechnologies.com> | 2012-04-10 12:14:27 -0400 |
|---|---|---|
| committer | Eli Collins <elic@assurancetechnologies.com> | 2012-04-10 12:14:27 -0400 |
| commit | f74deb32c6af1eac658e7d30ee3fe20e8fdf141c (patch) | |
| tree | 40fe6d4fcc412d2429b01812f629fcf187aa8971 /passlib/utils | |
| parent | 575ad2bfaf04cc1d75d61a30e469b52afdd8ccb8 (diff) | |
| download | passlib-f74deb32c6af1eac658e7d30ee3fe20e8fdf141c.tar.gz | |
passlib.exc: added constructors for common errors, should normalize error messages
Diffstat (limited to 'passlib/utils')
| -rw-r--r-- | passlib/utils/handlers.py | 105 |
1 files changed, 60 insertions, 45 deletions
diff --git a/passlib/utils/handlers.py b/passlib/utils/handlers.py index 7c8b747..bb7eadb 100644 --- a/passlib/utils/handlers.py +++ b/passlib/utils/handlers.py @@ -69,19 +69,36 @@ UC_HEX_CHARS = UPPER_HEX_CHARS LC_HEX_CHARS = LOWER_HEX_CHARS #========================================================= -#parsing helpers +# parsing helpers #========================================================= -def parse_mc2(hash, prefix, name="<unnamed>", sep=u("$")): - "parse hash using 2-part modular crypt format" - assert isinstance(prefix, unicode) - assert isinstance(sep, unicode) - #eg: MD5-Crypt: $1$salt[$checksum] +_UDOLLAR = u("$") +_UZERO = u("0") + +def parse_mc2(hash, prefix, sep=_UDOLLAR, handler=None): + """parse hash using 2-part modular crypt format. + + this expects a hash of the format :samp:`{prefix}{salt}[${checksum}]`, + such as md5_crypt, and parses it into salt / checksum portions. + + :arg hash: the hash to parse (bytes or unicode) + :arg prefix: the identifying prefix (unicode) + :param sep: field separator (unicode, defaults to ``$``). + :param handler: handler class to pass to error constructors. + + :returns: + a ``(salt, chk | None)`` tuple. + """ + # detect prefix if not hash: - raise ValueError("no hash specified") + raise exc.MissingHashError(handler) if isinstance(hash, bytes): hash = hash.decode('ascii') + assert isinstance(prefix, unicode) if not hash.startswith(prefix): - raise ValueError("not a valid %s hash (wrong prefix)" % (name,)) + raise exc.InvalidHashError(handler) + + # parse 2-part hash or 1-part config string + assert isinstance(sep, unicode) parts = hash[len(prefix):].split(sep) if len(parts) == 2: salt, chk = parts @@ -89,19 +106,33 @@ def parse_mc2(hash, prefix, name="<unnamed>", sep=u("$")): elif len(parts) == 1: return parts[0], None else: - raise ValueError("not a valid %s hash (malformed)" % (name,)) + raise exc.MalformedHashError(handler) -def parse_mc3(hash, prefix, name="<unnamed>", sep=u("$")): - "parse hash using 3-part modular crypt format" - assert isinstance(prefix, unicode) - assert isinstance(sep, unicode) - #eg: SHA1-Crypt: $sha1$rounds$salt[$checksum] +def parse_mc3(hash, prefix, sep=_UDOLLAR, handler=None): + """parse hash using 3-part modular crypt format. + + this expects a hash of the format :samp:`{prefix}[{rounds}$]{salt}[${checksum}]`, + such as sha1_crypt, and parses it into rounds / salt / checksum portions. + + :arg hash: the hash to parse (bytes or unicode) + :arg prefix: the identifying prefix (unicode) + :param sep: field separator (unicode, defaults to ``$``). + :param handler: handler class to pass to error constructors. + + :returns: + a ``(rounds : str, salt, chk | None)`` tuple. + """ + # detect prefix if not hash: - raise ValueError("no hash specified") + raise exc.MissingHashError(handler) if isinstance(hash, bytes): hash = hash.decode('ascii') + assert isinstance(prefix, unicode) if not hash.startswith(prefix): - raise ValueError("not a valid %s hash" % (name,)) + raise exc.InvalidHashError(handler) + + # parse 3-part hash or 2-part config string + assert isinstance(sep, unicode) parts = hash[len(prefix):].split(sep) if len(parts) == 3: rounds, salt, chk = parts @@ -110,7 +141,7 @@ def parse_mc3(hash, prefix, name="<unnamed>", sep=u("$")): rounds, salt = parts return rounds, salt, None else: - raise ValueError("not a valid %s hash" % (name,)) + raise exc.MalformedHashError(handler) #===================================================== #formatting helpers @@ -131,22 +162,6 @@ def render_mc3(ident, rounds, salt, checksum, sep=u("$")): hash = u("%s%s%s%s") % (ident, rounds, sep, salt) return uascii_to_str(hash) -#========================================================================== -# not proper exceptions, just predefined error message constructors -# used by various handlers. -#========================================================================== -def ChecksumSizeError(handler, size, raw=False): - name = handler.name - unit = "bytes" if raw else "chars" - return ValueError("checksum wrong size (%s checksum must be " - "exactly %d %s" % (name, size, unit)) - -def MissingDigestError(handler): - "raised when verify() method gets passed config string instead of hash" - name = handler.name - return ValueError("expected %s hash, got %s config string instead" % - (name, name)) - #===================================================== #GenericHandler #===================================================== @@ -343,7 +358,7 @@ class GenericHandler(object): # check size cc = self.checksum_size if cc and len(checksum) != cc: - raise ChecksumSizeError(self, cc, raw=raw) + raise exc.ChecksumSizeError(self, raw=raw) # check charset if not raw: @@ -478,7 +493,7 @@ class GenericHandler(object): self = cls.from_string(hash, **context) chk = self.checksum if chk is None: - raise MissingDigestError(cls) + raise exc.MissingDigestError(cls) return consteq(self._calc_checksum(secret), chk) #========================================================= @@ -549,7 +564,7 @@ class StaticHandler(GenericHandler): if hash.startswith(prefix): hash = hash[len(prefix):] else: - raise ValueError("not a valid %s hash" % (cls.name,)) + raise exc.InvalidHashError(cls) return cls(checksum=hash, **context) @classmethod @@ -570,7 +585,7 @@ class StaticHandler(GenericHandler): def genhash(cls, secret, config, **context): # since it has no settings, just verify config, and call encrypt() if config is not None and not cls.identify(config): - raise ValueError("not a %s hash" % (cls.name,)) + raise exc.InvalidHashError(cls) return cls.encrypt(secret, **context) __cc_compat_hack = False @@ -736,13 +751,13 @@ class HasManyIdents(GenericHandler): def _parse_ident(cls, hash): """extract ident prefix from hash, helper for subclasses' from_string()""" if not hash: - raise ValueError("no hash specified") + raise exc.MissingHashError(cls) if isinstance(hash, bytes): hash = hash.decode("ascii") for ident in cls.ident_values: if hash.startswith(ident): return ident, hash[len(ident):] - raise ValueError("invalid %s hash" % (cls.name,)) + raise exc.InvalidHashError(cls) #========================================================= #eoc @@ -1253,9 +1268,9 @@ class HasManyBackends(GenericHandler): if cls.has_backend(name): break else: - raise MissingBackendError(cls._no_backends_msg()) + raise exc.MissingBackendError(cls._no_backends_msg()) elif not cls.has_backend(name): - raise MissingBackendError("%s backend not available: %r" % + raise exc.MissingBackendError("%s backend not available: %r" % (cls.name, name)) cls._calc_checksum = getattr(cls, "_calc_checksum_" + name) cls._backend = name @@ -1318,7 +1333,7 @@ class PrefixWrapper(object): if isinstance(ident, bytes): ident = ident.decode("ascii") if ident[:len(prefix)] != prefix[:len(ident)]: - raise ValueError("ident agree with prefix") + raise ValueError("ident must agree with prefix") self._ident = ident _wrapped_name = None @@ -1419,7 +1434,7 @@ class PrefixWrapper(object): hash = hash.decode('ascii') prefix = self.prefix if not hash.startswith(prefix): - raise ValueError("not a valid %s hash" % (self.name,)) + raise exc.InvalidHashError(self) #NOTE: always passing to handler as unicode, to save reconversion return self.orig_prefix + hash[len(prefix):] @@ -1431,7 +1446,7 @@ class PrefixWrapper(object): hash = hash.decode('ascii') orig_prefix = self.orig_prefix if not hash.startswith(orig_prefix): - raise ValueError("not a valid %s hash" % (self.wrapped.name,)) + raise exc.InvalidHashError(self.wrapped) wrapped = self.prefix + hash[len(orig_prefix):] return uascii_to_str(wrapped) @@ -1462,7 +1477,7 @@ class PrefixWrapper(object): def verify(self, secret, hash, **kwds): if not hash: - raise ValueError("no %s hash specified" % (self.name,)) + raise exc.MissingHashError(self) hash = self._unwrap_hash(hash) return self.wrapped.verify(secret, hash, **kwds) |
