Make the session thread safe.

As sessions in pgAdmin4 are filesystem based session, they need locking
for avoiding the access from multiple threads, specially running as an
WSGI application.

Fixes #3547
This commit is contained in:
Harshal Dhumal 2018-08-22 11:55:39 +05:30 committed by Ashesh Vashi
parent 70c95fcdd5
commit 013ad7446f
2 changed files with 44 additions and 33 deletions

View File

@ -20,5 +20,6 @@ Bug fixes
| `Bug #3407 <https://redmine.postgresql.org/issues/3407>`_ - Fix keyboard shortcuts layout in the preferences panel.
| `Bug #3461 <https://redmine.postgresql.org/issues/3461>`_ - Ensure that refreshing a node also updates the Property list.
| `Bug #3528 <https://redmine.postgresql.org/issues/3528>`_ - Handle connection errors properly in the query tool.
| `Bug #3547 <https://redmine.postgresql.org/issues/3578>`_ - Make session implementation thread safe
| `Bug #3558 <https://redmine.postgresql.org/issues/3558>`_ - Fix sort/filter dialog editing issue.
| `Bug #3578 <https://redmine.postgresql.org/issues/3578>`_ - Ensure sql for Role should be visible in SQL panel for GPDB.

View File

@ -24,6 +24,7 @@ import random
import string
import time
from uuid import uuid4
from threading import Lock
from flask import current_app, request, flash, redirect
from flask_login import login_url
from pgadmin.utils.ajax import make_json_response
@ -50,6 +51,9 @@ def _calc_hmac(body, secret):
).decode()
sess_lock = Lock()
class ManagedSession(CallbackDict, SessionMixin):
def __init__(self, initial=None, sid=None, new=False, randval=None,
hmac_digest=None):
@ -111,6 +115,7 @@ class CachingSessionManager(SessionManager):
def _normalize(self):
if len(self._cache) > self.num_to_store:
# Flush 20% of the cache
with sess_lock:
while len(self._cache) > (self.num_to_store * 0.8):
self._cache.popitem(False)
@ -122,23 +127,27 @@ class CachingSessionManager(SessionManager):
if request.path.startswith(sp):
return session
with sess_lock:
self._cache[session.sid] = session
self._normalize()
return session
def remove(self, sid):
with sess_lock:
self.parent.remove(sid)
if sid in self._cache:
del self._cache[sid]
def exists(self, sid):
with sess_lock:
if sid in self._cache:
return True
return self.parent.exists(sid)
def get(self, sid, digest):
session = None
with sess_lock:
if sid in self._cache:
session = self._cache[sid]
if session.hmac_digest != digest:
@ -161,6 +170,7 @@ class CachingSessionManager(SessionManager):
return session
def put(self, session):
with sess_lock:
self.parent.put(session)
# Do not store the session if skip paths