mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
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
This commit is contained in:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user