auth.py 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. import base64
  2. import re
  3. import pyparsing as pp
  4. from .error import *
  5. try: # pyparsing>=3.0.0
  6. downcaseTokens = pp.common.downcaseTokens
  7. except AttributeError:
  8. downcaseTokens = pp.downcaseTokens
  9. UNQUOTE_PAIRS = re.compile(r"\\(.)")
  10. unquote = lambda s, l, t: UNQUOTE_PAIRS.sub(r"\1", t[0][1:-1])
  11. # https://tools.ietf.org/html/rfc7235#section-1.2
  12. # https://tools.ietf.org/html/rfc7235#appendix-B
  13. tchar = "!#$%&'*+-.^_`|~" + pp.nums + pp.alphas
  14. token = pp.Word(tchar).setName("token")
  15. token68 = pp.Combine(pp.Word("-._~+/" + pp.nums + pp.alphas) + pp.Optional(pp.Word("=").leaveWhitespace())).setName(
  16. "token68"
  17. )
  18. quoted_string = pp.dblQuotedString.copy().setName("quoted-string").setParseAction(unquote)
  19. auth_param_name = token.copy().setName("auth-param-name").addParseAction(downcaseTokens)
  20. auth_param = auth_param_name + pp.Suppress("=") + (quoted_string | token)
  21. params = pp.Dict(pp.delimitedList(pp.Group(auth_param)))
  22. scheme = token("scheme")
  23. challenge = scheme + (params("params") | token68("token"))
  24. authentication_info = params.copy()
  25. www_authenticate = pp.delimitedList(pp.Group(challenge))
  26. def _parse_authentication_info(headers, headername="authentication-info"):
  27. """https://tools.ietf.org/html/rfc7615
  28. """
  29. header = headers.get(headername, "").strip()
  30. if not header:
  31. return {}
  32. try:
  33. parsed = authentication_info.parseString(header)
  34. except pp.ParseException as ex:
  35. # print(ex.explain(ex))
  36. raise MalformedHeader(headername)
  37. return parsed.asDict()
  38. def _parse_www_authenticate(headers, headername="www-authenticate"):
  39. """Returns a dictionary of dictionaries, one dict per auth_scheme."""
  40. header = headers.get(headername, "").strip()
  41. if not header:
  42. return {}
  43. try:
  44. parsed = www_authenticate.parseString(header)
  45. except pp.ParseException as ex:
  46. # print(ex.explain(ex))
  47. raise MalformedHeader(headername)
  48. retval = {
  49. challenge["scheme"].lower(): challenge["params"].asDict()
  50. if "params" in challenge
  51. else {"token": challenge.get("token")}
  52. for challenge in parsed
  53. }
  54. return retval