Update SQLAlchemy, Flask, Flask-SQLAlchemy, and other packages to current versions. #5901

- Update Flask, Flask-SQLAlchemy, Flask-Babel, Flask-Security-Too, Flask-SocketIO, pytz, psutil, SQLAlchemy, bcrypt, cryptography, eventlet, Authlib, requests python packages
- Remove pinned dnspython, Werkzeug packages from requirements.txt
This commit is contained in:
Aditya Toshniwal 2023-03-15 11:57:16 +05:30 committed by GitHub
parent 294282c7ca
commit 292d76b39e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 176 additions and 202 deletions

View File

@ -60,7 +60,7 @@ _create_python_virtualenv() {
pip3 install wheel
# Install the requirements
pip3 install --no-cache-dir --no-binary psycopg2 -r "${SOURCEDIR}/requirements.txt"
pip3 install --no-cache-dir --no-binary psycopg -r "${SOURCEDIR}/requirements.txt"
# Fixup the paths in the venv activation scripts
sed -i 's/VIRTUAL_ENV=.*/VIRTUAL_ENV="\/usr\/pgadmin4\/venv"/g' venv/bin/activate

View File

@ -8,56 +8,46 @@
#
###############################################################################
Flask==2.0.3; python_version <= '3.6'
Flask==2.1.*; python_version >= '3.7'
Flask==2.2.*
Flask-Gravatar==0.*
Flask-Login==0.*
Flask-Mail==0.*
Flask-Migrate==4.*
dnspython==2.2.1
greenlet==1.1.2; python_version <= '3.10'
Flask-SQLAlchemy==2.5.*
Flask-SQLAlchemy==3.0.*
Flask-WTF==1.0.1
Flask-Compress==1.*
Flask-Paranoid==0.*
Flask-Babel==2.*
Flask-Security-Too==4.1.*
Flask-SocketIO<=5.2.0
WTForms==3.*
Flask-Babel==3.0.*
Flask-Security-Too==5.1.*
Flask-SocketIO==5.3.*
WTForms==3.0.*
passlib==1.*
pytz==2021.*
pytz==2022.*
speaklater3==1.*
sqlparse==0.*
psutil==5.9.3
psycopg2==2.9.*; python_version < '3.7'
psycopg[c]==3.1.*; python_version >= '3.7'
psutil==5.9.*
psycopg[c]==3.1.*
python-dateutil==2.*
SQLAlchemy==1.4.44; python_version <= '3.6'
SQLAlchemy==1.4.*; python_version >= '3.7'
bcrypt==3.*
cryptography==3.*
SQLAlchemy==2.*
bcrypt==4.0.*
cryptography==39.0.*
pyOpenSSL>=23.* # required by cryptography
sshtunnel==0.*
ldap3==2.*
gssapi==1.7.*; python_version <= '3.6'
gssapi==1.8.*; python_version >= '3.7'
eventlet==0.33.0
gssapi==1.8.*
eventlet==0.33.3
httpagentparser==1.9.*
user-agents==2.2.0
pywinpty==1.1.*; sys_platform=="win32"
Authlib==0.15.*; python_version <= '3.6'
Authlib==1.1.*; python_version >= '3.7'
requests==2.25.*
Authlib==1.2.*
requests==2.28.*
pyotp==2.*
qrcode==7.*
Pillow==8.4.*; python_version <= '3.6'
Pillow==9.*; python_version >= '3.7'
boto3==1.23.*; python_version <= '3.6'
boto3==1.26.*; python_version >= '3.7'
botocore==1.26.*; python_version <= '3.6'
botocore==1.29.*; python_version >= '3.7'
Pillow==9.*
boto3==1.26.*
botocore==1.29.*
urllib3==1.26.*
Werkzeug==2.0.3; python_version <= '3.6'
Werkzeug==2.1.2; python_version >= '3.7'
azure-mgmt-rdbms==10.1.0
azure-mgmt-resource==21.0.0
azure-mgmt-subscription==3.0.0

View File

@ -859,80 +859,8 @@ AUTO_DISCOVER_SERVERS = True
#############################################################################
SERVER_HEARTBEAT_TIMEOUT = 30 # In seconds
##########################################################################
# Local config settings
##########################################################################
# User configs loaded from config_local, config_distro etc.
user_config_settings = {}
# Function to Extract settings from config_local, config_distro etc.
def get_variables_from_module(module_name):
module = globals().get(module_name, None)
variables = {}
if module:
variables = {key: value for key, value in module.__dict__.items()
if not (key.startswith('__') or key.startswith('_'))}
return variables
# Load distribution-specific config overrides
try:
import config_distro
config_distro_settings = get_variables_from_module('config_distro')
user_config_settings.update(config_distro_settings)
except ImportError:
pass
# Load local config overrides
try:
import config_local
config_local_settings = get_variables_from_module('config_local')
user_config_settings.update(config_local_settings)
except ImportError:
pass
# Load system config overrides. We do this last, so that the sysadmin can
# override anything they want from a config file that's in a protected system
# directory and away from pgAdmin to avoid invalidating signatures.
system_config_dir = '/etc/pgadmin'
if sys.platform.startswith('win32'):
system_config_dir = os.environ['CommonProgramFiles'] + '/pgadmin'
elif sys.platform.startswith('darwin'):
system_config_dir = '/Library/Preferences/pgadmin'
if os.path.exists(system_config_dir + '/config_system.py'):
try:
sys.path.insert(0, system_config_dir)
import config_system
config_system_settings = get_variables_from_module('config_system')
user_config_settings.update(config_system_settings)
except ImportError:
pass
# Update settings for 'LOG_FILE', 'SQLITE_PATH', 'SESSION_DB_PATH',
# 'AZURE_CREDENTIAL_CACHE_DIR', 'KERBEROS_CCACHE_DIR', 'STORAGE_DIR'
# of DATA_DIR is user defined
data_dir_dependent_settings = ['LOG_FILE', 'SQLITE_PATH', 'SESSION_DB_PATH',
'AZURE_CREDENTIAL_CACHE_DIR',
'KERBEROS_CCACHE_DIR', 'STORAGE_DIR']
if 'DATA_DIR' in user_config_settings:
for setting in data_dir_dependent_settings:
if setting not in user_config_settings:
data_dir = user_config_settings['DATA_DIR']
file_dir_name = os.path.basename(locals().get(setting))
locals().update({setting: os.path.join(data_dir, file_dir_name)})
# Finally update config user configs
locals().update(user_config_settings)
# Override DEFAULT_SERVER value from environment variable.
if 'PGADMIN_CONFIG_DEFAULT_SERVER' in os.environ:
DEFAULT_SERVER = os.environ['PGADMIN_CONFIG_DEFAULT_SERVER']
# Disable USER_INACTIVITY_TIMEOUT when SERVER_MODE=False
if not SERVER_MODE:
USER_INACTIVITY_TIMEOUT = 0
# Enable PSQL in Desktop Mode.
ENABLE_PSQL = True
#############################################################################
# Patch the default config with custom config and other manipulations
#############################################################################
from pgadmin.evaluate_config import evaluate_and_patch_config
locals().update(evaluate_and_patch_config(locals()))

View File

@ -123,9 +123,9 @@ def upgrade():
if version < 11:
# get metadata from current connection
meta = sa.MetaData(bind=op.get_bind())
meta = sa.MetaData()
# define table representation
meta.reflect(only=('role',))
meta.reflect(op.get_bind(), only=('role',))
role_table = sa.Table('role', meta)
op.execute(
@ -166,9 +166,9 @@ def upgrade():
'value': security_password_salt}])
# get metadata from current connection
meta = sa.MetaData(bind=op.get_bind())
meta = sa.MetaData()
# define table representation
meta.reflect(only=('version',))
meta.reflect(op.get_bind(), only=('version',))
version_table = sa.Table('version', meta)
op.execute(

View File

@ -35,9 +35,9 @@ def upgrade():
['username', 'auth_source'])
# For internal email is a user name, so update the existing records.
meta = sa.MetaData(bind=op.get_bind())
meta = sa.MetaData()
# define table representation
meta.reflect(only=('user',))
meta.reflect(op.get_bind(), only=('user',))
user_table = sa.Table('user', meta)
op.execute(

View File

@ -27,9 +27,9 @@ depends_on = None
def upgrade():
# get metadata from current connection
meta = sa.MetaData(bind=op.get_bind())
meta = sa.MetaData()
# define table representation
meta.reflect(only=('server',))
meta.reflect(op.get_bind(), only=('server',))
server_table = sa.Table('server', meta)
op.execute(
server_table.update().where(server_table.c.connect_timeout == 0 or

View File

@ -29,9 +29,9 @@ def upgrade():
op.add_column('user', sa.Column('fs_uniquifier', sa.String(),
nullable=True))
meta = sa.MetaData(bind=op.get_bind())
meta = sa.MetaData()
# define table representation
meta.reflect(only=('user',))
meta.reflect(op.get_bind(), only=('user',))
user_table = sa.Table('user', meta)
op.execute(

View File

@ -30,9 +30,9 @@ def upgrade():
# If password is already exists for any existing server then change the
# save_password column to 1 (True) else set 0
# get metadata from current connection
meta = sa.MetaData(bind=op.get_bind())
meta = sa.MetaData()
# define table representation
meta.reflect(only=('server',))
meta.reflect(op.get_bind(), only=('server',))
server_table = sa.Table('server', meta)
op.execute(

View File

@ -50,18 +50,18 @@ def migrate_connection_params(table_name):
pass
# define table representation
meta = sa.MetaData(bind=op.get_bind())
meta.reflect(only=(table_name,))
meta = sa.MetaData()
meta.reflect(op.get_bind(), only=(table_name,))
server_table = sa.Table(table_name, meta)
# Create a select statement
stmt = sa.select([
stmt = sa.select(
server_table.columns.id, server_table.columns.ssl_mode,
server_table.columns.sslcert, server_table.columns.sslkey,
server_table.columns.sslrootcert, server_table.columns.sslcrl,
server_table.columns.sslcompression, server_table.columns.hostaddr,
server_table.columns.passfile, server_table.columns.connect_timeout
])
)
# Fetch the data from the server table
results = op.get_bind().execute(stmt).fetchall()

View File

@ -114,7 +114,8 @@ def upgrade():
current_app.config['SECURITY_PASSWORD_SALT'] = current_salt
current_app.config['SECRET_KEY'] = secret_key
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
Security(current_app, user_datastore, register_blueprint=False)
Security(current_app.app_context().app, user_datastore,
register_blueprint=False)
else:
current_app.config['SECURITY_PASSWORD_SALT'] = current_salt
current_app.config['SECRET_KEY'] = secret_key

View File

@ -40,10 +40,6 @@ if (3, 10) > sys.version_info > (3, 8) and os.name == 'posix':
# This was causing issue in psycopg3
from eventlet import hubs
hubs.use_hub("poll")
# Ref: https://github.com/miguelgrinberg/python-socketio/issues/567
# Resolve BigAnimal API issue
import selectors
selectors.DefaultSelector = selectors.PollSelector
import config
import setup
@ -101,7 +97,6 @@ if not os.path.isfile(config.SQLITE_PATH):
# it can be imported
##########################################################################
app = create_app()
app.debug = False
app.config['sessions'] = dict()
if setup_db_required:
@ -163,22 +158,6 @@ def main():
setattr(sys, _name,
open(os.devnull, 'r' if _name == 'stdin' else 'w'))
# Build Javascript files when DEBUG
if config.DEBUG:
from pgadmin.utils.javascript.javascript_bundler import \
JavascriptBundler, JsState
app.debug = True
javascript_bundler = JavascriptBundler()
javascript_bundler.bundle()
if javascript_bundler.report() == JsState.NONE:
app.logger.error(
"Unable to generate javascript.\n"
"To run the app ensure that yarn install command runs "
"successfully"
)
raise RuntimeError("No generated javascript, aborting")
# Output a startup message if we're not under the runtime and startup.
# If we're under WSGI, we don't need to worry about this
if not app.PGADMIN_RUNTIME:
@ -213,19 +192,19 @@ def main():
app.run(
host=config.DEFAULT_SERVER,
port=config.EFFECTIVE_SERVER_PORT,
debug=config.DEBUG,
use_reloader=(
(not app.PGADMIN_RUNTIME) and app.debug and
(not app.PGADMIN_RUNTIME) and
os.environ.get("WERKZEUG_RUN_MAIN") is not None
),
threaded=config.THREADED_MODE
)
else:
# Can use cheroot instead of flask dev server when not in debug
# 10 is default thread count in CherootServer
# num_threads = 10 if config.THREADED_MODE else 1
try:
socketio.run(
app,
debug=config.DEBUG,
allow_unsafe_werkzeug=True,
host=config.DEFAULT_SERVER,
port=config.EFFECTIVE_SERVER_PORT,
)

View File

@ -299,9 +299,6 @@ def create_app(app_name=None):
# Initialise i18n
babel = Babel(app)
app.logger.debug('Available translations: %s' % babel.list_translations())
@babel.localeselector
def get_locale():
"""Get the language for the user."""
language = 'en'
@ -335,6 +332,7 @@ def create_app(app_name=None):
return language
babel.init_app(app, locale_selector=get_locale)
##########################################################################
# Setup authentication
##########################################################################
@ -456,21 +454,18 @@ def create_app(app_name=None):
# Run migration for the first time i.e. create database
# If version not available, user must have aborted. Tables are not
# created and so its an empty db
try:
if get_version() == -1:
db_upgrade(app)
else:
schema_version = get_version()
if get_version() == -1:
db_upgrade(app)
else:
schema_version = get_version()
# Run migration if current schema version is greater than
# the schema version stored in version table.
if CURRENT_SCHEMA_VERSION > schema_version:
db_upgrade(app)
# Update schema version to the latest
set_version(CURRENT_SCHEMA_VERSION)
db.session.commit()
except Exception as e:
app.logger.error(e)
# Run migration if current schema version is greater than
# the schema version stored in version table.
if CURRENT_SCHEMA_VERSION > schema_version:
db_upgrade(app)
# Update schema version to the latest
set_version(CURRENT_SCHEMA_VERSION)
db.session.commit()
# Run the migration as per specified by the user.
if config.CONFIG_DATABASE_URI is not None and \
@ -719,7 +714,9 @@ def create_app(app_name=None):
svr_superuser, svr_port, svr_discovery_id,
svr_comment)
except Exception:
except Exception as e:
print(str(e))
db.session.rollback()
pass
@user_logged_in.connect_via(app)

View File

@ -83,7 +83,7 @@ def login():
Entry point for all the authentication sources.
The user input will be validated and authenticated.
"""
form = _security.login_form()
form = _security.forms.get('login_form').cls(request.form)
if OAUTH2 in config.AUTHENTICATION_SOURCES \
and 'oauth2_button' in request.form:
# Sending empty form as oauth2 does not require form attribute
@ -173,7 +173,7 @@ def login():
if 'auth_obj' in session:
session.pop('auth_obj')
flash(msg, 'danger')
form_class = _security.login_form
form_class = _security.forms.get('login_form').cls
form = form_class()
return _security.render_template(

View File

@ -177,7 +177,7 @@ class KerberosAuthentication(BaseAuthentication):
negotiate = False
headers = Headers()
authorization = request.headers.get("Authorization", None)
form_class = _security.login_form
form_class = _security.forms.get('login_form').cls
req_json = request.get_json(silent=True)
if req_json:

View File

@ -7,13 +7,12 @@
#
##########################################################################
from config import PG_DEFAULT_DRIVER
from pgadmin.utils.route import BaseTestGenerator
from regression.python_test_utils import test_utils as utils
from . import utils as servers_utils
from unittest.mock import patch, MagicMock
import json
from PG_DEFAULT_DRIVER import OperationalError
from psycopg import OperationalError
class ServersSSHConnectTestCase(BaseTestGenerator):

View File

@ -13,7 +13,7 @@ from abc import abstractmethod
import flask
from flask import render_template, current_app
from flask.views import View, MethodViewType
from flask.views import View, MethodView
from flask_babel import gettext
from config import PG_DEFAULT_DRIVER
@ -140,7 +140,7 @@ class PGChildModule():
pass
class NodeView(View, metaclass=MethodViewType):
class NodeView(View, metaclass=type(MethodView)):
"""
A PostgreSQL Object has so many operaions/functions apart from CRUD
(Create, Read, Update, Delete):

View File

@ -0,0 +1,98 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2023, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import os
import sys
# User configs loaded from config_local, config_distro etc.
custom_config_settings = {}
# Function to Extract settings from config_local, config_distro etc.
def get_variables_from_module(module_name):
module = globals().get(module_name, None)
variables = {}
if module:
variables = {key: value for key, value in module.__dict__.items()
if not (key.startswith('__') or key.startswith('_'))}
return variables
# Load distribution-specific config overrides
try:
import config_distro
config_distro_settings = get_variables_from_module('config_distro')
custom_config_settings.update(config_distro_settings)
except ImportError:
pass
# Load local config overrides
try:
import config_local
config_local_settings = get_variables_from_module('config_local')
custom_config_settings.update(config_local_settings)
except ImportError:
pass
# Load system config overrides. We do this last, so that the sysadmin can
# override anything they want from a config file that's in a protected system
# directory and away from pgAdmin to avoid invalidating signatures.
system_config_dir = '/etc/pgadmin'
if sys.platform.startswith('win32'):
system_config_dir = os.environ['CommonProgramFiles'] + '/pgadmin'
elif sys.platform.startswith('darwin'):
system_config_dir = '/Library/Preferences/pgadmin'
if os.path.exists(system_config_dir + '/config_system.py'):
try:
sys.path.insert(0, system_config_dir)
import config_system
config_system_settings = get_variables_from_module('config_system')
custom_config_settings.update(config_system_settings)
except ImportError:
pass
def evaluate_and_patch_config(config: dict) -> dict:
# Update settings for 'LOG_FILE', 'SQLITE_PATH', 'SESSION_DB_PATH',
# 'AZURE_CREDENTIAL_CACHE_DIR', 'KERBEROS_CCACHE_DIR', 'STORAGE_DIR'
# of DATA_DIR is user defined
data_dir_dependent_settings = \
['LOG_FILE', 'SQLITE_PATH', 'SESSION_DB_PATH',
'AZURE_CREDENTIAL_CACHE_DIR', 'KERBEROS_CCACHE_DIR', 'STORAGE_DIR']
if 'DATA_DIR' in custom_config_settings:
for setting in data_dir_dependent_settings:
if setting not in custom_config_settings:
data_dir = custom_config_settings['DATA_DIR']
file_dir_name = os.path.basename(config.get(setting))
config.update(
{setting: os.path.join(data_dir, file_dir_name)})
# To use psycopg3 driver, need to specify +psycopg in conn URI
if 'CONFIG_DATABASE_URI' in custom_config_settings:
db_uri = custom_config_settings['CONFIG_DATABASE_URI']
if db_uri.startswith('postgresql:'):
custom_config_settings['CONFIG_DATABASE_URI'] = \
'postgresql+psycopg:{0}'.format(db_uri[db_uri.find(':') + 1:])
# Finally update config user configs
config.update(custom_config_settings)
# Override DEFAULT_SERVER value from environment variable.
if 'PGADMIN_CONFIG_DEFAULT_SERVER' in os.environ:
config['DEFAULT_SERVER'] = os.environ['PGADMIN_CONFIG_DEFAULT_SERVER']
# Disable USER_INACTIVITY_TIMEOUT when SERVER_MODE=False
if not config.get('SERVER_MODE'):
config['USER_INACTIVITY_TIMEOUT'] = 0
# Enable PSQL in Desktop Mode.
config['ENABLE_PSQL'] = True
return config

View File

@ -73,25 +73,6 @@ class PgAdminDbBinaryString(types.TypeDecorator):
return value
class PgAdminJSONString(types.TypeDecorator):
"""
This function is used to return a string representing a json object from
an object and vise versa.
"""
impl = types.String
def process_bind_param(self, value, dialect):
if value is not None:
value = json.dumps(value)
return value
def process_result_value(self, value, dialect):
if value is not None:
value = json.loads(value)
return value
class Version(db.Model):
"""Version numbers for reference/upgrade purposes"""
__tablename__ = 'version'
@ -218,7 +199,7 @@ class Server(db.Model):
shared = db.Column(db.Boolean(), nullable=False)
kerberos_conn = db.Column(db.Boolean(), nullable=False, default=0)
cloud_status = db.Column(db.Integer(), nullable=False, default=0)
connection_params = db.Column(MutableDict.as_mutable(PgAdminJSONString))
connection_params = db.Column(MutableDict.as_mutable(types.JSON))
@property
def serialize(self):
@ -458,7 +439,7 @@ class SharedServer(db.Model):
tunnel_identity_file = db.Column(db.String(64), nullable=True)
tunnel_password = db.Column(PgAdminDbBinaryString())
shared = db.Column(db.Boolean(), nullable=False)
connection_params = db.Column(MutableDict.as_mutable(PgAdminJSONString))
connection_params = db.Column(MutableDict.as_mutable(types.JSON))
class Macros(db.Model):

View File

@ -304,7 +304,7 @@ def panel(trans_id):
params['bgcolor'] = None
params['fgcolor'] = None
s = Server.query.filter_by(id=params['sid']).first()
s = Server.query.filter_by(id=int(params['sid'])).first()
if s.shared and s.user_id != current_user.id:
# Import here to avoid circular dependency
from pgadmin.browser.server_groups.servers import ServerModule

View File

@ -14,7 +14,7 @@ from flask import render_template, request, \
Response, abort, current_app, session
from flask_babel import gettext as _
from flask_security import login_required, roles_required, current_user
from flask_security.utils import encrypt_password
from flask_security.utils import hash_password
from werkzeug.exceptions import InternalServerError
import config
@ -438,7 +438,7 @@ def validate_password(data, new_data):
'confirmPassword' in data and data['confirmPassword'] != ""):
if data['newPassword'] == data['confirmPassword']:
new_data['password'] = encrypt_password(data['newPassword'])
new_data['password'] = hash_password(data['newPassword'])
else:
raise InternalServerError(_("Passwords do not match."))

View File

@ -279,7 +279,7 @@ class ManagedSessionInterface(SessionInterface):
self.manager = manager
def open_session(self, app, request):
cookie_val = request.cookies.get(app.session_cookie_name)
cookie_val = request.cookies.get(app.config['SESSION_COOKIE_NAME'])
if not cookie_val or '!' not in cookie_val:
return self.manager.new_session()
@ -296,7 +296,8 @@ class ManagedSessionInterface(SessionInterface):
if not session:
self.manager.remove(session.sid)
if session.modified:
response.delete_cookie(app.session_cookie_name, domain=domain)
response.delete_cookie(app.config['SESSION_COOKIE_NAME'],
domain=domain)
return
if not session.modified:
@ -310,7 +311,7 @@ class ManagedSessionInterface(SessionInterface):
cookie_exp = self.get_expiration_time(app, session)
response.set_cookie(
app.session_cookie_name,
app.config['SESSION_COOKIE_NAME'],
'%s!%s' % (session.sid, session.hmac_digest),
expires=cookie_exp,
secure=config.SESSION_COOKIE_SECURE,