1) Added support for setting PostgreSQL connection parameters. #4728
2) Fixed an issue where Kerberos authentication to the server is not imported/exported. #5732 3) Increase the length of the value column of the setting table. #5746 4) Upgrade Flask-Migrate to 4.0.0. #5525
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 57 KiB |
BIN
docs/en_US/images/server_parameters.png
Normal file
After Width: | Height: | Size: 87 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 48 KiB |
@ -15,10 +15,12 @@ Supported Database Servers
|
||||
New features
|
||||
************
|
||||
|
||||
| `Issue #4728 <https://github.com/pgadmin-org/pgadmin4/issues/4728>`_ - Added support for setting PostgreSQL connection parameters.
|
||||
|
||||
Housekeeping
|
||||
************
|
||||
|
||||
| `Issue #5525 <https://github.com/pgadmin-org/pgadmin4/issues/5525>`_ - Upgrade Flask-Migrate to 4.x.
|
||||
| `Issue #5723 <https://github.com/pgadmin-org/pgadmin4/issues/5723>`_ - Improve performance by removing signal-based zoom-in, zoom-out, etc functionality from the runtime environment.
|
||||
|
||||
Bug fixes
|
||||
@ -26,4 +28,6 @@ Bug fixes
|
||||
|
||||
| `Issue #5567 <https://github.com/pgadmin-org/pgadmin4/issues/5567>`_ - Fix orphan database connections resulting in an inability to connect to databases.
|
||||
| `Issue #5705 <https://github.com/pgadmin-org/pgadmin4/issues/5705>`_ - Ensure that all parts of the application recommend and enforce the same length of passwords.
|
||||
| `Issue #5732 <https://github.com/pgadmin-org/pgadmin4/issues/5732>`_ - Fixed an issue where Kerberos authentication to the server is not imported/exported.
|
||||
| `Issue #5751 <https://github.com/pgadmin-org/pgadmin4/issues/5751>`_ - Fix failing import servers CLI due to vulnerability fix.
|
||||
| `Issue #5746 <https://github.com/pgadmin-org/pgadmin4/issues/5746>`_ - Increase the length of the value column of the setting table.
|
||||
|
@ -82,17 +82,30 @@ Use the fields in the *Connection* tab to configure a connection:
|
||||
see
|
||||
`Section 33.16 of the Postgres documentation <https://www.postgresql.org/docs/current/libpq-pgservice.html>`_.
|
||||
|
||||
Click the *SSL* tab to continue.
|
||||
Click the *Parameters* tab to continue.
|
||||
|
||||
.. image:: images/server_ssl.png
|
||||
.. image:: images/server_parameters.png
|
||||
:alt: Server dialog ssl tab
|
||||
:align: center
|
||||
|
||||
Use the fields in the *SSL* tab to configure SSL:
|
||||
Use the fields in the *Parameters* tab to configure a connection:
|
||||
|
||||
* Use the drop-down list box in the *SSL* field to select the type of SSL
|
||||
connection the server should use. For more information about using SSL
|
||||
encryption, see
|
||||
Click on the *+* button to add a new parameter. Some of the parameters are:
|
||||
|
||||
* *Host address* using this field to specify the host IP address may save time
|
||||
by avoiding a DNS lookup on connection, but it may be useful to specify both
|
||||
a host name and address when using Kerberos, GSSAPI, or SSPI authentication
|
||||
methods, as well as for verify-full SSL certificate verification.
|
||||
* *Password File* field to specify the location of a password file
|
||||
(.pgpass). A .pgpass file allows a user to login without providing a password
|
||||
when they connect. For more information, see
|
||||
`Section 33.15 of the Postgres documentation <https://www.postgresql.org/docs/current/libpq-pgpass.html>`_.
|
||||
* *Connection timeout* field to specify the maximum wait for connection,
|
||||
in seconds. Zero or not specified means wait indefinitely. It is not
|
||||
recommended to use a timeout of less than 2 seconds. By default it is set to
|
||||
10 seconds.
|
||||
* *SSL mode* field to select the type of SSL connection the server should use.
|
||||
For more information about using SSL encryption, see
|
||||
`Section 33.18 of the Postgres documentation <https://www.postgresql.org/docs/current/libpq-ssl.html>`_.
|
||||
|
||||
If pgAdmin is installed in Server mode (the default mode), you can use the
|
||||
@ -100,27 +113,27 @@ platform-specific File manager dialog to upload files that support SSL
|
||||
encryption to the server. To access the File manager dialog, click the
|
||||
icon that is located to the right of each of the following fields.
|
||||
|
||||
* Use the *Client certificate* field to specify the file containing the client
|
||||
* *Client certificate* field to specify the file containing the client
|
||||
SSL certificate. This file will replace the default
|
||||
*~/.postgresql/postgresql.crt* if pgAdmin is installed in Desktop mode, and
|
||||
*<STORAGE_DIR>/<USERNAME>/.postgresql/postgresql.crt* if pgAdmin is installed
|
||||
in Web mode. This parameter is ignored if an SSL connection is not made.
|
||||
* Use the *Client certificate key* field to specify the file containing the
|
||||
* *Client certificate key* field to specify the file containing the
|
||||
secret key used for the client certificate. This file will replace the
|
||||
default *~/.postgresql/postgresql.key* if pgAdmin is installed in Desktop
|
||||
mode, and *<STORAGE_DIR>/<USERNAME>/.postgresql/postgresql.key* if pgAdmin
|
||||
is installed in Web mode. This parameter is ignored if an SSL connection is
|
||||
not made.
|
||||
* Use the *Root certificate* field to specify the file containing the SSL
|
||||
* *Root certificate* field to specify the file containing the SSL
|
||||
certificate authority. This file will replace the default
|
||||
*~/.postgresql/root.crt*. This parameter is ignored if an SSL connection is
|
||||
not made.
|
||||
* Use the *Certificate revocation list* field to specify the file containing
|
||||
* *Certificate revocation list* field to specify the file containing
|
||||
the SSL certificate revocation list. This list will replace the default list,
|
||||
found in *~/.postgresql/root.crl*. This parameter is ignored if an SSL
|
||||
connection is not made.
|
||||
* When *SSL compression?* is set to *True*, data sent over SSL connections will
|
||||
be compressed. The default value is *False* (compression is disabled). This
|
||||
* *SSL compression?* is set to *True*, data sent over SSL connections will
|
||||
be compressed. The default value is *False* (compression is disabled). This
|
||||
parameter is ignored if an SSL connection is not made.
|
||||
|
||||
.. warning:: In Server mode, certificates, private keys, and the revocation list
|
||||
@ -175,20 +188,11 @@ Click the *Advanced* tab to continue.
|
||||
|
||||
Use the fields in the *Advanced* tab to configure a connection:
|
||||
|
||||
* Specify the IP address of the server host in the *Host address* field. Using
|
||||
this field to specify the host IP address may save time by avoiding a DNS
|
||||
lookup on connection, but it may be useful to specify both a host name and
|
||||
address when using Kerberos, GSSAPI, or SSPI authentication methods, as well
|
||||
as for verify-full SSL certificate verification.
|
||||
* Use the *DB restriction* field to provide a SQL restriction that will be used
|
||||
against the pg_database table to limit the databases that you see. For
|
||||
example, you might enter: *live_db test_db* so that only live_db and test_db
|
||||
are shown in the pgAdmin browser. Separate entries with a comma or tab as you
|
||||
type.
|
||||
* Use the *Password File* field to specify the location of a password file
|
||||
(.pgpass). A .pgpass file allows a user to login without providing a password
|
||||
when they connect. For more information, see
|
||||
`Section 33.15 of the Postgres documentation <https://www.postgresql.org/docs/current/libpq-pgpass.html>`_.
|
||||
* Use the *Password exec command* field to specify a shell command to be executed
|
||||
to retrieve a password to be used for SQL authentication. The ``stdout`` of the
|
||||
command will be used as the SQL password. This may be useful when the password
|
||||
@ -199,10 +203,6 @@ Use the fields in the *Advanced* tab to configure a connection:
|
||||
the password will not expire until your pgAdmin session does.
|
||||
Zero means the command will be executed for each new connection or reconnection that is made.
|
||||
If the generated password is not valid indefinitely, set this value to slightly before it will expire.
|
||||
* Use the *Connection timeout* field to specify the maximum wait for connection,
|
||||
in seconds. Zero or not specified means wait indefinitely. It is not
|
||||
recommended to use a timeout of less than 2 seconds. By default it is set to
|
||||
10 seconds.
|
||||
|
||||
.. note:: The password file option is only supported when pgAdmin is using libpq
|
||||
v10.0 or later to connect to the server.
|
||||
|
@ -13,7 +13,7 @@ Flask==2.1.*; python_version >= '3.7'
|
||||
Flask-Gravatar==0.*
|
||||
Flask-Login==0.*
|
||||
Flask-Mail==0.*
|
||||
Flask-Migrate==3.*
|
||||
Flask-Migrate==4.*
|
||||
dnspython==2.2.1
|
||||
greenlet==1.1.2; python_version <= '3.10'
|
||||
Flask-SQLAlchemy==2.5.*
|
||||
|
99
web/migrations/versions/f656e56dfdc8_.py
Normal file
@ -0,0 +1,99 @@
|
||||
##########################################################################
|
||||
#
|
||||
# pgAdmin 4 - PostgreSQL Tools
|
||||
#
|
||||
# Copyright (C) 2013 - 2023, The pgAdmin Development Team
|
||||
# This software is released under the PostgreSQL Licence
|
||||
#
|
||||
##########################################################################
|
||||
""" Used for connection parameter changes in the server table.
|
||||
|
||||
Revision ID: f656e56dfdc8
|
||||
Revises: f79844e926ae
|
||||
Create Date: 2023-01-02 14:52:48.109290
|
||||
|
||||
"""
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'f656e56dfdc8'
|
||||
down_revision = 'f79844e926ae'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def migrate_connection_params(table_name):
|
||||
"""
|
||||
This function is used to add connection parameter as JSON data and drop
|
||||
unused columns.
|
||||
"""
|
||||
op.add_column(table_name,
|
||||
sa.Column('connection_params', sa.JSON()))
|
||||
|
||||
# define table representation
|
||||
meta = sa.MetaData(bind=op.get_bind())
|
||||
meta.reflect(only=(table_name,))
|
||||
server_table = sa.Table(table_name, meta)
|
||||
|
||||
# Create a select statement
|
||||
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()
|
||||
for rows in results:
|
||||
connection_params = {}
|
||||
server_id = 0
|
||||
for key, value in rows.items():
|
||||
if key == 'id':
|
||||
server_id = value
|
||||
# Name is changed from ssl_mode to sslmode
|
||||
if key == 'ssl_mode':
|
||||
key = 'sslmode'
|
||||
|
||||
if value is not None and key != 'id':
|
||||
connection_params[key] = value
|
||||
|
||||
# Update the newly added column with JSON data.
|
||||
op.execute(
|
||||
server_table.update().where(server_table.columns.id == server_id)
|
||||
.values(connection_params=connection_params)
|
||||
)
|
||||
|
||||
# Drop unused columns
|
||||
with op.batch_alter_table(table_name) as batch_op:
|
||||
if table_name == 'server':
|
||||
batch_op.drop_constraint('ck_ssl_mode')
|
||||
batch_op.drop_column('ssl_mode')
|
||||
batch_op.drop_column('sslcert')
|
||||
batch_op.drop_column('sslkey')
|
||||
batch_op.drop_column('sslrootcert')
|
||||
batch_op.drop_column('sslcrl')
|
||||
batch_op.drop_column('sslcompression')
|
||||
batch_op.drop_column('hostaddr')
|
||||
batch_op.drop_column('passfile')
|
||||
batch_op.drop_column('connect_timeout')
|
||||
|
||||
|
||||
def upgrade():
|
||||
migrate_connection_params('server')
|
||||
migrate_connection_params('sharedserver')
|
||||
|
||||
# Increasing the length of the value column of the setting table.
|
||||
with op.batch_alter_table("setting") as batch_op:
|
||||
batch_op.alter_column('value',
|
||||
existing_type=sa.String(length=1024),
|
||||
type_=sa.String(length=2048),
|
||||
existing_nullable=False)
|
||||
|
||||
|
||||
def downgrade():
|
||||
# pgAdmin only upgrades, downgrade not implemented.
|
||||
pass
|
@ -608,7 +608,8 @@ def create_app(app_name=None):
|
||||
port=port,
|
||||
maintenance_db='postgres',
|
||||
username=superuser,
|
||||
ssl_mode='prefer',
|
||||
connection_params={'sslmode': 'prefer',
|
||||
'connect_timeout': 10},
|
||||
comment=comment,
|
||||
discovery_id=discovery_id)
|
||||
|
||||
|
@ -157,9 +157,19 @@ class ServerModule(sg.ServerGroupPluginModule):
|
||||
server.tunnel_username = sharedserver.tunnel_username
|
||||
server.tunnel_password = sharedserver.tunnel_password
|
||||
server.save_password = sharedserver.save_password
|
||||
server.passfile = sharedserver.passfile
|
||||
if hasattr(server, 'connection_params') and \
|
||||
hasattr(sharedserver, 'connection_params') and \
|
||||
'passfile' in server.connection_params and \
|
||||
'passfile' in sharedserver.connection_params:
|
||||
server.connection_params['passfile'] = \
|
||||
sharedserver.connection_params['passfile']
|
||||
server.servergroup_id = sharedserver.servergroup_id
|
||||
server.sslcert = sharedserver.sslcert
|
||||
if hasattr(server, 'connection_params') and \
|
||||
hasattr(sharedserver, 'connection_params') and \
|
||||
'sslcert' in server.connection_params and \
|
||||
'sslcert' in sharedserver.connection_params:
|
||||
server.connection_params['sslcert'] = \
|
||||
sharedserver.connection_params['sslcert']
|
||||
server.username = sharedserver.username
|
||||
server.server_owner = sharedserver.server_owner
|
||||
server.password = sharedserver.password
|
||||
@ -346,29 +356,23 @@ class ServerModule(sg.ServerGroupPluginModule):
|
||||
servergroup_id=gid,
|
||||
name=data.name,
|
||||
host=data.host,
|
||||
hostaddr=data.hostaddr,
|
||||
port=data.port,
|
||||
maintenance_db=data.maintenance_db,
|
||||
username=None,
|
||||
save_password=0,
|
||||
ssl_mode=data.ssl_mode,
|
||||
comment=None,
|
||||
role=data.role,
|
||||
sslcert=None,
|
||||
sslkey=None,
|
||||
sslrootcert=None,
|
||||
sslcrl=None,
|
||||
bgcolor=data.bgcolor if data.bgcolor else None,
|
||||
fgcolor=data.fgcolor if data.fgcolor else None,
|
||||
service=data.service if data.service else None,
|
||||
connect_timeout=0,
|
||||
use_ssh_tunnel=data.use_ssh_tunnel,
|
||||
tunnel_host=data.tunnel_host,
|
||||
tunnel_port=22,
|
||||
tunnel_username=None,
|
||||
tunnel_authentication=0,
|
||||
tunnel_identity_file=None,
|
||||
shared=True
|
||||
shared=True,
|
||||
connection_params=data.connection_params
|
||||
)
|
||||
db.session.add(shared_server)
|
||||
db.session.commit()
|
||||
@ -486,11 +490,53 @@ class ServerNode(PGChildNodeView):
|
||||
)
|
||||
data[field] = dummy_ssl_file
|
||||
# For Desktop mode, we will allow to default
|
||||
else:
|
||||
data[field] = None
|
||||
|
||||
return flag, data
|
||||
|
||||
def convert_connection_parameter(self, params):
|
||||
"""
|
||||
This function is used to convert the connection parameter based
|
||||
on the instance type.
|
||||
"""
|
||||
conn_params = None
|
||||
# if params is of type list then it is coming from the frontend,
|
||||
# and we have to convert it into the dict and store it into the
|
||||
# database
|
||||
if isinstance(params, list):
|
||||
conn_params = {}
|
||||
for item in params:
|
||||
conn_params[item['name']] = item['value']
|
||||
# if params is of type dict then it is coming from the database,
|
||||
# and we have to convert it into the list of params to show on GUI.
|
||||
elif isinstance(params, dict):
|
||||
conn_params = []
|
||||
for key, value in params.items():
|
||||
if value is not None:
|
||||
conn_params.append(
|
||||
{'name': key, 'keyword': key, 'value': value})
|
||||
|
||||
return conn_params
|
||||
|
||||
def update_connection_parameter(self, data, server):
|
||||
"""
|
||||
This function is used to update the connection parameters.
|
||||
"""
|
||||
if 'connection_params' in data and \
|
||||
hasattr(server, 'connection_params'):
|
||||
existing_conn_params = getattr(server, 'connection_params')
|
||||
new_conn_params = data['connection_params']
|
||||
if 'deleted' in new_conn_params:
|
||||
for item in new_conn_params['deleted']:
|
||||
del existing_conn_params[item['name']]
|
||||
if 'added' in new_conn_params:
|
||||
for item in new_conn_params['added']:
|
||||
existing_conn_params[item['name']] = item['value']
|
||||
if 'changed' in new_conn_params:
|
||||
for item in new_conn_params['changed']:
|
||||
existing_conn_params[item['name']] = item['value']
|
||||
|
||||
data['connection_params'] = existing_conn_params
|
||||
|
||||
@login_required
|
||||
def nodes(self, gid):
|
||||
res = []
|
||||
@ -701,27 +747,18 @@ class ServerNode(PGChildNodeView):
|
||||
config_param_map = {
|
||||
'name': 'name',
|
||||
'host': 'host',
|
||||
'hostaddr': 'hostaddr',
|
||||
'port': 'port',
|
||||
'db': 'maintenance_db',
|
||||
'username': 'username',
|
||||
'sslmode': 'ssl_mode',
|
||||
'gid': 'servergroup_id',
|
||||
'comment': 'comment',
|
||||
'role': 'role',
|
||||
'db_res': 'db_res',
|
||||
'passfile': 'passfile',
|
||||
'passexec_cmd': 'passexec_cmd',
|
||||
'passexec_expiration': 'passexec_expiration',
|
||||
'sslcert': 'sslcert',
|
||||
'sslkey': 'sslkey',
|
||||
'sslrootcert': 'sslrootcert',
|
||||
'sslcrl': 'sslcrl',
|
||||
'sslcompression': 'sslcompression',
|
||||
'bgcolor': 'bgcolor',
|
||||
'fgcolor': 'fgcolor',
|
||||
'service': 'service',
|
||||
'connect_timeout': 'connect_timeout',
|
||||
'use_ssh_tunnel': 'use_ssh_tunnel',
|
||||
'tunnel_host': 'tunnel_host',
|
||||
'tunnel_port': 'tunnel_port',
|
||||
@ -730,15 +767,14 @@ class ServerNode(PGChildNodeView):
|
||||
'tunnel_identity_file': 'tunnel_identity_file',
|
||||
'shared': 'shared',
|
||||
'kerberos_conn': 'kerberos_conn',
|
||||
'connection_params': 'connection_params'
|
||||
}
|
||||
|
||||
disp_lbl = {
|
||||
'name': gettext('name'),
|
||||
'hostaddr': gettext('Host name/address'),
|
||||
'port': gettext('Port'),
|
||||
'db': gettext('Maintenance database'),
|
||||
'username': gettext('Username'),
|
||||
'sslmode': gettext('SSL Mode'),
|
||||
'comment': gettext('Comments'),
|
||||
'role': gettext('Role')
|
||||
}
|
||||
@ -750,12 +786,16 @@ class ServerNode(PGChildNodeView):
|
||||
if 'db_res' in data:
|
||||
data['db_res'] = ','.join(data['db_res'])
|
||||
|
||||
hostaddr = data.get('hostaddr')
|
||||
if hostaddr and not is_valid_ipaddress(hostaddr):
|
||||
# Update connection parameter if any.
|
||||
self.update_connection_parameter(data, server)
|
||||
|
||||
if 'connection_params' in data and \
|
||||
'hostaddr' in data['connection_params'] and \
|
||||
not is_valid_ipaddress(data['connection_params']['hostaddr']):
|
||||
return make_json_response(
|
||||
success=0,
|
||||
status=400,
|
||||
errormsg=gettext('Host address not valid')
|
||||
errormsg=gettext('Not a valid Host address')
|
||||
)
|
||||
|
||||
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
|
||||
@ -849,8 +889,7 @@ class ServerNode(PGChildNodeView):
|
||||
|
||||
if connected:
|
||||
for arg in (
|
||||
'hostaddr', 'db', 'sslmode',
|
||||
'role', 'service'
|
||||
'db', 'role', 'service'
|
||||
):
|
||||
if arg in data:
|
||||
return forbidden(
|
||||
@ -911,10 +950,6 @@ class ServerNode(PGChildNodeView):
|
||||
def properties(self, gid, sid):
|
||||
"""Return list of attributes of a server"""
|
||||
|
||||
sslcert = None
|
||||
sslkey = None
|
||||
sslrootcert = None
|
||||
sslcrl = None
|
||||
server = Server.query.filter_by(
|
||||
id=sid).first()
|
||||
|
||||
@ -941,19 +976,13 @@ class ServerNode(PGChildNodeView):
|
||||
shared_server)
|
||||
server_owner = server.server_owner
|
||||
|
||||
is_ssl = True if server.ssl_mode in self.SSL_MODES else False
|
||||
|
||||
if is_ssl:
|
||||
sslcert = server.sslcert
|
||||
sslkey = server.sslkey
|
||||
sslrootcert = server.sslrootcert
|
||||
sslcrl = server.sslcrl
|
||||
|
||||
use_ssh_tunnel = 0
|
||||
tunnel_host = None
|
||||
tunnel_port = 22
|
||||
tunnel_username = None
|
||||
tunnel_authentication = 0
|
||||
connection_params = \
|
||||
self.convert_connection_parameter(server.connection_params)
|
||||
|
||||
if server.use_ssh_tunnel:
|
||||
use_ssh_tunnel = server.use_ssh_tunnel
|
||||
@ -968,7 +997,6 @@ class ServerNode(PGChildNodeView):
|
||||
'server_owner': server_owner,
|
||||
'user_id': server.user_id,
|
||||
'host': server.host,
|
||||
'hostaddr': server.hostaddr,
|
||||
'port': server.port,
|
||||
'db': server.maintenance_db,
|
||||
'shared': server.shared if config.SERVER_MODE else None,
|
||||
@ -979,26 +1007,16 @@ class ServerNode(PGChildNodeView):
|
||||
'role': server.role,
|
||||
'connected': connected,
|
||||
'version': manager.ver,
|
||||
'sslmode': server.ssl_mode,
|
||||
'server_type': manager.server_type if connected else 'pg',
|
||||
'bgcolor': server.bgcolor,
|
||||
'fgcolor': server.fgcolor,
|
||||
'db_res': server.db_res.split(',') if server.db_res else None,
|
||||
'passfile': server.passfile if server.passfile else None,
|
||||
'passexec_cmd':
|
||||
server.passexec_cmd if server.passexec_cmd else None,
|
||||
'passexec_expiration':
|
||||
server.passexec_expiration if server.passexec_expiration
|
||||
else None,
|
||||
'sslcert': sslcert,
|
||||
'sslkey': sslkey,
|
||||
'sslrootcert': sslrootcert,
|
||||
'sslcrl': sslcrl,
|
||||
'sslcompression': True if is_ssl and server.sslcompression
|
||||
else False,
|
||||
'service': server.service if server.service else None,
|
||||
'connect_timeout':
|
||||
server.connect_timeout if server.connect_timeout else 0,
|
||||
'use_ssh_tunnel': use_ssh_tunnel,
|
||||
'tunnel_host': tunnel_host,
|
||||
'tunnel_port': tunnel_port,
|
||||
@ -1009,7 +1027,9 @@ class ServerNode(PGChildNodeView):
|
||||
'kerberos_conn': bool(server.kerberos_conn),
|
||||
'gss_authenticated': manager.gss_authenticated,
|
||||
'gss_encrypted': manager.gss_encrypted,
|
||||
'cloud_status': server.cloud_status
|
||||
'cloud_status': server.cloud_status,
|
||||
'connection_params': connection_params,
|
||||
'connection_string': manager.connection_string
|
||||
}
|
||||
|
||||
return ajax_response(response)
|
||||
@ -1017,11 +1037,7 @@ class ServerNode(PGChildNodeView):
|
||||
@login_required
|
||||
def create(self, gid):
|
||||
"""Add a server node to the settings database"""
|
||||
required_args = [
|
||||
'name',
|
||||
'db',
|
||||
'sslmode',
|
||||
]
|
||||
required_args = ['name', 'db']
|
||||
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
@ -1057,8 +1073,11 @@ class ServerNode(PGChildNodeView):
|
||||
).format(arg)
|
||||
)
|
||||
|
||||
hostaddr = data.get('hostaddr')
|
||||
if hostaddr and not is_valid_ipaddress(data['hostaddr']):
|
||||
connection_params = self.convert_connection_parameter(
|
||||
data.get('connection_params', []))
|
||||
|
||||
if 'hostaddr' in connection_params and \
|
||||
not is_valid_ipaddress(connection_params['hostaddr']):
|
||||
return make_json_response(
|
||||
success=0,
|
||||
status=400,
|
||||
@ -1066,7 +1085,10 @@ class ServerNode(PGChildNodeView):
|
||||
)
|
||||
|
||||
# To check ssl configuration
|
||||
is_ssl, data = self.check_ssl_fields(data)
|
||||
is_ssl, connection_params = self.check_ssl_fields(connection_params)
|
||||
# set the connection params again in the data
|
||||
if 'connection_params' in data:
|
||||
data['connection_params'] = connection_params
|
||||
|
||||
server = None
|
||||
|
||||
@ -1076,26 +1098,18 @@ class ServerNode(PGChildNodeView):
|
||||
servergroup_id=data.get('gid', gid),
|
||||
name=data.get('name'),
|
||||
host=data.get('host', None),
|
||||
hostaddr=hostaddr,
|
||||
port=data.get('port'),
|
||||
maintenance_db=data.get('db', None),
|
||||
username=data.get('username'),
|
||||
save_password=1 if data.get('save_password', False) and
|
||||
config.ALLOW_SAVE_PASSWORD else 0,
|
||||
ssl_mode=data.get('sslmode'),
|
||||
comment=data.get('comment', None),
|
||||
role=data.get('role', None),
|
||||
db_res=','.join(data['db_res'])
|
||||
if 'db_res' in data else None,
|
||||
sslcert=data.get('sslcert', None),
|
||||
sslkey=data.get('sslkey', None),
|
||||
sslrootcert=data.get('sslrootcert', None),
|
||||
sslcrl=data.get('sslcrl', None),
|
||||
sslcompression=1 if is_ssl and data['sslcompression'] else 0,
|
||||
bgcolor=data.get('bgcolor', None),
|
||||
fgcolor=data.get('fgcolor', None),
|
||||
service=data.get('service', None),
|
||||
connect_timeout=data.get('connect_timeout', 0),
|
||||
use_ssh_tunnel=1 if data.get('use_ssh_tunnel', False) else 0,
|
||||
tunnel_host=data.get('tunnel_host', None),
|
||||
tunnel_port=data.get('tunnel_port', 22),
|
||||
@ -1104,10 +1118,10 @@ class ServerNode(PGChildNodeView):
|
||||
False) else 0,
|
||||
tunnel_identity_file=data.get('tunnel_identity_file', None),
|
||||
shared=data.get('shared', None),
|
||||
passfile=data.get('passfile', None),
|
||||
passexec_cmd=data.get('passexec_cmd', None),
|
||||
passexec_expiration=data.get('passexec_expiration', None),
|
||||
kerberos_conn=1 if data.get('kerberos_conn', False) else 0,
|
||||
connection_params=connection_params
|
||||
)
|
||||
db.session.add(server)
|
||||
db.session.commit()
|
||||
@ -1131,10 +1145,9 @@ class ServerNode(PGChildNodeView):
|
||||
have_password = True
|
||||
password = data['password']
|
||||
password = encrypt(password, crypt_key)
|
||||
elif 'passfile' in data and data["passfile"] != '':
|
||||
elif 'passfile' in data['connection_params'] and \
|
||||
data['connection_params']['passfile'] != '':
|
||||
passfile = data['passfile']
|
||||
setattr(server, 'passfile', passfile)
|
||||
db.session.commit()
|
||||
|
||||
if 'tunnel_password' in data and data["tunnel_password"] != '':
|
||||
have_tunnel_password = True
|
||||
@ -1391,14 +1404,20 @@ class ServerNode(PGChildNodeView):
|
||||
return internal_server_error(errormsg=str(e))
|
||||
if 'password' not in data and (server.kerberos_conn is False or
|
||||
server.kerberos_conn is None):
|
||||
|
||||
passfile_param = None
|
||||
if hasattr(server, 'connection_params') and \
|
||||
'passfile' in server.connection_params:
|
||||
passfile_param = server.connection_params['passfile']
|
||||
|
||||
conn_passwd = getattr(conn, 'password', None)
|
||||
if conn_passwd is None and not server.save_password and \
|
||||
server.passfile is None and \
|
||||
passfile_param is None and \
|
||||
server.passexec_cmd is None and \
|
||||
server.service is None:
|
||||
prompt_password = True
|
||||
elif server.passfile and server.passfile != '':
|
||||
passfile = server.passfile
|
||||
elif passfile_param and passfile_param != '':
|
||||
passfile = passfile_param
|
||||
else:
|
||||
password = conn_passwd or server.password
|
||||
else:
|
||||
@ -1657,8 +1676,11 @@ class ServerNode(PGChildNodeView):
|
||||
# If there is no password found for the server
|
||||
# then check for pgpass file
|
||||
if not server.password and not manager.password and \
|
||||
server.passfile and manager.passfile and \
|
||||
server.passfile == manager.passfile:
|
||||
hasattr(server, 'connection_params') and \
|
||||
'passfile' in server.connection_params and \
|
||||
manager.get_connection_param_value('passfile') and \
|
||||
server.connection_params['passfile'] == \
|
||||
manager.get_connection_param_value('passfile'):
|
||||
is_passfile = True
|
||||
|
||||
# Check for password only if there is no pgpass file used
|
||||
@ -1859,8 +1881,11 @@ class ServerNode(PGChildNodeView):
|
||||
)
|
||||
|
||||
if (not server.password or not manager.password) and \
|
||||
server.passfile and manager.passfile and \
|
||||
server.passfile == manager.passfile:
|
||||
hasattr(server, 'connection_params') and \
|
||||
'passfile' in server.connection_params and \
|
||||
manager.get_connection_param_value('passfile') and \
|
||||
server.connection_params['passfile'] == \
|
||||
manager.get_connection_param_value('passfile'):
|
||||
is_pgpass = True
|
||||
return make_json_response(
|
||||
success=1,
|
||||
|
@ -2252,9 +2252,10 @@ class MViewNode(ViewNode, VacuumSettings):
|
||||
manager.export_password_env(p.id)
|
||||
# Check for connection timeout and if it is greater than 0
|
||||
# then set the environment variable PGCONNECT_TIMEOUT.
|
||||
if manager.connect_timeout > 0:
|
||||
timeout = manager.get_connection_param_value('connect_timeout')
|
||||
if timeout and timeout > 0:
|
||||
env = dict()
|
||||
env['PGCONNECT_TIMEOUT'] = str(manager.connect_timeout)
|
||||
env['PGCONNECT_TIMEOUT'] = str(timeout)
|
||||
p.set_env_variables(server, env=env)
|
||||
else:
|
||||
p.set_env_variables(server)
|
||||
|
@ -29,6 +29,7 @@ define('pgadmin.node.server', [
|
||||
type: 'server',
|
||||
dialogHelp: url_for('help.static', {'filename': 'server_dialog.html'}),
|
||||
label: gettext('Server'),
|
||||
width: pgBrowser.stdW.md + 'px',
|
||||
canDrop: function(node){
|
||||
let serverOwner = node.user_id;
|
||||
return !(serverOwner != current_user.id && !_.isUndefined(serverOwner));
|
||||
|
@ -9,15 +9,12 @@
|
||||
|
||||
import gettext from 'sources/gettext';
|
||||
import _ from 'lodash';
|
||||
import {Address4, Address6} from 'ip-address';
|
||||
|
||||
|
||||
import BaseUISchema from 'sources/SchemaView/base_schema.ui';
|
||||
import pgAdmin from 'sources/pgadmin';
|
||||
import {default as supportedServers} from 'pgadmin.server.supported_servers';
|
||||
|
||||
import current_user from 'pgadmin.user_management.current_user';
|
||||
import { isEmptyString } from 'sources/validators';
|
||||
import VariableSchema from './variable.ui';
|
||||
|
||||
export default class ServerSchema extends BaseUISchema {
|
||||
constructor(serverGroupOptions=[], userId=0, initValues={}) {
|
||||
@ -27,9 +24,7 @@ export default class ServerSchema extends BaseUISchema {
|
||||
name: '',
|
||||
bgcolor: '',
|
||||
fgcolor: '',
|
||||
sslmode: 'prefer',
|
||||
host: '',
|
||||
hostaddr: '',
|
||||
port: 5432,
|
||||
db: 'postgres',
|
||||
username: current_user.name,
|
||||
@ -38,14 +33,8 @@ export default class ServerSchema extends BaseUISchema {
|
||||
password: undefined,
|
||||
save_password: false,
|
||||
db_res: [],
|
||||
passfile: undefined,
|
||||
passexec: undefined,
|
||||
passexec_expiration: undefined,
|
||||
sslcompression: false,
|
||||
sslcert: undefined,
|
||||
sslkey: undefined,
|
||||
sslrootcert: undefined,
|
||||
sslcrl: undefined,
|
||||
service: undefined,
|
||||
use_ssh_tunnel: 0,
|
||||
tunnel_host: undefined,
|
||||
@ -55,16 +44,22 @@ export default class ServerSchema extends BaseUISchema {
|
||||
tunnel_password: undefined,
|
||||
tunnel_authentication: false,
|
||||
save_tunnel_password: false,
|
||||
connect_timeout: 10,
|
||||
connection_string: undefined,
|
||||
connection_params: [
|
||||
{'name': 'sslmode', 'value': 'prefer', 'keyword': 'sslmode'},
|
||||
{'name': 'connect_timeout', 'value': 10, 'keyword': 'connect_timeout'}],
|
||||
...initValues,
|
||||
});
|
||||
|
||||
this.serverGroupOptions = serverGroupOptions;
|
||||
this.paramSchema = new VariableSchema(this.getConnectionParameters(), null, null, ['name', 'keyword', 'value']);
|
||||
this.userId = userId;
|
||||
_.bindAll(this, 'isShared', 'isSSL');
|
||||
_.bindAll(this, 'isShared');
|
||||
}
|
||||
|
||||
get SSL_MODES() { return ['prefer', 'require', 'verify-ca', 'verify-full']; }
|
||||
initialise(state) {
|
||||
this.paramSchema.setAllReadOnly(this.isConnected(state));
|
||||
}
|
||||
|
||||
isShared(state) {
|
||||
return !this.isNew(state) && this.userId != current_user.id && state.shared;
|
||||
@ -74,16 +69,6 @@ export default class ServerSchema extends BaseUISchema {
|
||||
return Boolean(state.connected);
|
||||
}
|
||||
|
||||
isSSL(state) {
|
||||
return this.SSL_MODES.indexOf(state.sslmode) == -1;
|
||||
}
|
||||
|
||||
isValidLib() {
|
||||
// older version of libpq do not support 'passfile' parameter in
|
||||
// connect method, valid libpq must have version >= 100000
|
||||
return pgAdmin.Browser.utils.pg_libpq_version < 100000;
|
||||
}
|
||||
|
||||
get baseFields() {
|
||||
let obj = this;
|
||||
return [
|
||||
@ -148,8 +133,10 @@ export default class ServerSchema extends BaseUISchema {
|
||||
{
|
||||
id: 'comment', label: gettext('Comments'), type: 'multiline', group: null,
|
||||
mode: ['properties', 'edit', 'create'],
|
||||
},
|
||||
{
|
||||
}, {
|
||||
id: 'connection_string', label: gettext('Connection String'), type: 'multiline',
|
||||
group: gettext('Connection'), mode: ['properties'], readonly: true,
|
||||
}, {
|
||||
id: 'host', label: gettext('Host name/address'), type: 'text', group: gettext('Connection'),
|
||||
mode: ['properties', 'edit', 'create'], disabled: obj.isShared,
|
||||
depChange: (state)=>{
|
||||
@ -228,103 +215,13 @@ export default class ServerSchema extends BaseUISchema {
|
||||
id: 'service', label: gettext('Service'), type: 'text',
|
||||
mode: ['properties', 'edit', 'create'], readonly: obj.isConnected,
|
||||
group: gettext('Connection'),
|
||||
},
|
||||
{
|
||||
id: 'sslmode', label: gettext('SSL mode'), type: 'select', group: gettext('SSL'),
|
||||
controlProps: {
|
||||
allowClear: false,
|
||||
},
|
||||
mode: ['properties', 'edit', 'create'], disabled: obj.isConnected,
|
||||
options: [
|
||||
{label: gettext('Allow'), value: 'allow'},
|
||||
{label: gettext('Prefer'), value: 'prefer'},
|
||||
{label: gettext('Require'), value: 'require'},
|
||||
{label: gettext('Disable'), value: 'disable'},
|
||||
{label: gettext('Verify-CA'), value: 'verify-ca'},
|
||||
{label: gettext('Verify-Full'), value: 'verify-full'},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'sslcert', label: gettext('Client certificate'), type: 'file',
|
||||
group: gettext('SSL'), mode: ['edit', 'create'],
|
||||
disabled: obj.isSSL, readonly: obj.isConnected,
|
||||
controlProps: {
|
||||
dialogType: 'select_file', supportedTypes: ['*'],
|
||||
},
|
||||
deps: ['sslmode'],
|
||||
},
|
||||
{
|
||||
id: 'sslkey', label: gettext('Client certificate key'), type: 'file',
|
||||
group: gettext('SSL'), mode: ['edit', 'create'],
|
||||
disabled: obj.isSSL, readonly: obj.isConnected,
|
||||
controlProps: {
|
||||
dialogType: 'select_file', supportedTypes: ['*'],
|
||||
},
|
||||
deps: ['sslmode'],
|
||||
},{
|
||||
id: 'sslrootcert', label: gettext('Root certificate'), type: 'file',
|
||||
group: gettext('SSL'), mode: ['edit', 'create'],
|
||||
disabled: obj.isSSL, readonly: obj.isConnected,
|
||||
controlProps: {
|
||||
dialogType: 'select_file', supportedTypes: ['*'],
|
||||
},
|
||||
deps: ['sslmode'],
|
||||
},{
|
||||
id: 'sslcrl', label: gettext('Certificate revocation list'), type: 'file',
|
||||
group: gettext('SSL'), mode: ['edit', 'create'],
|
||||
disabled: obj.isSSL, readonly: obj.isConnected,
|
||||
controlProps: {
|
||||
dialogType: 'select_file', supportedTypes: ['*'],
|
||||
},
|
||||
deps: ['sslmode'],
|
||||
},
|
||||
{
|
||||
id: 'sslcompression', label: gettext('SSL compression?'), type: 'switch',
|
||||
mode: ['edit', 'create'], group: gettext('SSL'),
|
||||
disabled: obj.isSSL, readonly: obj.isConnected,
|
||||
deps: ['sslmode'],
|
||||
},
|
||||
{
|
||||
id: 'sslcert', label: gettext('Client certificate'), type: 'text',
|
||||
group: gettext('SSL'), mode: ['properties'],
|
||||
deps: ['sslmode'],
|
||||
visible: function(state) {
|
||||
let sslcert = state.sslcert;
|
||||
return !_.isUndefined(sslcert) && !_.isNull(sslcert);
|
||||
},
|
||||
},{
|
||||
id: 'sslkey', label: gettext('Client certificate key'), type: 'text',
|
||||
group: gettext('SSL'), mode: ['properties'],
|
||||
deps: ['sslmode'],
|
||||
visible: function(state) {
|
||||
let sslkey = state.sslkey;
|
||||
return !_.isUndefined(sslkey) && !_.isNull(sslkey);
|
||||
},
|
||||
},{
|
||||
id: 'sslrootcert', label: gettext('Root certificate'), type: 'text',
|
||||
group: gettext('SSL'), mode: ['properties'],
|
||||
deps: ['sslmode'],
|
||||
visible: function(state) {
|
||||
let sslrootcert = state.sslrootcert;
|
||||
return !_.isUndefined(sslrootcert) && !_.isNull(sslrootcert);
|
||||
},
|
||||
},{
|
||||
id: 'sslcrl', label: gettext('Certificate revocation list'), type: 'text',
|
||||
group: gettext('SSL'), mode: ['properties'],
|
||||
deps: ['sslmode'],
|
||||
visible: function(state) {
|
||||
let sslcrl = state.sslcrl;
|
||||
return !_.isUndefined(sslcrl) && !_.isNull(sslcrl);
|
||||
},
|
||||
},{
|
||||
id: 'sslcompression', label: gettext('SSL compression?'), type: 'switch',
|
||||
mode: ['properties'], group: gettext('SSL'),
|
||||
deps: ['sslmode'],
|
||||
visible: function(state) {
|
||||
return _.indexOf(obj.SSL_MODES, state.sslmode) != -1;
|
||||
},
|
||||
},
|
||||
{
|
||||
}, {
|
||||
id: 'connection_params', label: gettext('Connection Parameters'),
|
||||
type: 'collection', group: gettext('Parameters'),
|
||||
schema: this.paramSchema, mode: ['edit', 'create'], uniqueCol: ['name'],
|
||||
canAdd: (state)=> !obj.isConnected(state), canEdit: false,
|
||||
canDelete: (state)=> !obj.isConnected(state),
|
||||
}, {
|
||||
id: 'use_ssh_tunnel', label: gettext('Use SSH tunneling'), type: 'switch',
|
||||
mode: ['properties', 'edit', 'create'], group: gettext('SSH Tunnel'),
|
||||
disabled: function() {
|
||||
@ -401,9 +298,6 @@ export default class ServerSchema extends BaseUISchema {
|
||||
disabled: function(state) {
|
||||
return (!current_user.allow_save_tunnel_password || !state.use_ssh_tunnel);
|
||||
},
|
||||
}, {
|
||||
id: 'hostaddr', label: gettext('Host address'), type: 'text', group: gettext('Advanced'),
|
||||
mode: ['properties', 'edit', 'create'], readonly: obj.isConnected,
|
||||
},
|
||||
{
|
||||
id: 'db_res', label: gettext('DB restriction'), type: 'select', group: gettext('Advanced'),
|
||||
@ -411,22 +305,6 @@ export default class ServerSchema extends BaseUISchema {
|
||||
mode: ['properties', 'edit', 'create'], readonly: obj.isConnected, controlProps: {
|
||||
multiple: true, allowClear: false, creatable: true, noDropdown: true, placeholder: 'Specify the databases to be restrict...'},
|
||||
},
|
||||
{
|
||||
id: 'passfile', label: gettext('Password file'), type: 'file',
|
||||
group: gettext('Advanced'), mode: ['edit', 'create'],
|
||||
disabled: obj.isValidLib, readonly: obj.isConnected,
|
||||
controlProps: {
|
||||
dialogType: 'select_file', supportedTypes: ['*'],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'passfile', label: gettext('Password file'), type: 'text',
|
||||
group: gettext('Advanced'), mode: ['properties'],
|
||||
visible: function(state) {
|
||||
let passfile = state.passfile;
|
||||
return !_.isUndefined(passfile) && !_.isNull(passfile);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'passexec_cmd', label: gettext('Password exec command'), type: 'text',
|
||||
group: gettext('Advanced'),
|
||||
@ -439,12 +317,6 @@ export default class ServerSchema extends BaseUISchema {
|
||||
visible: function(state) {
|
||||
return !_.isEmpty(state.passexec_cmd);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'connect_timeout', label: gettext('Connection timeout (seconds)'),
|
||||
type: 'int', group: gettext('Advanced'),
|
||||
mode: ['properties', 'edit', 'create'], readonly: obj.isConnected,
|
||||
min: 0,
|
||||
}
|
||||
];
|
||||
}
|
||||
@ -453,30 +325,12 @@ export default class ServerSchema extends BaseUISchema {
|
||||
let errmsg = null;
|
||||
|
||||
if (isEmptyString(state.service)) {
|
||||
errmsg = gettext('Either Host name, Address or Service must be specified.');
|
||||
if(isEmptyString(state.host) && isEmptyString(state.hostaddr)) {
|
||||
errmsg = gettext('Either Host name or Service must be specified.');
|
||||
if(isEmptyString(state.host)) {
|
||||
setError('host', errmsg);
|
||||
return true;
|
||||
} else {
|
||||
setError('host', null);
|
||||
setError('hostaddr', null);
|
||||
}
|
||||
|
||||
/* IP address validate */
|
||||
if (state.hostaddr) {
|
||||
try {
|
||||
new Address4(state.hostaddr);
|
||||
} catch(e) {
|
||||
try {
|
||||
new Address6(state.hostaddr);
|
||||
} catch(ex) {
|
||||
errmsg = gettext('Host address must be valid IPv4 or IPv6 address.');
|
||||
setError('hostaddr', errmsg);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setError('hostaddr', null);
|
||||
}
|
||||
|
||||
/* Hostname, IP address validate */
|
||||
@ -507,7 +361,7 @@ export default class ServerSchema extends BaseUISchema {
|
||||
setError('port', null);
|
||||
}
|
||||
} else {
|
||||
_.each(['host', 'hostaddr', 'db', 'username', 'port'], (item) => {
|
||||
_.each(['host', 'db', 'username', 'port'], (item) => {
|
||||
setError(item, null);
|
||||
});
|
||||
}
|
||||
@ -549,4 +403,92 @@ export default class ServerSchema extends BaseUISchema {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
getConnectionParameters() {
|
||||
return [{
|
||||
'value': 'hostaddr', 'label': gettext('Host address'), 'vartype': 'string'
|
||||
}, {
|
||||
'value': 'passfile', 'label': gettext('Password file'), 'vartype': 'file'
|
||||
}, {
|
||||
'value': 'channel_binding', 'label': gettext('Channel binding'), 'vartype': 'enum',
|
||||
'enumvals': [gettext('prefer'), gettext('require'), gettext('disable')],
|
||||
'min_server_version': '13'
|
||||
}, {
|
||||
'value': 'connect_timeout', 'label': gettext('Connection timeout (seconds)'), 'vartype': 'integer'
|
||||
}, {
|
||||
'value': 'client_encoding', 'label': gettext('Client encoding'), 'vartype': 'string'
|
||||
}, {
|
||||
'value': 'options', 'label': gettext('Options'), 'vartype': 'string'
|
||||
}, {
|
||||
'value': 'application_name', 'label': gettext('Application name'), 'vartype': 'string'
|
||||
}, {
|
||||
'value': 'fallback_application_name', 'label': gettext('Fallback application name'), 'vartype': 'string'
|
||||
}, {
|
||||
'value': 'keepalives', 'label': gettext('Keepalives'), 'vartype': 'integer'
|
||||
}, {
|
||||
'value': 'keepalives_idle', 'label': gettext('Keepalives idle (seconds)'), 'vartype': 'integer'
|
||||
}, {
|
||||
'value': 'keepalives_interval', 'label': gettext('Keepalives interval (seconds)'), 'vartype': 'integer'
|
||||
}, {
|
||||
'value': 'keepalives_count', 'label': gettext('Keepalives count'), 'vartype': 'integer'
|
||||
}, {
|
||||
'value': 'tcp_user_timeout', 'label': gettext('TCP user timeout (milliseconds)'), 'vartype': 'integer',
|
||||
'min_server_version': '12'
|
||||
}, {
|
||||
'value': 'tty', 'label': gettext('TTY'), 'vartype': 'string',
|
||||
'max_server_version': '13'
|
||||
}, {
|
||||
'value': 'replication', 'label': gettext('Replication'), 'vartype': 'enum',
|
||||
'enumvals': [gettext('on'), gettext('off'), gettext('database')],
|
||||
'min_server_version': '11'
|
||||
}, {
|
||||
'value': 'gssencmode', 'label': gettext('GSS encmode'), 'vartype': 'enum',
|
||||
'enumvals': [gettext('prefer'), gettext('require'), gettext('disable')],
|
||||
'min_server_version': '12'
|
||||
}, {
|
||||
'value': 'sslmode', 'label': gettext('SSL mode'), 'vartype': 'enum',
|
||||
'enumvals': [gettext('allow'), gettext('prefer'), gettext('require'),
|
||||
gettext('disable'), gettext('verify-ca'), gettext('verify-full')]
|
||||
}, {
|
||||
'value': 'sslcompression', 'label': gettext('SSL compression?'), 'vartype': 'bool',
|
||||
}, {
|
||||
'value': 'sslcert', 'label': gettext('Client certificate'), 'vartype': 'file'
|
||||
}, {
|
||||
'value': 'sslkey', 'label': gettext('Client certificate key'), 'vartype': 'file'
|
||||
}, {
|
||||
'value': 'sslpassword', 'label': gettext('SSL password'), 'vartype': 'string',
|
||||
'min_server_version': '13'
|
||||
}, {
|
||||
'value': 'sslrootcert', 'label': gettext('Root certificate'), 'vartype': 'file'
|
||||
}, {
|
||||
'value': 'sslcrl', 'label': gettext('Certificate revocation list'), 'vartype': 'file',
|
||||
}, {
|
||||
'value': 'sslcrldir', 'label': gettext('Certificate revocation list directory'), 'vartype': 'file',
|
||||
'min_server_version': '14'
|
||||
}, {
|
||||
'value': 'sslsni', 'label': gettext('Server name indication'), 'vartype': 'bool',
|
||||
'min_server_version': '14'
|
||||
}, {
|
||||
'value': 'requirepeer', 'label': gettext('Require peer'), 'vartype': 'string',
|
||||
}, {
|
||||
'value': 'ssl_min_protocol_version', 'label': gettext('SSL min protocol version'),
|
||||
'vartype': 'enum', 'min_server_version': '13',
|
||||
'enumvals': [gettext('TLSv1'), gettext('TLSv1.1'), gettext('TLSv1.2'),
|
||||
gettext('TLSv1.3')]
|
||||
}, {
|
||||
'value': 'ssl_max_protocol_version', 'label': gettext('SSL max protocol version'),
|
||||
'vartype': 'enum', 'min_server_version': '13',
|
||||
'enumvals': [gettext('TLSv1'), gettext('TLSv1.1'), gettext('TLSv1.2'),
|
||||
gettext('TLSv1.3')]
|
||||
}, {
|
||||
'value': 'krbsrvname', 'label': gettext('Kerberos service name'), 'vartype': 'string',
|
||||
}, {
|
||||
'value': 'gsslib', 'label': gettext('GSS library'), 'vartype': 'string',
|
||||
}, {
|
||||
'value': 'target_session_attrs', 'label': gettext('Target session attribute'),
|
||||
'vartype': 'enum',
|
||||
'enumvals': [gettext('any'), gettext('read-write'), gettext('read-only'),
|
||||
gettext('primary'), gettext('standby'), gettext('prefer-standby')]
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
@ -51,12 +51,18 @@ export default class VariableSchema extends BaseUISchema {
|
||||
value: undefined,
|
||||
role: null,
|
||||
database: null,
|
||||
keyword: null,
|
||||
});
|
||||
this.vnameOptions = vnameOptions;
|
||||
this.databaseOptions = databaseOptions;
|
||||
this.roleOptions = roleOptions;
|
||||
this.varTypes = {};
|
||||
this.keys = keys;
|
||||
this.allReadOnly = false;
|
||||
}
|
||||
|
||||
setAllReadOnly(isReadOnly) {
|
||||
this.allReadOnly = isReadOnly;
|
||||
}
|
||||
|
||||
setVarTypes(options) {
|
||||
@ -67,6 +73,19 @@ export default class VariableSchema extends BaseUISchema {
|
||||
});
|
||||
}
|
||||
|
||||
getPlaceHolderMsg(variable) {
|
||||
let msg = '';
|
||||
if (variable?.min_server_version && variable?.max_server_version) {
|
||||
msg = gettext('%s <= Supported version >= %s', variable?.max_server_version, variable?.min_server_version);
|
||||
} else if (variable?.min_server_version) {
|
||||
msg = gettext('Supported version >= %s', variable?.min_server_version);
|
||||
} else if (variable?.max_server_version) {
|
||||
msg = gettext('Supported version <= %s', variable?.max_server_version);
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
getValueFieldProps(variable) {
|
||||
switch(variable?.vartype) {
|
||||
case 'bool':
|
||||
@ -74,17 +93,44 @@ export default class VariableSchema extends BaseUISchema {
|
||||
case 'enum':
|
||||
return {
|
||||
cell: 'select',
|
||||
options: (variable.enumvals || []).map((val)=>({
|
||||
options: (variable.enumvals || []).map((val)=>(typeof(val)=='string' ? {
|
||||
label: val,
|
||||
value: val
|
||||
}))
|
||||
}: val)),
|
||||
controlProps: {
|
||||
placeholder: this.getPlaceHolderMsg(variable)
|
||||
}
|
||||
};
|
||||
case 'integer':
|
||||
return 'int';
|
||||
return {
|
||||
cell: 'int',
|
||||
controlProps: {
|
||||
placeholder: this.getPlaceHolderMsg(variable)
|
||||
}
|
||||
};
|
||||
case 'real':
|
||||
return 'numeric';
|
||||
return {
|
||||
cell: 'numeric',
|
||||
controlProps: {
|
||||
placeholder: this.getPlaceHolderMsg(variable)
|
||||
}
|
||||
};
|
||||
case 'string':
|
||||
return 'text';
|
||||
return {
|
||||
cell: 'text',
|
||||
controlProps: {
|
||||
placeholder: this.getPlaceHolderMsg(variable)
|
||||
}
|
||||
};
|
||||
case 'file':
|
||||
return {
|
||||
cell: 'file',
|
||||
controlProps: {
|
||||
dialogType: 'select_file',
|
||||
supportedTypes: ['*'],
|
||||
placeholder: this.getPlaceHolderMsg(variable)
|
||||
}
|
||||
};
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
@ -99,8 +145,8 @@ export default class VariableSchema extends BaseUISchema {
|
||||
},
|
||||
{
|
||||
id: 'name', label: gettext('Name'), type:'text',
|
||||
readonly: function(state) {
|
||||
return !obj.isNew(state);
|
||||
editable: function(state) {
|
||||
return obj.isNew(state) || !obj.allReadOnly;
|
||||
},
|
||||
cell: ()=>({
|
||||
cell: 'select',
|
||||
@ -109,9 +155,16 @@ export default class VariableSchema extends BaseUISchema {
|
||||
controlProps: { allowClear: false },
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: 'keyword', label: gettext('Keyword'), type: '', cell: '',
|
||||
deps: ['name'], minWidth: 25,
|
||||
depChange: (state, source, topState, actionObj)=>{
|
||||
return { keyword: actionObj.value };
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'value', label: gettext('Value'), type: 'text',
|
||||
deps: ['name'],
|
||||
deps: ['name'], editable: !obj.allReadOnly,
|
||||
depChange: (state, source)=>{
|
||||
if(source[source.length-1] == 'name') {
|
||||
let variable = this.varTypes[state.name];
|
||||
|
@ -207,21 +207,6 @@
|
||||
"status_code": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Add server with advanced properties",
|
||||
"url": "/browser/server/obj/",
|
||||
"is_positive_test": true,
|
||||
"owner_server": true,
|
||||
"test_data": {
|
||||
"passfile": "test.pgpass",
|
||||
"hostaddr": "127.0.0.1"
|
||||
},
|
||||
"mocking_required": false,
|
||||
"mock_data": {},
|
||||
"expected_data": {
|
||||
"status_code": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Add server with background/foreground color",
|
||||
"url": "/browser/server/obj/",
|
||||
@ -805,22 +790,6 @@
|
||||
"status_code": 410
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Update server with incorrect hostaddr",
|
||||
"url": "/browser/server/obj/",
|
||||
"is_positive_test": true,
|
||||
"test_data": {
|
||||
"comment": "PLACE_HOLDER",
|
||||
"hostaddr": "PLACE_HOLDER",
|
||||
"db_res": "PLACE_HOLDER",
|
||||
"id": "PLACE_HOLDER"
|
||||
},
|
||||
"mocking_required": false,
|
||||
"mock_data": {},
|
||||
"expected_data": {
|
||||
"status_code": 400
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "update a server , make server shared",
|
||||
"url": "/browser/server/obj/",
|
||||
@ -938,21 +907,6 @@
|
||||
"status_code": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "update advanced properties of server",
|
||||
"url": "/browser/server/obj/",
|
||||
"is_positive_test": true,
|
||||
"owner_server": true,
|
||||
"test_data": {
|
||||
"passfile": "test_01.pgpass",
|
||||
"hostaddr": "127.0.0.1"
|
||||
},
|
||||
"mocking_required": false,
|
||||
"mock_data": {},
|
||||
"expected_data": {
|
||||
"status_code": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "remove ssl properties from server",
|
||||
"url": "/browser/server/obj/",
|
||||
@ -972,21 +926,6 @@
|
||||
"status_code": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "remove advanced properties from server",
|
||||
"url": "/browser/server/obj/",
|
||||
"is_positive_test": true,
|
||||
"owner_server": true,
|
||||
"test_data": {
|
||||
"passfile": "",
|
||||
"hostaddr": ""
|
||||
},
|
||||
"mocking_required": false,
|
||||
"mock_data": {},
|
||||
"expected_data": {
|
||||
"status_code": 200
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Update server with background/foreground color",
|
||||
"url": "/browser/server/obj/",
|
||||
@ -1046,22 +985,6 @@
|
||||
"status_code": 410
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Update server with incorrect hostaddr",
|
||||
"url": "/browser/server/obj/",
|
||||
"is_positive_test": true,
|
||||
"test_data": {
|
||||
"comment": "PLACE_HOLDER",
|
||||
"hostaddr": "PLACE_HOLDER",
|
||||
"db_res": "PLACE_HOLDER",
|
||||
"id": "PLACE_HOLDER"
|
||||
},
|
||||
"mocking_required": false,
|
||||
"mock_data": {},
|
||||
"expected_data": {
|
||||
"status_code": 400
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "update a server , make server shared",
|
||||
"url": "/browser/server/obj/",
|
||||
|
@ -20,11 +20,11 @@ def create_server(server, SERVER_GROUP):
|
||||
server['shared'] = False
|
||||
server_details = (1, SERVER_GROUP, server['name'], server['host'],
|
||||
server['port'], server['db'], server['username'],
|
||||
server['role'], server['sslmode'], server['comment'],
|
||||
server['role'], server['comment'],
|
||||
server['shared'])
|
||||
cur.execute('INSERT INTO server (user_id, servergroup_id, name, host, '
|
||||
'port, maintenance_db, username, role, ssl_mode,'
|
||||
' comment, shared) VALUES (?,?,?,?,?,?,?,?,?,?,?)',
|
||||
'port, maintenance_db, username, role,'
|
||||
' comment, shared) VALUES (?,?,?,?,?,?,?,?,?,?)',
|
||||
server_details)
|
||||
server_id = cur.lastrowid
|
||||
conn.commit()
|
||||
|
@ -808,13 +808,22 @@ class BatchProcess():
|
||||
"""Set environment variables"""
|
||||
if server:
|
||||
# Set SSL related ENV variables
|
||||
if server.sslcert and server.sslkey and server.sslrootcert:
|
||||
if hasattr(server, 'connection_params') and \
|
||||
server.connection_params and \
|
||||
'sslcert' in server.connection_params and \
|
||||
'sslkey' in server.connection_params and \
|
||||
'sslrootcert' in server.connection_params:
|
||||
# SSL environment variables
|
||||
sslcert = get_complete_file_path(server.sslcert)
|
||||
sslkey = get_complete_file_path(server.sslkey)
|
||||
sslrootcert = get_complete_file_path(server.sslrootcert)
|
||||
sslcert = get_complete_file_path(
|
||||
server.connection_params['sslcert'])
|
||||
sslkey = get_complete_file_path(
|
||||
server.connection_params['sslkey'])
|
||||
sslrootcert = get_complete_file_path(
|
||||
server.connection_params['sslrootcert'])
|
||||
|
||||
self.env['PGSSLMODE'] = server.ssl_mode
|
||||
self.env['PGSSLMODE'] = server.connection_params['sslmode'] \
|
||||
if hasattr(server, 'connection_params') and \
|
||||
'sslmode' in server.connection_params else 'prefer'
|
||||
self.env['PGSSLCERT'] = '' if sslcert is None else sslcert
|
||||
self.env['PGSSLKEY'] = '' if sslkey is None else sslkey
|
||||
self.env['PGSSLROOTCERT'] = \
|
||||
|
@ -48,9 +48,8 @@ def _create_server(data):
|
||||
name=data.get('name'),
|
||||
maintenance_db=data.get('db'),
|
||||
username=data.get('username'),
|
||||
ssl_mode='prefer',
|
||||
cloud_status=data.get('cloud_status'),
|
||||
connect_timeout=30,
|
||||
connection_params={'sslmode': 'prefer', 'connect_timeout': 30}
|
||||
)
|
||||
|
||||
db.session.add(server)
|
||||
|
@ -20,8 +20,10 @@ things:
|
||||
|
||||
from flask_security import UserMixin, RoleMixin
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from sqlalchemy.ext.mutable import MutableDict
|
||||
import sqlalchemy.types as types
|
||||
import uuid
|
||||
import json
|
||||
|
||||
##########################################################################
|
||||
#
|
||||
@ -31,7 +33,7 @@ import uuid
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
SCHEMA_VERSION = 34
|
||||
SCHEMA_VERSION = 35
|
||||
|
||||
##########################################################################
|
||||
#
|
||||
@ -71,6 +73,25 @@ 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'
|
||||
@ -149,7 +170,6 @@ class Server(db.Model):
|
||||
)
|
||||
name = db.Column(db.String(128), nullable=False)
|
||||
host = db.Column(db.String(128), nullable=True)
|
||||
hostaddr = db.Column(db.String(128), nullable=True)
|
||||
port = db.Column(
|
||||
db.Integer(),
|
||||
db.CheckConstraint('port >= 1 AND port <= 65534'),
|
||||
@ -163,13 +183,6 @@ class Server(db.Model):
|
||||
nullable=False
|
||||
)
|
||||
role = db.Column(db.String(64), nullable=True)
|
||||
ssl_mode = db.Column(
|
||||
db.String(16),
|
||||
db.CheckConstraint(
|
||||
"ssl_mode IN ('allow', 'prefer', 'require', 'disable', "
|
||||
"'verify-ca', 'verify-full')"
|
||||
),
|
||||
nullable=False)
|
||||
comment = db.Column(db.String(1024), nullable=True)
|
||||
discovery_id = db.Column(db.String(128), nullable=True)
|
||||
servers = db.relationship(
|
||||
@ -178,22 +191,11 @@ class Server(db.Model):
|
||||
lazy='joined'
|
||||
)
|
||||
db_res = db.Column(db.Text(), nullable=True)
|
||||
passfile = db.Column(db.Text(), nullable=True)
|
||||
passexec_cmd = db.Column(db.Text(), nullable=True)
|
||||
passexec_expiration = db.Column(db.Integer(), nullable=True)
|
||||
sslcert = db.Column(db.Text(), nullable=True)
|
||||
sslkey = db.Column(db.Text(), nullable=True)
|
||||
sslrootcert = db.Column(db.Text(), nullable=True)
|
||||
sslcrl = db.Column(db.Text(), nullable=True)
|
||||
sslcompression = db.Column(
|
||||
db.Integer(),
|
||||
db.CheckConstraint('sslcompression >= 0 AND sslcompression <= 1'),
|
||||
nullable=False
|
||||
)
|
||||
bgcolor = db.Column(db.String(10), nullable=True)
|
||||
fgcolor = db.Column(db.String(10), nullable=True)
|
||||
service = db.Column(db.Text(), nullable=True)
|
||||
connect_timeout = db.Column(db.Integer(), nullable=False)
|
||||
use_ssh_tunnel = db.Column(
|
||||
db.Integer(),
|
||||
db.CheckConstraint('use_ssh_tunnel >= 0 AND use_ssh_tunnel <= 1'),
|
||||
@ -216,6 +218,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))
|
||||
|
||||
@property
|
||||
def serialize(self):
|
||||
@ -226,35 +229,27 @@ class Server(db.Model):
|
||||
"servergroup_id": self.servergroup_id,
|
||||
"name": self.name,
|
||||
"host": self.host,
|
||||
"hostaddr": self.hostaddr,
|
||||
"port": self.port,
|
||||
"maintenance_db": self.maintenance_db,
|
||||
"username": self.username,
|
||||
"password": self.password,
|
||||
"save_password": self.save_password,
|
||||
"role": self.role,
|
||||
"ssl_mode": self.ssl_mode,
|
||||
"comment": self.comment,
|
||||
"discovery_id": self.discovery_id,
|
||||
"db_res": self.db_res,
|
||||
"passfile": self.passfile,
|
||||
"passexec_cmd": self.passexec_cmd,
|
||||
"passexec_expiration": self.passexec_expiration,
|
||||
"sslcert": self.sslcert,
|
||||
"sslkey": self.sslkey,
|
||||
"sslrootcert": self.sslrootcert,
|
||||
"sslcrl": self.sslcrl,
|
||||
"sslcompression": self.sslcompression,
|
||||
"bgcolor": self.bgcolor,
|
||||
"fgcolor": self.fgcolor,
|
||||
"service": self.service,
|
||||
"connect_timeout": self.connect_timeout,
|
||||
"use_ssh_tunnel": self.use_ssh_tunnel,
|
||||
"tunnel_host": self.tunnel_host,
|
||||
"tunnel_port": self.tunnel_port,
|
||||
"tunnel_authentication": self.tunnel_authentication,
|
||||
"tunnel_identity_file": self.tunnel_identity_file,
|
||||
"tunnel_password": self.tunnel_password
|
||||
"tunnel_password": self.tunnel_password,
|
||||
"connection_params": self.connection_params
|
||||
}
|
||||
|
||||
|
||||
@ -420,7 +415,6 @@ class SharedServer(db.Model):
|
||||
)
|
||||
name = db.Column(db.String(128), nullable=False)
|
||||
host = db.Column(db.String(128), nullable=True)
|
||||
hostaddr = db.Column(db.String(128), nullable=True)
|
||||
port = db.Column(
|
||||
db.Integer(),
|
||||
nullable=True)
|
||||
@ -433,13 +427,6 @@ class SharedServer(db.Model):
|
||||
nullable=False
|
||||
)
|
||||
role = db.Column(db.String(64), nullable=True)
|
||||
ssl_mode = db.Column(
|
||||
db.String(16),
|
||||
db.CheckConstraint(
|
||||
"ssl_mode IN ('allow', 'prefer', 'require', 'disable', "
|
||||
"'verify-ca', 'verify-full')"
|
||||
),
|
||||
nullable=False)
|
||||
comment = db.Column(db.String(1024), nullable=True)
|
||||
discovery_id = db.Column(db.String(128), nullable=True)
|
||||
servers = db.relationship(
|
||||
@ -448,20 +435,9 @@ class SharedServer(db.Model):
|
||||
lazy='joined'
|
||||
)
|
||||
db_res = db.Column(db.Text(), nullable=True)
|
||||
passfile = db.Column(db.Text(), nullable=True)
|
||||
sslcert = db.Column(db.Text(), nullable=True)
|
||||
sslkey = db.Column(db.Text(), nullable=True)
|
||||
sslrootcert = db.Column(db.Text(), nullable=True)
|
||||
sslcrl = db.Column(db.Text(), nullable=True)
|
||||
sslcompression = db.Column(
|
||||
db.Integer(),
|
||||
db.CheckConstraint('sslcompression >= 0 AND sslcompression <= 1'),
|
||||
nullable=False
|
||||
)
|
||||
bgcolor = db.Column(db.String(10), nullable=True)
|
||||
fgcolor = db.Column(db.String(10), nullable=True)
|
||||
service = db.Column(db.Text(), nullable=True)
|
||||
connect_timeout = db.Column(db.Integer(), nullable=False)
|
||||
use_ssh_tunnel = db.Column(
|
||||
db.Integer(),
|
||||
db.CheckConstraint('use_ssh_tunnel >= 0 AND use_ssh_tunnel <= 1'),
|
||||
@ -482,6 +458,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))
|
||||
|
||||
|
||||
class Macros(db.Model):
|
||||
|
@ -405,9 +405,10 @@ def create_backup_objects_job(sid):
|
||||
manager.export_password_env(p.id)
|
||||
# Check for connection timeout and if it is greater than 0 then
|
||||
# set the environment variable PGCONNECT_TIMEOUT.
|
||||
if manager.connect_timeout > 0:
|
||||
timeout = manager.get_connection_param_value('connect_timeout')
|
||||
if timeout and timeout > 0:
|
||||
env = dict()
|
||||
env['PGCONNECT_TIMEOUT'] = str(manager.connect_timeout)
|
||||
env['PGCONNECT_TIMEOUT'] = str(timeout)
|
||||
p.set_env_variables(server, env=env)
|
||||
else:
|
||||
p.set_env_variables(server)
|
||||
|
@ -251,9 +251,10 @@ def create_maintenance_job(sid, did):
|
||||
manager.export_password_env(p.id)
|
||||
# Check for connection timeout and if it is greater than 0 then
|
||||
# set the environment variable PGCONNECT_TIMEOUT.
|
||||
if manager.connect_timeout > 0:
|
||||
timeout = manager.get_connection_param_value('connect_timeout')
|
||||
if timeout and timeout > 0:
|
||||
env = dict()
|
||||
env['PGCONNECT_TIMEOUT'] = str(manager.connect_timeout)
|
||||
env['PGCONNECT_TIMEOUT'] = str(timeout)
|
||||
p.set_env_variables(server, env=env)
|
||||
else:
|
||||
p.set_env_variables(server)
|
||||
|
@ -377,68 +377,19 @@ def get_connection_str(psql_utility, db, manager):
|
||||
:param db: database name to connect specific db.
|
||||
:return: connection attribute list for PSQL connection.
|
||||
"""
|
||||
conn_attr = get_conn_str_win(manager, db)
|
||||
manager.export_password_env('PGPASSWORD')
|
||||
db = db.replace('"', '\\"')
|
||||
db = db.replace("'", "\\'")
|
||||
database = db if db != '' else 'postgres'
|
||||
user = underscore_unescape(manager.user) if manager.user else 'postgres'
|
||||
conn_attr = manager.create_connection_string(database, user)
|
||||
|
||||
conn_attr_list = list()
|
||||
conn_attr_list.append(psql_utility)
|
||||
conn_attr_list.append(conn_attr)
|
||||
return conn_attr_list
|
||||
|
||||
|
||||
def get_conn_str_win(manager, db):
|
||||
"""
|
||||
Get connection attributes for psql connection.
|
||||
:param manager:
|
||||
:param db:
|
||||
:return:
|
||||
"""
|
||||
manager.export_password_env('PGPASSWORD')
|
||||
db = db.replace('"', '\\"')
|
||||
db = db.replace("'", "\\'")
|
||||
conn_attr =\
|
||||
'host=\'{0}\' port=\'{1}\' dbname=\'{2}\' user=\'{3}\' ' \
|
||||
'sslmode=\'{4}\' sslcompression=\'{5}\' ' \
|
||||
''.format(
|
||||
manager.local_bind_host if manager.use_ssh_tunnel else
|
||||
manager.host,
|
||||
manager.local_bind_port if manager.use_ssh_tunnel else
|
||||
manager.port,
|
||||
db if db != '' else 'postgres',
|
||||
underscore_unescape(manager.user) if manager.user else 'postgres',
|
||||
manager.ssl_mode,
|
||||
True if manager.sslcompression else False,
|
||||
)
|
||||
|
||||
if manager.hostaddr:
|
||||
conn_attr = " {0} hostaddr='{1}'".format(conn_attr, manager.hostaddr)
|
||||
|
||||
if manager.passfile:
|
||||
conn_attr = " {0} passfile='{1}'".format(conn_attr,
|
||||
get_complete_file_path(
|
||||
manager.passfile))
|
||||
|
||||
if get_complete_file_path(manager.sslcert):
|
||||
conn_attr = " {0} sslcert='{1}'".format(
|
||||
conn_attr, get_complete_file_path(manager.sslcert))
|
||||
|
||||
if get_complete_file_path(manager.sslkey):
|
||||
conn_attr = " {0} sslkey='{1}'".format(
|
||||
conn_attr, get_complete_file_path(manager.sslkey))
|
||||
|
||||
if get_complete_file_path(manager.sslrootcert):
|
||||
conn_attr = " {0} sslrootcert='{1}'".format(
|
||||
conn_attr, get_complete_file_path(manager.sslrootcert))
|
||||
|
||||
if get_complete_file_path(manager.sslcrl):
|
||||
conn_attr = " {0} sslcrl='{1}'".format(
|
||||
conn_attr, get_complete_file_path(manager.sslcrl))
|
||||
|
||||
if manager.service:
|
||||
conn_attr = " {0} service='{1}'".format(
|
||||
conn_attr, get_complete_file_path(manager.service))
|
||||
|
||||
return conn_attr
|
||||
|
||||
|
||||
def enter_key_press(data):
|
||||
"""
|
||||
Handel the Enter key press event.
|
||||
|
@ -382,9 +382,10 @@ def create_restore_job(sid):
|
||||
manager.export_password_env(p.id)
|
||||
# Check for connection timeout and if it is greater than 0 then
|
||||
# set the environment variable PGCONNECT_TIMEOUT.
|
||||
if manager.connect_timeout > 0:
|
||||
timeout = manager.get_connection_param_value('connect_timeout')
|
||||
if timeout and timeout > 0:
|
||||
env = dict()
|
||||
env['PGCONNECT_TIMEOUT'] = str(manager.connect_timeout)
|
||||
env['PGCONNECT_TIMEOUT'] = str(timeout)
|
||||
p.set_env_variables(server, env=env)
|
||||
else:
|
||||
p.set_env_variables(server)
|
||||
|
@ -441,31 +441,26 @@ def dump_database_servers(output_file, selected_servers,
|
||||
add_value(attr_dict, "Name", server.name)
|
||||
add_value(attr_dict, "Group", group_name)
|
||||
add_value(attr_dict, "Host", server.host)
|
||||
add_value(attr_dict, "HostAddr", server.hostaddr)
|
||||
add_value(attr_dict, "Port", server.port)
|
||||
add_value(attr_dict, "MaintenanceDB", server.maintenance_db)
|
||||
add_value(attr_dict, "Username", server.username)
|
||||
add_value(attr_dict, "Role", server.role)
|
||||
add_value(attr_dict, "SSLMode", server.ssl_mode)
|
||||
add_value(attr_dict, "Comment", server.comment)
|
||||
add_value(attr_dict, "Shared", server.shared)
|
||||
add_value(attr_dict, "DBRestriction", server.db_res)
|
||||
add_value(attr_dict, "PassFile", server.passfile)
|
||||
add_value(attr_dict, "SSLCert", server.sslcert)
|
||||
add_value(attr_dict, "SSLKey", server.sslkey)
|
||||
add_value(attr_dict, "SSLRootCert", server.sslrootcert)
|
||||
add_value(attr_dict, "SSLCrl", server.sslcrl)
|
||||
add_value(attr_dict, "SSLCompression", server.sslcompression)
|
||||
add_value(attr_dict, "BGColor", server.bgcolor)
|
||||
add_value(attr_dict, "FGColor", server.fgcolor)
|
||||
add_value(attr_dict, "Service", server.service)
|
||||
add_value(attr_dict, "Timeout", server.connect_timeout)
|
||||
add_value(attr_dict, "UseSSHTunnel", server.use_ssh_tunnel)
|
||||
add_value(attr_dict, "TunnelHost", server.tunnel_host)
|
||||
add_value(attr_dict, "TunnelPort", server.tunnel_port)
|
||||
add_value(attr_dict, "TunnelUsername", server.tunnel_username)
|
||||
add_value(attr_dict, "TunnelAuthentication",
|
||||
server.tunnel_authentication)
|
||||
add_value(attr_dict, "KerberosAuthentication",
|
||||
server.kerberos_conn),
|
||||
add_value(attr_dict, "ConnectionParameters",
|
||||
server.connection_params)
|
||||
|
||||
servers_dumped = servers_dumped + 1
|
||||
|
||||
@ -549,14 +544,12 @@ def validate_json_data(data, is_admin):
|
||||
if errmsg:
|
||||
return errmsg
|
||||
|
||||
for attrib in ("SSLMode", "MaintenanceDB"):
|
||||
errmsg = check_attrib(attrib)
|
||||
if errmsg:
|
||||
return errmsg
|
||||
errmsg = check_attrib("MaintenanceDB")
|
||||
if errmsg:
|
||||
return errmsg
|
||||
|
||||
if "Host" not in obj and "HostAddr" not in obj and not \
|
||||
is_service_attrib_available:
|
||||
return gettext("'Host', 'HostAddr' or 'Service' attribute not "
|
||||
if "Host" not in obj and not is_service_attrib_available:
|
||||
return gettext("'Host' or 'Service' attribute not "
|
||||
"found for server '%s'" % server)
|
||||
|
||||
for server in skip_servers:
|
||||
@ -639,36 +632,37 @@ def load_database_servers(input_file, selected_servers,
|
||||
new_server.name = obj["Name"]
|
||||
new_server.servergroup_id = group_id
|
||||
new_server.user_id = user_id
|
||||
new_server.ssl_mode = obj["SSLMode"]
|
||||
new_server.maintenance_db = obj["MaintenanceDB"]
|
||||
|
||||
new_server.host = obj.get("Host", None)
|
||||
|
||||
new_server.hostaddr = obj.get("HostAddr", None)
|
||||
|
||||
new_server.port = obj.get("Port", None)
|
||||
|
||||
new_server.username = obj.get("Username", None)
|
||||
|
||||
new_server.role = obj.get("Role", None)
|
||||
|
||||
new_server.ssl_mode = obj["SSLMode"]
|
||||
|
||||
new_server.comment = obj.get("Comment", None)
|
||||
|
||||
new_server.db_res = obj.get("DBRestriction", None)
|
||||
|
||||
new_server.passfile = obj.get("PassFile", None)
|
||||
if 'ConnectionParameters' in obj:
|
||||
new_server.connection_params = \
|
||||
obj.get("ConnectionParameters", None)
|
||||
else:
|
||||
# JSON file format is old before introduction of the
|
||||
# connection parameters.
|
||||
conn_param = dict()
|
||||
for item in ['HostAddr', 'SSLMode', 'PassFile', 'SSLCert',
|
||||
'SSLKey', 'SSLRootCert', 'SSLCrl', 'Timeout',
|
||||
'SSLCompression']:
|
||||
if item in obj:
|
||||
key = item.lower()
|
||||
if item == 'Timeout':
|
||||
key = 'connect_timeout'
|
||||
conn_param[key] = obj.get(item)
|
||||
|
||||
new_server.sslcert = obj.get("SSLCert", None)
|
||||
|
||||
new_server.sslkey = obj.get("SSLKey", None)
|
||||
|
||||
new_server.sslrootcert = obj.get("SSLRootCert", None)
|
||||
|
||||
new_server.sslcrl = obj.get("SSLCrl", None)
|
||||
|
||||
new_server.sslcompression = obj.get("SSLCompression", None)
|
||||
new_server.connection_params = conn_param
|
||||
|
||||
new_server.bgcolor = obj.get("BGColor", None)
|
||||
|
||||
@ -676,8 +670,6 @@ def load_database_servers(input_file, selected_servers,
|
||||
|
||||
new_server.service = obj.get("Service", None)
|
||||
|
||||
new_server.connect_timeout = obj.get("Timeout", None)
|
||||
|
||||
new_server.use_ssh_tunnel = obj.get("UseSSHTunnel", None)
|
||||
|
||||
new_server.tunnel_host = obj.get("TunnelHost", None)
|
||||
@ -689,8 +681,9 @@ def load_database_servers(input_file, selected_servers,
|
||||
new_server.tunnel_authentication = \
|
||||
obj.get("TunnelAuthentication", None)
|
||||
|
||||
new_server.shared = \
|
||||
obj.get("Shared", None)
|
||||
new_server.shared = obj.get("Shared", None)
|
||||
|
||||
new_server.kerberos_conn = obj.get("KerberosAuthentication", None)
|
||||
|
||||
db.session.add(new_server)
|
||||
|
||||
|
@ -254,8 +254,6 @@ class Connection(BaseConnection):
|
||||
else:
|
||||
return True, None
|
||||
|
||||
pg_conn = None
|
||||
passfile = None
|
||||
manager = self.manager
|
||||
crypt_key_present, crypt_key = get_crypt_key()
|
||||
|
||||
@ -299,7 +297,7 @@ class Connection(BaseConnection):
|
||||
# we will check for pgpass file availability from connection manager
|
||||
# if it's present then we will use it
|
||||
if not password and not encpass and not passfile:
|
||||
passfile = manager.passfile if manager.passfile else None
|
||||
passfile = manager.get_connection_param_value('passfile')
|
||||
if manager.passexec:
|
||||
password = manager.passexec.get()
|
||||
|
||||
@ -315,8 +313,10 @@ class Connection(BaseConnection):
|
||||
os.environ['PGAPPNAME'] = '{0} - {1}'.format(
|
||||
config.APP_NAME, conn_id)
|
||||
|
||||
ssl_key = get_complete_file_path(manager.sslkey)
|
||||
if ssl_key and manager.ssl_mode in \
|
||||
ssl_key = get_complete_file_path(
|
||||
manager.get_connection_param_value('sslkey'))
|
||||
sslmode = manager.get_connection_param_value('sslmode')
|
||||
if ssl_key and sslmode in \
|
||||
['require', 'verify-ca', 'verify-full']:
|
||||
ssl_key_file_permission = \
|
||||
int(oct(os.stat(ssl_key).st_mode)[-3:])
|
||||
@ -324,27 +324,12 @@ class Connection(BaseConnection):
|
||||
os.chmod(ssl_key, 0o600)
|
||||
|
||||
with ConnectionLocker(manager.kerberos_conn):
|
||||
pg_conn = psycopg2.connect(
|
||||
host=manager.local_bind_host if manager.use_ssh_tunnel
|
||||
else manager.host,
|
||||
hostaddr=manager.local_bind_host if manager.use_ssh_tunnel
|
||||
else manager.hostaddr,
|
||||
port=manager.local_bind_port if manager.use_ssh_tunnel
|
||||
else manager.port,
|
||||
database=database,
|
||||
user=user,
|
||||
password=password,
|
||||
async_=self.async_,
|
||||
passfile=get_complete_file_path(passfile),
|
||||
sslmode=manager.ssl_mode,
|
||||
sslcert=get_complete_file_path(manager.sslcert),
|
||||
sslkey=ssl_key,
|
||||
sslrootcert=get_complete_file_path(manager.sslrootcert),
|
||||
sslcrl=get_complete_file_path(manager.sslcrl),
|
||||
sslcompression=True if manager.sslcompression else False,
|
||||
service=manager.service,
|
||||
connect_timeout=manager.connect_timeout
|
||||
)
|
||||
# Create the connection string
|
||||
connection_string = manager.create_connection_string(
|
||||
database, user, password)
|
||||
|
||||
pg_conn = psycopg2.connect(connection_string,
|
||||
async_=self.async_)
|
||||
|
||||
# If connection is asynchronous then we will have to wait
|
||||
# until the connection is ready to use.
|
||||
@ -1413,7 +1398,6 @@ WHERE db.datname = current_database()""")
|
||||
def reset(self):
|
||||
if self.conn and self.conn.closed:
|
||||
self.conn = None
|
||||
pg_conn = None
|
||||
manager = self.manager
|
||||
|
||||
is_return, return_value, password = self._decrypt_password(manager)
|
||||
@ -1422,26 +1406,11 @@ WHERE db.datname = current_database()""")
|
||||
|
||||
try:
|
||||
with ConnectionLocker(manager.kerberos_conn):
|
||||
pg_conn = psycopg2.connect(
|
||||
host=manager.local_bind_host if manager.use_ssh_tunnel
|
||||
else manager.host,
|
||||
hostaddr=manager.local_bind_host if manager.use_ssh_tunnel
|
||||
else manager.hostaddr,
|
||||
port=manager.local_bind_port if manager.use_ssh_tunnel
|
||||
else manager.port,
|
||||
database=self.db,
|
||||
user=manager.user,
|
||||
password=password,
|
||||
passfile=get_complete_file_path(manager.passfile),
|
||||
sslmode=manager.ssl_mode,
|
||||
sslcert=get_complete_file_path(manager.sslcert),
|
||||
sslkey=get_complete_file_path(manager.sslkey),
|
||||
sslrootcert=get_complete_file_path(manager.sslrootcert),
|
||||
sslcrl=get_complete_file_path(manager.sslcrl),
|
||||
sslcompression=True if manager.sslcompression else False,
|
||||
service=manager.service,
|
||||
connect_timeout=manager.connect_timeout
|
||||
)
|
||||
# Create the connection string
|
||||
connection_string = manager.create_connection_string(
|
||||
self.db, manager.user, password)
|
||||
|
||||
pg_conn = psycopg2.connect(connection_string)
|
||||
|
||||
except psycopg2.Error as e:
|
||||
if e.pgerror:
|
||||
@ -1725,30 +1694,12 @@ Failed to reset the connection to the server due to following error:
|
||||
|
||||
try:
|
||||
with ConnectionLocker(self.manager.kerberos_conn):
|
||||
pg_conn = psycopg2.connect(
|
||||
host=self.manager.local_bind_host if
|
||||
self.manager.use_ssh_tunnel else self.manager.host,
|
||||
hostaddr=self.manager.local_bind_host if
|
||||
self.manager.use_ssh_tunnel else
|
||||
self.manager.hostaddr,
|
||||
port=self.manager.local_bind_port if
|
||||
self.manager.use_ssh_tunnel else self.manager.port,
|
||||
database=self.db,
|
||||
user=self.manager.user,
|
||||
password=password,
|
||||
passfile=get_complete_file_path(self.manager.passfile),
|
||||
sslmode=self.manager.ssl_mode,
|
||||
sslcert=get_complete_file_path(self.manager.sslcert),
|
||||
sslkey=get_complete_file_path(self.manager.sslkey),
|
||||
sslrootcert=get_complete_file_path(
|
||||
self.manager.sslrootcert
|
||||
),
|
||||
sslcrl=get_complete_file_path(self.manager.sslcrl),
|
||||
sslcompression=True if self.manager.sslcompression
|
||||
else False,
|
||||
service=self.manager.service,
|
||||
connect_timeout=self.manager.connect_timeout
|
||||
)
|
||||
# Create the connection string
|
||||
connection_string = \
|
||||
self.manager.create_connection_string(
|
||||
self.db, self.manager.user, password)
|
||||
|
||||
pg_conn = psycopg2.connect(connection_string)
|
||||
|
||||
# Get the cursor and run the query
|
||||
cur = pg_conn.cursor()
|
||||
@ -1756,7 +1707,6 @@ Failed to reset the connection to the server due to following error:
|
||||
|
||||
# Close the connection
|
||||
pg_conn.close()
|
||||
pg_conn = None
|
||||
|
||||
except psycopg2.Error as e:
|
||||
status = False
|
||||
|
@ -49,6 +49,7 @@ class ServerManager():
|
||||
self.local_bind_port = None
|
||||
self.tunnel_object = None
|
||||
self.tunnel_created = False
|
||||
self.connection_string = ''
|
||||
|
||||
self.update(server)
|
||||
|
||||
@ -65,7 +66,6 @@ class ServerManager():
|
||||
|
||||
self.sid = server.id
|
||||
self.host = server.host
|
||||
self.hostaddr = server.hostaddr
|
||||
self.port = server.port
|
||||
self.db = server.maintenance_db
|
||||
self.shared = server.shared
|
||||
@ -73,23 +73,14 @@ class ServerManager():
|
||||
self.user = server.username
|
||||
self.password = server.password
|
||||
self.role = server.role
|
||||
self.ssl_mode = server.ssl_mode
|
||||
self.pinged = datetime.datetime.now()
|
||||
self.db_info = dict()
|
||||
self.server_types = None
|
||||
self.db_res = server.db_res
|
||||
self.passfile = server.passfile
|
||||
self.passexec = \
|
||||
PasswordExec(server.passexec_cmd, server.passexec_expiration) \
|
||||
if server.passexec_cmd else None
|
||||
self.sslcert = server.sslcert
|
||||
self.sslkey = server.sslkey
|
||||
self.sslrootcert = server.sslrootcert
|
||||
self.sslcrl = server.sslcrl
|
||||
self.sslcompression = True if server.sslcompression else False
|
||||
self.service = server.service
|
||||
self.connect_timeout = \
|
||||
server.connect_timeout if server.connect_timeout else 0
|
||||
if config.SUPPORT_SSH_TUNNEL:
|
||||
self.use_ssh_tunnel = server.use_ssh_tunnel
|
||||
self.tunnel_host = server.tunnel_host
|
||||
@ -113,6 +104,9 @@ class ServerManager():
|
||||
self.kerberos_conn = server.kerberos_conn
|
||||
self.gss_authenticated = False
|
||||
self.gss_encrypted = False
|
||||
self.connection_params = server.connection_params
|
||||
self.connection_string = self.create_connection_string(self.db,
|
||||
self.user)
|
||||
|
||||
for con in self.connections:
|
||||
self.connections[con]._release()
|
||||
@ -623,3 +617,53 @@ WHERE db.oid = {0}""".format(did))
|
||||
self.local_bind_port = None
|
||||
self.tunnel_object = None
|
||||
self.tunnel_created = False
|
||||
|
||||
def get_connection_param_value(self, param_name):
|
||||
"""
|
||||
This function return the value of param_name if found in the
|
||||
connection parameter.
|
||||
"""
|
||||
value = None
|
||||
if self.connection_params and param_name in self.connection_params:
|
||||
value = self.connection_params[param_name]
|
||||
|
||||
return value
|
||||
|
||||
def create_connection_string(self, database, user, password=None):
|
||||
"""
|
||||
This function is used to create connection string based on the
|
||||
parameters.
|
||||
"""
|
||||
full_connection_string = \
|
||||
'host=\'{0}\' port=\'{1}\' dbname=\'{2}\' user=\'{3}\''.format(
|
||||
self.local_bind_host if self.use_ssh_tunnel else self.host,
|
||||
self.local_bind_port if self.use_ssh_tunnel else self.port,
|
||||
database, user)
|
||||
|
||||
# Loop through all the connection parameters set in the server dialog.
|
||||
if self.connection_params and isinstance(self.connection_params, dict):
|
||||
for key, value in self.connection_params.items():
|
||||
# Getting complete file path if the key is one of the below.
|
||||
if key in ['passfile', 'sslcert', 'sslkey', 'sslrootcert',
|
||||
'sslcrl', 'service', 'sslcrldir']:
|
||||
value = get_complete_file_path(value)
|
||||
|
||||
# In case of host address need to check ssh tunnel flag.
|
||||
if key == 'hostaddr':
|
||||
value = self.local_bind_host if self.use_ssh_tunnel else \
|
||||
value
|
||||
|
||||
full_connection_string = \
|
||||
"{0} {1}='{2}'".format(full_connection_string, key, value)
|
||||
|
||||
# Password should not be visible into the connection string, so
|
||||
# setting the class variable with password to 'xxxxxxx'.
|
||||
if password:
|
||||
self.connection_string = "{0} password='xxxxxxx'".format(
|
||||
full_connection_string)
|
||||
|
||||
if password:
|
||||
full_connection_string = "{0} password='{1}'".format(
|
||||
full_connection_string, password)
|
||||
|
||||
return full_connection_string
|
||||
|
@ -54,14 +54,9 @@ describe('ServerSchema', ()=>{
|
||||
let setError = jasmine.createSpy('setError');
|
||||
|
||||
schemaObj.validate(state, setError);
|
||||
expect(setError).toHaveBeenCalledWith('host', 'Either Host name, Address or Service must be specified.');
|
||||
|
||||
state.hostaddr = 'incorrectip';
|
||||
schemaObj.validate(state, setError);
|
||||
expect(setError).toHaveBeenCalledWith('hostaddr', 'Host address must be valid IPv4 or IPv6 address.');
|
||||
expect(setError).toHaveBeenCalledWith('host', 'Either Host name or Service must be specified.');
|
||||
|
||||
state.host = '127.0.0.1';
|
||||
state.hostaddr = null;
|
||||
schemaObj.validate(state, setError);
|
||||
expect(setError).toHaveBeenCalledWith('username', 'Username must be specified.');
|
||||
|
||||
|
@ -74,9 +74,18 @@ describe('VariableSchema', ()=>{
|
||||
expect(schemaObj.getValueFieldProps({vartype: 'enum', enumvals: []})).toEqual(jasmine.objectContaining({
|
||||
cell: 'select',
|
||||
}));
|
||||
expect(schemaObj.getValueFieldProps({vartype: 'integer'})).toBe('int');
|
||||
expect(schemaObj.getValueFieldProps({vartype: 'real'})).toBe('numeric');
|
||||
expect(schemaObj.getValueFieldProps({vartype: 'string'})).toBe('text');
|
||||
expect(schemaObj.getValueFieldProps({vartype: 'integer'})).toEqual(jasmine.objectContaining({
|
||||
cell: 'int',
|
||||
}));
|
||||
expect(schemaObj.getValueFieldProps({vartype: 'real'})).toEqual(jasmine.objectContaining({
|
||||
cell: 'numeric',
|
||||
}));
|
||||
expect(schemaObj.getValueFieldProps({vartype: 'string'})).toEqual(jasmine.objectContaining({
|
||||
cell: 'text',
|
||||
}));
|
||||
expect(schemaObj.getValueFieldProps({vartype: 'file'})).toEqual(jasmine.objectContaining({
|
||||
cell: 'file',
|
||||
}));
|
||||
expect(schemaObj.getValueFieldProps({})).toBe('');
|
||||
});
|
||||
|
||||
|
@ -611,10 +611,10 @@ def create_server(server):
|
||||
cur = conn.cursor()
|
||||
server_details = (1, SERVER_GROUP, server['name'], server['host'],
|
||||
server['port'], server['db'], server['username'],
|
||||
server['role'], server['sslmode'], server['comment'])
|
||||
server['role'], server['comment'])
|
||||
cur.execute('INSERT INTO server (user_id, servergroup_id, name, host, '
|
||||
'port, maintenance_db, username, role, ssl_mode,'
|
||||
' comment) VALUES (?,?,?,?,?,?,?,?,?,?)', server_details)
|
||||
'port, maintenance_db, username, role,'
|
||||
' comment) VALUES (?,?,?,?,?,?,?,?,?)', server_details)
|
||||
server_id = cur.lastrowid
|
||||
conn.commit()
|
||||
conn.close()
|
||||
@ -776,7 +776,7 @@ def get_db_server(sid):
|
||||
cur = conn.cursor()
|
||||
server = cur.execute(
|
||||
'SELECT name, host, port, maintenance_db,'
|
||||
' username, ssl_mode FROM server where id=%s' % sid
|
||||
' username FROM server where id=%s' % sid
|
||||
)
|
||||
server = server.fetchone()
|
||||
if server:
|
||||
@ -785,14 +785,13 @@ def get_db_server(sid):
|
||||
db_port = server[2]
|
||||
db_name = server[3]
|
||||
username = server[4]
|
||||
ssl_mode = server[5]
|
||||
config_servers = test_setup.config_data['server_credentials']
|
||||
# Get the db password from config file for appropriate server
|
||||
db_password = get_db_password(config_servers, name, host, db_port)
|
||||
if db_password:
|
||||
# Drop database
|
||||
connection = get_db_connection(
|
||||
db_name, username, db_password, host, db_port, ssl_mode
|
||||
db_name, username, db_password, host, db_port
|
||||
)
|
||||
conn.close()
|
||||
return connection
|
||||
|