-/*global Proxmox*/
Ext.define('pmg-users', {
extend: 'Ext.data.Model',
fields: [
- 'userid', 'firstname', 'lastname' , 'email', 'comment',
- 'role', 'keys', 'realm',
+ 'userid', 'firstname', 'lastname', 'email', 'comment',
+ 'role', 'keys', 'realm', 'totp-lock',
{ type: 'boolean', name: 'enable' },
- { type: 'date', dateFormat: 'timestamp', name: 'expire' }
+ { type: 'date', dateFormat: 'timestamp', name: 'expire' },
],
proxy: {
type: 'proxmox',
- url: "/api2/json/access/users"
+ url: "/api2/json/access/users",
},
- idProperty: 'userid'
+ idProperty: 'userid',
});
Ext.define('PMG.UserView', {
sorters: [
{
property: 'realm',
- direction: 'ASC'
+ direction: 'ASC',
},
{
property: 'userid',
- direction: 'ASC'
- }
- ]
+ direction: 'ASC',
+ },
+ ],
},
controller: {
var view = this.getView();
var win = Ext.create('PMG.UserEdit', {});
- win.on('destroy', function() { view.reload() });
+ win.on('destroy', function() { view.reload(); });
win.show();
},
var rec = view.selModel.getSelection()[0];
var win = Ext.create('PMG.UserEdit', {
- userid: rec.data.userid
+ userid: rec.data.userid,
});
- win.on('destroy', function() { view.reload() });
+ win.on('destroy', function() { view.reload(); });
win.show();
},
onPassword: function(btn, event, rec) {
var view = this.getView();
- var win = Ext.create('Proxmox.window.PasswordEdit',{
- userid: rec.data.userid
+ var win = Ext.create('Proxmox.window.PasswordEdit', {
+ userid: rec.data.userid,
});
- win.on('destroy', function() { view.reload() });
+ win.on('destroy', function() { view.reload(); });
win.show();
},
onAfterRemove: function(btn, res) {
var view = this.getView();
view.reload();
- }
+ },
+
+ onUnlockTfa: function(btn, event, rec) {
+ let me = this;
+ let view = me.getView();
+ Ext.Msg.confirm(
+ Ext.String.format(gettext('Unlock TFA authentication for {0}'), rec.data.userid),
+ gettext("Locked 2nd factors can happen if the user's password was leaked. Are you sure you want to unlock the user?"),
+ function(btn_response) {
+ if (btn_response === 'yes') {
+ Proxmox.Utils.API2Request({
+ url: `/access/users/${rec.data.userid}/unlock-tfa`,
+ waitMsgTarget: view,
+ method: 'PUT',
+ failure: function(response, options) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ },
+ success: function(response, options) {
+ view.reload();
+ },
+ });
+ }
+ },
+ );
+ },
},
listeners: {
{
text: gettext('Add'),
reference: 'addBtn',
- handler: 'onAdd'
+ handler: 'onAdd',
},
{
xtype: 'proxmoxButton',
text: gettext('Edit'),
disabled: true,
- handler: 'onEdit'
+ handler: 'onEdit',
},
{
xtype: 'proxmoxStdRemoveButton',
baseurl: '/access/users',
reference: 'removeBtn',
callback: 'onAfterRemove',
- waitMsgTarget: true
+ waitMsgTarget: true,
},
{
xtype: 'proxmoxButton',
text: gettext('Password'),
disabled: true,
- handler: 'onPassword'
- }
+ handler: 'onPassword',
+ },
+ '-',
+ {
+ xtype: 'proxmoxButton',
+ text: gettext('Unlock TFA'),
+ handler: 'onUnlockTfa',
+ disabled: true,
+ enableFn: ({ data }) =>
+ data['totp-locked'] || (data['tfa-locked-until'] > (new Date().getTime() / 1000)),
+ },
],
columns: [
width: 200,
sortable: true,
renderer: 'renderUsername',
- dataIndex: 'userid'
+ dataIndex: 'userid',
},
{
header: gettext('Realm'),
width: 100,
sortable: true,
- dataIndex: 'realm'
+ dataIndex: 'realm',
},
{
header: gettext('Role'),
width: 150,
sortable: true,
renderer: PMG.Utils.format_user_role,
- dataIndex: 'role'
+ dataIndex: 'role',
},
{
header: gettext('Enabled'),
width: 80,
sortable: true,
renderer: Proxmox.Utils.format_boolean,
- dataIndex: 'enable'
+ dataIndex: 'enable',
},
{
header: gettext('Expire'),
width: 80,
sortable: true,
renderer: Proxmox.Utils.format_expire,
- dataIndex: 'expire'
+ dataIndex: 'expire',
},
{
header: gettext('Name'),
width: 150,
sortable: true,
renderer: 'renderFullName',
- dataIndex: 'firstname'
+ dataIndex: 'firstname',
+ },
+ {
+ header: gettext('TFA Lock'),
+ width: 120,
+ sortable: true,
+ dataIndex: 'totp-locked',
+ renderer: function(v, metaData, record) {
+ let locked_until = record.data['tfa-locked-until'];
+ if (locked_until !== undefined) {
+ let now = new Date().getTime() / 1000;
+ if (locked_until > now) {
+ return gettext('Locked');
+ }
+ }
+
+ if (record.data['totp-locked']) {
+ return gettext('TOTP Locked');
+ }
+
+ return Proxmox.Utils.noText;
+ },
},
{
header: gettext('Comment'),
sortable: false,
renderer: Ext.String.htmlEncode,
dataIndex: 'comment',
- flex: 1
- }
+ flex: 1,
+ },
],
reload: function() {
var me = this;
me.store.load();
- }
+ },
});