Hi,
PFA updated patch.
--
*Harshal Dhumal*
*Software Engineer *
EenterpriseDB <http://www.enterprisedb.com>
On Thu, May 12, 2016 at 1:27 PM, Harshal Dhumal <
[email protected]> wrote:
> Hi,
> This patch needs a rebase. (as "Named restore point" patch is committed.)
>
> --
> *Harshal Dhumal*
> *Software Engineer *
>
>
>
> EenterpriseDB <http://www.enterprisedb.com>
>
> On Wed, May 11, 2016 at 12:35 PM, Harshal Dhumal <
> [email protected]> wrote:
>
>> Hi,
>>
>> PFA patch for changing server password.
>>
>>
>> --
>> *Harshal Dhumal*
>> *Software Engineer *
>>
>>
>>
>> EenterpriseDB <http://www.enterprisedb.com>
>>
>
>
diff --git a/web/pgadmin/browser/server_groups/servers/__init__.py b/web/pgadmin/browser/server_groups/servers/__init__.py
index a79f1d4..ad82a8c 100644
--- a/web/pgadmin/browser/server_groups/servers/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/__init__.py
@@ -195,7 +195,9 @@ class ServerNode(PGChildNodeView):
[{'post': 'create_restore_point'}],
'connect': [{
'get': 'connect_status', 'post': 'connect', 'delete': 'disconnect'
- }]
+ }],
+ 'change_password': [{
+ 'post': 'change_password'}]
})
def nodes(self, gid):
@@ -838,4 +840,93 @@ class ServerNode(PGChildNodeView):
)
return internal_server_error(errormsg=str(e))
+ def change_password(self, gid, sid):
+ try:
+ data = json.loads(request.form['data'])
+ 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'] == ''):
+ return make_json_response(
+ status=400,
+ success=0,
+ errormsg=gettext(
+ "Couldn't find the required parameter(s)."
+ )
+ )
+
+ if data['newPassword'] != data['confirmPassword']:
+ return make_json_response(
+ status=200,
+ success=0,
+ errormsg=gettext(
+ "Passwords do not match."
+ )
+ )
+
+ # 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."))
+
+ from pgadmin.utils.driver import get_driver
+ manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
+ conn = manager.connection()
+
+ 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."))
+
+ # Hash new password before saving it.
+ password = pqencryptpassword(data['newPassword'], manager.user)
+
+ SQL = render_template("/".join([
+ 'servers/sql',
+ '9.2_plus' if manager.version >= 90200 else '9.1_plus',
+ 'alter_with_encrypted_password.sql'
+ ]),
+ conn=conn, _=gettext,
+ user=manager.user, encrypted_password=password)
+
+ status, res = conn.execute_scalar(SQL)
+
+ 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:
+ 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,
+ success=1,
+ info=gettext(
+ "Password changed successfully."
+ )
+ )
+
+ except Exception as e:
+
+ return internal_server_error(errormsg=str(e))
+
ServerNode.register_node_view(blueprint)
diff --git a/web/pgadmin/browser/server_groups/servers/templates/servers/servers.js b/web/pgadmin/browser/server_groups/servers/templates/servers/servers.js
index 5300055..9a48ef0 100644
--- a/web/pgadmin/browser/server_groups/servers/templates/servers/servers.js
+++ b/web/pgadmin/browser/server_groups/servers/templates/servers/servers.js
@@ -48,6 +48,11 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
applies: ['tools', 'context'], callback: 'restore_point',
category: 'restore', priority: 7, label: '{{ _('Add named restore point') }}',
icon: 'fa fa-anchor', enable : 'is_applicable'
+ },{
+ name: 'change_passowrd', node: 'server', module: this,
+ applies: ['file'], callback: 'change_passowrd',
+ priority: 7, label: '{{ _('Change Passowrd...') }}',
+ icon: 'fa fa-lock', enable : 'is_connected'
}]);
pgBrowser.messages['PRIV_GRANTEE_NOT_SPECIFIED'] =
@@ -214,6 +219,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
return false;
},
+
/* Add restore point */
restore_point: function(args) {
var input = args || {};
@@ -254,6 +260,159 @@ function($, _, S, pgAdmin, pgBrowser, alertify) {
);
}
+
+ /* Change password */
+ change_passowrd: function(args){
+ var input = args || {};
+ obj = this,
+ t = pgBrowser.tree,
+ 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);
+
+ if (!d)
+ return false;
+ if(!alertify.changeServerPassword) {
+ var newPasswordModel = Backbone.Model.extend({
+ defaults: {
+ password: undefined,
+ newPassword: undefined,
+ confirmPassword: undefined
+ },
+ validate: function() {
+ return null;
+ }
+ }),
+ passwordChangeFields = [{
+ name: 'password', label: '{{ _('Current Password') }}',
+ type: 'password', disabled: false, control: 'input',
+ required: true
+ },{
+ name: 'newPassword', label: '{{ _('New Password') }}',
+ type: 'password', disabled: false, control: 'input',
+ required: true
+ },{
+ name: 'confirmPassword', label: '{{ _('Confirm Password') }}',
+ type: 'password', disabled: false, control: 'input',
+ required: true
+ }];
+
+
+ alertify.dialog('changeServerPassword' ,function factory() {
+ return {
+ main: function(params) {
+ var title = '{{ _('Change password for ') }}' + params.label
+ this.set('title', title);
+ },
+ setup:function() {
+ return {
+ buttons: [{
+ text: '{{ _('Ok') }}', key: 27, className: 'btn btn-primary', attrs:{name:'submit'}
+ },{
+ text: '{{ _('Cancel') }}', key: 27, className: 'btn btn-danger', attrs:{name:'cancel'}
+ }],
+ // Set options for dialog
+ options: {
+ padding : !1,
+ overflow: !1,
+ model: 0,
+ resizable: true,
+ maximizable: true,
+ pinnable: false
+ }
+ };
+ },
+ hooks: {
+ // triggered when the dialog is closed
+ onclose: function() {
+ if (this.view) {
+ this.view.remove({data: true, internal: true, silent: true});
+ }
+ }
+ },
+ prepare: function() {
+ var self = this;
+ // Disable Backup button until user provides Filename
+ this.__internal.buttons[0].element.disabled = true;
+ var $container = $("<div class='obj_properties'></div>");
+ var t = pgBrowser.tree,
+ i = t.selected(),
+ d = i && i.length == 1 ? t.itemData(i) : undefined,
+ node = d && pgBrowser.Nodes[d._type];
+
+ if (!d)
+ return;
+
+ var view = this.view = new Backform.Form({
+ el: $container,
+ model: new newPasswordModel,
+ fields: passwordChangeFields});
+
+ 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');
+
+ if (_.isUndefined(password) || _.isNull(password) || password == '' ||
+ _.isUndefined(newPassword) || _.isNull(newPassword) || newPassword == '' ||
+ _.isUndefined(confirmPassword) || _.isNull(confirmPassword) || confirmPassword == '') {
+ self.__internal.buttons[0].element.disabled = true;
+ } else if (newPassword != confirmPassword) {
+ self.__internal.buttons[0].element.disabled = true;
+ setTimeout(function() {
+ that.errorModel.set('confirmPassword', '{{ _('Passwords do not match.') }}');
+ } ,400);
+ }else {
+ that.errorModel.clear();
+ self.__internal.buttons[0].element.disabled = false;
+ }
+ });
+ },
+ // Callback functions when click on the buttons of the Alertify dialogs
+ callback: function(e) {
+ if (e.button.element.name == "submit") {
+ var self = this,
+ args = this.view.model.toJSON();
+
+ e.cancel = true;
+
+ $.ajax({
+ url: url,
+ method:'POST',
+ data:{'data': JSON.stringify(args) },
+ success: function(res) {
+ if (res.success) {
+ alertify.success(res.info);
+ self.close();
+ } else {
+ alertify.error(res.errormsg);
+ }
+ },
+ error: function(xhr, status, error) {
+ try {
+ var err = $.parseJSON(xhr.responseText);
+ if (err.success == 0) {
+ alertify.error(err.errormsg);
+ }
+ } catch (e) {}
+ t.unload(i);
+ }
+ });
+ }
+ }
+ };
+ });
+ }
+ alertify.changeServerPassword(d).resizeTo('40%','45%');
+ return false;
+ }
},
model: pgAdmin.Browser.Node.Model.extend({
defaults: {
diff --git a/web/pgadmin/utils/crypto.py b/web/pgadmin/utils/crypto.py
index 98411a6..be1b52d 100644
--- a/web/pgadmin/utils/crypto.py
+++ b/web/pgadmin/utils/crypto.py
@@ -12,6 +12,7 @@
from Crypto.Cipher import AES
from Crypto import Random
import base64
+import hashlib
padding_string = b'}'
@@ -68,3 +69,39 @@ def pad(str):
# Add padding to make key 32 bytes long
return str + ((32 - len(str) % 32) * padding_string)
+
+
+def pqencryptpassword(password, user):
+
+ """
+ pqencryptpassword -- to encrypt a password
+ This is intended to be used by client applications that wish to send
+ commands like ALTER USER joe PASSWORD 'pwd'. The password need not
+ be sent in cleartext if it is encrypted on the client side. This is
+ good because it ensures the cleartext password won't end up in logs,
+ pg_stat displays, etc. We export the function so that clients won't
+ be dependent on low-level details like whether the enceyption is MD5
+ or something else.
+
+ Arguments are the cleartext password, and the SQL name of the user it
+ is for.
+
+ Return value is "md5" followed by a 32-hex-digit MD5 checksum..
+
+ Args:
+ password:
+ user:
+
+ Returns:
+
+ """
+
+ m = hashlib.md5()
+
+ # Place salt at the end because it may be known by users trying to crack
+ # the MD5 output.
+
+ m.update(password.encode())
+ m.update(user.encode())
+
+ return "md5" + m.hexdigest()
--
Sent via pgadmin-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers