[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