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 <
[email protected]> 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 <[email protected]>
> 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 <
>> [email protected]> 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 <[email protected]> wrote:
>>>
>>>> Harshal, can you help with this please?
>>>>
>>>> On Sun, Oct 8, 2017 at 12:39 AM, David Gilman <[email protected]>
>>>> 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()