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.

90 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 json
  23. import pickle
  24. import logging
  25. import functools
  26. from werkzeug.contrib.sessions import SessionStore
  27. from odoo.tools import config
  28. _logger = logging.getLogger(__name__)
  29. try:
  30. import redis
  31. except ImportError:
  32. pass
  33. SESSION_TIMEOUT = 60 * 60 * 24 * 7
  34. def retry_redis(func):
  35. @functools.wraps(func)
  36. def wrapper(self, *args, **kwargs):
  37. for attempts in range(1, 6):
  38. try:
  39. return func(self, *args, **kwargs)
  40. except redis.ConnectionError as error:
  41. _logger.warn("SessionStore connection failed! (%s/5)" % attempts)
  42. if attempts >= 5:
  43. raise error
  44. return wrapper
  45. class RedisSessionStore(SessionStore):
  46. def __init__(self, *args, **kwargs):
  47. super(RedisSessionStore, self).__init__(*args, **kwargs)
  48. self.prefix = config.get('session_store_prefix', '')
  49. self.server = redis.Redis(
  50. host=config.get('session_store_host', 'localhost'),
  51. port=int(config.get('session_store_port', 6379)),
  52. db=int(config.get('session_store_dbindex', 1)),
  53. password=config.get('session_store_pass', None)
  54. )
  55. def _encode_session_key(self, key):
  56. return key.encode('utf-8') if isinstance(key, str) else key
  57. def _get_session_key(self, sid):
  58. return self._encode_session_key(self.prefix + sid)
  59. @retry_redis
  60. def save(self, session):
  61. key = self._get_session_key(session.sid)
  62. payload = pickle.dumps(dict(session), pickle.HIGHEST_PROTOCOL)
  63. self.server.setex(name=key, value=payload, time=SESSION_TIMEOUT)
  64. @retry_redis
  65. def delete(self, session):
  66. self.server.delete(self._get_session_key(session.sid))
  67. @retry_redis
  68. def get(self, sid):
  69. if not self.is_valid_key(sid):
  70. return self.new()
  71. key = self._get_session_key(sid)
  72. payload = self.server.get(key)
  73. if payload:
  74. self.server.setex(name=key, value=payload, time=SESSION_TIMEOUT)
  75. return self.session_class(pickle.loads(payload), sid, False)
  76. else:
  77. return self.session_class({}, sid, False)