]>
Commit | Line | Data |
---|---|---|
1 | Ext.define('PVE.dc.UserView', { | |
2 | extend: 'Ext.grid.GridPanel', | |
3 | ||
4 | alias: ['widget.pveUserView'], | |
5 | ||
6 | onlineHelp: 'pveum_users', | |
7 | ||
8 | stateful: true, | |
9 | stateId: 'grid-users', | |
10 | ||
11 | initComponent: function() { | |
12 | var me = this; | |
13 | ||
14 | var caps = Ext.state.Manager.get('GuiCap'); | |
15 | ||
16 | var store = new Ext.data.Store({ | |
17 | id: "users", | |
18 | model: 'pmx-users', | |
19 | sorters: { | |
20 | property: 'userid', | |
21 | direction: 'ASC', | |
22 | }, | |
23 | }); | |
24 | let reload = () => store.load(); | |
25 | ||
26 | let sm = Ext.create('Ext.selection.RowModel', {}); | |
27 | ||
28 | let remove_btn = Ext.create('Proxmox.button.StdRemoveButton', { | |
29 | selModel: sm, | |
30 | baseurl: '/access/users/', | |
31 | dangerous: true, | |
32 | enableFn: rec => caps.access['User.Modify'] && rec.data.userid !== 'root@pam', | |
33 | callback: () => reload(), | |
34 | }); | |
35 | let run_editor = function() { | |
36 | var rec = sm.getSelection()[0]; | |
37 | if (!rec || !caps.access['User.Modify']) { | |
38 | return; | |
39 | } | |
40 | Ext.create('PVE.dc.UserEdit', { | |
41 | userid: rec.data.userid, | |
42 | autoShow: true, | |
43 | listeners: { | |
44 | destroy: () => reload(), | |
45 | }, | |
46 | }); | |
47 | }; | |
48 | let edit_btn = new Proxmox.button.Button({ | |
49 | text: gettext('Edit'), | |
50 | disabled: true, | |
51 | enableFn: function(rec) { | |
52 | return !!caps.access['User.Modify']; | |
53 | }, | |
54 | selModel: sm, | |
55 | handler: run_editor, | |
56 | }); | |
57 | let pwchange_btn = new Proxmox.button.Button({ | |
58 | text: gettext('Password'), | |
59 | disabled: true, | |
60 | selModel: sm, | |
61 | enableFn: function(record) { | |
62 | let type = record.data['realm-type']; | |
63 | if (type) { | |
64 | if (PVE.Utils.authSchema[type]) { | |
65 | return !!PVE.Utils.authSchema[type].pwchange; | |
66 | } | |
67 | } | |
68 | return false; | |
69 | }, | |
70 | handler: function(btn, event, rec) { | |
71 | Ext.create('Proxmox.window.PasswordEdit', { | |
72 | userid: rec.data.userid, | |
73 | autoShow: true, | |
74 | listeners: { | |
75 | destroy: () => reload(), | |
76 | }, | |
77 | }); | |
78 | }, | |
79 | }); | |
80 | ||
81 | var perm_btn = new Proxmox.button.Button({ | |
82 | text: gettext('Permissions'), | |
83 | disabled: true, | |
84 | selModel: sm, | |
85 | handler: function(btn, event, rec) { | |
86 | Ext.create('PVE.dc.PermissionView', { | |
87 | userid: rec.data.userid, | |
88 | autoShow: true, | |
89 | listeners: { | |
90 | destroy: () => reload(), | |
91 | }, | |
92 | }); | |
93 | }, | |
94 | }); | |
95 | ||
96 | let unlock_btn = new Proxmox.button.Button({ | |
97 | text: gettext('Unlock TFA'), | |
98 | disabled: true, | |
99 | selModel: sm, | |
100 | enableFn: rec => !!(caps.access['User.Modify'] && | |
101 | (rec.data['totp-locked'] || rec.data['tfa-locked-until'])), | |
102 | handler: function(btn, event, rec) { | |
103 | Ext.Msg.confirm( | |
104 | Ext.String.format(gettext('Unlock TFA authentication for {0}'), rec.data.userid), | |
105 | gettext("Locked 2nd factors can happen if the user's password was leaked. Are you sure you want to unlock the user?"), | |
106 | function(btn_response) { | |
107 | if (btn_response === 'yes') { | |
108 | Proxmox.Utils.API2Request({ | |
109 | url: `/access/users/${rec.data.userid}/unlock-tfa`, | |
110 | waitMsgTarget: me, | |
111 | method: 'PUT', | |
112 | failure: function(response, options) { | |
113 | Ext.Msg.alert(gettext('Error'), response.htmlStatus); | |
114 | }, | |
115 | success: function(response, options) { | |
116 | reload(); | |
117 | }, | |
118 | }); | |
119 | } | |
120 | }, | |
121 | ); | |
122 | }, | |
123 | }); | |
124 | ||
125 | Ext.apply(me, { | |
126 | store: store, | |
127 | selModel: sm, | |
128 | tbar: [ | |
129 | { | |
130 | text: gettext('Add'), | |
131 | disabled: !caps.access['User.Modify'], | |
132 | handler: function() { | |
133 | Ext.create('PVE.dc.UserEdit', { | |
134 | autoShow: true, | |
135 | listeners: { | |
136 | destroy: () => reload(), | |
137 | }, | |
138 | }); | |
139 | }, | |
140 | }, | |
141 | '-', | |
142 | edit_btn, | |
143 | remove_btn, | |
144 | '-', | |
145 | pwchange_btn, | |
146 | '-', | |
147 | perm_btn, | |
148 | '-', | |
149 | unlock_btn, | |
150 | ], | |
151 | viewConfig: { | |
152 | trackOver: false, | |
153 | }, | |
154 | columns: [ | |
155 | { | |
156 | header: gettext('User name'), | |
157 | width: 200, | |
158 | sortable: true, | |
159 | renderer: Proxmox.Utils.render_username, | |
160 | dataIndex: 'userid', | |
161 | }, | |
162 | { | |
163 | header: gettext('Realm'), | |
164 | width: 100, | |
165 | sortable: true, | |
166 | renderer: Proxmox.Utils.render_realm, | |
167 | dataIndex: 'userid', | |
168 | }, | |
169 | { | |
170 | header: gettext('Enabled'), | |
171 | width: 80, | |
172 | sortable: true, | |
173 | renderer: Proxmox.Utils.format_boolean, | |
174 | dataIndex: 'enable', | |
175 | }, | |
176 | { | |
177 | header: gettext('Expire'), | |
178 | width: 80, | |
179 | sortable: true, | |
180 | renderer: Proxmox.Utils.format_expire, | |
181 | dataIndex: 'expire', | |
182 | }, | |
183 | { | |
184 | header: gettext('Name'), | |
185 | width: 150, | |
186 | sortable: true, | |
187 | renderer: PVE.Utils.render_full_name, | |
188 | dataIndex: 'firstname', | |
189 | }, | |
190 | { | |
191 | header: 'TFA', | |
192 | width: 120, | |
193 | sortable: true, | |
194 | renderer: function(v, metaData, record) { | |
195 | let tfa_type = PVE.Parser.parseTfaType(v); | |
196 | if (tfa_type === undefined) { | |
197 | return Proxmox.Utils.noText; | |
198 | } | |
199 | ||
200 | if (tfa_type !== 1) { | |
201 | return tfa_type; | |
202 | } | |
203 | ||
204 | let locked_until = record.data['tfa-locked-until']; | |
205 | if (locked_until !== undefined) { | |
206 | let now = new Date().getTime() / 1000; | |
207 | if (locked_until > now) { | |
208 | return gettext('Locked'); | |
209 | } | |
210 | } | |
211 | ||
212 | if (record.data['totp-locked']) { | |
213 | return gettext('TOTP Locked'); | |
214 | } | |
215 | ||
216 | return Proxmox.Utils.yesText; | |
217 | }, | |
218 | dataIndex: 'keys', | |
219 | }, | |
220 | { | |
221 | header: gettext('Groups'), | |
222 | dataIndex: 'groups', | |
223 | renderer: Ext.htmlEncode, | |
224 | flex: 2, | |
225 | }, | |
226 | { | |
227 | header: gettext('Comment'), | |
228 | sortable: false, | |
229 | renderer: Ext.String.htmlEncode, | |
230 | dataIndex: 'comment', | |
231 | flex: 3, | |
232 | }, | |
233 | ], | |
234 | listeners: { | |
235 | activate: reload, | |
236 | itemdblclick: run_editor, | |
237 | }, | |
238 | }); | |
239 | ||
240 | me.callParent(); | |
241 | ||
242 | Proxmox.Utils.monStoreErrors(me, store); | |
243 | }, | |
244 | }); |