]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/Workspace.js
www: redirect user TFA button to TFA view
[pve-manager.git] / www / manager6 / Workspace.js
1 /*
2 * Workspace base class
3 *
4 * popup login window when auth fails (call onLogin handler)
5 * update (re-login) ticket every 15 minutes
6 *
7 */
8
9 Ext.define('PVE.Workspace', {
10 extend: 'Ext.container.Viewport',
11
12 title: 'Proxmox Virtual Environment',
13
14 loginData: null, // Data from last login call
15
16 onLogin: function(loginData) {
17 // override me
18 },
19
20 // private
21 updateLoginData: function(loginData) {
22 let me = this;
23 me.loginData = loginData;
24 Proxmox.Utils.setAuthData(loginData);
25
26 let rt = me.down('pveResourceTree');
27 rt.setDatacenterText(loginData.clustername);
28 PVE.ClusterName = loginData.clustername;
29
30 if (loginData.cap) {
31 Ext.state.Manager.set('GuiCap', loginData.cap);
32 }
33 me.response401count = 0;
34
35 me.onLogin(loginData);
36 },
37
38 // private
39 showLogin: function() {
40 let me = this;
41
42 Proxmox.Utils.authClear();
43 Ext.state.Manager.clear('GuiCap');
44 Proxmox.UserName = null;
45 me.loginData = null;
46
47 if (!me.login) {
48 me.login = Ext.create('PVE.window.LoginWindow', {
49 handler: function(data) {
50 me.login = null;
51 me.updateLoginData(data);
52 Proxmox.Utils.checked_command(Ext.emptyFn); // display subscription status
53 },
54 });
55 }
56 me.onLogin(null);
57 me.login.show();
58 },
59
60 initComponent: function() {
61 let me = this;
62
63 Ext.tip.QuickTipManager.init();
64
65 // fixme: what about other errors
66 Ext.Ajax.on('requestexception', function(conn, response, options) {
67 if ((response.status === 401 || response.status === '401') && !PVE.Utils.silenceAuthFailures) { // auth failure
68 // don't immediately show as logged out to cope better with some big
69 // upgrades, which may temporarily produce a false positive 401 err
70 me.response401count++;
71 if (me.response401count > 5) {
72 me.showLogin();
73 }
74 }
75 });
76
77 me.callParent();
78
79 if (!Proxmox.Utils.authOK()) {
80 me.showLogin();
81 } else if (me.loginData) {
82 me.onLogin(me.loginData);
83 }
84
85 Ext.TaskManager.start({
86 run: function() {
87 let ticket = Proxmox.Utils.authOK();
88 if (!ticket || !Proxmox.UserName) {
89 return;
90 }
91
92 Ext.Ajax.request({
93 params: {
94 username: Proxmox.UserName,
95 password: ticket,
96 },
97 url: '/api2/json/access/ticket',
98 method: 'POST',
99 success: function(response, opts) {
100 let obj = Ext.decode(response.responseText);
101 me.updateLoginData(obj.data);
102 },
103 });
104 },
105 interval: 15 * 60 * 1000,
106 });
107 },
108 });
109
110 Ext.define('PVE.StdWorkspace', {
111 extend: 'PVE.Workspace',
112
113 alias: ['widget.pveStdWorkspace'],
114
115 // private
116 setContent: function(comp) {
117 let me = this;
118
119 let view = me.child('#content');
120 let layout = view.getLayout();
121 let current = layout.getActiveItem();
122
123 if (comp) {
124 Proxmox.Utils.setErrorMask(view, false);
125 comp.border = false;
126 view.add(comp);
127 if (current !== null && layout.getNext()) {
128 layout.next();
129 let task = Ext.create('Ext.util.DelayedTask', function() {
130 view.remove(current);
131 });
132 task.delay(10);
133 }
134 } else {
135 view.removeAll(); // helper for cleaning the content when logging out
136 }
137 },
138
139 selectById: function(nodeid) {
140 let me = this;
141 me.down('pveResourceTree').selectById(nodeid);
142 },
143
144 onLogin: function(loginData) {
145 let me = this;
146
147 me.updateUserInfo();
148
149 if (loginData) {
150 PVE.data.ResourceStore.startUpdate();
151
152 Proxmox.Utils.API2Request({
153 url: '/version',
154 method: 'GET',
155 success: function(response) {
156 PVE.VersionInfo = response.result.data;
157 me.updateVersionInfo();
158 },
159 });
160
161 Proxmox.Utils.API2Request({
162 url: '/cluster/sdn',
163 method: 'GET',
164 success: function(response) {
165 PVE.SDNInfo = response.result.data;
166 },
167 failure: function(response) {
168 PVE.SDNInfo = null;
169 let ui = Ext.ComponentQuery.query('treelistitem[text="SDN"]')[0];
170 if (ui) {
171 ui.addCls('x-hidden-display');
172 }
173 },
174 });
175
176 Proxmox.Utils.API2Request({
177 url: '/access/domains',
178 method: 'GET',
179 success: function(response) {
180 let [_username, realm] = Proxmox.Utils.parse_userid(Proxmox.UserName);
181 response.result.data.forEach((domain) => {
182 if (domain.realm === realm) {
183 let schema = PVE.Utils.authSchema[domain.type];
184 if (schema) {
185 me.query('#tfaitem')[0].setHidden(!schema.tfa);
186 me.query('#passworditem')[0].setHidden(!schema.pwchange);
187 }
188 }
189 });
190 },
191 });
192 }
193 },
194
195 updateUserInfo: function() {
196 let me = this;
197 let ui = me.query('#userinfo')[0];
198 ui.setText(Ext.String.htmlEncode(Proxmox.UserName || ''));
199 ui.updateLayout();
200 },
201
202 updateVersionInfo: function() {
203 let me = this;
204
205 let ui = me.query('#versioninfo')[0];
206
207 if (PVE.VersionInfo) {
208 let version = PVE.VersionInfo.version;
209 ui.update('Virtual Environment ' + version);
210 } else {
211 ui.update('Virtual Environment');
212 }
213 ui.updateLayout();
214 },
215
216 initComponent: function() {
217 let me = this;
218
219 Ext.History.init();
220
221 let appState = Ext.create('PVE.StateProvider');
222 Ext.state.Manager.setProvider(appState);
223
224 let selview = Ext.create('PVE.form.ViewSelector');
225
226 let rtree = Ext.createWidget('pveResourceTree', {
227 viewFilter: selview.getViewFilter(),
228 flex: 1,
229 selModel: {
230 selType: 'treemodel',
231 listeners: {
232 selectionchange: function(sm, selected) {
233 if (selected.length <= 0) {
234 return;
235 }
236 let treeNode = selected[0];
237 let treeTypeToClass = {
238 root: 'PVE.dc.Config',
239 node: 'PVE.node.Config',
240 qemu: 'PVE.qemu.Config',
241 lxc: 'PVE.lxc.Config',
242 storage: 'PVE.storage.Browser',
243 sdn: 'PVE.sdn.Browser',
244 pool: 'pvePoolConfig',
245 };
246 PVE.curSelectedNode = treeNode;
247 me.setContent({
248 xtype: treeTypeToClass[treeNode.data.type || 'root'] || 'pvePanelConfig',
249 showSearch: treeNode.data.id === 'root' || Ext.isDefined(treeNode.data.groupbyid),
250 pveSelNode: treeNode,
251 workspace: me,
252 viewFilter: selview.getViewFilter(),
253 });
254 },
255 },
256 },
257 });
258
259 selview.on('select', function(combo, records) {
260 if (records) {
261 let view = combo.getViewFilter();
262 rtree.setViewFilter(view);
263 }
264 });
265
266 let caps = appState.get('GuiCap');
267
268 let createVM = Ext.createWidget('button', {
269 pack: 'end',
270 margin: '3 5 0 0',
271 baseCls: 'x-btn',
272 iconCls: 'fa fa-desktop',
273 text: gettext("Create VM"),
274 disabled: !caps.vms['VM.Allocate'],
275 handler: function() {
276 let wiz = Ext.create('PVE.qemu.CreateWizard', {});
277 wiz.show();
278 },
279 });
280
281 let createCT = Ext.createWidget('button', {
282 pack: 'end',
283 margin: '3 5 0 0',
284 baseCls: 'x-btn',
285 iconCls: 'fa fa-cube',
286 text: gettext("Create CT"),
287 disabled: !caps.vms['VM.Allocate'],
288 handler: function() {
289 let wiz = Ext.create('PVE.lxc.CreateWizard', {});
290 wiz.show();
291 },
292 });
293
294 appState.on('statechange', function(sp, key, value) {
295 if (key === 'GuiCap' && value) {
296 caps = value;
297 createVM.setDisabled(!caps.vms['VM.Allocate']);
298 createCT.setDisabled(!caps.vms['VM.Allocate']);
299 }
300 });
301
302 Ext.apply(me, {
303 layout: { type: 'border' },
304 border: false,
305 items: [
306 {
307 region: 'north',
308 title: gettext('Header'), // for ARIA
309 header: false, // avoid rendering the title
310 layout: {
311 type: 'hbox',
312 align: 'middle',
313 },
314 baseCls: 'x-plain',
315 defaults: {
316 baseCls: 'x-plain',
317 },
318 border: false,
319 margin: '2 0 2 5',
320 items: [
321 {
322 xtype: 'proxmoxlogo',
323 },
324 {
325 minWidth: 150,
326 id: 'versioninfo',
327 html: 'Virtual Environment',
328 style: {
329 'font-size': '14px',
330 'line-height': '18px',
331 },
332 },
333 {
334 xtype: 'pveGlobalSearchField',
335 tree: rtree,
336 },
337 {
338 flex: 1,
339 },
340 {
341 xtype: 'proxmoxHelpButton',
342 hidden: false,
343 baseCls: 'x-btn',
344 iconCls: 'fa fa-book x-btn-icon-el-default-toolbar-small ',
345 listenToGlobalEvent: false,
346 onlineHelp: 'pve_documentation_index',
347 text: gettext('Documentation'),
348 margin: '0 5 0 0',
349 },
350 createVM,
351 createCT,
352 {
353 pack: 'end',
354 margin: '0 5 0 0',
355 id: 'userinfo',
356 xtype: 'button',
357 baseCls: 'x-btn',
358 style: {
359 // proxmox dark grey p light grey as border
360 backgroundColor: '#464d4d',
361 borderColor: '#ABBABA',
362 },
363 iconCls: 'fa fa-user',
364 menu: [
365 {
366 iconCls: 'fa fa-gear',
367 text: gettext('My Settings'),
368 handler: function() {
369 var win = Ext.create('PVE.window.Settings');
370 win.show();
371 },
372 },
373 {
374 text: gettext('Password'),
375 itemId: 'passworditem',
376 iconCls: 'fa fa-fw fa-key',
377 handler: function() {
378 var win = Ext.create('Proxmox.window.PasswordEdit', {
379 userid: Proxmox.UserName,
380 });
381 win.show();
382 },
383 },
384 {
385 text: 'TFA',
386 itemId: 'tfaitem',
387 iconCls: 'fa fa-fw fa-lock',
388 handler: function(btn, event, rec) {
389 Ext.state.Manager.getProvider().set('dctab', { value: 'tfa' }, true);
390 me.selectById('root');
391 },
392 },
393 {
394 iconCls: 'fa fa-language',
395 text: gettext('Language'),
396 handler: function() {
397 Ext.create('Proxmox.window.LanguageEditWindow')
398 .show();
399 },
400 },
401 '-',
402 {
403 iconCls: 'fa fa-fw fa-sign-out',
404 text: gettext("Logout"),
405 handler: function() {
406 PVE.data.ResourceStore.loadData([], false);
407 me.showLogin();
408 me.setContent(null);
409 var rt = me.down('pveResourceTree');
410 rt.setDatacenterText(undefined);
411 rt.clearTree();
412
413 // empty the stores of the StatusPanel child items
414 var statusPanels = Ext.ComponentQuery.query('pveStatusPanel grid');
415 Ext.Array.forEach(statusPanels, function(comp) {
416 if (comp.getStore()) {
417 comp.getStore().loadData([], false);
418 }
419 });
420 },
421 },
422 ],
423 },
424 ],
425 },
426 {
427 region: 'center',
428 stateful: true,
429 stateId: 'pvecenter',
430 minWidth: 100,
431 minHeight: 100,
432 id: 'content',
433 xtype: 'container',
434 layout: { type: 'card' },
435 border: false,
436 margin: '0 5 0 0',
437 items: [],
438 },
439 {
440 region: 'west',
441 stateful: true,
442 stateId: 'pvewest',
443 itemId: 'west',
444 xtype: 'container',
445 border: false,
446 layout: { type: 'vbox', align: 'stretch' },
447 margin: '0 0 0 5',
448 split: true,
449 width: 200,
450 items: [selview, rtree],
451 listeners: {
452 resize: function(panel, width, height) {
453 var viewWidth = me.getSize().width;
454 if (width > viewWidth - 100) {
455 panel.setWidth(viewWidth - 100);
456 }
457 },
458 },
459 },
460 {
461 xtype: 'pveStatusPanel',
462 stateful: true,
463 stateId: 'pvesouth',
464 itemId: 'south',
465 region: 'south',
466 margin: '0 5 5 5',
467 title: gettext('Logs'),
468 collapsible: true,
469 header: false,
470 height: 200,
471 split: true,
472 listeners: {
473 resize: function(panel, width, height) {
474 var viewHeight = me.getSize().height;
475 if (height > viewHeight - 150) {
476 panel.setHeight(viewHeight - 150);
477 }
478 },
479 },
480 },
481 ],
482 });
483
484 me.callParent();
485
486 me.updateUserInfo();
487
488 // on resize, center all modal windows
489 Ext.on('resize', function() {
490 let modalWindows = Ext.ComponentQuery.query('window[modal]');
491 if (modalWindows.length > 0) {
492 modalWindows.forEach(win => win.alignTo(me, 'c-c'));
493 }
494 });
495 },
496 });
497