Ensure password changes are successful if authenticating using a pgpass file. Fixes #2720

This commit is contained in:
Murtuza Zabuawala
2017-12-13 13:28:07 +00:00
committed by Dave Page
parent 55254a649f
commit 4246a3b22f
4 changed files with 210 additions and 69 deletions

View File

@@ -240,7 +240,8 @@ class ServerNode(PGChildNodeView):
'change_password': [{'post': 'change_password'}],
'wal_replay': [{
'delete': 'pause_wal_replay', 'put': 'resume_wal_replay'
}]
}],
'check_pgpass': [{'get': 'check_pgpass'}]
})
EXP_IP4 = "^\s*((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\."\
"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\."\
@@ -1118,12 +1119,43 @@ class ServerNode(PGChildNodeView):
"""
try:
data = json.loads(request.form['data'], encoding='utf-8')
if data and ('password' not in data or
data['password'] == '' or
'newPassword' not in data or
data['newPassword'] == '' or
'confirmPassword' not in data or
data['confirmPassword'] == ''):
# Fetch Server Details
server = Server.query.filter_by(id=sid).first()
if server is None:
return bad_request(gettext("Server not found."))
# Fetch User Details.
user = User.query.filter_by(id=current_user.id).first()
if user is None:
return unauthorized(gettext("Unauthorized request."))
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
conn = manager.connection()
is_passfile = False
# If there is no password found for the server
# then check for pgpass file
if not server.password and not manager.password:
if server.passfile and manager.passfile and \
server.passfile == manager.passfile:
is_passfile = True
# Check for password only if there is no pgpass file used
if not is_passfile:
if data and ('password' not in data or data['password'] == ''):
return make_json_response(
status=400,
success=0,
errormsg=gettext(
"Could not find the required parameter(s)."
)
)
if data and ('newPassword' not in data or
data['newPassword'] == '' or
'confirmPassword' not in data or
data['confirmPassword'] == ''):
return make_json_response(
status=400,
success=0,
@@ -1141,29 +1173,18 @@ class ServerNode(PGChildNodeView):
)
)
# Fetch Server Details
server = Server.query.filter_by(id=sid).first()
if server is None:
return bad_request(gettext("Server not found."))
# Check against old password only if no pgpass file
if not is_passfile:
decrypted_password = decrypt(manager.password, user.password)
# Fetch User Details.
user = User.query.filter_by(id=current_user.id).first()
if user is None:
return unauthorized(gettext("Unauthorized request."))
if isinstance(decrypted_password, bytes):
decrypted_password = decrypted_password.decode()
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
conn = manager.connection()
password = data['password']
decrypted_password = decrypt(manager.password, user.password)
if isinstance(decrypted_password, bytes):
decrypted_password = decrypted_password.decode()
password = data['password']
# Validate old password before setting new.
if password != decrypted_password:
return unauthorized(gettext("Incorrect password."))
# Validate old password before setting new.
if password != decrypted_password:
return unauthorized(gettext("Incorrect password."))
# Hash new password before saving it.
password = pqencryptpassword(data['newPassword'], manager.user)
@@ -1178,15 +1199,17 @@ class ServerNode(PGChildNodeView):
if not status:
return internal_server_error(errormsg=res)
password = encrypt(data['newPassword'], user.password)
# Check if old password was stored in pgadmin4 sqlite database.
# If yes then update that password.
if server.password is not None and config.ALLOW_SAVE_PASSWORD:
setattr(server, 'password', password)
db.session.commit()
# Also update password in connection manager.
manager.password = password
manager.update_session()
# Store password in sqlite only if no pgpass file
if not is_passfile:
password = encrypt(data['newPassword'], user.password)
# Check if old password was stored in pgadmin4 sqlite database.
# If yes then update that password.
if server.password is not None and config.ALLOW_SAVE_PASSWORD:
setattr(server, 'password', password)
db.session.commit()
# Also update password in connection manager.
manager.password = password
manager.update_session()
return make_json_response(
status=200,
@@ -1277,5 +1300,46 @@ class ServerNode(PGChildNodeView):
"""
return self.wal_replay(sid, True)
def check_pgpass(self, gid, sid):
"""
This function is used to check whether server is connected
using pgpass file or not
Args:
gid: Group id
sid: Server id
"""
is_pgpass = False
server = Server.query.filter_by(
user_id=current_user.id, id=sid
).first()
if server is None:
return make_json_response(
success=0,
errormsg=gettext("Could not find the required server.")
)
try:
manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
conn = manager.connection()
if not conn.connected():
return gone(
errormsg=_('Please connect the server.')
)
if not server.password or not manager.password:
if server.passfile and manager.passfile and \
server.passfile == manager.passfile:
is_pgpass = True
return make_json_response(
success=1,
data=dict({'is_pgpass': is_pgpass}),
)
except Exception as e:
current_app.logger.error(
'Cannot able to fetch pgpass status'
)
return internal_server_error(errormsg=str(e))
ServerNode.register_node_view(blueprint)

