[pve-devel] [RFC PATCH manager] lxc: wizard: ssh key

Wolfgang Bumiller w.bumiller at proxmox.com
Mon Apr 11 12:21:08 CEST 2016


---
This is a first implementation for the SSH key text field.
The input box seems a little short for an SSH key, which isn't really
a problem since you don't typically "read" them, but it's been
suggested that it might be nice to have a longer line. (Bigger dialog
window or the box spanning both columns.)
Also the button placement/text isn't ideal.
(Note that the button only shows if the browser supports the
FileReader API. You can then also drag&drop files from your favorite
file browser onto the text field.)

 www/manager6/Parser.js           |  30 ++++++++
 www/manager6/lxc/CreateWizard.js | 155 +++++++++++++++++++++++++++++----------
 2 files changed, 146 insertions(+), 39 deletions(-)

diff --git a/www/manager6/Parser.js b/www/manager6/Parser.js
index d67fbfa..dafa4c5 100644
--- a/www/manager6/Parser.js
+++ b/www/manager6/Parser.js
@@ -485,4 +485,34 @@ Ext.define('PVE.Parser', { statics: {
 
 	return cpustr + optstr;
     },
+
+    parseSSHKey: function(key) {
+	//                |--- options can have quotes--|     type    key        comment
+	var keyre = /^(?:((?:[^\s"]|\"(?:\\.|[^"\\])*")+)\s+)?(\S+)\s+(\S+)(?:\s+(.*))?$/;
+	var typere = /^(?:ssh-(?:dss|rsa|ed25519)|ecdsa-sha2-nistp\d+)$/;
+
+	var m = key.match(keyre);
+	if (!m) {
+	    return null;
+	}
+	if (m.length < 3 || !m[2]) { // [2] is always either type or key
+	    return null;
+	}
+	if (m[1] && m[1].match(typere)) {
+	    return {
+		type: m[1],
+		key: m[2],
+		comment: m[3]
+	    };
+	}
+	if (m[2].match(typere)) {
+	    return {
+		options: m[1],
+		type: m[2],
+		key: m[3],
+		comment: m[4]
+	    };
+	}
+	return null;
+    }
 }});
diff --git a/www/manager6/lxc/CreateWizard.js b/www/manager6/lxc/CreateWizard.js
index a1a7b57..ad4a100 100644
--- a/www/manager6/lxc/CreateWizard.js
+++ b/www/manager6/lxc/CreateWizard.js
@@ -2,6 +2,15 @@
 Ext.define('PVE.lxc.CreateWizard', {
     extend: 'PVE.window.Wizard',
 
+    loadSSHKeyFromFile: function(file) {
+	var me = this;
+	var reader = new FileReader();
+	reader.onload = function(evt) {
+	    me.sshkeyfield.setValue(evt.target.result);
+	};
+	reader.readAsText(file);
+    },
+
     initComponent: function() {
 	var me = this;
 
@@ -50,6 +59,106 @@ Ext.define('PVE.lxc.CreateWizard', {
 	    create: true
 	});
 
+	var passwordfield = Ext.createWidget('textfield', {
+	    inputType: 'password',
+	    name: 'password',
+	    value: '',
+	    fieldLabel: gettext('Password'),
+	    allowBlank: false,
+	    minLength: 5,
+	    change: function(f, value) {
+		if (!me.rendered) {
+		    return;
+		}
+		me.down('field[name=confirmpw]').validate();
+	    }
+	});
+
+	me.sshkeyfield = Ext.createWidget('textfield', {
+	    xtype: 'textfield',
+	    name: 'ssh-public-keys',
+	    value: '',
+	    fieldLabel: gettext('SSH public key'),
+	    allowBlank: true,
+	    validator: function(value) {
+		if (value.length) {
+		    var key = PVE.Parser.parseSSHKey(value);
+		    if (!key) {
+			return "Failed to recognize ssh key";
+		    }
+		    me.down('field[name=password]').allowBlank = true;
+		} else {
+		    me.down('field[name=password]').allowBlank = false;
+		}
+		me.down('field[name=password]').validate();
+		return true;
+	    },
+	    afterRender: function() {
+		if (!window.FileReader) {
+		    // No FileReader support in this browser
+		    return;
+		}
+		var cancel = function(ev) {
+		    ev = ev.event;
+		    if (ev.preventDefault) {
+			ev.preventDefault();
+		    }
+		};
+		me.sshkeyfield.inputEl.on('dragover', cancel);
+		me.sshkeyfield.inputEl.on('dragenter', cancel);
+		me.sshkeyfield.inputEl.on('drop', function(ev) {
+		    ev = ev.event;
+		    if (ev.preventDefault) {
+			ev.preventDefault();
+		    }
+		    var files = ev.dataTransfer.files;
+		    me.loadSSHKeyFromFile(files[0]);
+		});
+	    },
+	});
+
+	var column2 = [
+	    {
+		xtype: 'pvePoolSelector',
+		fieldLabel: gettext('Resource Pool'),
+		name: 'pool',
+		value: '',
+		allowBlank: true
+	    },
+	    passwordfield,
+	    {
+		xtype: 'textfield',
+		inputType: 'password',
+		name: 'confirmpw',
+		value: '',
+		fieldLabel: gettext('Confirm password'),
+		allowBlank: true,
+		validator: function(value) {
+		    var pw = me.down('field[name=password]').getValue();
+		    if (pw !== value) {
+			return "Passwords does not match!";
+		    }
+		    return true;
+		}
+	    },
+	    me.sshkeyfield
+	];
+
+	if (window.FileReader) {
+	    column2.push({
+		xtype: 'filebutton',
+		name: 'file',
+		text: gettext('Load SSH Key File'),
+		listeners: {
+		    change: function(btn, e, value) {
+			e = e.event;
+			me.loadSSHKeyFromFile(e.target.files[0]);
+			btn.reset();
+		    }
+		}
+	    });
+	}
+
 	Ext.applyIf(me, {
 	    subject: gettext('LXC Container'),
 	    items: [
@@ -89,45 +198,7 @@ Ext.define('PVE.lxc.CreateWizard', {
 			    allowBlank: true
 			}
 		    ],
-		    column2: [
-			{
-			    xtype: 'pvePoolSelector',
-			    fieldLabel: gettext('Resource Pool'),
-			    name: 'pool',
-			    value: '',
-			    allowBlank: true
-			},
-			{
-			    xtype: 'textfield',
-			    inputType: 'password',
-			    name: 'password',
-			    value: '',
-			    fieldLabel: gettext('Password'),
-			    allowBlank: false,
-			    minLength: 5,
-			    change: function(f, value) {
-				if (!me.rendered) {
-				    return;
-				}
-				me.down('field[name=confirmpw]').validate();
-			    }
-			},
-			{
-			    xtype: 'textfield',
-			    inputType: 'password',
-			    name: 'confirmpw',
-			    value: '',
-			    fieldLabel: gettext('Confirm password'),
-			    allowBlank: false,
-			    validator: function(value) {
-				var pw = me.down('field[name=password]').getValue();
-				if (pw !== value) {
-				    return "Passwords does not match!";
-				}
-				return true;
-			    }
-			}
-		    ],
+		    column2: column2,
 		    onGetValues: function(values) {
 			delete values.confirmpw;
 			if (!values.pool) {
@@ -203,6 +274,12 @@ Ext.define('PVE.lxc.CreateWizard', {
 			delete kv.nodename;
 			delete kv.tmplstorage;
 
+			if (!kv['ssh-public-keys'].length) {
+			    delete kv['ssh-public-keys'];
+			} else if (!kv['password'].length) {
+			    delete kv['password'];
+			}
+
 			PVE.Utils.API2Request({
 			    url: '/nodes/' + nodename + '/lxc',
 			    waitMsgTarget: me,
-- 
2.1.4




More information about the pve-devel mailing list