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.

87 lines
3.0 KiB

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