pgadmin4/web/pgadmin/browser/server_groups/servers/static/js/server.js

1395 lines
52 KiB
JavaScript
Raw Normal View History

2019-01-02 04:24:12 -06:00
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
2020-01-02 08:43:50 -06:00
// Copyright (C) 2013 - 2020, The pgAdmin Development Team
2019-01-02 04:24:12 -06:00
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
define('pgadmin.node.server', [
'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone',
'sources/pgadmin', 'pgadmin.browser',
'pgadmin.server.supported_servers', 'pgadmin.user_management.current_user',
'pgadmin.alertifyjs', 'pgadmin.backform',
'sources/browser/server_groups/servers/model_validation',
'pgadmin.browser.server.privilege',
], function(
gettext, url_for, $, _, Backbone, pgAdmin, pgBrowser,
supported_servers, current_user, Alertify, Backform,
modelValidation
) {
if (!pgBrowser.Nodes['server']) {
var SSL_MODES = ['prefer', 'require', 'verify-ca', 'verify-full'];
pgBrowser.SecLabelModel = pgBrowser.Node.Model.extend({
defaults: {
provider: undefined,
label: undefined,
},
schema: [{
id: 'provider', label: gettext('Provider'),
type: 'text', editable: true,
cellHeaderClasses:'width_percent_50',
},{
id: 'label', label: gettext('Security label'),
type: 'text', editable: true,
cellHeaderClasses:'override_label_class_font_size',
}],
validate: function() {
this.errorModel.clear();
if (_.isUndefined(this.get('label')) ||
_.isNull(this.get('label')) ||
String(this.get('label')).replace(/^\s+|\s+$/g, '') == '') {
var errmsg = gettext('Security label must be specified.');
this.errorModel.set('label', errmsg);
return errmsg;
}
return null;
},
});
pgAdmin.Browser.Nodes['server'] = pgAdmin.Browser.Node.extend({
parent_type: 'server_group',
type: 'server',
dialogHelp: url_for('help.static', {'filename': 'server_dialog.html'}),
label: gettext('Server'),
canDrop: true,
dropAsRemove: true,
dropPriority: 5,
hasStatistics: true,
hasCollectiveStatistics: true,
can_expand: function(d) {
return d && d.connected;
},
Init: function() {
/* Avoid multiple registration of same menus */
if (this.initialized)
return;
this.initialized = true;
pgBrowser.add_menus([{
name: 'create_server_on_sg', node: 'server_group', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 1, label: gettext('Server...'),
data: {action: 'create'}, icon: 'wcTabIcon icon-server',
},{
name: 'create_server', node: 'server', module: this,
applies: ['object', 'context'], callback: 'show_obj_properties',
category: 'create', priority: 3, label: gettext('Server...'),
data: {action: 'create'}, icon: 'wcTabIcon icon-server',
},{
name: 'connect_server', node: 'server', module: this,
applies: ['object', 'context'], callback: 'connect_server',
category: 'connect', priority: 4, label: gettext('Connect Server'),
icon: 'fa fa-link', enable : 'is_not_connected',
},{
name: 'disconnect_server', node: 'server', module: this,
applies: ['object', 'context'], callback: 'disconnect_server',
category: 'drop', priority: 5, label: gettext('Disconnect Server'),
icon: 'fa fa-chain-broken', enable : 'is_connected',
},{
name: 'reload_configuration', node: 'server', module: this,
applies: ['tools', 'context'], callback: 'reload_configuration',
category: 'reload', priority: 6, label: gettext('Reload Configuration'),
icon: 'fa fa-repeat', enable : 'enable_reload_config',
},{
name: 'restore_point', node: 'server', module: this,
applies: ['tools', 'context'], callback: 'restore_point',
category: 'restore', priority: 9, label: gettext('Add Named Restore Point...'),
icon: 'fa fa-anchor', enable : 'is_applicable',
2016-05-13 02:51:20 -05:00
},{
name: 'change_password', node: 'server', module: this,
applies: ['object'], callback: 'change_password',
label: gettext('Change Password...'), priority: 10,
icon: 'fa fa-lock', enable : 'is_connected',
},{
name: 'wal_replay_pause', node: 'server', module: this,
applies: ['tools', 'context'], callback: 'pause_wal_replay',
category: 'wal_replay_pause', priority: 7, label: gettext('Pause Replay of WAL'),
icon: 'fa fa-pause-circle', enable : 'wal_pause_enabled',
},{
name: 'wal_replay_resume', node: 'server', module: this,
applies: ['tools', 'context'], callback: 'resume_wal_replay',
category: 'wal_replay_resume', priority: 8, label: gettext('Resume Replay of WAL'),
icon: 'fa fa-play-circle', enable : 'wal_resume_enabled',
},{
name: 'clear_saved_password', node: 'server', module: this,
applies: ['object', 'context'], callback: 'clear_saved_password',
label: gettext('Clear Saved Password'), icon: 'fa fa-eraser',
priority: 11,
enable: function(node) {
if (node && node._type === 'server' &&
node.is_password_saved) {
return true;
}
return false;
},
},{
name: 'clear_sshtunnel_password', node: 'server', module: this,
applies: ['object', 'context'], callback: 'clear_sshtunnel_password',
label: gettext('Clear SSH Tunnel Password'), icon: 'fa fa-eraser',
priority: 12,
enable: function(node) {
if (node && node._type === 'server' &&
node.is_tunnel_password_saved) {
return true;
}
return false;
},
}]);
_.bindAll(this, 'connection_lost');
pgBrowser.Events.on(
'pgadmin:server:connection:lost', this.connection_lost
);
},
is_not_connected: function(node) {
return (node && node.connected != true);
},
is_connected: function(node) {
return (node && node.connected == true);
},
enable_reload_config: function(node) {
// Must be connected & is Super user
if (node && node._type == 'server' &&
node.connected && node.user.is_superuser) {
return true;
}
return false;
},
is_applicable: function(node) {
// Must be connected & super user & not in recovery mode
if (node && node._type == 'server' &&
node.connected && node.user.is_superuser
&& node.in_recovery == false) {
return true;
}
return false;
},
wal_pause_enabled: function(node) {
// Must be connected & is Super user & in Recovery mode
if (node && node._type == 'server' &&
node.connected && node.user.is_superuser
&& node.in_recovery == true
&& node.wal_pause == false) {
return true;
}
return false;
},
wal_resume_enabled: function(node) {
// Must be connected & is Super user & in Recovery mode
if (node && node._type == 'server' &&
node.connected && node.user.is_superuser
&& node.in_recovery == true
&& node.wal_pause == true) {
return true;
}
return false;
},
callbacks: {
/* Connect the server */
connect_server: function(args){
var input = args || {},
obj = this,
t = pgBrowser.tree,
i = input.item || t.selected(),
d = i && i.length == 1 ? t.itemData(i) : undefined;
if (!d)
return false;
connect_to_server(obj, d, t, i, false);
return false;
},
/* Disconnect the server */
disconnect_server: function(args, notify) {
var input = args || {},
obj = this,
t = pgBrowser.tree,
i = 'item' in input ? input.item : t.selected(),
d = i && i.length == 1 ? t.itemData(i) : undefined;
if (!d)
return false;
notify = notify || _.isUndefined(notify) || _.isNull(notify);
var disconnect = function() {
$.ajax({
url: obj.generate_url(i, 'connect', d, true),
type:'DELETE',
})
.done(function(res) {
if (res.success == 1) {
Alertify.success(res.info);
d = t.itemData(i);
t.removeIcon(i);
d.connected = false;
d.icon = 'icon-server-not-connected';
t.addIcon(i, {icon: d.icon});
obj.callbacks.refresh.apply(obj, [null, i]);
if (pgBrowser.serverInfo && d._id in pgBrowser.serverInfo) {
delete pgBrowser.serverInfo[d._id];
}
pgBrowser.enable_disable_menus(i);
// Trigger server disconnect event
pgBrowser.Events.trigger(
'pgadmin:server:disconnect',
{item: i, data: d}, false
);
}
else {
try {
Alertify.error(res.errormsg);
} catch (e) {
console.warn(e.stack || e);
}
t.unload(i);
}
})
.fail(function(xhr, status, error) {
Alertify.pgRespErrorNotify(xhr, error);
t.unload(i);
});
};
if (notify) {
Alertify.confirm(
gettext('Disconnect server'),
gettext('Are you sure you want to disconnect the server %s?', d.label),
function() { disconnect(); },
function() { return true;}
);
} else {
disconnect();
}
return false;
},
/* Connect the server (if not connected), before opening this node */
beforeopen: function(item, data) {
if(!data || data._type != 'server') {
return false;
}
pgBrowser.tree.addIcon(item, {icon: data.icon});
if (!data.connected) {
connect_to_server(this, data, pgBrowser.tree, item, false);
return false;
}
return true;
},
added: function(item, data) {
pgBrowser.serverInfo = pgBrowser.serverInfo || {};
pgBrowser.serverInfo[data._id] = _.extend({}, data);
// Call added method of node.js
pgAdmin.Browser.Node.callbacks.added.apply(this, arguments);
if(data.was_connected) {
fetch_connection_status(this, data, pgBrowser.tree, item);
}
return true;
},
/* Reload configuration */
reload_configuration: function(args){
var input = args || {},
obj = this,
t = pgBrowser.tree,
i = input.item || t.selected(),
d = i && i.length == 1 ? t.itemData(i) : undefined;
if (!d)
return false;
Alertify.confirm(
gettext('Reload server configuration'),
gettext('Are you sure you want to reload the server configuration on %s?', d.label),
function() {
$.ajax({
url: obj.generate_url(i, 'reload', d, true),
method:'GET',
})
.done(function(res) {
if (res.data.status) {
Alertify.success(res.data.result);
}
else {
Alertify.error(res.data.result);
}
})
.fail(function(xhr, status, error) {
Alertify.pgRespErrorNotify(xhr, error);
t.unload(i);
});
},
function() { return true; }
);
return false;
},
/* Add restore point */
restore_point: function(args) {
var input = args || {},
obj = this,
t = pgBrowser.tree,
i = input.item || t.selected(),
d = i && i.length == 1 ? t.itemData(i) : undefined;
if (!d)
return false;
Alertify.prompt(
gettext('Enter the name of the restore point to add'), '',
// We will execute this function when user clicks on the OK button
function(evt, value) {
// If user has provided a value, send it to the server
if(!_.isUndefined(value) && !_.isNull(value) && value !== ''
&& String(value).replace(/^\s+|\s+$/g, '') !== '') {
$.ajax({
url: obj.generate_url(i, 'restore_point', d, true),
method:'POST',
data:{ 'value': JSON.stringify(value) },
})
.done(function(res) {
Alertify.success(res.data.result, 10);
})
.fail(function(xhr, status, error) {
Alertify.pgRespErrorNotify(xhr, error);
t.unload(i);
});
} else {
evt.cancel = true;
Alertify.error( gettext('Please enter a valid name.'), 10);
}
},
// We will execute this function when user clicks on the Cancel
// button. Do nothing just close it.
function(evt) { evt.cancel = false; }
).set({'title': gettext('Restore point name')});
2016-05-13 02:51:20 -05:00
},
/* Change password */
change_password: function(args){
var input = args || {},
obj = this,
t = pgBrowser.tree,
i = input.item || t.selected(),
d = i && i.length == 1 ? t.itemData(i) : undefined,
url = obj.generate_url(i, 'change_password', d, true),
is_pgpass_file_used = false,
check_pgpass_url = obj.generate_url(i, 'check_pgpass', d, true);
2016-05-13 02:51:20 -05:00
if (!d)
return false;
if(!Alertify.changeServerPassword) {
2016-05-13 02:51:20 -05:00
var newPasswordModel = Backbone.Model.extend({
defaults: {
user_name: undefined,
password: undefined,
newPassword: undefined,
confirmPassword: undefined,
2016-05-13 02:51:20 -05:00
},
validate: function() {
return null;
},
2016-05-13 02:51:20 -05:00
}),
passwordChangeFields = [{
name: 'user_name', label: gettext('User'),
type: 'text', readonly: true, control: 'input',
},{
name: 'password', label: gettext('Current Password'),
type: 'password', disabled: function() { return is_pgpass_file_used; },
control: 'input', required: true,
},{
name: 'newPassword', label: gettext('New Password'),
type: 'password', disabled: false, control: 'input',
required: true,
},{
name: 'confirmPassword', label: gettext('Confirm Password'),
type: 'password', disabled: false, control: 'input',
required: true,
}];
Alertify.dialog('changeServerPassword' ,function factory() {
2016-05-13 02:51:20 -05:00
return {
main: function(params) {
2020-04-16 02:25:19 -05:00
var title = gettext('Change Password');
2016-05-13 02:51:20 -05:00
this.set('title', title);
this.user_name = params.user.name;
},
setup:function() {
2016-05-13 02:51:20 -05:00
return {
buttons: [{
text: gettext('Cancel'), key: 27,
className: 'btn btn-secondary fa fa-times pg-alertify-button', attrs: {name: 'cancel'},
},{
text: gettext('OK'), key: 13, className: 'btn btn-primary fa fa-check pg-alertify-button',
attrs: {name:'submit'},
2016-05-13 02:51:20 -05:00
}],
// Set options for dialog
options: {
padding : !1,
overflow: !1,
modal:false,
2016-05-13 02:51:20 -05:00
resizable: true,
maximizable: true,
pinnable: false,
closableByDimmer: false,
},
2016-05-13 02:51:20 -05:00
};
},
hooks: {
// triggered when the dialog is closed
onclose: function() {
if (this.view) {
this.view.remove({data: true, internal: true, silent: true});
}
},
2016-05-13 02:51:20 -05:00
},
prepare: function() {
var self = this;
// Disable Ok button until user provides input
this.__internal.buttons[1].element.disabled = true;
2016-05-13 02:51:20 -05:00
var $container = $('<div class=\'change_password\'></div>'),
newpasswordmodel = new newPasswordModel(
{'user_name': self.user_name}
),
view = this.view = new Backform.Form({
el: $container,
model: newpasswordmodel,
fields: passwordChangeFields,
});
2016-05-13 02:51:20 -05:00
view.render();
this.elements.content.appendChild($container.get(0));
// Listen to model & if filename is provided then enable Backup button
this.view.model.on('change', function() {
var that = this,
password = this.get('password'),
newPassword = this.get('newPassword'),
confirmPassword = this.get('confirmPassword');
2016-05-13 02:51:20 -05:00
// Only check password field if pgpass file is not available
if ((!is_pgpass_file_used &&
(_.isUndefined(password) || _.isNull(password) || password == '')) ||
2016-05-13 02:51:20 -05:00
_.isUndefined(newPassword) || _.isNull(newPassword) || newPassword == '' ||
_.isUndefined(confirmPassword) || _.isNull(confirmPassword) || confirmPassword == '') {
self.__internal.buttons[1].element.disabled = true;
2016-05-13 02:51:20 -05:00
} else if (newPassword != confirmPassword) {
self.__internal.buttons[1].element.disabled = true;
2016-05-13 02:51:20 -05:00
this.errorTimeout && clearTimeout(this.errorTimeout);
this.errorTimeout = setTimeout(function() {
that.errorModel.set('confirmPassword', gettext('Passwords do not match.'));
} ,400);
2016-05-13 02:51:20 -05:00
}else {
that.errorModel.clear();
self.__internal.buttons[1].element.disabled = false;
2016-05-13 02:51:20 -05:00
}
});
},
// Callback functions when click on the buttons of the Alertify dialogs
callback: function(e) {
if (e.button.element.name == 'submit') {
2016-05-13 02:51:20 -05:00
var self = this,
alertArgs = this.view.model.toJSON();
2016-05-13 02:51:20 -05:00
e.cancel = true;
$.ajax({
url: url,
method:'POST',
data:{'data': JSON.stringify(alertArgs) },
})
.done(function(res) {
if (res.success) {
// Notify user to update pgpass file
if(is_pgpass_file_used) {
Alertify.alert(
gettext('Change Password'),
gettext('Please make sure to disconnect the server'
+ ' and update the new password in the pgpass file'
+ ' before performing any other operation')
);
}
Alertify.success(res.info);
self.close();
} else {
Alertify.error(res.errormsg);
}
})
.fail(function(xhr, status, error) {
Alertify.pgRespErrorNotify(xhr, error);
});
2016-05-13 02:51:20 -05:00
}
},
2016-05-13 02:51:20 -05:00
};
});
}
// Call to check if server is using pgpass file or not
$.ajax({
url: check_pgpass_url,
method:'GET',
})
.done(function(res) {
if (res.success && res.data.is_pgpass) {
is_pgpass_file_used = true;
}
Alertify.changeServerPassword(d).resizeTo('40%','52%');
})
.fail(function(xhr, status, error) {
Alertify.pgRespErrorNotify(xhr, error);
});
2016-05-13 02:51:20 -05:00
return false;
},
/* Pause WAL Replay */
pause_wal_replay: function(args) {
var input = args || {},
obj = this,
t = pgBrowser.tree,
i = input.item || t.selected(),
d = i && i.length == 1 ? t.itemData(i) : undefined;
if (!d)
return false;
$.ajax({
url: obj.generate_url(i, 'wal_replay' , d, true),
type:'DELETE',
dataType: 'json',
})
.done(function(res) {
if (res.success == 1) {
Alertify.success(res.info);
t.itemData(i).wal_pause=res.data.wal_pause;
t.unload(i);
t.setInode(i);
t.deselect(i);
// Fetch updated data from server
setTimeout(function() {
t.select(i);
}, 10);
}
})
.fail(function(xhr, status, error) {
Alertify.pgRespErrorNotify(xhr, error);
t.unload(i);
});
},
/* Resume WAL Replay */
resume_wal_replay: function(args) {
var input = args || {},
obj = this,
t = pgBrowser.tree,
i = input.item || t.selected(),
d = i && i.length == 1 ? t.itemData(i) : undefined;
if (!d)
return false;
$.ajax({
url: obj.generate_url(i, 'wal_replay' , d, true),
type:'PUT',
dataType: 'json',
})
.done(function(res) {
if (res.success == 1) {
Alertify.success(res.info);
t.itemData(i).wal_pause=res.data.wal_pause;
t.unload(i);
t.setInode(i);
t.deselect(i);
// Fetch updated data from server
setTimeout(function() {
t.select(i);
}, 10);
}
})
.fail(function(xhr, status, error) {
Alertify.pgRespErrorNotify(xhr, error);
t.unload(i);
});
},
/* Cleat saved database server password */
clear_saved_password: function(args){
var input = args || {},
obj = this,
t = pgBrowser.tree,
i = input.item || t.selected(),
d = i && i.length == 1 ? t.itemData(i) : undefined;
if (!d)
return false;
Alertify.confirm(
gettext('Clear saved password'),
gettext('Are you sure you want to clear the saved password for server %s?', d.label),
function() {
$.ajax({
url: obj.generate_url(i, 'clear_saved_password', d, true),
method:'PUT',
})
.done(function(res) {
if (res.success == 1) {
Alertify.success(res.info);
t.itemData(i).is_password_saved=res.data.is_password_saved;
}
else {
Alertify.error(res.info);
}
})
.fail(function(xhr, status, error) {
Alertify.pgRespErrorNotify(xhr, error);
});
},
function() { return true; }
);
return false;
},
/* Reset stored ssh tunnel password */
clear_sshtunnel_password: function(args){
var input = args || {},
obj = this,
t = pgBrowser.tree,
i = input.item || t.selected(),
d = i && i.length == 1 ? t.itemData(i) : undefined;
if (!d)
return false;
Alertify.confirm(
gettext('Clear SSH Tunnel password'),
gettext('Are you sure you want to clear the saved password of SSH Tunnel for server %s?', d.label),
function() {
$.ajax({
url: obj.generate_url(i, 'clear_sshtunnel_password', d, true),
method:'PUT',
})
.done(function(res) {
if (res.success == 1) {
Alertify.success(res.info);
t.itemData(i).is_tunnel_password_saved=res.data.is_tunnel_password_saved;
}
else {
Alertify.error(res.info);
}
})
.fail(function(xhr, status, error) {
Alertify.pgRespErrorNotify(xhr, error);
});
},
function() { return true; }
);
return false;
},
},
model: pgAdmin.Browser.Node.Model.extend({
defaults: {
2016-05-05 10:42:16 -05:00
gid: undefined,
id: undefined,
name: '',
sslmode: 'prefer',
host: '',
hostaddr: '',
port: 5432,
db: 'postgres',
username: current_user.name,
role: null,
connect_now: true,
password: undefined,
save_password: false,
db_res: '',
passfile: undefined,
sslcompression: false,
sslcert: undefined,
sslkey: undefined,
sslrootcert: undefined,
sslcrl: undefined,
service: undefined,
use_ssh_tunnel: 0,
tunnel_host: undefined,
tunnel_port: 22,
tunnel_username: undefined,
tunnel_identity_file: undefined,
tunnel_password: undefined,
tunnel_authentication: 0,
save_tunnel_password: false,
connect_timeout: 10,
},
2016-05-05 10:42:16 -05:00
// Default values!
initialize: function(attrs, args) {
var isNew = (_.size(attrs) === 0);
if (isNew) {
this.set({'gid': args.node_info['server_group']._id});
2016-05-05 10:42:16 -05:00
}
pgAdmin.Browser.Node.Model.prototype.initialize.apply(this, arguments);
},
schema: [{
id: 'id', label: gettext('ID'), type: 'int', mode: ['properties'],
},{
id: 'name', label: gettext('Name'), type: 'text',
mode: ['properties', 'edit', 'create'],
2016-03-16 04:58:14 -05:00
},{
id: 'gid', label: gettext('Server group'), type: 'int',
control: 'node-list-by-id', node: 'server_group',
mode: ['create', 'edit'], select2: {allowClear: false},
},{
id: 'server_type', label: gettext('Server type'), type: 'options',
2016-03-16 04:58:14 -05:00
mode: ['properties'], visible: 'isConnected',
'options': supported_servers,
},{
id: 'connected', label: gettext('Connected?'), type: 'switch',
mode: ['properties'], group: gettext('Connection'), 'options': {
'onText': gettext('True'), 'offText': gettext('False'), 'size': 'mini',
},
},{
id: 'version', label: gettext('Version'), type: 'text', group: null,
mode: ['properties'], visible: 'isConnected',
},{
id: 'bgcolor', label: gettext('Background'), type: 'color',
group: null, mode: ['edit', 'create'], disabled: 'isfgColorSet',
deps: ['fgcolor'],
},{
id: 'fgcolor', label: gettext('Foreground'), type: 'color',
group: null, mode: ['edit', 'create'], disabled: 'isConnected',
},{
id: 'connect_now', controlLabel: gettext('Connect now?'), type: 'checkbox',
group: null, mode: ['create'],
},{
id: 'comment', label: gettext('Comments'), type: 'multiline', group: null,
mode: ['properties', 'edit', 'create'],
},{
id: 'host', label: gettext('Host name/address'), type: 'text', group: gettext('Connection'),
mode: ['properties', 'edit', 'create'],
control: Backform.InputControl.extend({
onChange: function() {
Backform.InputControl.prototype.onChange.apply(this, arguments);
if (!this.model || !this.model.changed) {
this.model.inform_text = undefined;
return;
}
if(this.model.origSessAttrs.host != this.model.changed.host && !this.model.isNew() && this.model.get('connected'))
{
this.model.inform_text = gettext(
'To apply changes to the connection configuration, please disconnect from the server and then reconnect.'
);
} else {
this.model.inform_text = undefined;
}
},
}),
},{
id: 'port', label: gettext('Port'), type: 'int', group: gettext('Connection'),
mode: ['properties', 'edit', 'create'], min: 1, max: 65535,
control: Backform.InputControl.extend({
onChange: function() {
Backform.InputControl.prototype.onChange.apply(this, arguments);
if (!this.model || !this.model.changed) {
this.model.inform_text = undefined;
return;
}
if(this.model.origSessAttrs.port != this.model.changed.port && !this.model.isNew() && this.model.get('connected'))
{
this.model.inform_text = gettext(
'To apply changes to the connection configuration, please disconnect from the server and then reconnect.'
);
} else {
this.model.inform_text = undefined;
}
},
}),
},{
id: 'db', label: gettext('Maintenance database'), type: 'text', group: gettext('Connection'),
mode: ['properties', 'edit', 'create'], readonly: 'isConnected',
},{
id: 'username', label: gettext('Username'), type: 'text', group: gettext('Connection'),
mode: ['properties', 'edit', 'create'],
control: Backform.InputControl.extend({
onChange: function() {
Backform.InputControl.prototype.onChange.apply(this, arguments);
if (!this.model || !this.model.changed) {
this.model.inform_text = undefined;
return;
}
if(this.model.origSessAttrs.username != this.model.changed.username && !this.model.isNew() && this.model.get('connected'))
{
this.model.inform_text = gettext(
'To apply changes to the connection configuration, please disconnect from the server and then reconnect.'
);
} else {
this.model.inform_text = undefined;
}
},
}),
},{
id: 'password', label: gettext('Password'), type: 'password', maxlength: '2000',
group: gettext('Connection'), control: 'input', mode: ['create'], deps: ['connect_now'],
visible: function(model) {
return model.get('connect_now') && model.isNew();
},
},{
id: 'save_password', controlLabel: gettext('Save password?'),
type: 'checkbox', group: gettext('Connection'), mode: ['create'],
deps: ['connect_now'], visible: function(model) {
return model.get('connect_now') && model.isNew();
},
disabled: function() {
if (!current_user.allow_save_password)
return true;
return false;
},
},{
id: 'role', label: gettext('Role'), type: 'text', group: gettext('Connection'),
mode: ['properties', 'edit', 'create'], readonly: 'isConnected',
},{
id: 'service', label: gettext('Service'), type: 'text',
mode: ['properties', 'edit', 'create'], readonly: 'isConnected',
group: gettext('Connection'),
},{
id: 'sslmode', label: gettext('SSL mode'), control: 'select2', group: gettext('SSL'),
select2: {
allowClear: false,
minimumResultsForSearch: Infinity,
},
mode: ['properties', 'edit', 'create'], disabled: '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: 'text',
group: gettext('SSL'), mode: ['edit', 'create'],
disabled: 'isSSL', readonly: 'isConnected', control: Backform.FileControl,
dialog_type: 'select_file', supp_types: ['*'],
deps: ['sslmode'],
},{
id: 'sslkey', label: gettext('Client certificate key'), type: 'text',
group: gettext('SSL'), mode: ['edit', 'create'],
disabled: 'isSSL', readonly: 'isConnected', control: Backform.FileControl,
dialog_type: 'select_file', supp_types: ['*'],
deps: ['sslmode'],
},{
id: 'sslrootcert', label: gettext('Root certificate'), type: 'text',
group: gettext('SSL'), mode: ['edit', 'create'],
disabled: 'isSSL', readonly: 'isConnected', control: Backform.FileControl,
dialog_type: 'select_file', supp_types: ['*'],
deps: ['sslmode'],
},{
id: 'sslcrl', label: gettext('Certificate revocation list'), type: 'text',
group: gettext('SSL'), mode: ['edit', 'create'],
disabled: 'isSSL', readonly: 'isConnected', control: Backform.FileControl,
dialog_type: 'select_file', supp_types: ['*'],
deps: ['sslmode'],
},{
id: 'sslcompression', label: gettext('SSL compression?'), type: 'switch',
mode: ['edit', 'create'], group: gettext('SSL'),
'options': {'size': 'mini'},
deps: ['sslmode'], disabled: 'isSSL', readonly: 'isConnected',
},{
id: 'sslcert', label: gettext('Client certificate'), type: 'text',
group: gettext('SSL'), mode: ['properties'],
deps: ['sslmode'],
visible: function(model) {
var sslcert = model.get('sslcert');
return !_.isUndefined(sslcert) && !_.isNull(sslcert);
},
},{
id: 'sslkey', label: gettext('Client certificate key'), type: 'text',
group: gettext('SSL'), mode: ['properties'],
deps: ['sslmode'],
visible: function(model) {
var sslkey = model.get('sslkey');
return !_.isUndefined(sslkey) && !_.isNull(sslkey);
},
},{
id: 'sslrootcert', label: gettext('Root certificate'), type: 'text',
group: gettext('SSL'), mode: ['properties'],
deps: ['sslmode'],
visible: function(model) {
var sslrootcert = model.get('sslrootcert');
return !_.isUndefined(sslrootcert) && !_.isNull(sslrootcert);
},
},{
id: 'sslcrl', label: gettext('Certificate revocation list'), type: 'text',
group: gettext('SSL'), mode: ['properties'],
deps: ['sslmode'],
visible: function(model) {
var sslcrl = model.get('sslcrl');
return !_.isUndefined(sslcrl) && !_.isNull(sslcrl);
},
},{
id: 'sslcompression', label: gettext('SSL compression?'), type: 'switch',
mode: ['properties'], group: gettext('SSL'),
'options': {'size': 'mini'},
deps: ['sslmode'], visible: function(model) {
var sslmode = model.get('sslmode');
return _.indexOf(SSL_MODES, sslmode) != -1;
},
},{
id: 'use_ssh_tunnel', label: gettext('Use SSH tunneling'), type: 'switch',
mode: ['properties', 'edit', 'create'], group: gettext('SSH Tunnel'),
'options': {'size': 'mini'},
disabled: function(model) {
if (!pgAdmin.Browser.utils.support_ssh_tunnel) {
setTimeout(function() {
model.set('use_ssh_tunnel', 0);
}, 10);
return true;
}
return false;
},
readonly: 'isConnected',
},{
id: 'tunnel_host', label: gettext('Tunnel host'), type: 'text', group: gettext('SSH Tunnel'),
mode: ['properties', 'edit', 'create'], deps: ['use_ssh_tunnel'],
disabled: function(model) {
return !model.get('use_ssh_tunnel');
},
readonly: 'isConnected',
},{
id: 'tunnel_port', label: gettext('Tunnel port'), type: 'int', group: gettext('SSH Tunnel'),
mode: ['properties', 'edit', 'create'], deps: ['use_ssh_tunnel'], max: 65535,
disabled: function(model) {
return !model.get('use_ssh_tunnel');
},
readonly: 'isConnected',
},{
id: 'tunnel_username', label: gettext('Username'), type: 'text', group: gettext('SSH Tunnel'),
mode: ['properties', 'edit', 'create'], deps: ['use_ssh_tunnel'],
disabled: function(model) {
return !model.get('use_ssh_tunnel');
},
readonly: 'isConnected',
},{
id: 'tunnel_authentication', label: gettext('Authentication'), type: 'switch',
mode: ['properties', 'edit', 'create'], group: gettext('SSH Tunnel'),
'options': {'onText': gettext('Identity file'),
'offText': gettext('Password'), 'size': 'mini', width: '90'},
deps: ['use_ssh_tunnel'],
disabled: function(model) {
return !model.get('use_ssh_tunnel');
},
readonly: 'isConnected',
}, {
id: 'tunnel_identity_file', label: gettext('Identity file'), type: 'text',
group: gettext('SSH Tunnel'), mode: ['properties', 'edit', 'create'],
control: Backform.FileControl, dialog_type: 'select_file', supp_types: ['*'],
deps: ['tunnel_authentication', 'use_ssh_tunnel'],
disabled: function(model) {
let file = model.get('tunnel_identity_file');
if (!model.get('tunnel_authentication') && file) {
setTimeout(function() {
model.set('tunnel_identity_file', null);
}, 10);
}
return !model.get('tunnel_authentication') || !model.get('use_ssh_tunnel');
},
},{
id: 'tunnel_password', label: gettext('Password'), type: 'password',
group: gettext('SSH Tunnel'), control: 'input', mode: ['create'],
deps: ['use_ssh_tunnel'],
disabled: function(model) {
return !model.get('use_ssh_tunnel');
},
readonly: 'isConnected',
}, {
id: 'save_tunnel_password', controlLabel: gettext('Save password?'),
type: 'checkbox', group: gettext('SSH Tunnel'), mode: ['create'],
deps: ['connect_now', 'use_ssh_tunnel'], visible: function(model) {
return model.get('connect_now') && model.isNew();
},
disabled: function(model) {
if (!current_user.allow_save_tunnel_password ||
!model.get('use_ssh_tunnel'))
return true;
return false;
},
}, {
id: 'hostaddr', label: gettext('Host address'), type: 'text', group: gettext('Advanced'),
mode: ['properties', 'edit', 'create'], readonly: 'isConnected',
},{
id: 'db_res', label: gettext('DB restriction'), type: 'select2', group: gettext('Advanced'),
mode: ['properties', 'edit', 'create'], readonly: 'isConnected', select2: {multiple: true, allowClear: false,
tags: true, tokenSeparators: [','], first_empty: false, selectOnClose: true, emptyOptions: true},
},{
id: 'passfile', label: gettext('Password file'), type: 'text',
group: gettext('Advanced'), mode: ['edit', 'create'],
disabled: 'isValidLib', readonly: 'isConnected', control: Backform.FileControl,
dialog_type: 'select_file', supp_types: ['*'],
},{
id: 'passfile', label: gettext('Password file'), type: 'text',
group: gettext('Advanced'), mode: ['properties'],
visible: function(model) {
var passfile = model.get('passfile');
return !_.isUndefined(passfile) && !_.isNull(passfile);
},
},{
id: 'connect_timeout', label: gettext('Connection timeout (seconds)'),
type: 'int', group: gettext('Advanced'),
mode: ['properties', 'edit', 'create'], readonly: 'isConnected',
min: 0,
}],
validate: function() {
const validateModel = new modelValidation.ModelValidation(this);
return validateModel.validate();
},
isConnected: function(model) {
return model.get('connected');
},
isfgColorSet: function(model) {
var bgcolor = model.get('bgcolor'),
fgcolor = model.get('fgcolor');
if(model.get('connected')) {
return true;
}
// If fgcolor is set and bgcolor is not set then force bgcolor
// to set as white
if(_.isUndefined(bgcolor) || _.isNull(bgcolor) || !bgcolor) {
if(fgcolor) {
model.set('bgcolor', '#ffffff');
}
}
return false;
},
isSSL: function(model) {
var ssl_mode = model.get('sslmode');
return _.indexOf(SSL_MODES, ssl_mode) == -1;
},
isValidLib: function() {
// older version of libpq do not support 'passfile' parameter in
// connect method, valid libpq must have version >= 100000
return pgBrowser.utils.pg_libpq_version < 100000;
},
}),
connection_lost: function(i, resp) {
if (pgBrowser.tree) {
var t = pgBrowser.tree,
d = i && t.itemData(i),
self = this;
while (d && d._type != 'server') {
i = t.parent(i);
d = i && t.itemData(i);
}
if (i && d && d._type == 'server') {
if (_.isUndefined(d.is_connecting) || !d.is_connecting) {
d.is_connecting = true;
var disconnect = function(_sid) {
if (d._id == _sid) {
d.is_connecting = false;
// Stop listening to the connection cancellation event
pgBrowser.Events.off(
'pgadmin:server:connect:cancelled', disconnect
);
// Connection to the database will also be cancelled
pgBrowser.Events.trigger(
'pgadmin:database:connect:cancelled',_sid,
resp.data.database || d.db
);
// Make sure - the server is disconnected properly
pgBrowser.Events.trigger(
'pgadmin:server:disconnect',
{item: i, data: d}, false
);
}
};
// Listen for the server connection cancellation event
pgBrowser.Events.on(
'pgadmin:server:connect:cancelled', disconnect
);
Alertify.confirm(
gettext('Connection lost'),
gettext('Would you like to reconnect to the database?'),
function() {
connect_to_server(self, d, t, i, true);
},
function() {
d.is_connecting = false;
t.unload(i);
t.setInode(i);
t.addIcon(i, {icon: 'icon-database-not-connected'});
pgBrowser.Events.trigger(
'pgadmin:server:connect:cancelled', i, d, self
);
t.select(i);
});
}
}
}
},
});
var connect_to_server = function(obj, data, tree, item, reconnect) {
var wasConnected = reconnect || data.connected,
onFailure = function(
xhr, status, error, _node, _data, _tree, _item, _wasConnected
) {
data.connected = false;
// It should be attempt to reconnect.
// Let's not change the status of the tree node now.
if (!_wasConnected) {
tree.setInode(_item);
tree.addIcon(_item, {icon: 'icon-server-not-connected'});
}
Alertify.pgNotifier('error', xhr, error, function(msg) {
setTimeout(function() {
if (msg == 'CRYPTKEY_SET') {
connect_to_server(_node, _data, _tree, _item, _wasConnected);
} else {
Alertify.dlgServerPass(
gettext('Connect to Server'),
msg, _node, _data, _tree, _item, _wasConnected
).resizeTo();
}
}, 100);
});
},
onSuccess = function(res, node, _data, _tree, _item, _wasConnected) {
if (res && res.data) {
if (typeof res.data.icon == 'string') {
_tree.removeIcon(_item);
_data.icon = res.data.icon;
_tree.addIcon(_item, {icon: _data.icon});
}
_.extend(_data, res.data);
_data.is_connecting = false;
var serverInfo = pgBrowser.serverInfo =
pgBrowser.serverInfo || {};
serverInfo[_data._id] = _.extend({}, _data);
Alertify.success(res.info);
obj.trigger('connected', obj, _item, _data);
// Generate the event that server is connected
pgBrowser.Events.trigger(
'pgadmin:server:connected', _data._id, _item, _data
);
// Generate the event that database is connected
pgBrowser.Events.trigger(
'pgadmin:database:connected', _data._id, _data.db, _item, _data
);
// We're not reconnecting
if (!_wasConnected) {
_tree.setInode(_item);
_tree.deselect(_item);
setTimeout(function() {
_tree.select(_item);
_tree.open(_item);
}, 10);
} else {
// We just need to refresh the tree now.
setTimeout(function() {
node.callbacks.refresh.apply(node, [true]);
}, 10);
}
}
};
// Ask Password and send it back to the connect server
if (!Alertify.dlgServerPass) {
Alertify.dialog('dlgServerPass', function factory() {
return {
main: function(
title, message, node, _data, _tree, _item,
_status, _onSuccess, _onFailure, _onCancel
) {
this.set('title', title);
this.message = message;
this.tree = _tree;
this.nodeData = _data;
this.nodeItem = _item;
this.node= node;
this.connected = _status;
this.onSuccess = _onSuccess || onSuccess;
this.onFailure = _onFailure || onFailure;
this.onCancel = _onCancel || onCancel;
},
setup:function() {
return {
buttons:[{
text: gettext('Cancel'), className: 'btn btn-secondary fa fa-times pg-alertify-button',
key: 27,
},{
text: gettext('OK'), key: 13, className: 'btn btn-primary fa fa-check pg-alertify-button',
}],
focus: {element: '#password', select: true},
options: {
modal: 0, resizable: false, maximizable: false, pinnable: false,
},
};
},
build:function() {},
prepare:function() {
this.setContent(this.message);
},
callback: function(closeEvent) {
var _tree = this.tree,
_item = this.nodeItem,
_node = this.node,
_data = this.nodeData,
_status = this.connected,
_onSuccess = this.onSuccess,
_onFailure = this.onFailure,
_onCancel = this.onCancel;
if (closeEvent.button.text == gettext('OK')) {
var _url = _node.generate_url(_item, 'connect', _data, true);
if (!_status) {
_tree.setLeaf(_item);
_tree.removeIcon(_item);
_tree.addIcon(_item, {icon: 'icon-server-connecting'});
}
$.ajax({
type: 'POST',
timeout: 30000,
url: _url,
data: $('#frmPassword').serialize(),
})
.done(function(res) {
return _onSuccess(
res, _node, _data, _tree, _item, _status
);
})
.fail(function(xhr, status, error) {
return _onFailure(
xhr, status, error, _node, _data, _tree, _item, _status
);
});
} else {
_onCancel && typeof(_onCancel) == 'function' &&
_onCancel(_tree, _item, _data, _status);
}
},
};
});
}
var onCancel = function(_tree, _item, _data, _status) {
_data.is_connecting = false;
_tree.unload(_item);
_tree.setInode(_item);
_tree.removeIcon(_item);
_tree.addIcon(_item, {icon: 'icon-server-not-connected'});
obj.trigger('connect:cancelled', data._id, data.db, obj, _item, _data);
pgBrowser.Events.trigger(
'pgadmin:server:connect:cancelled', data._id, _item, _data, obj
);
pgBrowser.Events.trigger(
'pgadmin:database:connect:cancelled', data._id, data.db, _item, _data, obj
);
if (_status) {
_tree.select(_item);
}
};
/* Wait till the existing request completes */
if(data.is_connecting) {
return;
}
data.is_connecting = true;
tree.setLeaf(item);
tree.removeIcon(item);
tree.addIcon(item, {icon: 'icon-server-connecting'});
var url = obj.generate_url(item, 'connect', data, true);
$.post(url)
.done(function(res) {
if (res.success == 1) {
return onSuccess(
res, obj, data, tree, item, wasConnected
);
}
})
.fail(function(xhr, status, error) {
return onFailure(
xhr, status, error, obj, data, tree, item, wasConnected
);
})
.always(function(){
data.is_connecting = false;
});
};
var fetch_connection_status = function(obj, data, tree, item) {
var url = obj.generate_url(item, 'connect', data, true);
tree.setLeaf(item);
tree.removeIcon(item);
tree.addIcon(item, {icon: 'icon-server-connecting'});
$.get(url)
.done(function(res) {
tree.setInode(item);
if (res && res.data) {
if (typeof res.data.icon == 'string') {
tree.removeIcon(item);
data.icon = res.data.icon;
tree.addIcon(item, {icon: data.icon});
}
_.extend(data, res.data);
var serverInfo = pgBrowser.serverInfo = pgBrowser.serverInfo || {};
serverInfo[data._id] = _.extend({}, data);
if(data.errmsg) {
Alertify.error(data.errmsg);
}
}
})
.fail(function(xhr, status, error) {
tree.setInode(item);
tree.addIcon(item, {icon: 'icon-server-not-connected'});
Alertify.pgRespErrorNotify(xhr, error);
});
};
}
return pgBrowser.Nodes['server'];
});