Signed-off-by: Wolfgang Bumiller <w.bumil...@proxmox.com>
---
 www/index.html.tpl                 |   1 +
 www/manager6/Makefile              |   1 +
 www/manager6/Workspace.js          |   6 +-
 www/manager6/dc/U2FEdit.js         | 145 +++++++++++++++++++++++++++++++++++++
 www/manager6/dc/UserView.js        |  15 +++-
 www/manager6/window/LoginWindow.js | 121 ++++++++++++++++++++++++-------
 6 files changed, 257 insertions(+), 32 deletions(-)
 create mode 100644 www/manager6/dc/U2FEdit.js

diff --git a/www/index.html.tpl b/www/index.html.tpl
index a972e3aa..eca75a6f 100644
--- a/www/index.html.tpl
+++ b/www/index.html.tpl
@@ -22,6 +22,7 @@
     [%- ELSE %]
     <script type="text/javascript" src="/pve2/ext6/ext-all.js"></script>
     <script type="text/javascript" src="/pve2/ext6/charts.js"></script>
+    <script type="text/javascript" src="/pve2/js/u2f-lib.js"></script>
     [% END %]
     <script type="text/javascript">
     Proxmox = {
diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index a2bd4576..e6d0e698 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -180,6 +180,7 @@ JSSRC=                                                      
\
        dc/Guests.js                                    \
        dc/OptionView.js                                \
        dc/StorageView.js                               \
+       dc/U2FEdit.js                                   \
        dc/UserEdit.js                                  \
        dc/UserView.js                                  \
        dc/PoolView.js                                  \
diff --git a/www/manager6/Workspace.js b/www/manager6/Workspace.js
index f75356c5..aed61324 100644
--- a/www/manager6/Workspace.js
+++ b/www/manager6/Workspace.js
@@ -19,16 +19,12 @@ Ext.define('PVE.Workspace', {
     updateLoginData: function(loginData) {
        var me = this;
        me.loginData = loginData;
-       Proxmox.CSRFPreventionToken = loginData.CSRFPreventionToken;
-       Proxmox.UserName = loginData.username;
+       Proxmox.Utils.setAuthData(loginData);
 
        if (loginData.cap) {
            Ext.state.Manager.set('GuiCap', loginData.cap);
        }
 
-       // creates a session cookie (expire = null) 
-       // that way the cookie gets deleted after browser window close
-       Ext.util.Cookies.set('PVEAuthCookie', loginData.ticket, null, '/', 
null, true);
        me.onLogin(loginData);
     },
 
diff --git a/www/manager6/dc/U2FEdit.js b/www/manager6/dc/U2FEdit.js
new file mode 100644
index 00000000..0cb416f8
--- /dev/null
+++ b/www/manager6/dc/U2FEdit.js
@@ -0,0 +1,145 @@
+Ext.define('PVE.window.U2FEdit', {
+    extend: 'Proxmox.window.Edit',
+
+    initComponent : function() {
+       var me = this;
+
+       if (!me.userid) {
+           throw "no userid specified";
+       }
+
+       var pwfield;
+       if (Proxmox.UserName !== 'root@pam') {
+           pwfield = Ext.createWidget('textfield', {
+               inputType: 'password',
+               fieldLabel: gettext('Password'),
+               minLength: 5,
+               name: 'password',
+           });
+       }
+
+       var delete_btn = new Proxmox.button.Button({
+           text: gettext('Delete'),
+           handler: function() {
+               var params = {
+                   userid: me.userid,
+                   action: 'delete'
+               };
+               if (Ext.isDefined(pwfield)) {
+                   var pw = pwfield.getValue();
+                   if (pw.length) {
+                       params.password = pw;
+                   }
+               }
+               Proxmox.Utils.API2Request({
+                   url: '/api2/extjs/access/u2f',
+                   params: params,
+                   method: 'PUT',
+                   waitMsgTarget: me,
+                   success: function(response, opts) {
+                       me.close();
+                   },
+                   failure: function(response, opts) {
+                       Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+                   }
+               });
+           }
+       });
+
+       var finish_fn;
+       var register_fn;
+
+       var register_btn = new Proxmox.button.Button({
+           text: gettext('Register'),
+           handler: function() {
+               var params = {
+                   userid: me.userid,
+                   action: 'new'
+               };
+               if (Ext.isDefined(pwfield)) {
+                   var pw = pwfield.getValue();
+                   if (pw.length) {
+                       params.password = pw;
+                   }
+               }
+               Proxmox.Utils.API2Request({
+                   url: '/api2/extjs/access/u2f',
+                   params: params,
+                   method: 'PUT',
+                   waitMsgTarget: me,
+                   success: register_fn,
+                   failure: function(response, opts) {
+                       Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+                   }
+               });
+           }
+       });
+
+       register_fn = function(response, opts) {
+           var data = response.result.data;
+           var msg = Ext.Msg.show({
+               title: 'U2F: '+gettext('Setup'),
+               message: gettext('Please press the button on your U2F Device'),
+               buttons: []
+           });
+           Ext.Function.defer(function() {
+               u2f.register(data.appId, [data], [], function(data) {
+                   msg.close();
+                   if (data.errorCode) {
+                       Ext.Msg.alert(gettext('Error'), "U2F Error: 
"+data.errorCode);
+                       return;
+                   }
+                   finish_fn(data);
+               });
+           }, 500, me);
+       };
+
+       finish_fn = function(data) {
+           var params = {
+               userid: me.userid,
+               action: 'confirm',
+               response: JSON.stringify(data)
+           };
+           if (Ext.isDefined(pwfield)) {
+               var pw = pwfield.getValue();
+               if (pw.length) {
+                   params.password = pw;
+               }
+           }
+           Proxmox.Utils.API2Request({
+               url: '/api2/extjs/access/u2f',
+               params: params,
+               method: 'PUT',
+               waitMsgTarget: me,
+               success: function() {
+                   me.close();
+               },
+               failure: function(response, opts) {
+                   Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+               }
+           });
+       };
+
+
+       var items = [];
+       if (Ext.isDefined(pwfield)) {
+           items.push(pwfield);
+       }
+       items.push(
+           delete_btn,
+           register_btn,
+           {
+               xtype: 'hiddenfield',
+               name: 'userid',
+               value: me.userid
+           }
+       );
+       Ext.apply(me, {
+           subject: 'U2F',
+           url: '/api2/extjs/access/u2f',
+           items: items
+       });
+
+       me.callParent();
+    }
+});
diff --git a/www/manager6/dc/UserView.js b/www/manager6/dc/UserView.js
index 6dfc1041..c143464e 100644
--- a/www/manager6/dc/UserView.js
+++ b/www/manager6/dc/UserView.js
@@ -78,6 +78,19 @@ Ext.define('PVE.dc.UserView', {
            }
        });
 
+       var u2fchange_btn = new Proxmox.button.Button({
+           text: gettext('U2F'),
+           disabled: true,
+           selModel: sm,
+           handler: function(btn, event, rec) {
+               var win = Ext.create('PVE.window.U2FEdit',{
+                    userid: rec.data.userid
+               });
+               win.on('destroy', reload);
+               win.show();
+           }
+       });
+
         var tbar = [
             {
                text: gettext('Add'),
@@ -89,7 +102,7 @@ Ext.define('PVE.dc.UserView', {
                     win.show();
                }
             },
-           edit_btn, remove_btn, pwchange_btn
+           edit_btn, remove_btn, pwchange_btn, u2fchange_btn
         ];
 
        var render_full_name = function(firstname, metaData, record) {
diff --git a/www/manager6/window/LoginWindow.js 
b/www/manager6/window/LoginWindow.js
index 683fb54c..aa16fb52 100644
--- a/www/manager6/window/LoginWindow.js
+++ b/www/manager6/window/LoginWindow.js
@@ -13,39 +13,108 @@ Ext.define('PVE.window.LoginWindow', {
            var saveunField = this.lookupReference('saveunField');
            var view = this.getView();
 
-           if(form.isValid()){
-               view.el.mask(gettext('Please wait...'), 'x-mask-loading');
+           if (!form.isValid()) {
+               return;
+           }
+
+           var perform_u2f_fn;
+           var finish_u2f_fn;
+
+           var failure_fn = function(resp) {
+               view.el.unmask();
+               var handler = function() {
+                   var uf = me.lookupReference('usernameField');
+                   uf.focus(true, true);
+               };
+
+               Ext.MessageBox.alert(gettext('Error'),
+                                    gettext("Login failed. Please try again"),
+                                    handler);
+           };
+
+           var success_fn = function(data) {
+               var handler = view.handler || Ext.emptyFn;
+               handler.call(me, data);
+               view.close();
+           };
+
+           view.el.mask(gettext('Please wait...'), 'x-mask-loading');
+
+           // set or clear username
+           var sp = Ext.state.Manager.getProvider();
+           if (saveunField.getValue() === true) {
+               sp.set(unField.getStateId(), unField.getValue());
+           } else {
+               sp.clear(unField.getStateId());
+           }
+           sp.set(saveunField.getStateId(), saveunField.getValue());
+
+           form.submit({
+               failure: function(f, resp){
+                   failure_fn(resp);
+               },
+               success: function(f, resp){
+                   view.el.unmask();
 
-               // set or clear username
-               var sp = Ext.state.Manager.getProvider();
-               if (saveunField.getValue() === true) {
-                   sp.set(unField.getStateId(), unField.getValue());
-               } else {
-                   sp.clear(unField.getStateId());
+                   var data = resp.result.data;
+                   if (Ext.isDefined(data.U2FChallenge)) {
+                       perform_u2f_fn(data);
+                   } else {
+                       success_fn(data);
+                   }
                }
-               sp.set(saveunField.getStateId(), saveunField.getValue());
+           });
+
+           perform_u2f_fn = function(data) {
+               // Store first factor login information first:
+               data.LoggedOut = true;
+               Proxmox.Utils.setAuthData(data);
+               // Show the message:
+               var msg = Ext.Msg.show({
+                   title: 'U2F: '+gettext('Verification'),
+                   message: gettext('Please press the button on your U2F 
Device'),
+                   buttons: []
+               });
+               var chlg = data.U2FChallenge;
+               var key = {
+                   version: chlg.version,
+                   keyHandle: chlg.keyHandle
+               };
+               u2f.sign(chlg.appId, chlg.challenge, [key], function(res) {
+                   msg.close();
+                   if (res.errorCode) {
+                       Proxmox.Utils.authClear();
+                       Ext.Msg.alert(gettext('Error'), "U2F Error: 
"+res.errorCode);
+                       return;
+                   }
+                   delete res.errorCode;
+                   finish_u2f_fn(res);
+               });
+           };
 
-               form.submit({
-                   failure: function(f, resp){
+           finish_u2f_fn = function(res) {
+               view.el.mask(gettext('Please wait...'), 'x-mask-loading');
+               var params = { response: JSON.stringify(res) };
+               Proxmox.Utils.API2Request({
+                   url: '/api2/extjs/access/u2f',
+                   params: params,
+                   method: 'POST',
+                   timeout: 5000, // it'll delay both success & failure
+                   success: function(resp, opts) {
                        view.el.unmask();
-                       var handler = function() {
-                           var uf = me.lookupReference('usernameField');
-                           uf.focus(true, true);
-                       };
-
-                       Ext.MessageBox.alert(gettext('Error'),
-                                            gettext("Login failed. Please try 
again"),
-                                            handler);
+                       // Fill in what we copy over from the 1st factor:
+                       var data = resp.result.data;
+                       data.CSRFPreventionToken = Proxmox.CSRFPreventionToken;
+                       data.username = Proxmox.UserName;
+                       // Finish logging in:
+                       success_fn(data);
                    },
-                   success: function(f, resp){
-                       view.el.unmask();
-
-                       var handler = view.handler || Ext.emptyFn;
-                       handler.call(me, resp.result.data);
-                       view.close();
+                   failure: function(resp, opts) {
+                       Proxmox.Utils.authClear();
+                       failure_fn(resp);
                    }
                });
-           }
+           };
        },
 
        control: {
-- 
2.11.0


_______________________________________________
pve-devel mailing list
pve-devel@pve.proxmox.com
https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel

Reply via email to