Hi,

PFA updated patch.

-- 
*Harshal Dhumal*
*Software Engineer *



EenterpriseDB <http://www.enterprisedb.com>

On Thu, May 12, 2016 at 1:27 PM, Harshal Dhumal <
harshal.dhu...@enterprisedb.com> 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 <
> harshal.dhu...@enterprisedb.com> 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 (pgadmin-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgadmin-hackers

Reply via email to