[pve-devel] [PATCH manager v2 3/3] add node/Certificates.js and use it

Dominik Csapak d.csapak at proxmox.com
Fri May 4 11:53:35 CEST 2018


this adds the grid for showing the custom/builtin Certificates
and the means to upload and delete custom ones

Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
 www/manager6/Makefile             |   1 +
 www/manager6/node/Certificates.js | 379 ++++++++++++++++++++++++++++++++++++++
 www/manager6/node/Config.js       |   8 +
 3 files changed, 388 insertions(+)
 create mode 100644 www/manager6/node/Certificates.js

diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index c29824bf..81ddcc8d 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -97,6 +97,7 @@ JSSRC= 				                 	\
 	node/StatusView.js				\
 	node/Summary.js					\
 	node/Subscription.js				\
+	node/Certificates.js				\
 	node/ACME.js					\
 	node/Config.js					\
 	window/Migrate.js				\
diff --git a/www/manager6/node/Certificates.js b/www/manager6/node/Certificates.js
new file mode 100644
index 00000000..2bce927b
--- /dev/null
+++ b/www/manager6/node/Certificates.js
@@ -0,0 +1,379 @@
+Ext.define('PVE.node.CertificateView', {
+    extend: 'Ext.container.Container',
+    xtype: 'pveCertificatesView',
+
+    mixins: ['Proxmox.Mixin.CBind' ],
+
+    items: [
+	{
+	    xtype: 'pveCertView',
+	    border: 0,
+	    cbind: {
+		nodename: '{nodename}'
+	    }
+	},
+	{
+	    xtype: 'pveACMEView',
+	    border: 0,
+	    cbind: {
+		nodename: '{nodename}'
+	    }
+	}
+    ]
+
+});
+
+Ext.define('PVE.node.CertificateViewer', {
+    extend: 'Proxmox.window.Edit',
+
+    title: gettext('Certificate'),
+
+    fieldDefaults: {
+	labelWidth: 120
+    },
+    width: 800,
+    resizable: true,
+
+    items: [
+	{
+	    xtype: 'displayfield',
+	    fieldLabel: gettext('Name'),
+	    name: 'filename'
+	},
+	{
+	    xtype: 'displayfield',
+	    fieldLabel: gettext('Fingerprint'),
+	    name: 'fingerprint'
+	},
+	{
+	    xtype: 'displayfield',
+	    fieldLabel: gettext('Issuer'),
+	    name: 'issuer'
+	},
+	{
+	    xtype: 'displayfield',
+	    fieldLabel: gettext('Subject'),
+	    name: 'subject'
+	},
+	{
+	    xtype: 'displayfield',
+	    fieldLabel: gettext('Valid Since'),
+	    renderer: Proxmox.Utils.render_timestamp,
+	    name: 'notbefore'
+	},
+	{
+	    xtype: 'displayfield',
+	    fieldLabel: gettext('Expires'),
+	    renderer: Proxmox.Utils.render_timestamp,
+	    name: 'notafter'
+	},
+	{
+	    xtype: 'displayfield',
+	    fieldLabel: gettext('Subject Alternative Names'),
+	    name: 'san',
+	    renderer: PVE.Utils.render_san
+	},
+	{
+	    xtype: 'textarea',
+	    editable: false,
+	    grow: true,
+	    growMax: 200,
+	    fieldLabel: gettext('Certificate'),
+	    name: 'pem'
+	}
+    ],
+
+    initComponent: function() {
+	var me = this;
+
+	if (!me.cert) {
+	    throw "no cert given";
+	}
+
+	if (!me.nodename) {
+	    throw "no nodename given";
+	}
+
+	me.url = '/nodes/' + me.nodename + '/certificates/info';
+	me.callParent();
+
+	// hide OK/Reset button, because we just want to show data
+	me.down('toolbar[dock=bottom]').setVisible(false);
+
+	me.load({
+	    success: function(response) {
+		if (Ext.isArray(response.result.data)) {
+		    Ext.Array.each(response.result.data, function(item) {
+			if (item.filename === me.cert) {
+			    me.setValues(item);
+			    return false;
+			}
+		    });
+		}
+	    }
+	});
+    }
+});
+
+Ext.define('PVE.node.CertUpload', {
+    extend: 'Proxmox.window.Edit',
+    xtype: 'pveCertUpload',
+
+    title: gettext('Upload Custom Certificate'),
+    resizable: false,
+    isCreate: true,
+    submitText: gettext('Upload'),
+    method: 'POST',
+    width: 600,
+
+    apiCallDone: function(success, response, options) {
+	if (!success) {
+	    return;
+	}
+
+	var txt = gettext('pveproxy will be restarted with new certificates, please reload the GUI!');
+	Ext.getBody().mask(txt, ['pve-static-mask']);
+	// reload after 10 seconds automatically
+	Ext.defer(function() {
+	    window.location.reload(true);
+	}, 10000);
+    },
+
+    items: [
+	{
+	    fieldLabel: gettext('Private Key (Optional)'),
+	    labelAlign: 'top',
+	    emptyText: gettext('No change'),
+	    name: 'key',
+	    xtype: 'textarea'
+	},
+	{
+	    xtype: 'filebutton',
+	    text: gettext('From File'),
+	    listeners: {
+		change: function(btn, e, value) {
+		    var me = this.up('form');
+		    e = e.event;
+		    Ext.Array.each(e.target.files, function(file) {
+			PVE.Utils.loadSSHKeyFromFile(file, function(res) {
+			    me.down('field[name=key]').setValue(res);
+			});
+		    });
+		    btn.reset();
+		}
+	    }
+	},
+	{
+	    xtype: 'box',
+	    autoEl: 'hr'
+	},
+	{
+	    fieldLabel: gettext('Certificate Chain'),
+	    labelAlign: 'top',
+	    allowBlank: false,
+	    name: 'certificates',
+	    xtype: 'textarea'
+	},
+	{
+	    xtype: 'filebutton',
+	    text: gettext('From File'),
+	    listeners: {
+		change: function(btn, e, value) {
+		    var me = this.up('form');
+		    e = e.event;
+		    Ext.Array.each(e.target.files, function(file) {
+			PVE.Utils.loadSSHKeyFromFile(file, function(res) {
+			    me.down('field[name=certificates]').setValue(res);
+			});
+		    });
+		    btn.reset();
+		}
+	    }
+	},
+	{
+	    xtype: 'hidden',
+	    name: 'restart',
+	    value: '1'
+	},
+	{
+	    xtype: 'hidden',
+	    name: 'force',
+	    value: '1'
+	}
+    ],
+
+    initComponent: function() {
+	var me = this;
+
+	if (!me.nodename) {
+	    throw "no nodename given";
+	}
+
+	me.url = '/nodes/' + me.nodename + '/certificates/custom';
+
+	me.callParent();
+    }
+});
+
+Ext.define('pve-certificate', {
+    extend: 'Ext.data.Model',
+
+    fields: [ 'filename', 'fingerprint', 'issuer', 'notafter', 'notbefore', 'subject', 'san' ],
+    idProperty: 'filename'
+});
+
+Ext.define('PVE.node.Certificates', {
+    extend: 'Ext.grid.Panel',
+    xtype: 'pveCertView',
+
+    tbar: [
+	{
+	    xtype: 'button',
+	    text: gettext('Upload Custom Certificate'),
+	    handler: function() {
+		var me = this.up('grid');
+		var win = Ext.create('PVE.node.CertUpload', {
+		    nodename: me.nodename
+		});
+		win.show();
+		win.on('destroy', me.reload, me);
+	    }
+	},
+	{
+	    xtype: 'button',
+	    itemId: 'deletebtn',
+	    text: gettext('Delete Custom Certificate'),
+	    handler: function() {
+		var me = this.up('grid');
+		Proxmox.Utils.API2Request({
+		    url: '/nodes/' + me.nodename + '/certificates/custom?restart=1',
+		    method: 'DELETE',
+		    success: function(response, opt) {
+			var txt = gettext('pveproxy will be restarted with new certificates, please reload the GUI!');
+			Ext.getBody().mask(txt, ['pve-static-mask']);
+			// reload after 10 seconds automatically
+			Ext.defer(function() {
+			    window.location.reload(true);
+			}, 10000);
+		    },
+		    failure: function(response, opt) {
+			Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+		    }
+		});
+	    }
+	},
+	'-',
+	{
+	    xtype: 'proxmoxButton',
+	    itemId: 'viewbtn',
+	    disabled: true,
+	    text: gettext('View Certificate'),
+	    handler: function() {
+		var me = this.up('grid');
+		me.view_certificate();
+	    }
+	}
+    ],
+
+    columns: [
+	{
+	    header: gettext('File'),
+	    width: 150,
+	    dataIndex: 'filename'
+	},
+	{
+	    header: gettext('Issuer'),
+	    flex: 1,
+	    dataIndex: 'issuer'
+	},
+	{
+	    header: gettext('Subject'),
+	    flex: 1,
+	    dataIndex: 'subject'
+	},
+	{
+	    header: gettext('Valid Since'),
+	    width: 150,
+	    dataIndex: 'notbefore',
+	    renderer: Proxmox.Utils.render_timestamp
+	},
+	{
+	    header: gettext('Expires'),
+	    width: 150,
+	    dataIndex: 'notafter',
+	    renderer: Proxmox.Utils.render_timestamp
+	},
+	{
+	    header: gettext('Subject Alternative Names'),
+	    flex: 1,
+	    dataIndex: 'san',
+	    renderer: PVE.Utils.render_san
+	},
+	{
+	    header: gettext('Fingerprint'),
+	    dataIndex: 'fingerprint',
+	    hidden: true
+	},
+	{
+	    header: gettext('PEM'),
+	    dataIndex: 'pem',
+	    hidden: true
+	}
+    ],
+
+    reload: function() {
+	var me = this;
+	me.rstore.load();
+    },
+
+    set_button_status: function() {
+	var me = this;
+	var rec = me.rstore.getById('pveproxy-ssl.pem');
+
+	me.down('#deletebtn').setDisabled(!rec);
+    },
+
+    view_certificate: function() {
+	var me = this;
+	var selection = me.getSelection();
+	if (!selection || selection.length < 1) {
+	    return;
+	}
+	var win = Ext.create('PVE.node.CertificateViewer', {
+	    cert: selection[0].data.filename,
+	    nodename : me.nodename
+	});
+	win.show();
+    },
+
+    listeners: {
+	itemdblclick: 'view_certificate'
+    },
+
+    initComponent: function() {
+	var me = this;
+
+	if (!me.nodename) {
+	    throw "no nodename given";
+	}
+
+	me.rstore = Ext.create('Proxmox.data.UpdateStore', {
+	    storeid: 'certs-' + me.nodename,
+	    model: 'pve-certificate',
+	    proxy: {
+		type: 'proxmox',
+		    url: '/api2/json/nodes/' + me.nodename + '/certificates/info'
+	    }
+	});
+
+	me.store = {
+	    type: 'diff',
+	    rstore: me.rstore
+	};
+
+	me.callParent();
+
+	me.mon(me.rstore, 'load', me.set_button_status, me);
+	me.rstore.startUpdate();
+    }
+});
diff --git a/www/manager6/node/Config.js b/www/manager6/node/Config.js
index bd7784c9..9e9f49c5 100644
--- a/www/manager6/node/Config.js
+++ b/www/manager6/node/Config.js
@@ -169,6 +169,14 @@ Ext.define('PVE.node.Config', {
 		    xtype: 'proxmoxNodeNetworkView'
 		},
 		{
+		    title: gettext('Certificates'),
+		    iconCls: 'fa fa-certificate',
+		    itemId: 'certificates',
+		    groups: ['services'],
+		    nodename: nodename,
+		    xtype: 'pveCertificatesView'
+		},
+		{
 		    title: gettext('DNS'),
 		    iconCls: 'fa fa-globe',
 		    groups: ['services'],
-- 
2.11.0




More information about the pve-devel mailing list