/app/gae_bingo/identity.py

https://gitlab.com/gregtyka/server · Python · 131 lines · 69 code · 34 blank · 28 comment · 18 complexity · 5e4ec114d2924dc3b9d2139bc4d2558f MD5 · raw file

  1. from __future__ import absolute_import
  2. import base64
  3. import os
  4. import re
  5. from google.appengine.ext import db
  6. from gae_bingo import cookies
  7. from .models import GAEBingoIdentityModel
  8. from .config import current_logged_in_identity
  9. # NOTE: this request caching will need a bit of a touchup once Python 2.7 is released for GAE and concurrent requests are enabled.
  10. IDENTITY_CACHE = None
  11. LOGGED_IN_IDENTITY_CACHE = None
  12. IDENTITY_COOKIE_KEY = "gae_b_id"
  13. def logged_in_bingo_identity():
  14. global LOGGED_IN_IDENTITY_CACHE
  15. if LOGGED_IN_IDENTITY_CACHE is None:
  16. LOGGED_IN_IDENTITY_CACHE = current_logged_in_identity()
  17. return LOGGED_IN_IDENTITY_CACHE
  18. def identity(identity_val=None):
  19. """ Determines the Bingo identity for the specified user. If no user
  20. is specified, this will attempt to infer one based on cookies/logged in user
  21. identity_val -- a string or instance of GAEBingoIdentityModel specifying
  22. which bingo identity to retrieve.
  23. """
  24. global IDENTITY_CACHE
  25. if identity_val:
  26. # Don't cache for arbitrarily passed in identity_val
  27. return bingo_identity_for_value(identity_val, associate_with_cookie=False)
  28. if IDENTITY_CACHE is None:
  29. if is_bot():
  30. # Just make all bots identify as the same single user so they don't
  31. # bias results. Following simple suggestion in
  32. # http://www.bingocardcreator.com/abingo/faq
  33. IDENTITY_CACHE = "_gae_bingo_bot"
  34. else:
  35. # Try to get unique (hopefully persistent) identity from user's implementation,
  36. # otherwise grab the current cookie value, otherwise grab random value.
  37. IDENTITY_CACHE = str(get_logged_in_bingo_identity_value() or get_identity_cookie_value() or get_random_identity_value())
  38. return IDENTITY_CACHE
  39. def using_logged_in_bingo_identity():
  40. return identity() and identity() == get_logged_in_bingo_identity_value()
  41. def get_logged_in_bingo_identity_value():
  42. val = logged_in_bingo_identity()
  43. return bingo_identity_for_value(val)
  44. def bingo_identity_for_value(val, associate_with_cookie=True):
  45. if val is None:
  46. return None
  47. if isinstance(val, db.Model):
  48. if isinstance(val, GAEBingoIdentityModel):
  49. # If it's a db.Model that inherited from GAEBingoIdentityModel, return bingo identity
  50. if not val.gae_bingo_identity:
  51. if (is_random_identity_value(get_identity_cookie_value()) and
  52. associate_with_cookie):
  53. # If the current model doesn't have a bingo identity associated w/ it
  54. # and we have a random cookie value already set, associate it with this identity model.
  55. #
  56. # This keeps the user's experience consistent between using the site pre- and post-login.
  57. val.gae_bingo_identity = get_identity_cookie_value()
  58. else:
  59. # Otherwise just use the key, it's guaranteed to be unique
  60. val.gae_bingo_identity = str(val.key())
  61. val.put()
  62. return val.gae_bingo_identity
  63. # If it's just a normal db instance, just use its unique key
  64. return str(val.key())
  65. # Otherwise it's just a plain unique string
  66. return str(val)
  67. def get_random_identity_value():
  68. return "_gae_bingo_random:%s" % base64.urlsafe_b64encode(os.urandom(30))
  69. def is_random_identity_value(val):
  70. return val and val.startswith("_gae_bingo_random")
  71. def get_identity_cookie_value():
  72. cookie_val = cookies.get_cookie_value(IDENTITY_COOKIE_KEY)
  73. if cookie_val:
  74. try:
  75. return base64.urlsafe_b64decode(cookie_val)
  76. except:
  77. pass
  78. return None
  79. def set_identity_cookie_header():
  80. return cookies.set_cookie_value(IDENTITY_COOKIE_KEY, base64.urlsafe_b64encode(identity()))
  81. def delete_identity_cookie_header():
  82. return cookies.set_cookie_value(IDENTITY_COOKIE_KEY, "")
  83. def flush_identity_cache():
  84. global IDENTITY_CACHE, LOGGED_IN_IDENTITY_CACHE
  85. IDENTITY_CACHE = None
  86. LOGGED_IN_IDENTITY_CACHE = None
  87. # I am well aware that this is a far-from-perfect, hacky method of quickly
  88. # determining who's a bot or not. If necessary, in the future we could implement
  89. # a javascript check like a/bingo and django-lean do -- but for now, I'm sticking
  90. # w/ the simplest possible implementation for devs (don't need to add JS in any template code)
  91. # that doesn't strongly bias the statistical outcome (undetected bots aren't a distaster,
  92. # because they shouldn't favor one side over the other).
  93. bot_regex = re.compile("(Baidu|Gigabot|Googlebot|libwww-perl|lwp-trivial|msnbot|SiteUptime|Slurp|WordPress|ZIBB|ZyBorg)", re.IGNORECASE)
  94. def is_bot():
  95. return bool(bot_regex.search(os.environ.get("HTTP_USER_AGENT") or ""))