mirror of
https://git.proxmox.com/git/pve-manager
synced 2025-07-24 13:06:50 +00:00
ui: add Token Panel + Edit Window
modeled after UserView and related code. Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
This commit is contained in:
parent
34d0acbd90
commit
c831fbde20
@ -241,6 +241,9 @@ JSSRC= \
|
||||
dc/Cluster.js \
|
||||
dc/ClusterEdit.js \
|
||||
dc/PermissionView.js \
|
||||
dc/TokenView.js \
|
||||
dc/TokenEdit.js \
|
||||
form/TokenSelector.js \
|
||||
Workspace.js
|
||||
|
||||
lint: ${JSSRC}
|
||||
|
@ -87,6 +87,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',
|
||||
|
125
www/manager6/dc/TokenEdit.js
Normal file
125
www/manager6/dc/TokenEdit.js
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
});
|
203
www/manager6/dc/TokenView.js
Normal file
203
www/manager6/dc/TokenView.js
Normal file
@ -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();
|
||||
}
|
||||
});
|
91
www/manager6/form/TokenSelector.js
Normal file
91
www/manager6/form/TokenSelector.js
Normal file
@ -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'
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user