[pve-devel] [PATCH manager 3/3] ui: support u2f authentication
Wolfgang Bumiller
w.bumiller at proxmox.com
Wed Mar 27 11:16:30 CET 2019
Signed-off-by: Wolfgang Bumiller <w.bumiller at 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 ae7f610f..a4bbcc37 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-api.js"></script>
[% END %]
<script type="text/javascript">
Proxmox = {
diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index f1d59c46..39121997 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -198,6 +198,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 e88300f2..1d343525 100644
--- a/www/manager6/Workspace.js
+++ b/www/manager6/Workspace.js
@@ -19,8 +19,7 @@ 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);
var rt = me.down('pveResourceTree');
rt.setDatacenterText(loginData.clustername);
@@ -29,9 +28,6 @@ Ext.define('PVE.Workspace', {
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 at 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 4d0c5595..c555f61d 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_username = function(userid) {
diff --git a/www/manager6/window/LoginWindow.js b/www/manager6/window/LoginWindow.js
index 3ab5173c..a7588f32 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
More information about the pve-devel
mailing list