]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/node/Certificates.js
ui: cert upload: fix private key field sending empty string
[pve-manager.git] / www / manager6 / node / Certificates.js
1 Ext.define('PVE.node.CertificateView', {
2 extend: 'Ext.container.Container',
3 xtype: 'pveCertificatesView',
4
5 onlineHelp: 'sysadmin_certificate_management',
6
7 mixins: ['Proxmox.Mixin.CBind'],
8 scrollable: 'y',
9
10 items: [
11 {
12 xtype: 'pveCertView',
13 border: 0,
14 cbind: {
15 nodename: '{nodename}',
16 },
17 },
18 {
19 xtype: 'pveACMEView',
20 border: 0,
21 cbind: {
22 nodename: '{nodename}',
23 },
24 },
25 ],
26
27 });
28
29 Ext.define('PVE.node.CertificateViewer', {
30 extend: 'Proxmox.window.Edit',
31
32 title: gettext('Certificate'),
33
34 fieldDefaults: {
35 labelWidth: 120,
36 },
37 width: 800,
38
39 items: {
40 xtype: 'inputpanel',
41 maxHeight: 900,
42 scrollable: 'y',
43 columnT: [
44 {
45 xtype: 'displayfield',
46 fieldLabel: gettext('Name'),
47 name: 'filename',
48 },
49 {
50 xtype: 'displayfield',
51 fieldLabel: gettext('Fingerprint'),
52 name: 'fingerprint',
53 },
54 {
55 xtype: 'displayfield',
56 fieldLabel: gettext('Issuer'),
57 name: 'issuer',
58 },
59 {
60 xtype: 'displayfield',
61 fieldLabel: gettext('Subject'),
62 name: 'subject',
63 },
64 ],
65 column1: [
66 {
67 xtype: 'displayfield',
68 fieldLabel: gettext('Public Key Type'),
69 name: 'public-key-type',
70 },
71 {
72 xtype: 'displayfield',
73 fieldLabel: gettext('Public Key Size'),
74 name: 'public-key-bits',
75 },
76 ],
77 column2: [
78 {
79 xtype: 'displayfield',
80 fieldLabel: gettext('Valid Since'),
81 renderer: Proxmox.Utils.render_timestamp,
82 name: 'notbefore',
83 },
84 {
85 xtype: 'displayfield',
86 fieldLabel: gettext('Expires'),
87 renderer: Proxmox.Utils.render_timestamp,
88 name: 'notafter',
89 },
90 ],
91 columnB: [
92 {
93 xtype: 'displayfield',
94 fieldLabel: gettext('Subject Alternative Names'),
95 name: 'san',
96 renderer: PVE.Utils.render_san,
97 },
98 {
99 xtype: 'fieldset',
100 title: gettext('Raw Certificate'),
101 collapsible: true,
102 collapsed: true,
103 items: [{
104 xtype: 'textarea',
105 name: 'pem',
106 editable: false,
107 grow: true,
108 growMax: 350,
109 fieldStyle: {
110 'white-space': 'pre-wrap',
111 'font-family': 'monospace',
112 },
113 }],
114 },
115 ],
116 },
117
118 initComponent: function() {
119 let me = this;
120
121 if (!me.cert) {
122 throw "no cert given";
123 }
124 if (!me.nodename) {
125 throw "no nodename given";
126 }
127
128 me.url = `/nodes/${me.nodename}/certificates/info`;
129 me.callParent();
130
131 // hide OK/Reset button, because we just want to show data
132 me.down('toolbar[dock=bottom]').setVisible(false);
133
134 me.load({
135 success: function(response) {
136 if (Ext.isArray(response.result.data)) {
137 for (const item of response.result.data) {
138 if (item.filename === me.cert) {
139 me.setValues(item);
140 return;
141 }
142 }
143 }
144 },
145 });
146 },
147 });
148
149 Ext.define('PVE.node.CertUpload', {
150 extend: 'Proxmox.window.Edit',
151 xtype: 'pveCertUpload',
152
153 title: gettext('Upload Custom Certificate'),
154 resizable: false,
155 isCreate: true,
156 submitText: gettext('Upload'),
157 method: 'POST',
158 width: 600,
159
160 apiCallDone: function(success, response, options) {
161 if (!success) {
162 return;
163 }
164 let txt = gettext('API server will be restarted to use new certificates, please reload web-interface!');
165 Ext.getBody().mask(txt, ['pve-static-mask']);
166 Ext.defer(() => window.location.reload(true), 10000); // reload after 10 seconds automatically
167 },
168
169 items: {
170 xtype: 'inputpanel',
171 onGetValues: function(values) {
172 values.restart = 1;
173 values.force = 1;
174 if (!values.key) {
175 delete values.key;
176 }
177 return values;
178 },
179 items: [
180 {
181 fieldLabel: gettext('Private Key (Optional)'),
182 labelAlign: 'top',
183 emptyText: gettext('No change'),
184 name: 'key',
185 xtype: 'textarea',
186 },
187 {
188 xtype: 'filebutton',
189 text: gettext('From File'),
190 listeners: {
191 change: function(btn, e, value) {
192 let form = this.up('form');
193 for (const file of e.event.target.files) {
194 PVE.Utils.loadFile(file, res => form.down('field[name=key]').setValue(res));
195 }
196 btn.reset();
197 },
198 },
199 },
200 {
201 fieldLabel: gettext('Certificate Chain'),
202 labelAlign: 'top',
203 allowBlank: false,
204 name: 'certificates',
205 xtype: 'textarea',
206 },
207 {
208 xtype: 'filebutton',
209 text: gettext('From File'),
210 listeners: {
211 change: function(btn, e, value) {
212 let form = this.up('form');
213 for (const file of e.event.target.files) {
214 PVE.Utils.loadFile(file, res => form.down('field[name=certificates]').setValue(res));
215 }
216 btn.reset();
217 },
218 },
219 },
220 ],
221 },
222
223 initComponent: function() {
224 let me = this;
225 if (!me.nodename) {
226 throw "no nodename given";
227 }
228 me.url = `/nodes/${me.nodename}/certificates/custom`;
229
230 me.callParent();
231 },
232 });
233
234 Ext.define('pve-certificate', {
235 extend: 'Ext.data.Model',
236 fields: ['filename', 'fingerprint', 'issuer', 'notafter', 'notbefore', 'subject', 'san', 'public-key-bits', 'public-key-type'],
237 idProperty: 'filename',
238 });
239
240 Ext.define('PVE.node.Certificates', {
241 extend: 'Ext.grid.Panel',
242 xtype: 'pveCertView',
243
244 tbar: [
245 {
246 xtype: 'button',
247 text: gettext('Upload Custom Certificate'),
248 handler: function() {
249 let view = this.up('grid');
250 Ext.create('PVE.node.CertUpload', {
251 nodename: view.nodename,
252 listeners: {
253 destroy: () => view.reload(),
254 },
255 autoShow: true,
256 });
257 },
258 },
259 {
260 xtype: 'proxmoxStdRemoveButton',
261 itemId: 'deletebtn',
262 text: gettext('Delete Custom Certificate'),
263 dangerous: true,
264 selModel: false,
265 getUrl: function(rec) {
266 let view = this.up('grid');
267 return `/nodes/${view.nodename}/certificates/custom?restart=1`;
268 },
269 confirmMsg: gettext('Delete custom certificate and switch to generated one?'),
270 callback: function(options, success, response) {
271 if (success) {
272 let txt = gettext('API server will be restarted to use new certificates, please reload web-interface!');
273 Ext.getBody().mask(txt, ['pve-static-mask']);
274 // reload after 10 seconds automatically
275 Ext.defer(() => window.location.reload(true), 10000);
276 }
277 },
278 },
279 '-',
280 {
281 xtype: 'proxmoxButton',
282 itemId: 'viewbtn',
283 disabled: true,
284 text: gettext('View Certificate'),
285 handler: function() {
286 this.up('grid').viewCertificate();
287 },
288 },
289 ],
290
291 columns: [
292 {
293 header: gettext('File'),
294 width: 150,
295 dataIndex: 'filename',
296 },
297 {
298 header: gettext('Issuer'),
299 flex: 1,
300 dataIndex: 'issuer',
301 },
302 {
303 header: gettext('Subject'),
304 flex: 1,
305 dataIndex: 'subject',
306 },
307 {
308 header: gettext('Public Key Alogrithm'),
309 flex: 1,
310 dataIndex: 'public-key-type',
311 hidden: true,
312 },
313 {
314 header: gettext('Public Key Size'),
315 flex: 1,
316 dataIndex: 'public-key-bits',
317 hidden: true,
318 },
319 {
320 header: gettext('Valid Since'),
321 width: 150,
322 dataIndex: 'notbefore',
323 renderer: Proxmox.Utils.render_timestamp,
324 },
325 {
326 header: gettext('Expires'),
327 width: 150,
328 dataIndex: 'notafter',
329 renderer: Proxmox.Utils.render_timestamp,
330 },
331 {
332 header: gettext('Subject Alternative Names'),
333 flex: 1,
334 dataIndex: 'san',
335 renderer: PVE.Utils.render_san,
336 },
337 {
338 header: gettext('Fingerprint'),
339 dataIndex: 'fingerprint',
340 hidden: true,
341 },
342 {
343 header: gettext('PEM'),
344 dataIndex: 'pem',
345 hidden: true,
346 },
347 ],
348
349 reload: function() {
350 this.rstore.load();
351 },
352
353 viewCertificate: function() {
354 let me = this;
355 let selection = me.getSelection();
356 if (!selection || selection.length < 1) {
357 return;
358 }
359 var win = Ext.create('PVE.node.CertificateViewer', {
360 cert: selection[0].data.filename,
361 nodename: me.nodename,
362 });
363 win.show();
364 },
365
366 listeners: {
367 itemdblclick: 'viewCertificate',
368 },
369
370 initComponent: function() {
371 var me = this;
372
373 if (!me.nodename) {
374 throw "no nodename given";
375 }
376
377 me.rstore = Ext.create('Proxmox.data.UpdateStore', {
378 storeid: 'certs-' + me.nodename,
379 model: 'pve-certificate',
380 proxy: {
381 type: 'proxmox',
382 url: '/api2/json/nodes/' + me.nodename + '/certificates/info',
383 },
384 });
385
386 me.store = {
387 type: 'diff',
388 rstore: me.rstore,
389 };
390
391 me.callParent();
392
393 me.mon(me.rstore, 'load', store => me.down('#deletebtn').setDisabled(!store.getById('pveproxy-ssl.pem')));
394 me.rstore.startUpdate();
395 me.on('destroy', me.rstore.stopUpdate, me.rstore);
396 },
397 });