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.

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