@@ -66,6 +66,10 @@ class MongoDBSession(ServerSideSession):
6666 pass
6767
6868
69+ class ElasticsearchSession (ServerSideSession ):
70+ pass
71+
72+
6973class SqlAlchemySession (ServerSideSession ):
7074 pass
7175
@@ -221,12 +225,6 @@ def __init__(self, app, client, key_prefix, use_signer=False, permanent=True):
221225
222226 def _get_preferred_memcache_client (self , app ):
223227 server = "127.0.0.1:11211"
224- # try:
225- # import pylibmc
226- # except ImportError:
227- # pass
228- # else:
229- # return pylibmc.Client(servers)
230228
231229 try :
232230 import pymemcache
@@ -587,13 +585,13 @@ class Session(self.db.Model):
587585 __tablename__ = table
588586
589587 if sequence :
590- id = self .db .Column (
588+ id = self .db .Column ( # noqa: A003, VNE003, A001
591589 self .db .Integer , self .db .Sequence (sequence ), primary_key = True
592590 )
593591 else :
594- id = self .db .Column (
592+ id = self .db .Column ( # noqa: A003, VNE003, A001
595593 self .db .Integer , primary_key = True
596- ) # noqa: A003, VNE003
594+ )
597595
598596 session_id = self .db .Column (self .db .String (255 ), unique = True )
599597 data = self .db .Column (self .db .LargeBinary )
@@ -698,3 +696,107 @@ def save_session(self, app, session, response):
698696 secure = secure ,
699697 ** conditional_cookie_kwargs ,
700698 )
699+
700+
701+ class ElasticsearchSessionInterface (SessionInterface ):
702+ """A Session interface that uses Elasticsearch as backend.
703+ .. versionadded:: 0.X
704+ :param client: A ``elasticsearch.Elasticsearch`` instance.
705+ :param host: The elasticsearch host url you want to use.
706+ :param index: The elasticsearch index you want to use.
707+ :param key_prefix: A prefix that is added to all MongoDB store keys.
708+ :param use_signer: Whether to sign the session id cookie or not.
709+ :param permanent: Whether to use permanent session or not.
710+ """
711+
712+ serializer = None
713+ session_class = ElasticsearchSession
714+
715+ def __init__ (
716+ self , client , host , index , key_prefix , use_signer = False , permanent = True
717+ ):
718+ if client is None :
719+ from elasticsearch import Elasticsearch
720+
721+ client = Elasticsearch (host )
722+
723+ self .client = client
724+ self .index = index
725+ try : # noqa: SIM105
726+ self .client .indices .create (index = self .index )
727+ except :
728+ pass
729+ self .key_prefix = key_prefix
730+ self .use_signer = use_signer
731+ self .permanent = permanent
732+
733+ def open_session (self , app , request ):
734+ sid = request .cookies .get (app .config ["SESSION_COOKIE_NAME" ])
735+ if not sid :
736+ sid = self ._generate_sid ()
737+ return self .session_class (sid = sid , permanent = self .permanent )
738+ if self .use_signer :
739+ signer = self ._get_signer (app )
740+
741+ if signer is None :
742+ return None
743+
744+ try :
745+ sid_as_bytes = signer .unsign (sid )
746+ sid = sid_as_bytes .decode ()
747+ except BadSignature :
748+ sid = self ._generate_sid ()
749+ return self .session_class (sid = sid , permanent = self .permanent )
750+
751+ store_id = self .key_prefix + sid
752+ document = self .client .get (index = self .index , id = store_id , ignore = 404 )
753+ if document ["found" ]:
754+ expiration = document ["_source" ]["expiration" ]
755+
756+ expiration = datetime .strptime (expiration , "%Y-%m-%dT%H:%M:%S.%f%z" )
757+ if expiration <= datetime .utcnow ().replace (tzinfo = pytz .UTC ):
758+ # Delete expired session
759+ self .client .delete (index = self .index , id = store_id )
760+ document = None
761+ if document is not None :
762+ try :
763+ value = document ["_source" ]["val" ]
764+ return self .session_class (value , sid = sid )
765+ except :
766+ return self .session_class (sid = sid , permanent = self .permanent )
767+ return self .session_class (sid = sid , permanent = self .permanent )
768+
769+ def save_session (self , app , session , response ):
770+ domain = self .get_cookie_domain (app )
771+ path = self .get_cookie_path (app )
772+ store_id = self .key_prefix + session .sid
773+ if not session :
774+ if session .modified :
775+ self .client .delete (index = self .index , id = store_id )
776+ response .delete_cookie (
777+ app .config ["SESSION_COOKIE_NAME" ], domain = domain , path = path
778+ )
779+ return
780+
781+ httponly = self .get_cookie_httponly (app )
782+ secure = self .get_cookie_secure (app )
783+ expires = self .get_expiration_time (app , session )
784+ value = dict (session )
785+ self .client .index (
786+ index = self .index ,
787+ id = store_id ,
788+ body = {"id" : store_id , "val" : value , "expiration" : expires },
789+ )
790+ if self .use_signer :
791+ session_id = self ._get_signer (app ).sign (want_bytes (session .sid ))
792+ else :
793+ session_id = session .sid
794+ response .set_cookie (
795+ app .config ["SESSION_COOKIE_NAME" ],
796+ session_id ,
797+ expires = expires ,
798+ httponly = httponly ,
799+ domain = domain ,
800+ path = path ,
801+ secure = secure ,
802+ )
0 commit comments