View File

@@ -365,7 +365,9 @@ define('pgadmin.node.server', [
i = input.item || t.selected(),
d = i && i.length == 1 ? t.itemData(i) : undefined,
node = d && pgBrowser.Nodes[d._type],
url = obj.generate_url(i, 'change_password', d, true);
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);
if (!d)
return false;
@@ -387,8 +389,8 @@ define('pgadmin.node.server', [
type: 'text', disabled: true, control: 'input'
},{
name: 'password', label: gettext('Current Password'),
type: 'password', disabled: false, control: 'input',
required: true
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',
@@ -410,9 +412,11 @@ define('pgadmin.node.server', [
setup:function() {
return {
buttons: [{
text: gettext('Ok'), key: 13, className: 'btn btn-primary', attrs:{name:'submit'}
},{
text: gettext('Cancel'), key: 27, className: 'btn btn-danger', attrs:{name:'cancel'}
text: gettext('Ok'), key: 13, className: 'btn btn-primary',
attrs:{name:'submit'}
},{
text: gettext('Cancel'), key: 27, className: 'btn btn-danger',
attrs:{name:'cancel'}
}],
// Set options for dialog
options: {
@@ -436,15 +440,18 @@ define('pgadmin.node.server', [
},
prepare: function() {
var self = this;
// Disable Backup button until user provides Filename
// Disable Ok button until user provides input
this.__internal.buttons[0].element.disabled = true;
var $container = $("<div class='change_password'></div>"),
newpasswordmodel = new newPasswordModel({'user_name': self.user_name});
var view = this.view = new Backform.Form({
el: $container,
model: newpasswordmodel,
fields: passwordChangeFields});
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
});
view.render();
@@ -457,7 +464,9 @@ define('pgadmin.node.server', [
newPassword = this.get('newPassword'),
confirmPassword = this.get('confirmPassword');
if (_.isUndefined(password) || _.isNull(password) || password == '' ||
// Only check password field if pgpass file is not available
if ((!is_pgpass_file_used &&
(_.isUndefined(password) || _.isNull(password) || password == '')) ||
_.isUndefined(newPassword) || _.isNull(newPassword) || newPassword == '' ||
_.isUndefined(confirmPassword) || _.isNull(confirmPassword) || confirmPassword == '') {
self.__internal.buttons[0].element.disabled = true;
@@ -488,6 +497,16 @@ define('pgadmin.node.server', [
data:{'data': JSON.stringify(args) },
success: 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 {
@@ -509,7 +528,26 @@ define('pgadmin.node.server', [
});
}
alertify.changeServerPassword(d).resizeTo('40%','52%');
// Call to check if server is using pgpass file or not
$.ajax({
url: check_pgpass_url,
method:'GET',
success: function(res) {
if (res.success && res.data.is_pgpass) {
is_pgpass_file_used = true;
}
alertify.changeServerPassword(d).resizeTo('40%','52%');
},
error: function(xhr, status, error) {
try {
var err = $.parseJSON(xhr.responseText);
if (err.success == 0) {
alertify.error(err.errormsg);
}
} catch (e) {}
}
});
return false;
},