You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

91 lines
3.1 KiB

  1. ###################################################################################
  2. #
  3. # Copyright (c) 2017-2019 MuK IT GmbH.
  4. #
  5. # This file is part of MuK Session Store
  6. # (see https://mukit.at).
  7. #
  8. # This program is free software: you can redistribute it and/or modify
  9. # it under the terms of the GNU Lesser General Public License as published by
  10. # the Free Software Foundation, either version 3 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU Lesser General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU Lesser General Public License
  19. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. #
  21. ###################################################################################
  22. import functools
  23. import logging
  24. import pickle
  25. from odoo.tools import config
  26. from werkzeug.contrib.sessions import SessionStore
  27. _logger = logging.getLogger(__name__)
  28. try:
  29. import redis
  30. except ImportError:
  31. pass
  32. SESSION_TIMEOUT = 60 * 60 * 24 * 7
  33. def retry_redis(func):
  34. @functools.wraps(func)
  35. def wrapper(self, *args, **kwargs):
  36. for attempts in range(1, 6):
  37. try:
  38. return func(self, *args, **kwargs)
  39. except redis.ConnectionError as error:
  40. _logger.warn("SessionStore connection failed! (%s/5)" % attempts)
  41. if attempts >= 5:
  42. raise error
  43. return wrapper
  44. class RedisSessionStore(SessionStore):
  45. def __init__(self, *args, **kwargs):
  46. super(RedisSessionStore, self).__init__(*args, **kwargs)
  47. self.prefix = config.get("session_store_prefix", "")
  48. self.server = redis.Redis(
  49. host=config.get("session_store_host", "localhost"),
  50. port=int(config.get("session_store_port", 6379)),
  51. db=int(config.get("session_store_dbindex", 1)),
  52. password=config.get("session_store_pass", None),
  53. )
  54. def _encode_session_key(self, key):
  55. return key.encode("utf-8") if isinstance(key, str) else key
  56. def _get_session_key(self, sid):
  57. return self._encode_session_key(self.prefix + sid)
  58. @retry_redis
  59. def save(self, session):
  60. key = self._get_session_key(session.sid)
  61. payload = pickle.dumps(dict(session), pickle.HIGHEST_PROTOCOL)
  62. self.server.setex(name=key, value=payload, time=SESSION_TIMEOUT)
  63. @retry_redis
  64. def delete(self, session):
  65. self.server.delete(self._get_session_key(session.sid))
  66. @retry_redis
  67. def get(self, sid):
  68. if not self.is_valid_key(sid):
  69. return self.new()
  70. key = self._get_session_key(sid)
  71. payload = self.server.get(key)
  72. if payload:
  73. self.server.setex(name=key, value=payload, time=SESSION_TIMEOUT)
  74. return self.session_class(pickle.loads(payload), sid, False)
  75. else:
  76. return self.session_class({}, sid, False)