]> git.proxmox.com Git - proxmox-backup.git/blame - www/window/AddWebauthn.js
ui: cleanup order of declraing properties
[proxmox-backup.git] / www / window / AddWebauthn.js
CommitLineData
fbeac4ea
WB
1Ext.define('PBS.window.AddWebauthn', {
2 extend: 'Ext.window.Window',
3 alias: 'widget.pbsAddWebauthn',
4 mixins: ['Proxmox.Mixin.CBind'],
5
6 onlineHelp: 'user_mgmt',
7
8 modal: true,
9 resizable: false,
10 title: gettext('Add a Webauthn login token'),
11 width: 512,
12
13 user: undefined,
14 fixedUser: false,
15
16 initComponent: function() {
17 let me = this;
18 me.callParent();
19 Ext.GlobalEvents.fireEvent('proxmoxShowHelp', me.onlineHelp);
20 },
21
22 viewModel: {
23 data: {
24 valid: false,
958055a7 25 userid: null,
3189d051
TL
26 },
27 formulas: {
28 passwordConfirmText: (get) => {
29 let id = get('userid');
30 return Ext.String.format(gettext("Confirm password of '{0}'"), id);
31 },
fbeac4ea
WB
32 },
33 },
34
35 controller: {
36 xclass: 'Ext.app.ViewController',
37
38 control: {
39 'field': {
40 validitychange: function(field, valid) {
41 let me = this;
42 let viewmodel = me.getViewModel();
43 let form = me.lookup('webauthn_form');
44 viewmodel.set('valid', form.isValid());
45 },
46 },
47 '#': {
48 show: function() {
49 let me = this;
50 let view = me.getView();
51
52 if (Proxmox.UserName === 'root@pam') {
53 view.lookup('password').setVisible(false);
54 view.lookup('password').setDisabled(true);
55 }
56 },
57 },
58 },
59
60 registerWebauthn: async function() {
61 let me = this;
62 let values = me.lookup('webauthn_form').getValues();
63 values.type = "webauthn";
64
65 let userid = values.user;
66 delete values.user;
67
758a827c
WB
68 me.getView().mask(gettext('Please wait...'), 'x-mask-loading');
69
fbeac4ea
WB
70 try {
71 let register_response = await PBS.Async.api2({
72 url: `/api2/extjs/access/tfa/${userid}`,
73 method: 'POST',
74 params: values,
75 });
76
77 let data = register_response.result.data;
78 if (!data.challenge) {
79 throw "server did not respond with a challenge";
80 }
81
82 let challenge_obj = JSON.parse(data.challenge);
83
84 // Fix this up before passing it to the browser, but keep a copy of the original
85 // string to pass in the response:
86 let challenge_str = challenge_obj.publicKey.challenge;
87 challenge_obj.publicKey.challenge = PBS.Utils.base64url_to_bytes(challenge_str);
88 challenge_obj.publicKey.user.id =
89 PBS.Utils.base64url_to_bytes(challenge_obj.publicKey.user.id);
90
91 let msg = Ext.Msg.show({
92 title: `Webauthn: ${gettext('Setup')}`,
93 message: gettext('Please press the button on your Webauthn Device'),
94 buttons: [],
95 });
96
97 let token_response = await navigator.credentials.create(challenge_obj);
98
99 // We cannot pass ArrayBuffers to the API, so extract & convert the data.
100 let response = {
101 id: token_response.id,
102 type: token_response.type,
103 rawId: PBS.Utils.bytes_to_base64url(token_response.rawId),
104 response: {
105 attestationObject: PBS.Utils.bytes_to_base64url(
106 token_response.response.attestationObject,
107 ),
108 clientDataJSON: PBS.Utils.bytes_to_base64url(
109 token_response.response.clientDataJSON,
110 ),
111 },
112 };
113
114 msg.close();
115
116 let params = {
117 type: "webauthn",
118 challenge: challenge_str,
119 value: JSON.stringify(response),
120 };
121
122 if (values.password) {
123 params.password = values.password;
124 }
125
126 await PBS.Async.api2({
127 url: `/api2/extjs/access/tfa/${userid}`,
128 method: 'POST',
129 params,
130 });
131 } catch (error) {
132 console.error(error); // for debugging if it's not displayable...
133 Ext.Msg.alert(gettext('Error'), error);
134 }
135
136 me.getView().close();
137 },
138 },
139
140 items: [
141 {
142 xtype: 'form',
143 reference: 'webauthn_form',
144 layout: 'anchor',
646221cc 145 border: false,
fbeac4ea
WB
146 bodyPadding: 10,
147 fieldDefaults: {
148 anchor: '100%',
149 },
150 items: [
151 {
152 xtype: 'pmxDisplayEditField',
153 name: 'user',
154 cbind: {
155 editable: (get) => !get('fixedUser'),
958055a7 156 value: () => Proxmox.UserName,
fbeac4ea
WB
157 },
158 fieldLabel: gettext('User'),
159 editConfig: {
160 xtype: 'pbsUserSelector',
161 allowBlank: false,
162 },
163 renderer: Ext.String.htmlEncode,
3189d051
TL
164 listeners: {
165 change: function(field, newValue, oldValue) {
166 let vm = this.up('window').getViewModel();
167 vm.set('userid', newValue);
168 },
169 },
fbeac4ea
WB
170 },
171 {
172 xtype: 'textfield',
173 fieldLabel: gettext('Description'),
174 allowBlank: false,
175 name: 'description',
176 maxLength: 256,
3189d051 177 emptyText: gettext('For example: TFA device ID, required to identify multiple factors.'),
fbeac4ea
WB
178 },
179 {
180 xtype: 'textfield',
aab9a264
TL
181 name: 'password',
182 reference: 'password',
3189d051 183 fieldLabel: gettext('Verify Password'),
aab9a264 184 inputType: 'password',
fbeac4ea 185 minLength: 5,
fbeac4ea
WB
186 allowBlank: false,
187 validateBlank: true,
958055a7
TL
188 cbind: {
189 hidden: () => Proxmox.UserName === 'root@pam',
190 disabled: () => Proxmox.UserName === 'root@pam',
191 },
3189d051
TL
192 bind: {
193 emptyText: '{passwordConfirmText}',
194 },
fbeac4ea
WB
195 },
196 ],
197 },
198 ],
199
200 buttons: [
201 {
202 xtype: 'proxmoxHelpButton',
203 },
204 '->',
205 {
206 xtype: 'button',
207 text: gettext('Register Webauthn Device'),
208 handler: 'registerWebauthn',
209 bind: {
210 disabled: '{!valid}',
211 },
212 },
213 ],
214});