]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/grid/FirewallRules.js
ui: tfa/totp: fix setting issuer in secret URL
[pve-manager.git] / www / manager6 / grid / FirewallRules.js
CommitLineData
435cce27 1Ext.define('PVE.form.FWMacroSelector', {
0fc95a12 2 extend: 'Proxmox.form.ComboGrid',
435cce27 3 alias: 'widget.pveFWMacroSelector',
3d990919
EK
4 allowBlank: true,
5 autoSelect: false,
6 valueField: 'macro',
7 displayField: 'macro',
8 listConfig: {
9 columns: [
10 {
11 header: gettext('Macro'),
12 dataIndex: 'macro',
13 hideable: false,
14 width: 100
15 },
16 {
17 header: gettext('Description'),
91535f2b 18 renderer: Ext.String.htmlEncode,
3d990919
EK
19 flex: 1,
20 dataIndex: 'descr'
21 }
22 ]
23 },
435cce27
DM
24 initComponent: function() {
25 var me = this;
26
27 var store = Ext.create('Ext.data.Store', {
28 autoLoad: true,
29 fields: [ 'macro', 'descr' ],
30 idProperty: 'macro',
31 proxy: {
56a353b9 32 type: 'proxmox',
435cce27
DM
33 url: "/api2/json/cluster/firewall/macros"
34 },
35 sorters: {
36 property: 'macro',
37 order: 'DESC'
38 }
39 });
40
41 Ext.apply(me, {
22f2f9d6 42 store: store
435cce27
DM
43 });
44
45 me.callParent();
46 }
47});
48
49Ext.define('PVE.FirewallRulePanel', {
ef4ef788 50 extend: 'Proxmox.panel.InputPanel',
435cce27
DM
51
52 allow_iface: false,
53
54 list_refs_url: undefined,
55
56 onGetValues: function(values) {
57 var me = this;
58
59 // hack: editable ComboGrid returns nothing when empty, so we need to set ''
60 // Also, disabled text fields return nothing, so we need to set ''
61
3c37fe48 62 Ext.Array.each(['source', 'dest', 'macro', 'proto', 'sport', 'dport', 'log'], function(key) {
435cce27
DM
63 if (values[key] === undefined) {
64 values[key] = '';
65 }
66 });
67
68 delete values.modified_marker;
69
70 return values;
71 },
72
73 initComponent : function() {
74 var me = this;
75
76 if (!me.list_refs_url) {
77 throw "no list_refs_url specified";
78 }
79
80 me.column1 = [
81 {
82 // hack: we use this field to mark the form 'dirty' when the
83 // record has errors- so that the user can safe the unmodified
84 // form again.
85 xtype: 'hiddenfield',
86 name: 'modified_marker',
22f2f9d6 87 value: ''
435cce27
DM
88 },
89 {
09cacce7 90 xtype: 'proxmoxKVComboBox',
435cce27
DM
91 name: 'type',
92 value: 'in',
e7bc7f31 93 comboItems: [['in', 'in'], ['out', 'out']],
435cce27
DM
94 fieldLabel: gettext('Direction'),
95 allowBlank: false
96 },
97 {
09cacce7 98 xtype: 'proxmoxKVComboBox',
435cce27
DM
99 name: 'action',
100 value: 'ACCEPT',
e7bc7f31 101 comboItems: [['ACCEPT', 'ACCEPT'], ['DROP', 'DROP'], ['REJECT', 'REJECT']],
435cce27
DM
102 fieldLabel: gettext('Action'),
103 allowBlank: false
104 }
105 ];
106
107 if (me.allow_iface) {
108 me.column1.push({
dbed4c1c 109 xtype: 'proxmoxtextfield',
435cce27 110 name: 'iface',
d5e771ce 111 deleteEmpty: !me.isCreate,
435cce27
DM
112 value: '',
113 fieldLabel: gettext('Interface')
114 });
115 } else {
116 me.column1.push({
117 xtype: 'displayfield',
118 fieldLabel: '',
435cce27
DM
119 value: ''
120 });
121 }
122
fa94a977 123 me.column1.push(
435cce27
DM
124 {
125 xtype: 'displayfield',
126 fieldLabel: '',
127 height: 7,
128 value: ''
129 },
130 {
131 xtype: 'pveIPRefSelector',
132 name: 'source',
133 autoSelect: false,
134 editable: true,
135 base_url: me.list_refs_url,
136 value: '',
137 fieldLabel: gettext('Source')
138
139 },
140 {
141 xtype: 'pveIPRefSelector',
142 name: 'dest',
143 autoSelect: false,
144 editable: true,
145 base_url: me.list_refs_url,
146 value: '',
147 fieldLabel: gettext('Destination')
148 }
fa94a977 149 );
435cce27
DM
150
151
152 me.column2 = [
153 {
896c0d50 154 xtype: 'proxmoxcheckbox',
435cce27
DM
155 name: 'enable',
156 checked: false,
435cce27
DM
157 uncheckedValue: 0,
158 fieldLabel: gettext('Enable')
159 },
160 {
161 xtype: 'pveFWMacroSelector',
162 name: 'macro',
435cce27 163 fieldLabel: gettext('Macro'),
1fafdce8 164 editable: true,
435cce27
DM
165 allowBlank: true,
166 listeners: {
167 change: function(f, value) {
1fafdce8 168 if (value === null) {
435cce27
DM
169 me.down('field[name=proto]').setDisabled(false);
170 me.down('field[name=sport]').setDisabled(false);
171 me.down('field[name=dport]').setDisabled(false);
172 } else {
173 me.down('field[name=proto]').setDisabled(true);
174 me.down('field[name=proto]').setValue('');
175 me.down('field[name=sport]').setDisabled(true);
176 me.down('field[name=sport]').setValue('');
177 me.down('field[name=dport]').setDisabled(true);
7c7ae44f 178 me.down('field[name=dport]').setValue('');
435cce27
DM
179 }
180 }
181 }
182 },
183 {
184 xtype: 'pveIPProtocolSelector',
185 name: 'proto',
186 autoSelect: false,
187 editable: true,
188 value: '',
189 fieldLabel: gettext('Protocol')
190 },
191 {
192 xtype: 'displayfield',
193 fieldLabel: '',
194 height: 7,
195 value: ''
196 },
197 {
198 xtype: 'textfield',
199 name: 'sport',
200 value: '',
201 fieldLabel: gettext('Source port')
202 },
203 {
204 xtype: 'textfield',
205 name: 'dport',
435cce27
DM
206 value: '',
207 fieldLabel: gettext('Dest. port')
6db91b0f
TL
208 }
209 ];
210
211 me.advancedColumn1 = [
3c37fe48 212 {
6db91b0f 213 xtype: 'pveFirewallLogLevels'
435cce27
DM
214 }
215 ];
3c37fe48 216
435cce27
DM
217 me.columnB = [
218 {
219 xtype: 'textfield',
220 name: 'comment',
221 value: '',
222 fieldLabel: gettext('Comment')
223 }
224 ];
225
226 me.callParent();
227 }
228});
229
230Ext.define('PVE.FirewallRuleEdit', {
9fccc702 231 extend: 'Proxmox.window.Edit',
435cce27
DM
232
233 base_url: undefined,
234 list_refs_url: undefined,
235
236 allow_iface: false,
237
238 initComponent : function() {
d5e771ce 239
435cce27
DM
240 var me = this;
241
242 if (!me.base_url) {
243 throw "no base_url specified";
244 }
245 if (!me.list_refs_url) {
246 throw "no list_refs_url specified";
247 }
248
d5e771ce 249 me.isCreate = (me.rule_pos === undefined);
435cce27 250
d5e771ce 251 if (me.isCreate) {
435cce27
DM
252 me.url = '/api2/extjs' + me.base_url;
253 me.method = 'POST';
254 } else {
255 me.url = '/api2/extjs' + me.base_url + '/' + me.rule_pos.toString();
256 me.method = 'PUT';
257 }
258
259 var ipanel = Ext.create('PVE.FirewallRulePanel', {
d5e771ce 260 isCreate: me.isCreate,
435cce27
DM
261 list_refs_url: me.list_refs_url,
262 allow_iface: me.allow_iface,
263 rule_pos: me.rule_pos
264 });
265
266 Ext.apply(me, {
267 subject: gettext('Rule'),
268 isAdd: true,
269 items: [ ipanel ]
270 });
271
272 me.callParent();
273
d5e771ce 274 if (!me.isCreate) {
435cce27
DM
275 me.load({
276 success: function(response, options) {
277 var values = response.result.data;
278 ipanel.setValues(values);
279 if (values.errors) {
280 var field = me.query('[isFormField][name=modified_marker]')[0];
281 field.setValue(1);
282 Ext.Function.defer(function() {
283 var form = ipanel.up('form').getForm();
a764c5f7 284 form.markInvalid(values.errors);
435cce27
DM
285 }, 100);
286 }
287 }
288 });
330020d2
WL
289 } else if (me.rec) {
290 ipanel.setValues(me.rec.data);
435cce27
DM
291 }
292 }
293});
294
295Ext.define('PVE.FirewallGroupRuleEdit', {
9fccc702 296 extend: 'Proxmox.window.Edit',
435cce27
DM
297
298 base_url: undefined,
299
300 allow_iface: false,
301
302 initComponent : function() {
d5e771ce 303
435cce27
DM
304 var me = this;
305
d5e771ce 306 me.isCreate = (me.rule_pos === undefined);
435cce27 307
d5e771ce 308 if (me.isCreate) {
435cce27
DM
309 me.url = '/api2/extjs' + me.base_url;
310 me.method = 'POST';
311 } else {
312 me.url = '/api2/extjs' + me.base_url + '/' + me.rule_pos.toString();
313 me.method = 'PUT';
314 }
315
316 var column1 = [
317 {
318 xtype: 'hiddenfield',
319 name: 'type',
320 value: 'group'
321 },
322 {
323 xtype: 'pveSecurityGroupsSelector',
324 name: 'action',
325 value: '',
326 fieldLabel: gettext('Security Group'),
327 allowBlank: false
328 }
329 ];
330
331 if (me.allow_iface) {
332 column1.push({
dbed4c1c 333 xtype: 'proxmoxtextfield',
435cce27 334 name: 'iface',
d5e771ce 335 deleteEmpty: !me.isCreate,
435cce27
DM
336 value: '',
337 fieldLabel: gettext('Interface')
338 });
339 }
340
ef4ef788 341 var ipanel = Ext.create('Proxmox.panel.InputPanel', {
d5e771ce 342 isCreate: me.isCreate,
435cce27
DM
343 column1: column1,
344 column2: [
345 {
896c0d50 346 xtype: 'proxmoxcheckbox',
435cce27
DM
347 name: 'enable',
348 checked: false,
435cce27
DM
349 uncheckedValue: 0,
350 fieldLabel: gettext('Enable')
351 }
352 ],
353 columnB: [
354 {
355 xtype: 'textfield',
356 name: 'comment',
357 value: '',
358 fieldLabel: gettext('Comment')
359 }
360 ]
361 });
362
363 Ext.apply(me, {
364 subject: gettext('Rule'),
365 isAdd: true,
366 items: [ ipanel ]
367 });
368
369 me.callParent();
370
d5e771ce 371 if (!me.isCreate) {
435cce27
DM
372 me.load({
373 success: function(response, options) {
374 var values = response.result.data;
375 ipanel.setValues(values);
376 }
377 });
378 }
379 }
380});
381
382Ext.define('PVE.FirewallRules', {
383 extend: 'Ext.grid.Panel',
384 alias: 'widget.pveFirewallRules',
385
ba93a9c6
DC
386 onlineHelp: 'chapter_pve_firewall',
387
123e1c80
DC
388 stateful: true,
389 stateId: 'grid-firewall-rules',
390
435cce27
DM
391 base_url: undefined,
392 list_refs_url: undefined,
393
394 addBtn: undefined,
395 removeBtn: undefined,
396 editBtn: undefined,
397 groupBtn: undefined,
398
399 tbar_prefix: undefined,
400
401 allow_groups: true,
402 allow_iface: false,
403
404 setBaseUrl: function(url) {
405 var me = this;
406
407 me.base_url = url;
408
409 if (url === undefined) {
410 me.addBtn.setDisabled(true);
411 if (me.groupBtn) {
412 me.groupBtn.setDisabled(true);
413 }
414 me.store.removeAll();
415 } else {
416 me.addBtn.setDisabled(false);
3b1ca3ff 417 me.removeBtn.baseurl = url + '/';
435cce27
DM
418 if (me.groupBtn) {
419 me.groupBtn.setDisabled(false);
420 }
421 me.store.setProxy({
56a353b9 422 type: 'proxmox',
435cce27
DM
423 url: '/api2/json' + url
424 });
425
426 me.store.load();
427 }
428 },
429
430 moveRule: function(from, to) {
431 var me = this;
432
433 if (!me.base_url) {
434 return;
435 }
436
e7ade592 437 Proxmox.Utils.API2Request({
435cce27
DM
438 url: me.base_url + "/" + from,
439 method: 'PUT',
440 params: { moveto: to },
441 waitMsgTarget: me,
442 failure: function(response, options) {
443 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
444 },
445 callback: function() {
446 me.store.load();
447 }
448 });
449 },
450
451 updateRule: function(rule) {
452 var me = this;
453
454 if (!me.base_url) {
455 return;
456 }
457
458 rule.enable = rule.enable ? 1 : 0;
459
460 var pos = rule.pos;
461 delete rule.pos;
462 delete rule.errors;
463
e7ade592 464 Proxmox.Utils.API2Request({
435cce27
DM
465 url: me.base_url + '/' + pos.toString(),
466 method: 'PUT',
467 params: rule,
468 waitMsgTarget: me,
469 failure: function(response, options) {
470 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
471 },
472 callback: function() {
473 me.store.load();
474 }
475 });
476 },
477
435cce27
DM
478
479 initComponent: function() {
435cce27
DM
480 var me = this;
481
482 if (!me.list_refs_url) {
483 throw "no list_refs_url specified";
484 }
485
f3578e26 486 var store = Ext.create('Ext.data.Store',{
435cce27
DM
487 model: 'pve-fw-rule'
488 });
489
490 var reload = function() {
491 store.load();
492 };
493
494 var sm = Ext.create('Ext.selection.RowModel', {});
495
496 var run_editor = function() {
497 var rec = sm.getSelection()[0];
498 if (!rec) {
499 return;
500 }
501 var type = rec.data.type;
502
503 var editor;
504 if (type === 'in' || type === 'out') {
505 editor = 'PVE.FirewallRuleEdit';
506 } else if (type === 'group') {
507 editor = 'PVE.FirewallGroupRuleEdit';
508 } else {
509 return;
510 }
511
512 var win = Ext.create(editor, {
513 digest: rec.data.digest,
514 allow_iface: me.allow_iface,
515 base_url: me.base_url,
516 list_refs_url: me.list_refs_url,
517 rule_pos: rec.data.pos
518 });
519
520 win.show();
521 win.on('destroy', reload);
522 };
523
5720fafa 524 me.editBtn = Ext.create('Proxmox.button.Button',{
435cce27
DM
525 text: gettext('Edit'),
526 disabled: true,
527 selModel: sm,
528 handler: run_editor
529 });
530
531 me.addBtn = Ext.create('Ext.Button', {
532 text: gettext('Add'),
533 disabled: true,
534 handler: function() {
535 var win = Ext.create('PVE.FirewallRuleEdit', {
536 allow_iface: me.allow_iface,
537 base_url: me.base_url,
538 list_refs_url: me.list_refs_url
539 });
540 win.on('destroy', reload);
541 win.show();
542 }
543 });
544
330020d2
WL
545 var run_copy_editor = function() {
546 var rec = sm.getSelection()[0];
547
548 if (!rec) {
549 return;
550 }
551 var type = rec.data.type;
552
553
554 if (!(type === 'in' || type === 'out')) {
555 return;
556 }
557
558 var win = Ext.create('PVE.FirewallRuleEdit', {
559 allow_iface: me.allow_iface,
560 base_url: me.base_url,
561 list_refs_url: me.list_refs_url,
5eff07f7 562 rec: rec
330020d2
WL
563 });
564
565 win.show();
566 win.on('destroy', reload);
567 };
568
5720fafa 569 me.copyBtn = Ext.create('Proxmox.button.Button',{
330020d2
WL
570 text: gettext('Copy'),
571 selModel: sm,
03375edf
DC
572 enableFn: function(rec) {
573 return (rec.data.type === 'in' || rec.data.type === 'out');
574 },
330020d2 575 disabled: true,
5eff07f7 576 handler: run_copy_editor
330020d2
WL
577 });
578
435cce27
DM
579 if (me.allow_groups) {
580 me.groupBtn = Ext.create('Ext.Button', {
581 text: gettext('Insert') + ': ' +
582 gettext('Security Group'),
583 disabled: true,
584 handler: function() {
585 var win = Ext.create('PVE.FirewallGroupRuleEdit', {
586 allow_iface: me.allow_iface,
587 base_url: me.base_url
588 });
589 win.on('destroy', reload);
590 win.show();
591 }
592 });
593 }
594
3b1ca3ff 595 me.removeBtn = Ext.create('Proxmox.button.StdRemoveButton',{
435cce27 596 selModel: sm,
3b1ca3ff
DC
597 baseurl: me.base_url + '/',
598 confirmMsg: false,
599 getRecordName: function(rec) {
600 var rule = rec.data;
601 return rule.pos.toString() +
602 '?digest=' + encodeURIComponent(rule.digest);
603 },
604 callback: function() {
605 me.store.load();
435cce27
DM
606 }
607 });
608
609 var tbar = me.tbar_prefix ? [ me.tbar_prefix ] : [];
330020d2 610 tbar.push(me.addBtn, me.copyBtn);
435cce27
DM
611 if (me.groupBtn) {
612 tbar.push(me.groupBtn);
613 }
fa94a977 614 tbar.push(me.removeBtn, me.editBtn);
435cce27
DM
615
616 var render_errors = function(name, value, metaData, record) {
617 var errors = record.data.errors;
618 if (errors && errors[name]) {
3ab7e0ec 619 metaData.tdCls = 'proxmox-invalid-row';
435cce27
DM
620 var html = '<p>' + Ext.htmlEncode(errors[name]) + '</p>';
621 metaData.tdAttr = 'data-qwidth=600 data-qtitle="ERROR" data-qtip="' +
622 html.replace(/\"/g,'&quot;') + '"';
623 }
624 return value;
625 };
626
627 var columns = [
628 {
629 // similar to xtype: 'rownumberer',
630 dataIndex: 'pos',
631 resizable: false,
659a3350 632 width: 42,
435cce27
DM
633 sortable: false,
634 align: 'right',
635 hideable: false,
636 menuDisabled: true,
637 renderer: function(value, metaData, record, rowIdx, colIdx, store) {
638 metaData.tdCls = Ext.baseCSSPrefix + 'grid-cell-special';
639 if (value >= 0) {
640 return value;
641 }
642 return '';
643 }
644 },
645 {
646 xtype: 'checkcolumn',
647 header: gettext('Enable'),
648 dataIndex: 'enable',
649 listeners: {
7a4c3133
EK
650 checkchange: function(column, recordIndex, checked) {
651 var record = me.getStore().getData().items[recordIndex];
435cce27
DM
652 record.commit();
653 var data = {};
7a4c3133 654 Ext.Array.forEach(record.getFields(), function(field) {
435cce27
DM
655 data[field.name] = record.get(field.name);
656 });
657 if (!me.allow_iface || !data.iface) {
658 delete data.iface;
659 }
660 me.updateRule(data);
661 }
662 },
663 width: 50
664 },
665 {
666 header: gettext('Type'),
667 dataIndex: 'type',
668 renderer: function(value, metaData, record) {
669 return render_errors('type', value, metaData, record);
670 },
671 width: 50
672 },
673 {
674 header: gettext('Action'),
675 dataIndex: 'action',
676 renderer: function(value, metaData, record) {
677 return render_errors('action', value, metaData, record);
678 },
679 width: 80
680 },
681 {
682 header: gettext('Macro'),
683 dataIndex: 'macro',
684 renderer: function(value, metaData, record) {
685 return render_errors('macro', value, metaData, record);
686 },
687 width: 80
688 }
689 ];
690
691 if (me.allow_iface) {
692 columns.push({
693 header: gettext('Interface'),
694 dataIndex: 'iface',
695 renderer: function(value, metaData, record) {
696 return render_errors('iface', value, metaData, record);
697 },
698 width: 80
699 });
700 }
701
fa94a977 702 columns.push(
435cce27
DM
703 {
704 header: gettext('Source'),
705 dataIndex: 'source',
706 renderer: function(value, metaData, record) {
707 return render_errors('source', value, metaData, record);
708 },
709 width: 100
710 },
711 {
712 header: gettext('Destination'),
713 dataIndex: 'dest',
714 renderer: function(value, metaData, record) {
715 return render_errors('dest', value, metaData, record);
716 },
717 width: 100
718 },
719 {
720 header: gettext('Protocol'),
721 dataIndex: 'proto',
722 renderer: function(value, metaData, record) {
723 return render_errors('proto', value, metaData, record);
724 },
725 width: 100
726 },
727 {
728 header: gettext('Dest. port'),
729 dataIndex: 'dport',
730 renderer: function(value, metaData, record) {
731 return render_errors('dport', value, metaData, record);
732 },
733 width: 100
734 },
735 {
736 header: gettext('Source port'),
737 dataIndex: 'sport',
738 renderer: function(value, metaData, record) {
739 return render_errors('sport', value, metaData, record);
740 },
741 width: 100
742 },
3c37fe48
CE
743 {
744 header: gettext('Log level'),
745 dataIndex: 'log',
746 renderer: function(value, metaData, record) {
747 return render_errors('log', value, metaData, record);
748 },
749 width: 100
750 },
435cce27
DM
751 {
752 header: gettext('Comment'),
753 dataIndex: 'comment',
754 flex: 1,
755 renderer: function(value, metaData, record) {
756 return render_errors('comment', Ext.util.Format.htmlEncode(value), metaData, record);
757 }
758 }
fa94a977 759 );
435cce27
DM
760
761 Ext.apply(me, {
762 store: store,
763 selModel: sm,
764 tbar: tbar,
765 viewConfig: {
766 plugins: [
767 {
768 ptype: 'gridviewdragdrop',
769 dragGroup: 'FWRuleDDGroup',
770 dropGroup: 'FWRuleDDGroup'
771 }
772 ],
773 listeners: {
774 beforedrop: function(node, data, dropRec, dropPosition) {
775 if (!dropRec) {
776 return false; // empty view
777 }
778 var moveto = dropRec.get('pos');
779 if (dropPosition === 'after') {
780 moveto++;
781 }
782 var pos = data.records[0].get('pos');
783 me.moveRule(pos, moveto);
784 return 0;
785 },
786 itemdblclick: run_editor
787 }
788 },
789 sortableColumns: false,
790 columns: columns
791 });
792
793 me.callParent();
794
795 if (me.base_url) {
796 me.setBaseUrl(me.base_url); // load
797 }
798 }
799}, function() {
800
801 Ext.define('pve-fw-rule', {
802 extend: 'Ext.data.Model',
803 fields: [ { name: 'enable', type: 'boolean' },
804 'type', 'action', 'macro', 'source', 'dest', 'proto', 'iface',
805 'dport', 'sport', 'comment', 'pos', 'digest', 'errors' ],
806 idProperty: 'pos'
807 });
808
809});