]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/node/Certificates.js
ui: cert upload: fix private key field sending empty string
[pve-manager.git] / www / manager6 / node / Certificates.js
CommitLineData
c287e233
DC
1Ext.define('PVE.node.CertificateView', {
2 extend: 'Ext.container.Container',
3 xtype: 'pveCertificatesView',
4
16dedd0f
DC
5 onlineHelp: 'sysadmin_certificate_management',
6
8058410f 7 mixins: ['Proxmox.Mixin.CBind'],
d3d48736 8 scrollable: 'y',
c287e233
DC
9
10 items: [
11 {
12 xtype: 'pveCertView',
13 border: 0,
14 cbind: {
f6710aac
TL
15 nodename: '{nodename}',
16 },
c287e233
DC
17 },
18 {
19 xtype: 'pveACMEView',
20 border: 0,
21 cbind: {
f6710aac
TL
22 nodename: '{nodename}',
23 },
24 },
25 ],
c287e233
DC
26
27});
28
29Ext.define('PVE.node.CertificateViewer', {
30 extend: 'Proxmox.window.Edit',
31
32 title: gettext('Certificate'),
33
34 fieldDefaults: {
f6710aac 35 labelWidth: 120,
c287e233
DC
36 },
37 width: 800,
c287e233 38
2f9fc811
TL
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 },
c287e233
DC
117
118 initComponent: function() {
48bbb009 119 let me = this;
c287e233
DC
120
121 if (!me.cert) {
122 throw "no cert given";
123 }
c287e233
DC
124 if (!me.nodename) {
125 throw "no nodename given";
126 }
127
48bbb009 128 me.url = `/nodes/${me.nodename}/certificates/info`;
c287e233
DC
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)) {
48bbb009 137 for (const item of response.result.data) {
c287e233
DC
138 if (item.filename === me.cert) {
139 me.setValues(item);
48bbb009 140 return;
c287e233 141 }
48bbb009 142 }
c287e233 143 }
f6710aac 144 },
c287e233 145 });
f6710aac 146 },
c287e233
DC
147});
148
149Ext.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 }
48bbb009 164 let txt = gettext('API server will be restarted to use new certificates, please reload web-interface!');
c287e233 165 Ext.getBody().mask(txt, ['pve-static-mask']);
48bbb009 166 Ext.defer(() => window.location.reload(true), 10000); // reload after 10 seconds automatically
c287e233
DC
167 },
168
1904262b
MC
169 items: {
170 xtype: 'inputpanel',
171 onGetValues: function(values) {
172 values.restart = 1;
173 values.force = 1;
9536d5ef
MC
174 if (!values.key) {
175 delete values.key;
176 }
1904262b 177 return values;
c287e233 178 },
1904262b
MC
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 },
f6710aac
TL
198 },
199 },
1904262b
MC
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 },
f6710aac
TL
218 },
219 },
1904262b
MC
220 ],
221 },
c287e233
DC
222
223 initComponent: function() {
48bbb009 224 let me = this;
c287e233
DC
225 if (!me.nodename) {
226 throw "no nodename given";
227 }
48bbb009 228 me.url = `/nodes/${me.nodename}/certificates/custom`;
c287e233
DC
229
230 me.callParent();
f6710aac 231 },
c287e233
DC
232});
233
234Ext.define('pve-certificate', {
235 extend: 'Ext.data.Model',
8058410f 236 fields: ['filename', 'fingerprint', 'issuer', 'notafter', 'notbefore', 'subject', 'san', 'public-key-bits', 'public-key-type'],
f6710aac 237 idProperty: 'filename',
c287e233
DC
238});
239
240Ext.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() {
48bbb009
TL
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,
c287e233 256 });
f6710aac 257 },
c287e233
DC
258 },
259 {
028e4cd3 260 xtype: 'proxmoxStdRemoveButton',
c287e233
DC
261 itemId: 'deletebtn',
262 text: gettext('Delete Custom Certificate'),
028e4cd3
TL
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 }
f6710aac 277 },
c287e233
DC
278 },
279 '-',
280 {
281 xtype: 'proxmoxButton',
282 itemId: 'viewbtn',
283 disabled: true,
284 text: gettext('View Certificate'),
285 handler: function() {
48bbb009 286 this.up('grid').viewCertificate();
f6710aac
TL
287 },
288 },
c287e233
DC
289 ],
290
291 columns: [
292 {
293 header: gettext('File'),
294 width: 150,
f6710aac 295 dataIndex: 'filename',
c287e233
DC
296 },
297 {
298 header: gettext('Issuer'),
299 flex: 1,
f6710aac 300 dataIndex: 'issuer',
c287e233
DC
301 },
302 {
303 header: gettext('Subject'),
304 flex: 1,
f6710aac 305 dataIndex: 'subject',
c287e233 306 },
e81645f5
AA
307 {
308 header: gettext('Public Key Alogrithm'),
309 flex: 1,
310 dataIndex: 'public-key-type',
f6710aac 311 hidden: true,
e81645f5
AA
312 },
313 {
314 header: gettext('Public Key Size'),
315 flex: 1,
316 dataIndex: 'public-key-bits',
f6710aac 317 hidden: true,
e81645f5 318 },
c287e233
DC
319 {
320 header: gettext('Valid Since'),
321 width: 150,
322 dataIndex: 'notbefore',
f6710aac 323 renderer: Proxmox.Utils.render_timestamp,
c287e233
DC
324 },
325 {
326 header: gettext('Expires'),
327 width: 150,
328 dataIndex: 'notafter',
f6710aac 329 renderer: Proxmox.Utils.render_timestamp,
c287e233
DC
330 },
331 {
332 header: gettext('Subject Alternative Names'),
333 flex: 1,
334 dataIndex: 'san',
f6710aac 335 renderer: PVE.Utils.render_san,
c287e233
DC
336 },
337 {
338 header: gettext('Fingerprint'),
339 dataIndex: 'fingerprint',
f6710aac 340 hidden: true,
c287e233
DC
341 },
342 {
343 header: gettext('PEM'),
344 dataIndex: 'pem',
f6710aac
TL
345 hidden: true,
346 },
c287e233
DC
347 ],
348
349 reload: function() {
48bbb009 350 this.rstore.load();
c287e233
DC
351 },
352
48bbb009
TL
353 viewCertificate: function() {
354 let me = this;
355 let selection = me.getSelection();
c287e233
DC
356 if (!selection || selection.length < 1) {
357 return;
358 }
359 var win = Ext.create('PVE.node.CertificateViewer', {
360 cert: selection[0].data.filename,
8058410f 361 nodename: me.nodename,
c287e233
DC
362 });
363 win.show();
364 },
365
366 listeners: {
48bbb009 367 itemdblclick: 'viewCertificate',
c287e233
DC
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',
f6710aac
TL
382 url: '/api2/json/nodes/' + me.nodename + '/certificates/info',
383 },
c287e233
DC
384 });
385
386 me.store = {
387 type: 'diff',
f6710aac 388 rstore: me.rstore,
c287e233
DC
389 };
390
391 me.callParent();
392
48bbb009 393 me.mon(me.rstore, 'load', store => me.down('#deletebtn').setDisabled(!store.getById('pveproxy-ssl.pem')));
c287e233 394 me.rstore.startUpdate();
1f249769 395 me.on('destroy', me.rstore.stopUpdate, me.rstore);
f6710aac 396 },
c287e233 397});