]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/window/LoginWindow.js
ui: import: increase CPU limits to better match ESXi
[pve-manager.git] / www / manager6 / window / LoginWindow.js
1 /*global u2f*/
2 Ext.define('PVE.window.LoginWindow', {
3 extend: 'Ext.window.Window',
4
5 viewModel: {
6 data: {
7 openid: false,
8 },
9 formulas: {
10 button_text: function(get) {
11 if (get("openid") === true) {
12 return gettext("Login (OpenID redirect)");
13 } else {
14 return gettext("Login");
15 }
16 },
17 },
18 },
19
20 controller: {
21
22 xclass: 'Ext.app.ViewController',
23
24 onLogon: async function() {
25 var me = this;
26
27 var form = this.lookupReference('loginForm');
28 var unField = this.lookupReference('usernameField');
29 var saveunField = this.lookupReference('saveunField');
30 var view = this.getView();
31
32 if (!form.isValid()) {
33 return;
34 }
35
36 let creds = form.getValues();
37
38 if (this.getViewModel().data.openid === true) {
39 const redirectURL = location.origin;
40 Proxmox.Utils.API2Request({
41 url: '/api2/extjs/access/openid/auth-url',
42 params: {
43 realm: creds.realm,
44 "redirect-url": redirectURL,
45 },
46 method: 'POST',
47 success: function(resp, opts) {
48 window.location = resp.result.data;
49 },
50 failure: function(resp, opts) {
51 Proxmox.Utils.authClear();
52 form.unmask();
53 Ext.MessageBox.alert(
54 gettext('Error'),
55 gettext('OpenID redirect failed.') + `<br>${resp.htmlStatus}`,
56 );
57 },
58 });
59 return;
60 }
61
62 view.el.mask(gettext('Please wait...'), 'x-mask-loading');
63
64 // set or clear username
65 var sp = Ext.state.Manager.getProvider();
66 if (saveunField.getValue() === true) {
67 sp.set(unField.getStateId(), unField.getValue());
68 } else {
69 sp.clear(unField.getStateId());
70 }
71 sp.set(saveunField.getStateId(), saveunField.getValue());
72
73 try {
74 // Request updated authentication mechanism:
75 creds['new-format'] = 1;
76
77 let resp = await Proxmox.Async.api2({
78 url: '/api2/extjs/access/ticket',
79 params: creds,
80 method: 'POST',
81 });
82
83 let data = resp.result.data;
84 if (data.ticket.startsWith("PVE:!tfa!")) {
85 // Store first factor login information first:
86 data.LoggedOut = true;
87 Proxmox.Utils.setAuthData(data);
88
89 data = await me.performTFAChallenge(data);
90
91 // Fill in what we copy over from the 1st factor:
92 data.CSRFPreventionToken = Proxmox.CSRFPreventionToken;
93 data.username = Proxmox.UserName;
94 me.success(data);
95 } else if (Ext.isDefined(data.NeedTFA)) {
96 // Store first factor login information first:
97 data.LoggedOut = true;
98 Proxmox.Utils.setAuthData(data);
99
100 if (Ext.isDefined(data.U2FChallenge)) {
101 me.perform_u2f(data);
102 } else {
103 me.perform_otp();
104 }
105 } else {
106 me.success(data);
107 }
108 } catch (error) {
109 me.failure(error);
110 }
111 },
112
113 /* START NEW TFA CODE (pbs copy) */
114 performTFAChallenge: async function(data) {
115 let me = this;
116
117 let userid = data.username;
118 let ticket = data.ticket;
119 let challenge = JSON.parse(decodeURIComponent(
120 ticket.split(':')[1].slice("!tfa!".length),
121 ));
122
123 let resp = await new Promise((resolve, reject) => {
124 Ext.create('Proxmox.window.TfaLoginWindow', {
125 userid,
126 ticket,
127 challenge,
128 onResolve: value => resolve(value),
129 onReject: reject,
130 }).show();
131 });
132
133 return resp.result.data;
134 },
135 /* END NEW TFA CODE (pbs copy) */
136
137 failure: function(resp) {
138 var me = this;
139 var view = me.getView();
140 view.el.unmask();
141 var handler = function() {
142 var uf = me.lookupReference('usernameField');
143 uf.focus(true, true);
144 };
145
146 let emsg = gettext("Login failed. Please try again");
147
148 if (resp.failureType === "connect") {
149 emsg = gettext("Connection failure. Network error or Proxmox VE services not running?");
150 }
151
152 Ext.MessageBox.alert(gettext('Error'), emsg, handler);
153 },
154 success: function(data) {
155 var me = this;
156 var view = me.getView();
157 var handler = view.handler || Ext.emptyFn;
158 handler.call(me, data);
159 view.close();
160 },
161
162 perform_otp: function() {
163 var me = this;
164 var win = Ext.create('PVE.window.TFALoginWindow', {
165 onLogin: function(value) {
166 me.finish_tfa(value);
167 },
168 onCancel: function() {
169 Proxmox.LoggedOut = false;
170 Proxmox.Utils.authClear();
171 me.getView().show();
172 },
173 });
174 win.show();
175 },
176
177 perform_u2f: function(data) {
178 var me = this;
179 // Show the message:
180 var msg = Ext.Msg.show({
181 title: 'U2F: '+gettext('Verification'),
182 message: gettext('Please press the button on your U2F Device'),
183 buttons: [],
184 });
185 var chlg = data.U2FChallenge;
186 var key = {
187 version: chlg.version,
188 keyHandle: chlg.keyHandle,
189 };
190 u2f.sign(chlg.appId, chlg.challenge, [key], function(res) {
191 msg.close();
192 if (res.errorCode) {
193 Proxmox.Utils.authClear();
194 Ext.Msg.alert(gettext('Error'), PVE.Utils.render_u2f_error(res.errorCode));
195 return;
196 }
197 delete res.errorCode;
198 me.finish_tfa(JSON.stringify(res));
199 });
200 },
201 finish_tfa: function(res) {
202 var me = this;
203 var view = me.getView();
204 view.el.mask(gettext('Please wait...'), 'x-mask-loading');
205 Proxmox.Utils.API2Request({
206 url: '/api2/extjs/access/tfa',
207 params: {
208 response: res,
209 },
210 method: 'POST',
211 timeout: 5000, // it'll delay both success & failure
212 success: function(resp, opts) {
213 view.el.unmask();
214 // Fill in what we copy over from the 1st factor:
215 var data = resp.result.data;
216 data.CSRFPreventionToken = Proxmox.CSRFPreventionToken;
217 data.username = Proxmox.UserName;
218 // Finish logging in:
219 me.success(data);
220 },
221 failure: function(resp, opts) {
222 Proxmox.Utils.authClear();
223 me.failure(resp);
224 },
225 });
226 },
227
228 control: {
229 'field[name=username]': {
230 specialkey: function(f, e) {
231 if (e.getKey() === e.ENTER) {
232 var pf = this.lookupReference('passwordField');
233 if (!pf.getValue()) {
234 pf.focus(false);
235 }
236 }
237 },
238 },
239 'field[name=lang]': {
240 change: function(f, value) {
241 var dt = Ext.Date.add(new Date(), Ext.Date.YEAR, 10);
242 Ext.util.Cookies.set('PVELangCookie', value, dt);
243 this.getView().mask(gettext('Please wait...'), 'x-mask-loading');
244 window.location.reload();
245 },
246 },
247 'field[name=realm]': {
248 change: function(f, value) {
249 let record = f.store.getById(value);
250 if (record === undefined) return;
251 let data = record.data;
252 this.getViewModel().set("openid", data.type === "openid");
253 },
254 },
255 'button[reference=loginButton]': {
256 click: 'onLogon',
257 },
258 '#': {
259 show: function() {
260 var me = this;
261
262 var sp = Ext.state.Manager.getProvider();
263 var checkboxField = this.lookupReference('saveunField');
264 var unField = this.lookupReference('usernameField');
265
266 var checked = sp.get(checkboxField.getStateId());
267 checkboxField.setValue(checked);
268
269 if (checked === true) {
270 var username = sp.get(unField.getStateId());
271 unField.setValue(username);
272 var pwField = this.lookupReference('passwordField');
273 pwField.focus();
274 }
275
276 let auth = Proxmox.Utils.getOpenIDRedirectionAuthorization();
277 if (auth !== undefined) {
278 Proxmox.Utils.authClear();
279
280 let loginForm = this.lookupReference('loginForm');
281 loginForm.mask(gettext('OpenID login - please wait...'), 'x-mask-loading');
282
283 const redirectURL = location.origin;
284
285 Proxmox.Utils.API2Request({
286 url: '/api2/extjs/access/openid/login',
287 params: {
288 state: auth.state,
289 code: auth.code,
290 "redirect-url": redirectURL,
291 },
292 method: 'POST',
293 failure: function(response) {
294 loginForm.unmask();
295 let error = response.htmlStatus;
296 Ext.MessageBox.alert(
297 gettext('Error'),
298 gettext('OpenID login failed, please try again') + `<br>${error}`,
299 () => { window.location = redirectURL; },
300 );
301 },
302 success: function(response, options) {
303 loginForm.unmask();
304 let data = response.result.data;
305 history.replaceState(null, '', redirectURL);
306 me.success(data);
307 },
308 });
309 }
310 },
311 },
312 },
313 },
314
315 width: 400,
316 modal: true,
317 border: false,
318 draggable: true,
319 closable: false,
320 resizable: false,
321 layout: 'auto',
322
323 title: gettext('Proxmox VE Login'),
324
325 defaultFocus: 'usernameField',
326 defaultButton: 'loginButton',
327
328 items: [{
329 xtype: 'form',
330 layout: 'form',
331 url: '/api2/extjs/access/ticket',
332 reference: 'loginForm',
333
334 fieldDefaults: {
335 labelAlign: 'right',
336 allowBlank: false,
337 },
338
339 items: [
340 {
341 xtype: 'textfield',
342 fieldLabel: gettext('User name'),
343 name: 'username',
344 itemId: 'usernameField',
345 reference: 'usernameField',
346 stateId: 'login-username',
347 bind: {
348 visible: "{!openid}",
349 disabled: "{openid}",
350 },
351 },
352 {
353 xtype: 'textfield',
354 inputType: 'password',
355 fieldLabel: gettext('Password'),
356 name: 'password',
357 reference: 'passwordField',
358 bind: {
359 visible: "{!openid}",
360 disabled: "{openid}",
361 },
362 },
363 {
364 xtype: 'pmxRealmComboBox',
365 name: 'realm',
366 },
367 {
368 xtype: 'proxmoxLanguageSelector',
369 fieldLabel: gettext('Language'),
370 value: PVE.Utils.getUiLanguage(),
371 name: 'lang',
372 reference: 'langField',
373 submitValue: false,
374 },
375 ],
376 buttons: [
377 {
378 xtype: 'checkbox',
379 fieldLabel: gettext('Save User name'),
380 name: 'saveusername',
381 reference: 'saveunField',
382 stateId: 'login-saveusername',
383 labelWidth: 250,
384 labelAlign: 'right',
385 submitValue: false,
386 bind: {
387 visible: "{!openid}",
388 },
389 },
390 {
391 bind: {
392 text: "{button_text}",
393 },
394 reference: 'loginButton',
395 },
396 ],
397 }],
398 });