]>
git.proxmox.com Git - pve-manager.git/blob - www/manager6/dc/TFAEdit.js
7d19127dcd520001aea4e1cd8491ef0fc387ffb9
1 /*global u2f,QRCode,Uint8Array*/
2 /*jslint confusion: true*/
3 Ext
.define('PVE.window.TFAEdit', {
4 extend
: 'Ext.window.Window',
5 mixins
: ['Proxmox.Mixin.CBind'],
7 onlineHelp
: 'pveum_tfa_auth', // fake to ensure this gets a link target
11 title
: gettext('Two Factor Authentication'),
13 url
: '/api2/extjs/access/tfa',
21 updateQrCode: function() {
23 var values
= me
.lookup('totp_form').getValues();
24 var algorithm
= values
.algorithm
;
30 'otpauth://totp/' + encodeURIComponent(me
.userid
) +
31 '?secret=' + values
.secret
+
32 '&period=' + values
.step
+
33 '&digits=' + values
.digits
+
34 '&algorithm=' + algorithm
+
35 '&issuer=' + encodeURIComponent(values
.issuer
)
38 me
.lookup('challenge').setVisible(true);
39 me
.down('#qrbox').setVisible(true);
42 showError: function(error
) {
45 PVE
.Utils
.render_u2f_error(error
)
49 doU2FChallenge: function(response
) {
52 var data
= response
.result
.data
;
53 me
.lookup('password').setDisabled(true);
54 var msg
= Ext
.Msg
.show({
55 title
: 'U2F: '+gettext('Setup'),
56 message
: gettext('Please press the button on your U2F Device'),
59 Ext
.Function
.defer(function() {
60 u2f
.register(data
.appId
, [data
], [], function(data
) {
63 me
.showError(data
.errorCode
);
65 me
.respondToU2FChallenge(data
);
71 respondToU2FChallenge: function(data
) {
76 response
: JSON
.stringify(data
)
78 if (Proxmox
.UserName
!== 'root@pam') {
79 params
.password
= me
.lookup('password').value
;
81 Proxmox
.Utils
.API2Request({
82 url
: '/api2/extjs/access/tfa',
88 title
: gettext('Success'),
89 message
: gettext('U2F Device successfully connected.'),
93 failure: function(response
, opts
) {
94 Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
);
103 tfa_type
: null, // dependencies of formulas should not be undefined
108 canDeleteTFA: function(get) {
109 return (get('tfa_type') !== null && !get('tfa_required'));
111 canSetupTOTP: function(get) {
112 var tfa
= get('tfa_type');
113 return (tfa
=== null || tfa
=== 'totp' || tfa
=== 1);
115 canSetupU2F: function(get) {
116 var tfa
= get('tfa_type');
117 return (get('u2f_available') && (tfa
=== null || tfa
=== 'u2f' || tfa
=== 1));
122 afterLoading: function(realm_tfa_type
, user_tfa_type
) {
124 var viewmodel
= me
.getViewModel();
125 if (user_tfa_type
=== 'oath') {
126 user_tfa_type
= 'totp';
128 viewmodel
.set('tfa_type', user_tfa_type
|| null);
129 if (!realm_tfa_type
) {
130 // There's no TFA enforced by the realm, everything works.
131 viewmodel
.set('u2f_available', true);
132 viewmodel
.set('tfa_required', false);
133 } else if (realm_tfa_type
=== 'oath') {
134 // The realm explicitly requires TOTP
135 if (user_tfa_type
!== 'totp' && user_tfa_type
!== null) {
136 // user had a different tfa method, so
137 // we have to change back to the totp tab and
139 viewmodel
.set('tfa_type', null);
140 me
.lookup('tfatabs').setActiveTab(me
.lookup('totp_panel'));
141 me
.getController().randomizeSecret();
143 viewmodel
.set('tfa_required', true);
144 viewmodel
.set('u2f_available', false);
146 // The realm enforces some other TFA type (yubico)
151 gettext("Custom 2nd factor configuration is not supported on realms with '{0}' TFA."),
159 xclass
: 'Ext.app.ViewController',
161 'field[qrupdate=true]': {
163 var me
= this.getView();
168 validitychange: function(field
, valid
) {
170 var viewModel
= me
.getViewModel();
171 var form
= me
.lookup('totp_form');
172 var challenge
= me
.lookup('challenge');
173 var password
= me
.lookup('password');
174 viewModel
.set('valid', form
.isValid() && challenge
.isValid() && password
.isValid());
179 var me
= this.getView();
180 var viewmodel
= this.getViewModel();
182 var loadMaskContainer
= me
.down('#tfatabs');
183 Proxmox
.Utils
.API2Request({
184 url
: '/access/users/' + encodeURIComponent(me
.userid
) + '/tfa',
185 waitMsgTarget
: loadMaskContainer
,
187 success: function(response
, opts
) {
188 var data
= response
.result
.data
;
189 me
.afterLoading(data
.realm
, data
.user
);
191 failure: function(response
, opts
) {
192 Proxmox
.Utils
.setErrorMask(loadMaskContainer
, response
.htmlStatus
);
196 me
.qrdiv
= document
.createElement('center');
197 me
.qrcode
= new QRCode(me
.qrdiv
, {
200 correctLevel
: QRCode
.CorrectLevel
.M
202 me
.down('#qrbox').getEl().appendChild(me
.qrdiv
);
204 viewmodel
.set('tfa_type', me
.tfa_type
|| null);
206 this.randomizeSecret();
208 me
.down('#qrbox').setVisible(false);
209 me
.lookup('challenge').setVisible(false);
210 if (me
.tfa_type
=== 'u2f') {
211 var u2f_panel
= me
.lookup('u2f_panel');
212 me
.lookup('tfatabs').setActiveTab(u2f_panel
);
216 if (Proxmox
.UserName
=== 'root@pam') {
217 me
.lookup('password').setVisible(false);
218 me
.lookup('password').setDisabled(true);
223 tabchange: function(panel
, newcard
) {
224 var viewmodel
= this.getViewModel();
225 viewmodel
.set('in_totp_tab', newcard
.itemId
=== 'totp-panel');
230 applySettings: function() {
232 var values
= me
.lookup('totp_form').getValues();
234 userid
: me
.getView().userid
,
236 key
: 'v2-' + values
.secret
,
237 config
: PVE
.Parser
.printPropertyString({
239 digits
: values
.digits
,
242 // this is used to verify that the client generates the correct codes:
243 response
: me
.lookup('challenge').value
246 if (Proxmox
.UserName
!== 'root@pam') {
247 params
.password
= me
.lookup('password').value
;
250 Proxmox
.Utils
.API2Request({
251 url
: '/api2/extjs/access/tfa',
254 waitMsgTarget
: me
.getView(),
255 success: function(response
, opts
) {
256 me
.getView().close();
258 failure: function(response
, opts
) {
259 Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
);
264 deleteTFA: function() {
266 var values
= me
.lookup('totp_form').getValues();
268 userid
: me
.getView().userid
,
272 if (Proxmox
.UserName
!== 'root@pam') {
273 params
.password
= me
.lookup('password').value
;
276 Proxmox
.Utils
.API2Request({
277 url
: '/api2/extjs/access/tfa',
280 waitMsgTarget
: me
.getView(),
281 success: function(response
, opts
) {
282 me
.getView().close();
284 failure: function(response
, opts
) {
285 Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
);
290 randomizeSecret: function() {
292 var rnd
= new Uint8Array(16);
293 window
.crypto
.getRandomValues(rnd
);
295 rnd
.forEach(function(b
) {
296 // secret must be base32, so just use the first 5 bits
300 data
+= String
.fromCharCode(b
+ 0x41);
303 data
+= String
.fromCharCode(b
-26 + 0x32);
306 me
.lookup('tfa_secret').setValue(data
);
309 startU2FRegistration: function() {
313 userid
: me
.getView().userid
,
317 if (Proxmox
.UserName
!== 'root@pam') {
318 params
.password
= me
.lookup('password').value
;
321 Proxmox
.Utils
.API2Request({
322 url
: '/api2/extjs/access/tfa',
325 waitMsgTarget
: me
.getView(),
326 success: function(response
) {
327 me
.getView().doU2FChallenge(response
);
329 failure: function(response
, opts
) {
330 Ext
.Msg
.alert(gettext('Error'), response
.htmlStatus
);
340 reference
: 'tfatabs',
346 itemId
: 'totp-panel',
347 reference
: 'totp_panel',
351 disabled
: '{!canSetupTOTP}'
362 reference
: 'totp_form',
369 xtype
: 'displayfield',
370 fieldLabel
: gettext('User name'),
381 fieldLabel
: gettext('Secret'),
382 emptyText
: gettext('Unchanged'),
384 reference
: 'tfa_secret',
385 regex
: /^[A-Z2-7=]+$/,
386 regexText
: 'Must be base32 [A-Z2-7=]',
393 text
: gettext('Randomize'),
394 reference
: 'randomize_button',
395 handler
: 'randomizeSecret',
400 xtype
: 'numberfield',
401 fieldLabel
: gettext('Time period'),
403 // Google Authenticator ignores this and generates bogus data
410 xtype
: 'numberfield',
411 fieldLabel
: gettext('Digits'),
414 // Google Authenticator ignores this and generates bogus data
422 fieldLabel
: gettext('Issuer Name'),
424 value
: 'Proxmox Web UI',
432 visible
: false, // will be enabled when generating a qr code
434 'background-color': 'white',
442 fieldLabel
: gettext('Verification Code'),
444 reference
: 'challenge',
446 emptyText
: gettext('Scan QR code and enter TOTP auth. code to verify')
453 reference
: 'u2f_panel',
462 disabled
: '{!canSetupU2F}'
468 text
: gettext('To register a U2F device, connect the device, then click the button and follow the instructions.')
476 inputType
: 'password',
477 fieldLabel
: gettext('Password'),
479 reference
: 'password',
483 emptyText
: gettext('verify current password')
489 xtype
: 'proxmoxHelpButton'
493 text
: gettext('Apply'),
494 handler
: 'applySettings',
496 hidden
: '{!in_totp_tab}',
502 text
: gettext('Register U2F Device'),
503 handler
: 'startU2FRegistration',
505 hidden
: '{in_totp_tab}',
506 disabled
: '{tfa_type}'
510 text
: gettext('Delete'),
511 reference
: 'delete_button',
513 handler
: 'deleteTFA',
515 disabled
: '{!canDeleteTFA}'
520 initComponent: function() {
524 throw "no userid given";
529 Ext
.GlobalEvents
.fireEvent('proxmoxShowHelp', 'pveum_tfa_auth');