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