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

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

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
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.
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,
port=data.port,
maintenance_db=data.maintenance_db,
username=None,
username=data.shared_username,
save_password=0,
comment=None,
role=data.role,
@ -814,6 +814,7 @@ class ServerNode(PGChildNodeView):
'tunnel_authentication': 'tunnel_authentication',
'tunnel_identity_file': 'tunnel_identity_file',
'shared': 'shared',
'shared_username': 'shared_username',
'kerberos_conn': 'kerberos_conn',
'connection_params': 'connection_params'
}
@ -850,6 +851,10 @@ class ServerNode(PGChildNodeView):
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)
conn = manager.connection()
connected = conn.connected()
@ -1073,6 +1078,8 @@ class ServerNode(PGChildNodeView):
'port': server.port,
'db': server.maintenance_db,
'shared': server.shared if config.SERVER_MODE else None,
'shared_username': server.shared_username
if config.SERVER_MODE else None,
'username': server.username,
'gid': str(server.servergroup_id),
'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_expiration: undefined,
service: undefined,
shared_username: '',
use_ssh_tunnel: false,
tunnel_host: undefined,
tunnel_port: 22,
@ -130,6 +131,34 @@ export default class ServerSchema extends BaseUISchema {
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,
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_password = db.Column(PgAdminDbBinaryString())
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)
cloud_status = db.Column(db.Integer(), nullable=False, default=0)
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):
"""Define a preferences table for any modules."""

View File

@ -59,7 +59,8 @@ export default class DepListener {
if(actionObj.listener?.callback) {
state = this._getListenerData(state, actionObj.listener, actionObj);
} 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) {
for(const listener of allListeners) {
state = this._getListenerData(state, listener, actionObj);

View File

@ -130,7 +130,8 @@ def load_servers():
data = json.loads(j.read())
# 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:
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, "Comment", server.comment)
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, "BGColor", server.bgcolor)
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_username = obj.get("SharedUsername", None)
new_server.kerberos_conn = obj.get("KerberosAuthentication", None)
# if desktop mode