]> git.proxmox.com Git - proxmox-widget-toolkit.git/blame - src/panel/TfaView.js
tfa view: fix WebAuthn casing
[proxmox-widget-toolkit.git] / src / panel / TfaView.js
CommitLineData
ab2538f5
WB
1Ext.define('pmx-tfa-users', {
2 extend: 'Ext.data.Model',
3 fields: ['userid'],
4 idProperty: 'userid',
5 proxy: {
6 type: 'proxmox',
7 url: '/api2/json/access/tfa',
8 },
9});
10
11Ext.define('pmx-tfa-entry', {
12 extend: 'Ext.data.Model',
13 fields: ['fullid', 'userid', 'type', 'description', 'created', 'enable'],
14 idProperty: 'fullid',
15});
16
17
18Ext.define('Proxmox.panel.TfaView', {
19 extend: 'Ext.grid.GridPanel',
20 alias: 'widget.pmxTfaView',
20b39dd8 21 mixins: ['Proxmox.Mixin.CBind'],
ab2538f5
WB
22
23 title: gettext('Second Factors'),
24 reference: 'tfaview',
25
26 issuerName: 'Proxmox',
20b39dd8
WB
27 yubicoEnabled: false,
28
29 cbindData: function(initialConfig) {
30 let me = this;
31 return {
32 yubicoEnabled: me.yubicoEnabled,
33 };
34 },
ab2538f5
WB
35
36 store: {
37 type: 'diff',
38 autoDestroy: true,
39 autoDestroyRstore: true,
40 model: 'pmx-tfa-entry',
41 rstore: {
42 type: 'store',
43 proxy: 'memory',
44 storeid: 'pmx-tfa-entry',
45 model: 'pmx-tfa-entry',
46 },
47 },
48
49 controller: {
50 xclass: 'Ext.app.ViewController',
51
52 init: function(view) {
53 let me = this;
54 view.tfaStore = Ext.create('Proxmox.data.UpdateStore', {
55 autoStart: true,
56 interval: 5 * 1000,
57 storeid: 'pmx-tfa-users',
58 model: 'pmx-tfa-users',
59 });
60 view.tfaStore.on('load', this.onLoad, this);
61 view.on('destroy', view.tfaStore.stopUpdate);
62 Proxmox.Utils.monStoreErrors(view, view.tfaStore);
63 },
64
65 reload: function() { this.getView().tfaStore.load(); },
66
67 onLoad: function(store, data, success) {
68 if (!success) return;
69
70 let records = [];
71 Ext.Array.each(data, user => {
72 Ext.Array.each(user.data.entries, entry => {
73 records.push({
74 fullid: `${user.id}/${entry.id}`,
75 userid: user.id,
76 type: entry.type,
77 description: entry.description,
78 created: entry.created,
79 enable: entry.enable,
80 });
81 });
82 });
83
84 let rstore = this.getView().store.rstore;
85 rstore.loadData(records);
86 rstore.fireEvent('load', rstore, records, true);
87 },
88
89 addTotp: function() {
90 let me = this;
91
92 Ext.create('Proxmox.window.AddTotp', {
93 isCreate: true,
94 issuerName: me.getView().issuerName,
95 listeners: {
96 destroy: function() {
97 me.reload();
98 },
99 },
100 }).show();
101 },
102
103 addWebauthn: function() {
104 let me = this;
105
106 Ext.create('Proxmox.window.AddWebauthn', {
107 isCreate: true,
82a38653 108 autoShow: true,
ab2538f5 109 listeners: {
82a38653 110 destroy: () => me.reload(),
ab2538f5 111 },
82a38653 112 });
ab2538f5
WB
113 },
114
115 addRecovery: async function() {
116 let me = this;
117
118 Ext.create('Proxmox.window.AddTfaRecovery', {
82a38653 119 autoShow: true,
ab2538f5 120 listeners: {
82a38653 121 destroy: () => me.reload(),
ab2538f5 122 },
82a38653 123 });
ab2538f5
WB
124 },
125
20b39dd8
WB
126 addYubico: function() {
127 let me = this;
128
129 Ext.create('Proxmox.window.AddYubico', {
130 isCreate: true,
82a38653 131 autoShow: true,
20b39dd8 132 listeners: {
82a38653 133 destroy: () => me.reload(),
20b39dd8 134 },
82a38653 135 });
20b39dd8
WB
136 },
137
ab2538f5
WB
138 editItem: function() {
139 let me = this;
140 let view = me.getView();
141 let selection = view.getSelection();
142 if (selection.length !== 1 || selection[0].id.endsWith("/recovery")) {
143 return;
144 }
145
146 Ext.create('Proxmox.window.TfaEdit', {
147 'tfa-id': selection[0].data.fullid,
82a38653 148 autoShow: true,
ab2538f5 149 listeners: {
82a38653 150 destroy: () => me.reload(),
ab2538f5 151 },
82a38653 152 });
ab2538f5
WB
153 },
154
155 renderUser: fullid => fullid.split('/')[0],
156
157 renderEnabled: enabled => {
158 if (enabled === undefined) {
159 return Proxmox.Utils.yesText;
160 } else {
161 return Proxmox.Utils.format_boolean(enabled);
162 }
163 },
164
165 onRemoveButton: function(btn, event, record) {
166 let me = this;
167
168 Ext.create('Proxmox.tfa.confirmRemove', {
169 ...record.data,
170 callback: password => me.removeItem(password, record),
82a38653
TL
171 autoShow: true,
172 });
ab2538f5
WB
173 },
174
175 removeItem: async function(password, record) {
176 let me = this;
177
178 if (password !== null) {
179 password = '?password=' + encodeURIComponent(password);
180 } else {
181 password = '';
182 }
183
184 try {
185 me.getView().mask(gettext('Please wait...'), 'x-mask-loading');
186 await Proxmox.Async.api2({
187 url: `/api2/extjs/access/tfa/${record.id}${password}`,
188 method: 'DELETE',
189 });
190 me.reload();
191 } catch (response) {
192 Ext.Msg.alert(gettext('Error'), response.result.message);
193 } finally {
194 me.getView().unmask();
195 }
196 },
197 },
198
199 viewConfig: {
200 trackOver: false,
201 },
202
203 listeners: {
204 itemdblclick: 'editItem',
205 },
206
207 columns: [
208 {
209 header: gettext('User'),
210 width: 200,
211 sortable: true,
212 dataIndex: 'fullid',
213 renderer: 'renderUser',
214 },
215 {
216 header: gettext('Enabled'),
217 width: 80,
218 sortable: true,
219 dataIndex: 'enable',
220 renderer: 'renderEnabled',
221 },
222 {
223 header: gettext('TFA Type'),
224 width: 80,
225 sortable: true,
226 dataIndex: 'type',
227 },
228 {
229 header: gettext('Created'),
230 width: 150,
231 sortable: true,
232 dataIndex: 'created',
65c39bc0 233 renderer: t => !t ? 'N/A' : Proxmox.Utils.render_timestamp(t),
ab2538f5
WB
234 },
235 {
236 header: gettext('Description'),
237 width: 300,
238 sortable: true,
239 dataIndex: 'description',
240 renderer: Ext.String.htmlEncode,
241 flex: 1,
242 },
243 ],
244
245 tbar: [
246 {
247 text: gettext('Add'),
20b39dd8 248 cbind: {},
ab2538f5
WB
249 menu: {
250 xtype: 'menu',
251 items: [
252 {
253 text: gettext('TOTP'),
254 itemId: 'totp',
255 iconCls: 'fa fa-fw fa-clock-o',
256 handler: 'addTotp',
257 },
258 {
bfc6233d 259 text: gettext('WebAuthn'),
ab2538f5
WB
260 itemId: 'webauthn',
261 iconCls: 'fa fa-fw fa-shield',
262 handler: 'addWebauthn',
263 },
264 {
265 text: gettext('Recovery Keys'),
266 itemId: 'recovery',
267 iconCls: 'fa fa-fw fa-file-text-o',
268 handler: 'addRecovery',
269 },
20b39dd8 270 {
05da27ed 271 text: gettext('Yubico OTP'),
20b39dd8 272 itemId: 'yubico',
05da27ed 273 iconCls: 'fa fa-fw fa-yahoo', // close enough
20b39dd8
WB
274 handler: 'addYubico',
275 cbind: {
276 hidden: '{!yubicoEnabled}',
277 },
278 },
ab2538f5
WB
279 ],
280 },
281 },
282 '-',
283 {
284 xtype: 'proxmoxButton',
285 text: gettext('Edit'),
286 handler: 'editItem',
287 enableFn: rec => !rec.id.endsWith("/recovery"),
288 disabled: true,
289 },
290 {
291 xtype: 'proxmoxButton',
292 disabled: true,
293 text: gettext('Remove'),
294 getRecordName: rec => rec.data.description,
295 handler: 'onRemoveButton',
296 },
297 ],
298});