]>
Commit | Line | Data |
---|---|---|
60fead29 WB |
1 | Ext.define('Proxmox.panel.Certificates', { |
2 | extend: 'Ext.grid.Panel', | |
3 | xtype: 'pmxCertificates', | |
4 | ||
5 | // array of { name, id (=filename), url, deletable, reloadUi } | |
6 | uploadButtons: undefined, | |
7 | ||
8 | // The /info path for the current node. | |
9 | infoUrl: undefined, | |
10 | ||
11 | columns: [ | |
12 | { | |
13 | header: gettext('File'), | |
14 | width: 150, | |
15 | dataIndex: 'filename', | |
16 | }, | |
17 | { | |
18 | header: gettext('Issuer'), | |
19 | flex: 1, | |
20 | dataIndex: 'issuer', | |
21 | }, | |
22 | { | |
23 | header: gettext('Subject'), | |
24 | flex: 1, | |
25 | dataIndex: 'subject', | |
26 | }, | |
27 | { | |
28 | header: gettext('Public Key Alogrithm'), | |
29 | flex: 1, | |
30 | dataIndex: 'public-key-type', | |
31 | hidden: true, | |
32 | }, | |
33 | { | |
34 | header: gettext('Public Key Size'), | |
35 | flex: 1, | |
36 | dataIndex: 'public-key-bits', | |
37 | hidden: true, | |
38 | }, | |
39 | { | |
40 | header: gettext('Valid Since'), | |
41 | width: 150, | |
42 | dataIndex: 'notbefore', | |
43 | renderer: Proxmox.Utils.render_timestamp, | |
44 | }, | |
45 | { | |
46 | header: gettext('Expires'), | |
47 | width: 150, | |
48 | dataIndex: 'notafter', | |
49 | renderer: Proxmox.Utils.render_timestamp, | |
50 | }, | |
51 | { | |
52 | header: gettext('Subject Alternative Names'), | |
53 | flex: 1, | |
54 | dataIndex: 'san', | |
55 | renderer: Proxmox.Utils.render_san, | |
56 | }, | |
57 | { | |
58 | header: gettext('Fingerprint'), | |
59 | dataIndex: 'fingerprint', | |
60 | hidden: true, | |
61 | }, | |
62 | { | |
63 | header: gettext('PEM'), | |
64 | dataIndex: 'pem', | |
65 | hidden: true, | |
66 | }, | |
67 | ], | |
68 | ||
69 | reload: function() { | |
70 | let me = this; | |
71 | me.rstore.load(); | |
72 | }, | |
73 | ||
74 | delete_certificate: function() { | |
75 | let me = this; | |
76 | ||
77 | let rec = me.selModel.getSelection()[0]; | |
78 | if (!rec) { | |
79 | return; | |
80 | } | |
81 | ||
82 | let cert = me.certById[rec.id]; | |
83 | let url = cert.url; | |
84 | Proxmox.Utils.API2Request({ | |
85 | url: `/api2/extjs/${url}?restart=1`, | |
86 | method: 'DELETE', | |
87 | success: function(response, opt) { | |
88 | if (cert.reloadUid) { | |
08d092b6 TL |
89 | Ext.getBody().mask( |
90 | gettext('API server will be restarted to use new certificates, please reload web-interface!'), | |
91 | ['pve-static-mask'], | |
92 | ); | |
93 | // try to reload after 10 seconds automatically | |
94 | Ext.defer(() => window.location.reload(true), 10000); | |
60fead29 WB |
95 | } |
96 | }, | |
97 | failure: function(response, opt) { | |
98 | Ext.Msg.alert(gettext('Error'), response.htmlStatus); | |
99 | }, | |
100 | }); | |
101 | }, | |
102 | ||
103 | controller: { | |
104 | xclass: 'Ext.app.ViewController', | |
105 | view_certificate: function() { | |
106 | let me = this; | |
107 | let view = me.getView(); | |
108 | ||
109 | let selection = view.getSelection(); | |
110 | if (!selection || selection.length < 1) { | |
111 | return; | |
112 | } | |
113 | let win = Ext.create('Proxmox.window.CertificateViewer', { | |
114 | cert: selection[0].data.filename, | |
115 | url: `/api2/extjs/${view.infoUrl}`, | |
116 | }); | |
117 | win.show(); | |
118 | }, | |
119 | }, | |
120 | ||
121 | listeners: { | |
122 | itemdblclick: 'view_certificate', | |
123 | }, | |
124 | ||
125 | initComponent: function() { | |
126 | let me = this; | |
127 | ||
128 | if (!me.nodename) { | |
129 | // only used for the store name | |
130 | me.nodename = "_all"; | |
131 | } | |
132 | ||
133 | if (!me.uploadButtons) { | |
134 | throw "no upload buttons defined"; | |
135 | } | |
136 | ||
137 | if (!me.infoUrl) { | |
138 | throw "no certificate store url given"; | |
139 | } | |
140 | ||
141 | me.rstore = Ext.create('Proxmox.data.UpdateStore', { | |
142 | storeid: 'certs-' + me.nodename, | |
143 | model: 'proxmox-certificate', | |
144 | proxy: { | |
145 | type: 'proxmox', | |
146 | url: `/api2/extjs/${me.infoUrl}`, | |
147 | }, | |
148 | }); | |
149 | ||
150 | me.store = { | |
151 | type: 'diff', | |
152 | rstore: me.rstore, | |
153 | }; | |
154 | ||
155 | let tbar = []; | |
156 | ||
157 | me.deletableCertIds = {}; | |
158 | me.certById = {}; | |
159 | if (me.uploadButtons.length === 1) { | |
160 | let cert = me.uploadButtons[0]; | |
161 | ||
162 | if (!cert.url) { | |
163 | throw "missing certificate url"; | |
164 | } | |
165 | ||
166 | me.certById[cert.id] = cert; | |
167 | ||
168 | if (cert.deletable) { | |
169 | me.deletableCertIds[cert.id] = true; | |
170 | } | |
171 | ||
172 | tbar.push( | |
173 | { | |
174 | xtype: 'button', | |
175 | text: gettext('Upload Custom Certificate'), | |
176 | handler: function() { | |
177 | let grid = this.up('grid'); | |
178 | let win = Ext.create('Proxmox.window.CertificateUpload', { | |
179 | url: `/api2/extjs/${cert.url}`, | |
180 | reloadUi: cert.reloadUi, | |
181 | }); | |
182 | win.show(); | |
183 | win.on('destroy', grid.reload, grid); | |
184 | }, | |
185 | }, | |
186 | ); | |
187 | } else { | |
188 | let items = []; | |
189 | ||
190 | me.selModel = Ext.create('Ext.selection.RowModel', {}); | |
191 | ||
192 | for (const cert of me.uploadButtons) { | |
193 | if (!cert.id) { | |
194 | throw "missing id in certificate entry"; | |
195 | } | |
196 | ||
197 | if (!cert.url) { | |
198 | throw "missing url in certificate entry"; | |
199 | } | |
200 | ||
201 | if (!cert.name) { | |
202 | throw "missing name in certificate entry"; | |
203 | } | |
204 | ||
205 | me.certById[cert.id] = cert; | |
206 | ||
207 | if (cert.deletable) { | |
208 | me.deletableCertIds[cert.id] = true; | |
209 | } | |
210 | ||
211 | items.push({ | |
212 | text: Ext.String.format('Upload {0} Certificate', cert.name), | |
213 | handler: function() { | |
214 | let grid = this.up('grid'); | |
215 | let win = Ext.create('Proxmox.window.CertificateUpload', { | |
216 | url: `/api2/extjs/${cert.url}`, | |
217 | reloadUi: cert.reloadUi, | |
218 | }); | |
219 | win.show(); | |
220 | win.on('destroy', grid.reload, grid); | |
221 | }, | |
222 | }); | |
223 | } | |
224 | ||
225 | tbar.push( | |
226 | { | |
227 | text: gettext('Upload Custom Certificate'), | |
228 | menu: { | |
229 | xtype: 'menu', | |
230 | items, | |
231 | }, | |
232 | }, | |
233 | ); | |
234 | } | |
235 | ||
236 | tbar.push( | |
237 | { | |
238 | xtype: 'proxmoxButton', | |
239 | text: gettext('Delete Custom Certificate'), | |
240 | confirmMsg: rec => Ext.String.format( | |
241 | gettext('Are you sure you want to remove the certificate used for {0}'), | |
242 | me.certById[rec.id].name, | |
243 | ), | |
244 | callback: () => me.reload(), | |
245 | selModel: me.selModel, | |
246 | disabled: true, | |
247 | enableFn: rec => !!me.deletableCertIds[rec.id], | |
248 | handler: function() { me.delete_certificate(); }, | |
249 | }, | |
250 | '-', | |
251 | { | |
252 | xtype: 'proxmoxButton', | |
253 | itemId: 'viewbtn', | |
254 | disabled: true, | |
255 | text: gettext('View Certificate'), | |
256 | handler: 'view_certificate', | |
257 | }, | |
258 | ); | |
259 | Ext.apply(me, { tbar }); | |
260 | ||
261 | me.callParent(); | |
262 | ||
263 | me.rstore.startUpdate(); | |
264 | me.on('destroy', me.rstore.stopUpdate, me.rstore); | |
265 | }, | |
266 | }); |