modeled after UserView and related code. Signed-off-by: Fabian Grünbichler <f.gruenbich...@proxmox.com> --- www/manager6/Makefile | 3 + www/manager6/dc/Config.js | 8 ++ www/manager6/dc/TokenEdit.js | 125 ++++++++++++++++++ www/manager6/dc/TokenView.js | 203 +++++++++++++++++++++++++++++ www/manager6/form/TokenSelector.js | 91 +++++++++++++ 5 files changed, 430 insertions(+) create mode 100644 www/manager6/dc/TokenEdit.js create mode 100644 www/manager6/dc/TokenView.js create mode 100644 www/manager6/form/TokenSelector.js
diff --git a/www/manager6/Makefile b/www/manager6/Makefile index d232e635..087738e2 100644 --- a/www/manager6/Makefile +++ b/www/manager6/Makefile @@ -225,6 +225,9 @@ JSSRC= \ dc/Cluster.js \ dc/ClusterEdit.js \ dc/PermissionView.js \ + dc/TokenView.js \ + dc/TokenEdit.js \ + form/TokenSelector.js \ Workspace.js lint: ${JSSRC} diff --git a/www/manager6/dc/Config.js b/www/manager6/dc/Config.js index 52cd106f..429f1d93 100644 --- a/www/manager6/dc/Config.js +++ b/www/manager6/dc/Config.js @@ -86,6 +86,14 @@ Ext.define('PVE.dc.Config', { itemId: 'users' }); + me.items.push({ + xtype: 'pveTokenView', + groups: ['permissions'], + iconCls: 'fa fa-user-o', + title: gettext('API Tokens'), + itemId: 'apitokens' + }); + if (caps.dc['Sys.Audit']) { me.items.push({ xtype: 'pveGroupView', diff --git a/www/manager6/dc/TokenEdit.js b/www/manager6/dc/TokenEdit.js new file mode 100644 index 00000000..c047c175 --- /dev/null +++ b/www/manager6/dc/TokenEdit.js @@ -0,0 +1,125 @@ +Ext.define('PVE.dc.TokenEdit', { + extend: 'Proxmox.window.Edit', + alias: ['widget.pveDcTokenEdit'], + + isAdd: true, + + initComponent: function() { + var me = this; + + me.isCreate = !me.tokenid; + + var url; + var method; + var realm; + + if (me.isCreate) { + url = '/invalid'; + method = 'POST'; + } else { + url = '/api2/extjs/access/users/' + encodeURIComponent(me.userid) + '/token/' + encodeURIComponent(me.tokenid); + method = 'PUT'; + } + + var column1 = [ + { + xtype: me.isCreate ? 'pveUserSelector' : 'displayfield', + name: 'userid', + fieldLabel: gettext('User'), + value: me.userid, + allowBlank: false, + submitValue: me.isCreate ? true : false + }, + { + xtype: me.isCreate ? 'textfield' : 'displayfield', + name: 'tokenid', + fieldLabel: gettext('Token ID'), + value: me.tokenid, + allowBlank: false, + submitValue: me.isCreate ? true : false + } + ]; + + var column2 = [ + { + xtype: 'proxmoxcheckbox', + name: 'privsep', + checked: true, + uncheckedValue: 0, + fieldLabel: gettext('Privilege Separation') + }, + { + xtype: 'datefield', + name: 'expire', + emptyText: 'never', + format: 'Y-m-d', + submitFormat: 'U', + fieldLabel: gettext('Expire') + } + ]; + + var ipanel = Ext.create('Proxmox.panel.InputPanel', { + column1: column1, + column2: column2, + columnB: [ + { + xtype: 'textfield', + name: 'comment', + fieldLabel: gettext('Comment') + } + ], + onGetValues: function(values) { + // hack: ExtJS datefield does not submit 0, so we need to set that + if (!values.expire) { + values.expire = 0; + } + + if (me.isCreate) { + if (values.tokenid && values.userid) { + me.url = '/api2/extjs/access/users/' + encodeURIComponent(values.userid) + '/token/' + encodeURIComponent(values.tokenid); + } else { + me.url = '/invalid'; + } + delete values.userid; + delete values.tokenid; + } + + return values; + } + }); + + Ext.applyIf(me, { + subject: gettext('User'), + url: url, + method: method, + fieldDefaults: { + labelWidth: 110 // for spanish translation + }, + items: [ ipanel ] + }); + + me.callParent(); + + if (!me.isCreate) { + me.load({ + success: function(response, options) { + var data = response.result.data; + if (Ext.isDefined(data.expire)) { + if (data.expire) { + data.expire = new Date(data.expire * 1000); + } else { + // display 'never' instead of '1970-01-01' + data.expire = null; + } + } + me.setValues(data); + } + }); + } + }, + apiCallDone: function(success, response, options) { + if (success && response.result.data.value) { + Ext.Msg.alert(gettext('API Token'), gettext('Please record the following API token value - it will only be displayed now') + ':<br/>' + response.result.data.value); + } + } +}); diff --git a/www/manager6/dc/TokenView.js b/www/manager6/dc/TokenView.js new file mode 100644 index 00000000..201aac50 --- /dev/null +++ b/www/manager6/dc/TokenView.js @@ -0,0 +1,203 @@ +/*jslint confusion: true */ +Ext.define('PVE.dc.TokenView', { + extend: 'Ext.grid.GridPanel', + + alias: ['widget.pveTokenView'], + + onlineHelp: 'chapter_user_management', + + stateful: true, + stateId: 'grid-tokens', + + // use fixed user + userid: undefined, + + initComponent : function() { + var me = this; + + var caps = Ext.state.Manager.get('GuiCap'); + + var store = new Ext.data.Store({ + id: "tokens", + model: 'pve-tokens', + sorters: [ + { + property: 'userid', + order: 'ASC' + }, + { + property: 'tokenid', + order: 'ASC', + } + ] + }); + + var reload = function() { + Proxmox.Utils.API2Request({ + url: '/access/users/?full=1', + method: 'GET', + failure: function(response, opts) { + Proxmox.Utils.setErrorMask(me, response.htmlStatus); + me.load_task.delay(me.load_delay); + }, + success: function(response, opts) { + Proxmox.Utils.setErrorMask(me, false); + var result = Ext.decode(response.responseText); + var data = result.data || []; + var records = []; + Ext.Array.each(data, function(user) { + tokens = user.tokens || []; + Ext.Array.each(tokens, function(token) { + var r = {}; + r.id = user.userid + '!' + token.tokenid; + r.userid = user.userid; + r.tokenid = token.tokenid; + r.comment = token.comment; + r.expire = token.expire; + r.privsep = token.privsep === 1 ? true : false; + records.push(r); + }); + }); + store.loadData(records); + }, + }); + }; + + var sm = Ext.create('Ext.selection.RowModel', {}); + + var remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { + selModel: sm, + enableFn: function(rec) { + return !!caps.access['User.Modify']; + }, + callback: function() { + reload(); + }, + getUrl: function(rec) { + return '/access/users/' + encodeURIComponent(rec.data.userid) + '/token/' + encodeURIComponent(rec.data.tokenid); + } + }); + + var run_editor = function() { + var rec = sm.getSelection()[0]; + if (!rec || !caps.access['User.Modify']) { + return; + } + + var win = Ext.create('PVE.dc.TokenEdit', { + userid: rec.data.userid, + tokenid: rec.data.tokenid + }); + win.on('destroy', reload); + win.show(); + }; + + var edit_btn = new Proxmox.button.Button({ + text: gettext('Edit'), + disabled: true, + enableFn: function(rec) { + return !!caps.access['User.Modify']; + }, + selModel: sm, + handler: run_editor + }); + + var perm_btn = new Proxmox.button.Button({ + text: gettext('Permissions'), + disabled: false, + selModel: sm, + handler: function(btn, event, rec) { + var rec = sm.getSelection()[0]; + var win = Ext.create('PVE.dc.PermissionView', { + userid: rec.data.id + }); + win.show(); + } + }); + + var tbar = [ + { + text: gettext('Add'), + disabled: !caps.access['User.Modify'], + handler: function() { + var rec = sm.getSelection()[0]; + var data = {}; + if (rec && rec.data) { + data.userid = rec.data.userid; + } + var win = Ext.create('PVE.dc.TokenEdit', data); + win.on('destroy', reload); + win.show(); + } + }, + edit_btn, remove_btn, perm_btn + ]; + + var render_username = function(userid) { + return userid.match(/^(.+)(@[^@]+)$/)[1]; + }; + + var render_realm = function(userid) { + return userid.match(/@([^@]+)$/)[1]; + }; + + + Ext.apply(me, { + store: store, + selModel: sm, + tbar: tbar, + viewConfig: { + trackOver: false + }, + columns: [ + { + header: gettext('User name'), + width: 200, + sortable: true, + renderer: render_username, + dataIndex: 'userid' + }, + { + header: gettext('Realm'), + width: 100, + sortable: true, + renderer: render_realm, + dataIndex: 'userid' + }, + { + header: gettext('Token name'), + width: 100, + sortable: true, + dataIndex: 'tokenid' + }, + { + header: gettext('Expire'), + width: 80, + sortable: true, + renderer: Proxmox.Utils.format_expire, + dataIndex: 'expire' + }, + { + header: gettext('Comment'), + sortable: false, + renderer: Ext.String.htmlEncode, + dataIndex: 'comment', + flex: 1 + }, + { + header: gettext('Privilege Separation'), + width: 80, + sortable: true, + renderer: Proxmox.Utils.format_boolean, + dataIndex: 'privsep' + }, + ], + listeners: { + activate: reload, + itemdblclick: run_editor + } + }); + + me.callParent(); + } +}); diff --git a/www/manager6/form/TokenSelector.js b/www/manager6/form/TokenSelector.js new file mode 100644 index 00000000..932ecd7c --- /dev/null +++ b/www/manager6/form/TokenSelector.js @@ -0,0 +1,91 @@ +Ext.define('PVE.form.TokenSelector', { + extend: 'Proxmox.form.ComboGrid', + alias: ['widget.pveTokenSelector'], + + allowBlank: false, + autoSelect: false, + valueField: 'id', + displayField: 'id', + + editable: true, + anyMatch: true, + forceSelection: true, + + initComponent: function() { + var me = this; + + var store = new Ext.data.Store({ + model: 'pve-tokens', + sorters: [{ + property: 'userid' + }, + { + property: 'tokenid' + }] + }); + + Ext.apply(me, { + store: store, + listConfig: { + columns: [ + { + header: gettext('API Token'), + sortable: true, + dataIndex: 'id', + flex: 1 + }, + { + header: gettext('Comment'), + sortable: false, + dataIndex: 'comment', + renderer: Ext.String.htmlEncode, + flex: 1 + } + ] + } + }); + + me.callParent(); + + Proxmox.Utils.API2Request({ + url: '/access/users/?full=1', + method: 'GET', + failure: function(response, opts) { + Proxmox.Utils.setErrorMask(me, response.htmlStatus); + me.load_task.delay(me.load_delay); + }, + success: function(response, opts) { + Proxmox.Utils.setErrorMask(me, false); + var result = Ext.decode(response.responseText); + var data = result.data || []; + var records = []; + Ext.Array.each(data, function(user) { + tokens = user.tokens || []; + Ext.Array.each(tokens, function(token) { + var r = {}; + r.id = user.userid + '!' + token.tokenid; + r.comment = token.comment; + records.push(r); + }); + }); + store.loadData(records); + }, + }); + } + +}, function() { + + Ext.define('pve-tokens', { + extend: 'Ext.data.Model', + fields: [ + 'id', 'userid', 'tokenid', 'comment', + { type: 'boolean', name: 'privsep' }, + { type: 'date', dateFormat: 'timestamp', name: 'expire' } + ], + idProperty: 'id' + }); + +}); + + + -- 2.20.1 _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel