Allow setting custom username for shared servers, with default as username of server being shared.

pull/6853/head
Aditya Toshniwal 2023-10-11 12:56:06 +05:30 committed by GitHub
parent 4450145d31
commit fc411bfc49
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 90 additions and 36 deletions
web
migrations/versions
pgadmin
browser/server_groups/servers
static/js/SchemaView
tools/import_export_servers

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 67 KiB

View File

@ -44,6 +44,9 @@ Use the fields in the *General* tab to identify the server:
Please note that once the server is shared, it's icon is changed in the Please note that once the server is shared, it's icon is changed in the
object explorer. object explorer.
* Use the *Shared Username* field to fill the username of the shared server
connection. By default, it will take the username of the server being shared.
* Provide a comment about the server in the *Comments* field. * Provide a comment about the server in the *Comments* field.
Click the *Connection* tab to continue. Click the *Connection* tab to continue.

View File

@ -0,0 +1,41 @@
##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2023, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
""" Add the new column for shared server username and change
autoincrement in server table
Revision ID: 9426ad06a63b
Revises: f656e56dfdc8
Create Date: 2023-10-09 15:09:50.773035
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '9426ad06a63b'
down_revision = 'f656e56dfdc8'
branch_labels = None
depends_on = None
def upgrade():
# Added sqlite_autoincrement to force sqlite use auto increment instead of
# last row id for next record. For future server table changes, we need to
# add table_kwargs={'sqlite_autoincrement': True} as a param to
# batch_alter_table
with op.batch_alter_table(
"server", table_kwargs={'sqlite_autoincrement': True}) as batch_op:
batch_op.alter_column('id', autoincrement=True)
batch_op.add_column(sa.Column('shared_username', sa.String(64), nullable=True))
def downgrade():
# pgAdmin only upgrades, downgrade not implemented.
pass

View File

@ -376,7 +376,7 @@ class ServerModule(sg.ServerGroupPluginModule):
host=data.host, host=data.host,
port=data.port, port=data.port,
maintenance_db=data.maintenance_db, maintenance_db=data.maintenance_db,
username=None, username=data.shared_username,
save_password=0, save_password=0,
comment=None, comment=None,
role=data.role, role=data.role,
@ -814,6 +814,7 @@ class ServerNode(PGChildNodeView):
'tunnel_authentication': 'tunnel_authentication', 'tunnel_authentication': 'tunnel_authentication',
'tunnel_identity_file': 'tunnel_identity_file', 'tunnel_identity_file': 'tunnel_identity_file',
'shared': 'shared', 'shared': 'shared',
'shared_username': 'shared_username',
'kerberos_conn': 'kerberos_conn', 'kerberos_conn': 'kerberos_conn',
'connection_params': 'connection_params' 'connection_params': 'connection_params'
} }
@ -850,6 +851,10 @@ class ServerNode(PGChildNodeView):
errormsg=gettext('Not a valid Host address') errormsg=gettext('Not a valid Host address')
) )
# remove the shared username if shared is updated to False
if 'shared' in data and data['shared'] is False:
data['shared_username'] = ''
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid) manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
conn = manager.connection() conn = manager.connection()
connected = conn.connected() connected = conn.connected()
@ -1073,6 +1078,8 @@ class ServerNode(PGChildNodeView):
'port': server.port, 'port': server.port,
'db': server.maintenance_db, 'db': server.maintenance_db,
'shared': server.shared if config.SERVER_MODE else None, 'shared': server.shared if config.SERVER_MODE else None,
'shared_username': server.shared_username
if config.SERVER_MODE else None,
'username': server.username, 'username': server.username,
'gid': str(server.servergroup_id), 'gid': str(server.servergroup_id),
'group-name': sg.name if (sg and sg.name) else gettext('Servers'), 'group-name': sg.name if (sg and sg.name) else gettext('Servers'),

View File

@ -36,6 +36,7 @@ export default class ServerSchema extends BaseUISchema {
passexec: undefined, passexec: undefined,
passexec_expiration: undefined, passexec_expiration: undefined,
service: undefined, service: undefined,
shared_username: '',
use_ssh_tunnel: false, use_ssh_tunnel: false,
tunnel_host: undefined, tunnel_host: undefined,
tunnel_port: 22, tunnel_port: 22,
@ -130,6 +131,34 @@ export default class ServerSchema extends BaseUISchema {
return current_user.is_admin && pgAdmin.server_mode == 'True'; return current_user.is_admin && pgAdmin.server_mode == 'True';
}, },
}, },
{
id: 'shared_username', label: gettext('Shared Username'), type: 'text',
controlProps: { maxLength: 64},
mode: ['properties', 'create', 'edit'], deps: ['shared', 'username'],
readonly: (s)=>{
if(!this.origData.shared && s.shared) {
return false;
}
return true;
}, visible: (s)=>!obj.isShared(s),
depChange: (state, source, _topState, actionObj)=>{
let ret = {};
if(this.origData.shared) {
return ret;
}
if(source == 'username' && actionObj.oldState.username == state.shared_username) {
ret['shared_username'] = state.username;
}
if(source == 'shared') {
if(state.shared) {
ret['shared_username'] = state.username;
} else {
ret['shared_username'] = '';
}
}
return ret;
},
},
{ {
id: 'comment', label: gettext('Comments'), type: 'multiline', group: null, id: 'comment', label: gettext('Comments'), type: 'multiline', group: null,
mode: ['properties', 'edit', 'create'], mode: ['properties', 'edit', 'create'],

View File

@ -33,7 +33,7 @@ import config
# #
########################################################################## ##########################################################################
SCHEMA_VERSION = 35 SCHEMA_VERSION = 36
########################################################################## ##########################################################################
# #
@ -202,42 +202,11 @@ class Server(db.Model):
tunnel_identity_file = db.Column(db.String(64), nullable=True) tunnel_identity_file = db.Column(db.String(64), nullable=True)
tunnel_password = db.Column(PgAdminDbBinaryString()) tunnel_password = db.Column(PgAdminDbBinaryString())
shared = db.Column(db.Boolean(), nullable=False) shared = db.Column(db.Boolean(), nullable=False)
shared_username = db.Column(db.String(64), nullable=True)
kerberos_conn = db.Column(db.Boolean(), nullable=False, default=0) kerberos_conn = db.Column(db.Boolean(), nullable=False, default=0)
cloud_status = db.Column(db.Integer(), nullable=False, default=0) cloud_status = db.Column(db.Integer(), nullable=False, default=0)
connection_params = db.Column(MutableDict.as_mutable(types.JSON)) connection_params = db.Column(MutableDict.as_mutable(types.JSON))
@property
def serialize(self):
"""Return object data in easily serializable format"""
return {
"id": self.id,
"user_id": self.user_id,
"servergroup_id": self.servergroup_id,
"name": self.name,
"host": self.host,
"port": self.port,
"maintenance_db": self.maintenance_db,
"username": self.username,
"password": self.password,
"save_password": self.save_password,
"role": self.role,
"comment": self.comment,
"discovery_id": self.discovery_id,
"db_res": self.db_res,
"passexec_cmd": self.passexec_cmd,
"passexec_expiration": self.passexec_expiration,
"bgcolor": self.bgcolor,
"fgcolor": self.fgcolor,
"service": self.service,
"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,
"connection_params": self.connection_params
}
class ModulePreference(db.Model): class ModulePreference(db.Model):
"""Define a preferences table for any modules.""" """Define a preferences table for any modules."""

View File

@ -59,7 +59,8 @@ export default class DepListener {
if(actionObj.listener?.callback) { if(actionObj.listener?.callback) {
state = this._getListenerData(state, actionObj.listener, actionObj); state = this._getListenerData(state, actionObj.listener, actionObj);
} else { } else {
let allListeners = _.filter(this._depListeners, (entry)=>_.join(currPath, '|').startsWith(_.join(entry.source, '|'))); // adding a extra item in path to avoid incorrect matching like shared and shared_username
let allListeners = _.filter(this._depListeners, (entry)=>_.join(currPath.concat(['']), '|').startsWith(_.join(entry.source.concat(['']), '|')));
if(allListeners) { if(allListeners) {
for(const listener of allListeners) { for(const listener of allListeners) {
state = this._getListenerData(state, listener, actionObj); state = this._getListenerData(state, listener, actionObj);

View File

@ -130,7 +130,8 @@ def load_servers():
data = json.loads(j.read()) data = json.loads(j.read())
# Validate the json file and data # Validate the json file and data
errmsg = validate_json_data(data, False) errmsg = validate_json_data(
data, current_user.has_role("Administrator"))
if errmsg is not None: if errmsg is not None:
return internal_server_error(errmsg) return internal_server_error(errmsg)

View File

@ -493,6 +493,7 @@ def dump_database_servers(output_file, selected_servers,
add_value(attr_dict, "Role", server.role) add_value(attr_dict, "Role", server.role)
add_value(attr_dict, "Comment", server.comment) add_value(attr_dict, "Comment", server.comment)
add_value(attr_dict, "Shared", server.shared) add_value(attr_dict, "Shared", server.shared)
add_value(attr_dict, "SharedUsername", server.shared_username)
add_value(attr_dict, "DBRestriction", server.db_res) add_value(attr_dict, "DBRestriction", server.db_res)
add_value(attr_dict, "BGColor", server.bgcolor) add_value(attr_dict, "BGColor", server.bgcolor)
add_value(attr_dict, "FGColor", server.fgcolor) add_value(attr_dict, "FGColor", server.fgcolor)
@ -738,6 +739,8 @@ def load_database_servers(input_file, selected_servers,
new_server.shared = obj.get("Shared", None) new_server.shared = obj.get("Shared", None)
new_server.shared_username = obj.get("SharedUsername", None)
new_server.kerberos_conn = obj.get("KerberosAuthentication", None) new_server.kerberos_conn = obj.get("KerberosAuthentication", None)
# if desktop mode # if desktop mode