]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/Workspace.js
ui: support u2f authentication and configuration
[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
18 // private
19 updateLoginData: function(loginData) {
20 var me = this;
21 me.loginData = loginData;
22 Proxmox.Utils.setAuthData(loginData);
23
24 var rt = me.down('pveResourceTree');
25 rt.setDatacenterText(loginData.clustername);
26
27 if (loginData.cap) {
28 Ext.state.Manager.set('GuiCap', loginData.cap);
29 }
30
31 me.onLogin(loginData);
32 },
33
34 // private
35 showLogin: function() {
36 var me = this;
37
38 Proxmox.Utils.authClear();
39 Proxmox.UserName = null;
40 me.loginData = null;
41
42 if (!me.login) {
43 me.login = Ext.create('PVE.window.LoginWindow', {
44 handler: function(data) {
45 me.login = null;
46 me.updateLoginData(data);
47 Proxmox.Utils.checked_command(function() {}); // display subscription status
48 }
49 });
50 }
51 me.onLogin(null);
52 me.login.show();
53 },
54
55 initComponent : function() {
56 var me = this;
57
58 Ext.tip.QuickTipManager.init();
59
60 // fixme: what about other errors
61 Ext.Ajax.on('requestexception', function(conn, response, options) {
62 if (response.status == 401 && !PVE.Utils.silenceAuthFailures) { // auth failure
63 me.showLogin();
64 }
65 });
66
67 me.callParent();
68
69 if (!Proxmox.Utils.authOK()) {
70 me.showLogin();
71 } else {
72 if (me.loginData) {
73 me.onLogin(me.loginData);
74 }
75 }
76
77 Ext.TaskManager.start({
78 run: function() {
79 var ticket = Proxmox.Utils.authOK();
80 if (!ticket || !Proxmox.UserName) {
81 return;
82 }
83
84 Ext.Ajax.request({
85 params: {
86 username: Proxmox.UserName,
87 password: ticket
88 },
89 url: '/api2/json/access/ticket',
90 method: 'POST',
91 success: function(response, opts) {
92 var obj = Ext.decode(response.responseText);
93 me.updateLoginData(obj.data);
94 }
95 });
96 },
97 interval: 15*60*1000
98 });
99
100 }
101 });
102
103 Ext.define('PVE.StdWorkspace', {
104 extend: 'PVE.Workspace',
105
106 alias: ['widget.pveStdWorkspace'],
107
108 // private
109 setContent: function(comp) {
110 var me = this;
111
112 var cont = me.child('#content');
113
114 var lay = cont.getLayout();
115
116 var cur = lay.getActiveItem();
117
118 if (comp) {
119 Proxmox.Utils.setErrorMask(cont, false);
120 comp.border = false;
121 cont.add(comp);
122 if (cur !== null && lay.getNext()) {
123 lay.next();
124 var task = Ext.create('Ext.util.DelayedTask', function(){
125 cont.remove(cur);
126 });
127 task.delay(10);
128 }
129 }
130 else {
131 // helper for cleaning the content when logging out
132 cont.removeAll();
133 }
134 },
135
136 selectById: function(nodeid) {
137 var me = this;
138 var tree = me.down('pveResourceTree');
139 tree.selectById(nodeid);
140 },
141
142 onLogin: function(loginData) {
143 var me = this;
144
145 me.updateUserInfo();
146
147 if (loginData) {
148 PVE.data.ResourceStore.startUpdate();
149
150 Proxmox.Utils.API2Request({
151 url: '/version',
152 method: 'GET',
153 success: function(response) {
154 PVE.VersionInfo = response.result.data;
155 me.updateVersionInfo();
156 }
157 });
158 }
159 },
160
161 updateUserInfo: function() {
162 var me = this;
163
164 var ui = me.query('#userinfo')[0];
165
166 if (Proxmox.UserName) {
167 var msg = Ext.String.format(gettext("You are logged in as {0}"), "'" + Proxmox.UserName + "'");
168 ui.update('<div class="x-unselectable" style="white-space:nowrap;">' + msg + '</div>');
169 } else {
170 ui.update('');
171 }
172 ui.updateLayout();
173 },
174
175 updateVersionInfo: function() {
176 var me = this;
177
178 var ui = me.query('#versioninfo')[0];
179
180 if (PVE.VersionInfo) {
181 var version = PVE.VersionInfo.version + '-' + PVE.VersionInfo.release;
182 ui.update('Virtual Environment ' + version);
183 } else {
184 ui.update('Virtual Environment');
185 }
186 ui.updateLayout();
187 },
188
189 initComponent : function() {
190 var me = this;
191
192 Ext.History.init();
193
194 var sprovider = Ext.create('PVE.StateProvider');
195 Ext.state.Manager.setProvider(sprovider);
196
197 var selview = Ext.create('PVE.form.ViewSelector');
198
199 var rtree = Ext.createWidget('pveResourceTree', {
200 viewFilter: selview.getViewFilter(),
201 flex: 1,
202 selModel: {
203 selType: 'treemodel',
204 listeners: {
205 selectionchange: function(sm, selected) {
206 if (selected.length > 0) {
207 var n = selected[0];
208 var tlckup = {
209 root: 'PVE.dc.Config',
210 node: 'PVE.node.Config',
211 qemu: 'PVE.qemu.Config',
212 lxc: 'PVE.lxc.Config',
213 storage: 'PVE.storage.Browser',
214 pool: 'pvePoolConfig'
215 };
216 var comp = {
217 xtype: tlckup[n.data.type || 'root'] ||
218 'pvePanelConfig',
219 showSearch: (n.data.id === 'root') ||
220 Ext.isDefined(n.data.groupbyid),
221 pveSelNode: n,
222 workspace: me,
223 viewFilter: selview.getViewFilter()
224 };
225 PVE.curSelectedNode = n;
226 me.setContent(comp);
227 }
228 }
229 }
230 }
231 });
232
233 selview.on('select', function(combo, records) {
234 if (records) {
235 var view = combo.getViewFilter();
236 rtree.setViewFilter(view);
237 }
238 });
239
240 var caps = sprovider.get('GuiCap');
241
242 var createVM = Ext.createWidget('button', {
243 pack: 'end',
244 margin: '3 5 0 0',
245 baseCls: 'x-btn',
246 iconCls: 'fa fa-desktop',
247 text: gettext("Create VM"),
248 disabled: !caps.vms['VM.Allocate'],
249 handler: function() {
250 var wiz = Ext.create('PVE.qemu.CreateWizard', {});
251 wiz.show();
252 }
253 });
254
255 var createCT = Ext.createWidget('button', {
256 pack: 'end',
257 margin: '3 5 0 0',
258 baseCls: 'x-btn',
259 iconCls: 'fa fa-cube',
260 text: gettext("Create CT"),
261 disabled: !caps.vms['VM.Allocate'],
262 handler: function() {
263 var wiz = Ext.create('PVE.lxc.CreateWizard', {});
264 wiz.show();
265 }
266 });
267
268 sprovider.on('statechange', function(sp, key, value) {
269 if (key === 'GuiCap' && value) {
270 caps = value;
271 createVM.setDisabled(!caps.vms['VM.Allocate']);
272 createCT.setDisabled(!caps.vms['VM.Allocate']);
273 }
274 });
275
276 Ext.apply(me, {
277 layout: { type: 'border' },
278 border: false,
279 items: [
280 {
281 region: 'north',
282 layout: {
283 type: 'hbox',
284 align: 'middle'
285 },
286 baseCls: 'x-plain',
287 defaults: {
288 baseCls: 'x-plain'
289 },
290 border: false,
291 margin: '2 0 2 5',
292 items: [
293 {
294 html: '<a class="x-unselectable" target=_blank href="http://www.proxmox.com">' +
295 '<img style="padding-top:4px;padding-right:5px" src="/pve2/images/proxmox_logo.png"/></a>'
296 },
297 {
298 minWidth: 150,
299 id: 'versioninfo',
300 html: 'Virtual Environment'
301 },
302 {
303 xtype: 'pveGlobalSearchField',
304 tree: rtree
305 },
306 {
307 flex: 1
308 },
309 {
310 pack: 'end',
311 id: 'userinfo',
312 stateful: false
313 },
314 {
315 xtype: 'button',
316 margin: '0 10 0 3',
317 iconCls: 'fa black fa-gear',
318 userCls: 'pointer',
319 handler: function() {
320 var win = Ext.create('PVE.window.Settings');
321 win.show();
322 }
323 },
324 {
325 xtype: 'proxmoxHelpButton',
326 hidden: false,
327 baseCls: 'x-btn',
328 iconCls: 'fa fa-book x-btn-icon-el-default-toolbar-small ',
329 listenToGlobalEvent: false,
330 onlineHelp: 'pve_documentation_index',
331 text: gettext('Documentation'),
332 margin: '0 5 0 0'
333 },
334 createVM,
335 createCT,
336 {
337 pack: 'end',
338 margin: '0 5 0 0',
339 xtype: 'button',
340 baseCls: 'x-btn',
341 iconCls: 'fa fa-sign-out',
342 text: gettext("Logout"),
343 handler: function() {
344 PVE.data.ResourceStore.loadData([], false);
345 me.showLogin();
346 me.setContent(null);
347 var rt = me.down('pveResourceTree');
348 rt.setDatacenterText(undefined);
349 rt.clearTree();
350
351 // empty the stores of the StatusPanel child items
352 var statusPanels = Ext.ComponentQuery.query('pveStatusPanel grid');
353 Ext.Array.forEach(statusPanels, function(comp) {
354 if (comp.getStore()) {
355 comp.getStore().loadData([], false);
356 }
357 });
358 }
359 }
360 ]
361 },
362 {
363 region: 'center',
364 stateful: true,
365 stateId: 'pvecenter',
366 minWidth: 100,
367 minHeight: 100,
368 id: 'content',
369 xtype: 'container',
370 layout: { type: 'card' },
371 border: false,
372 margin: '0 5 0 0',
373 items: []
374 },
375 {
376 region: 'west',
377 stateful: true,
378 stateId: 'pvewest',
379 itemId: 'west',
380 xtype: 'container',
381 border: false,
382 layout: { type: 'vbox', align: 'stretch' },
383 margin: '0 0 0 5',
384 split: true,
385 width: 200,
386 items: [ selview, rtree ],
387 listeners: {
388 resize: function(panel, width, height) {
389 var viewWidth = me.getSize().width;
390 if (width > viewWidth - 100) {
391 panel.setWidth(viewWidth - 100);
392 }
393 }
394 }
395 },
396 {
397 xtype: 'pveStatusPanel',
398 stateful: true,
399 stateId: 'pvesouth',
400 itemId: 'south',
401 region: 'south',
402 margin:'0 5 5 5',
403 title: gettext('Logs'),
404 collapsible: true,
405 header: false,
406 height: 200,
407 split:true,
408 listeners: {
409 resize: function(panel, width, height) {
410 var viewHeight = me.getSize().height;
411 if (height > (viewHeight - 150)) {
412 panel.setHeight(viewHeight - 150);
413 }
414 }
415 }
416 }
417 ]
418 });
419
420 me.callParent();
421
422 me.updateUserInfo();
423
424 // on resize, center all modal windows
425 Ext.on('resize', function(){
426 var wins = Ext.ComponentQuery.query('window[modal]');
427 if (wins.length > 0) {
428 wins.forEach(function(win){
429 win.alignTo(me, 'c-c');
430 });
431 }
432 });
433 }
434 });
435