Hi Dave, Please find attached patch (old RM2713). Also for server group and server module login required checks were missing, I have added them in same patch
-- *Harshal Dhumal* *Sr. Software Engineer* EnterpriseDB India: http://www.enterprisedb.com The Enterprise PostgreSQL Company On Tue, Oct 10, 2017 at 11:39 AM, Harshal Dhumal < harshal.dhu...@enterprisedb.com> wrote: > Hi David, > > Thanks for you input. Session was not invalidated (otherwise execution > would not have reached to connection manager); Only value of '_id' was > changed for session. > > If we look at code > <https://github.com/maxcountryman/flask-login/blob/master/flask_login/utils.py#L333> > how '_id' is generated then we can see it uses remote address and user-agent > to generate it. I thing we should use another session identifier (sid - > session id) to map user connection from connection manager. > > -- > *Harshal Dhumal* > *Sr. Software Engineer* > > EnterpriseDB India: http://www.enterprisedb.com > The Enterprise PostgreSQL Company > > On Mon, Oct 9, 2017 at 10:18 PM, David Gilman <davidgilm...@gmail.com> > wrote: > >> You can probably stand down on this one. The issue was that my PC was >> switching back and forth between IPv4 and IPv6 for whatever reason and when >> that happened the cookie and my session would get invalidated. Maybe it is >> worth making code changes so you don't get Python tracebacks dumped to the >> error logs every time this happens but I have been able to resolve the >> issue by turning off IPv6 temporarily. That was an interesting one to >> troubleshoot! >> >> On Mon, Oct 9, 2017 at 4:30 AM, Harshal Dhumal < >> harshal.dhu...@enterprisedb.com> wrote: >> >>> sure Dave >>> >>> -- >>> *Harshal Dhumal* >>> *Sr. Software Engineer* >>> >>> EnterpriseDB India: http://www.enterprisedb.com >>> The Enterprise PostgreSQL Company >>> >>> On Mon, Oct 9, 2017 at 1:16 PM, Dave Page <dp...@pgadmin.org> wrote: >>> >>>> Harshal, can you help with this please? >>>> >>>> On Sun, Oct 8, 2017 at 12:39 AM, David Gilman <davidgilm...@gmail.com> >>>> wrote: >>>> >>>>> I'm trying out pgadmin4 v2.0 for the first time. It seems that after >>>>> only a few minutes (maybe even less than five) my pgadmin4 session will >>>>> get >>>>> logged out and I'll need to log in again and reopen everything from >>>>> scratch. This exception is thrown in the mod_wsgi logs: >>>>> >>>>> mod_wsgi (pid=5965): Exception occurred processing WSGI script >>>>> '/home/pgadmin/venv/lib/python2.7/site-packages/pgadmin4/pgA >>>>> dmin4.wsgi'. >>>>> Traceback (most recent call last): >>>>> File >>>>> "/home/pgadmin/venv/local/lib/python2.7/site-packages/flask/app.py", >>>>> line 2000, in __call__ >>>>> return self.wsgi_app(environ, start_response) >>>>> File >>>>> "/home/pgadmin/venv/local/lib/python2.7/site-packages/flask/app.py", >>>>> line 1991, in wsgi_app >>>>> response = self.make_response(self.handle_exception(e)) >>>>> File >>>>> "/home/pgadmin/venv/local/lib/python2.7/site-packages/flask/app.py", >>>>> line 1567, in handle_exception >>>>> reraise(exc_type, exc_value, tb) >>>>> File >>>>> "/home/pgadmin/venv/local/lib/python2.7/site-packages/flask/app.py", >>>>> line 1988, in wsgi_app >>>>> response = self.full_dispatch_request() >>>>> File >>>>> "/home/pgadmin/venv/local/lib/python2.7/site-packages/flask/app.py", >>>>> line 1641, in full_dispatch_request >>>>> rv = self.handle_user_exception(e) >>>>> File >>>>> "/home/pgadmin/venv/local/lib/python2.7/site-packages/flask/app.py", >>>>> line 1544, in handle_user_exception >>>>> reraise(exc_type, exc_value, tb) >>>>> File >>>>> "/home/pgadmin/venv/local/lib/python2.7/site-packages/flask/app.py", >>>>> line 1639, in full_dispatch_request >>>>> rv = self.dispatch_request() >>>>> File >>>>> "/home/pgadmin/venv/local/lib/python2.7/site-packages/flask/app.py", >>>>> line 1625, in dispatch_request >>>>> return self.view_functions[rule.endpoint](**req.view_args) >>>>> File >>>>> "/home/pgadmin/venv/local/lib/python2.7/site-packages/flask_login.py", >>>>> line 792, in decorated_view >>>>> return func(*args, **kwargs) >>>>> File >>>>> "/home/pgadmin/venv/lib/python2.7/site-packages/pgadmin4/pgadmin/dashboard/__init__.py", >>>>> line 169, in wrap >>>>> kwargs['sid'] >>>>> File "/home/pgadmin/venv/lib/python2.7/site-packages/pgadmin4/pga >>>>> dmin/utils/driver/psycopg2/__init__.py", line 2000, in >>>>> connection_manager >>>>> if session['_id'] not in self.managers: >>>>> File >>>>> "/home/pgadmin/venv/local/lib/python2.7/site-packages/werkzeug/local.py", >>>>> line 368, in <lambda> >>>>> __getitem__ = lambda x, i: x._get_current_object()[i] >>>>> KeyError: '_id' >>>>> >>>>> My setup: >>>>> pgadmin4 v2.0 . The configuration is all defaults except >>>>> for LOG_FILE/SQLITE_PATH/SESSION_DB_PATH/STORAGE_DIR. That >>>>> means MAX_SESSION_IDLE_TIME is at its default of 60 (minutes). >>>>> pgadmin4 is in server mode with mod_wsgi as a host. >>>>> PostgreSQL 9.4.14 - from the postgres apt repository. No changes made >>>>> to timeouts or anything in the postgresql.conf , it's all defaults. >>>>> Python 2.7 >>>>> psycopg2 2.7.3.1 >>>>> >>>>> I can confirm that the apache process hosting pgadmin4 is running >>>>> under the right UNIX user account and that it seems to have good >>>>> access/permissions to its scratch files on disk. I see updates being made >>>>> to pgadmin4.db and the sessions directory. >>>>> >>>>> -- >>>>> David Gilman >>>>> :DG< >>>>> >>>> >>>> >>>> >>>> -- >>>> Dave Page >>>> Blog: http://pgsnake.blogspot.com >>>> Twitter: @pgsnake >>>> >>>> EnterpriseDB UK: http://www.enterprisedb.com >>>> The Enterprise PostgreSQL Company >>>> >>> >>> >> >> >> -- >> David Gilman >> :DG< >> > >
diff --git a/web/pgadmin/browser/server_groups/__init__.py b/web/pgadmin/browser/server_groups/__init__.py index fd2b4ac..a45d4d7 100644 --- a/web/pgadmin/browser/server_groups/__init__.py +++ b/web/pgadmin/browser/server_groups/__init__.py @@ -13,9 +13,9 @@ import simplejson as json from abc import ABCMeta, abstractmethod import six -from flask import request, render_template, make_response, jsonify, current_app +from flask import request, jsonify from flask_babel import gettext -from flask_security import current_user +from flask_security import current_user, login_required from pgadmin.browser import BrowserPluginModule from pgadmin.browser.utils import NodeView from pgadmin.utils.ajax import make_json_response, gone, \ @@ -95,6 +95,7 @@ class ServerGroupView(NodeView): parent_ids = [] ids = [{'type': 'int', 'id': 'gid'}] + @login_required def list(self): res = [] @@ -108,6 +109,7 @@ class ServerGroupView(NodeView): return ajax_response(response=res, status=200) + @login_required def delete(self, gid): """Delete a server group node in the settings database""" @@ -149,6 +151,7 @@ class ServerGroupView(NodeView): return make_json_response(result=request.form) + @login_required def update(self, gid): """Update the server-group properties""" @@ -195,6 +198,7 @@ class ServerGroupView(NodeView): ) ) + @login_required def properties(self, gid): """Update the server-group properties""" @@ -217,6 +221,7 @@ class ServerGroupView(NodeView): status=200 ) + @login_required def create(self): """Creates new server-group """ data = request.form if request.form else json.loads( @@ -261,21 +266,27 @@ class ServerGroupView(NodeView): success=0, errormsg=gettext('No server group name was specified')) + @login_required def sql(self, gid): return make_json_response(status=422) + @login_required def modified_sql(self, gid): return make_json_response(status=422) + @login_required def statistics(self, gid): return make_json_response(status=422) + @login_required def dependencies(self, gid): return make_json_response(status=422) + @login_required def dependents(self, gid): return make_json_response(status=422) + @login_required def nodes(self, gid=None): """Return a JSON document listing the server groups for the user""" nodes = [] diff --git a/web/pgadmin/browser/server_groups/servers/__init__.py b/web/pgadmin/browser/server_groups/servers/__init__.py index bdff7b2..d3342b9 100644 --- a/web/pgadmin/browser/server_groups/servers/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/__init__.py @@ -13,7 +13,7 @@ import pgadmin.browser.server_groups as sg from flask import render_template, request, make_response, jsonify, \ current_app, url_for from flask_babel import gettext -from flask_security import current_user +from flask_security import current_user, login_required from pgadmin.browser.server_groups.servers.types import ServerType from pgadmin.browser.utils import PGChildNodeView from pgadmin.utils.ajax import make_json_response, bad_request, forbidden, \ @@ -73,6 +73,7 @@ class ServerModule(sg.ServerGroupPluginModule): """ return sg.ServerGroupModule.NODE_TYPE + @login_required def get_nodes(self, gid): """Return a JSON document listing the server groups for the user""" servers = Server.query.filter_by(user_id=current_user.id, @@ -276,6 +277,7 @@ class ServerNode(PGChildNodeView): return flag, data + @login_required def nodes(self, gid): res = [] """ @@ -324,7 +326,7 @@ class ServerNode(PGChildNodeView): return make_json_response(result=res) - + @login_required def node(self, gid, sid): """Return a JSON document listing the server groups for the user""" server = Server.query.filter_by(user_id=current_user.id, @@ -371,6 +373,7 @@ class ServerNode(PGChildNodeView): ) ) + @login_required def delete(self, gid, sid): """Delete a server node in the settings database.""" servers = Server.query.filter_by(user_id=current_user.id, id=sid) @@ -401,6 +404,7 @@ class ServerNode(PGChildNodeView): return make_json_response(success=1, info=gettext("Server deleted")) + @login_required def update(self, gid, sid): """Update the server settings""" server = Server.query.filter_by( @@ -520,6 +524,7 @@ class ServerNode(PGChildNodeView): ) ) + @login_required def list(self, gid): """ Return list of attributes of all servers. @@ -561,6 +566,7 @@ class ServerNode(PGChildNodeView): response=res ) + @login_required def properties(self, gid, sid): """Return list of attributes of a server""" server = Server.query.filter_by( @@ -615,6 +621,7 @@ class ServerNode(PGChildNodeView): } ) + @login_required def create(self, gid): """Add a server node to the settings database""" required_args = [ @@ -752,12 +759,15 @@ class ServerNode(PGChildNodeView): errormsg=str(e) ) + @login_required def sql(self, gid, sid): return make_json_response(data='') + @login_required def modified_sql(self, gid, sid): return make_json_response(data='') + @login_required def statistics(self, gid, sid): manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid) conn = manager.connection() @@ -781,9 +791,11 @@ class ServerNode(PGChildNodeView): ) ) + @login_required def dependencies(self, gid, sid): return make_json_response(data='') + @login_required def dependents(self, gid, sid): return make_json_response(data='') diff --git a/web/pgadmin/dashboard/__init__.py b/web/pgadmin/dashboard/__init__.py index 2214f53..382245a 100644 --- a/web/pgadmin/dashboard/__init__.py +++ b/web/pgadmin/dashboard/__init__.py @@ -8,10 +8,7 @@ ########################################################################## """A blueprint module implementing the dashboard frame.""" -MODULE_NAME = 'dashboard' - from functools import wraps - from flask import render_template, url_for, Response, g from flask_babel import gettext from flask_security import login_required @@ -25,6 +22,8 @@ from pgadmin.utils.preferences import Preferences from config import PG_DEFAULT_DRIVER +MODULE_NAME = 'dashboard' + class DashboardModule(PgAdminModule): def __init__(self, *args, **kwargs): diff --git a/web/pgadmin/utils/driver/psycopg2/__init__.py b/web/pgadmin/utils/driver/psycopg2/__init__.py index 527e175..6f56765 100644 --- a/web/pgadmin/utils/driver/psycopg2/__init__.py +++ b/web/pgadmin/utils/driver/psycopg2/__init__.py @@ -1997,8 +1997,8 @@ class Driver(BaseDriver): assert (sid is not None and isinstance(sid, int)) managers = None - if session['_id'] not in self.managers: - self.managers[session['_id']] = managers = dict() + if session.sid not in self.managers: + self.managers[session.sid] = managers = dict() if '__pgsql_server_managers' in session: session_managers = session['__pgsql_server_managers'].copy() session['__pgsql_server_managers'] = dict() @@ -2013,7 +2013,7 @@ class Driver(BaseDriver): manager._restore(session_managers[server_id]) manager.update_session() else: - managers = self.managers[session['_id']] + managers = self.managers[session.sid] managers['pinged'] = datetime.datetime.now() if str(sid) not in managers: @@ -2089,9 +2089,9 @@ class Driver(BaseDriver): manager = self.connection_manager(sid) if manager is not None: manager.release() - if session['_id'] in self.managers and \ - str(sid) in self.managers[session['_id']]: - del self.managers[session['_id']][str(sid)] + if session.sid in self.managers and \ + str(sid) in self.managers[session.sid]: + del self.managers[session.sid][str(sid)] def gc(self): """ @@ -2099,7 +2099,7 @@ class Driver(BaseDriver): server for more than config.MAX_SESSION_IDLE_TIME. """ - # Mininum session idle is 20 minutes + # Minimum session idle is 20 minutes max_idle_time = max(config.MAX_SESSION_IDLE_TIME or 60, 20) session_idle_timeout = datetime.timedelta(minutes=max_idle_time) @@ -2108,11 +2108,11 @@ class Driver(BaseDriver): for sess in self.managers: sess_mgr = self.managers[sess] - if sess == session.get('_id'): + if sess == session.sid: sess_mgr['pinged'] = curr_time continue - if (curr_time - sess_mgr['pinged'] >= session_idle_timeout): + if curr_time - sess_mgr['pinged'] >= session_idle_timeout: for mgr in [m for m in sess_mgr if isinstance(m, ServerManager)]: mgr.release()