[pve-devel] [PATCH v2 manager 7/9] www: add Token Panel + Edit Window
Fabian Grünbichler
f.gruenbichler at proxmox.com
Thu Nov 21 15:43:50 CET 2019
modeled after UserView and related code.
Signed-off-by: Fabian Grünbichler <f.gruenbichler at 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 13bede62..e7fb8010 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
More information about the pve-devel
mailing list