FirewallRuleForm validations

This commit is contained in:
Eduardo Silva 2024-03-01 00:25:45 -03:00
parent 2012c22973
commit 015d9b0927
4 changed files with 101 additions and 75 deletions

View File

@ -53,6 +53,10 @@ class RedirectRuleForm(forms.ModelForm):
class FirewallRuleForm(forms.ModelForm): class FirewallRuleForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
current_chain = kwargs.pop('current_chain', None)
super(FirewallRuleForm, self).__init__(*args, **kwargs)
firewall_settings, firewall_settings_created = FirewallSettings.objects.get_or_create(name='global') firewall_settings, firewall_settings_created = FirewallSettings.objects.get_or_create(name='global')
interface_list = [('', '------'),] interface_list = [('', '------'),]
interface_list.append((firewall_settings.wan_interface, firewall_settings.wan_interface + ' (WAN)')) interface_list.append((firewall_settings.wan_interface, firewall_settings.wan_interface + ' (WAN)'))
@ -64,9 +68,9 @@ class FirewallRuleForm(forms.ModelForm):
interface_list.append(('wg+', 'wg+ (Any WireGuard Interface)')) interface_list.append(('wg+', 'wg+ (Any WireGuard Interface)'))
description = forms.CharField(label='Description', required=False) description = forms.CharField(label='Description', required=False)
firewall_chain = forms.ChoiceField(label='Firewall Chain', choices=[('forward', 'FORWARD'), ('postrouting', 'POSTROUTING (nat)')], initial='forward') firewall_chain = forms.ChoiceField(label='Firewall Chain', choices=[('forward', 'FORWARD'), ('postrouting', 'POSTROUTING (nat)')])
in_interface = forms.ChoiceField(label='In Interface', choices=interface_list, required=False) in_interface = forms.ChoiceField(label='In Interface', required=False)
out_interface = forms.ChoiceField(label='Out Interface', choices=interface_list, required=False) out_interface = forms.ChoiceField(label='Out Interface', required=False)
source_ip = forms.GenericIPAddressField(label='Source IP', required=False) source_ip = forms.GenericIPAddressField(label='Source IP', required=False)
source_netmask = forms.IntegerField(label='Source Netmask', initial=32, min_value=0, max_value=32) source_netmask = forms.IntegerField(label='Source Netmask', initial=32, min_value=0, max_value=32)
source_peer = forms.ModelMultipleChoiceField(label='Source Peer', queryset=Peer.objects.all(), required=False) source_peer = forms.ModelMultipleChoiceField(label='Source Peer', queryset=Peer.objects.all(), required=False)
@ -87,10 +91,30 @@ class FirewallRuleForm(forms.ModelForm):
not_state = forms.BooleanField(label='Not State', required=False) not_state = forms.BooleanField(label='Not State', required=False)
rule_action = forms.ChoiceField(label='Rule Action', initial='accept', choices=[('accept', 'ACCEPT'), ('reject', 'REJECT'), ('drop', 'DROP'), ('masquerade', 'MASQUERADE')]) rule_action = forms.ChoiceField(label='Rule Action', initial='accept', choices=[('accept', 'ACCEPT'), ('reject', 'REJECT'), ('drop', 'DROP'), ('masquerade', 'MASQUERADE')])
sort_order = forms.IntegerField(label='Sort Order', initial=0, min_value=0) sort_order = forms.IntegerField(label='Sort Order', initial=0, min_value=0)
self.fields['firewall_chain'].initial = current_chain
self.fields['in_interface'].choices = interface_list
self.fields['out_interface'].choices = interface_list
class Meta: class Meta:
model = FirewallRule model = FirewallRule
fields = ['description', 'firewall_chain', 'in_interface', 'out_interface', 'source_ip', 'source_netmask', 'source_peer', 'source_peer_include_networks', 'not_source', 'destination_ip', 'destination_netmask', 'destination_peer', 'destination_peer_include_networks', 'not_destination', 'protocol', 'destination_port', 'state_new', 'state_related', 'state_established', 'state_invalid', 'state_untracked', 'not_state', 'rule_action', 'sort_order'] fields = ['description', 'firewall_chain', 'in_interface', 'out_interface', 'source_ip', 'source_netmask', 'source_peer', 'source_peer_include_networks', 'not_source', 'destination_ip', 'destination_netmask', 'destination_peer', 'destination_peer_include_networks', 'not_destination', 'protocol', 'destination_port', 'state_new', 'state_related', 'state_established', 'state_invalid', 'state_untracked', 'not_state', 'rule_action', 'sort_order']
def clean(self):
cleaned_data = super().clean()
firewall_chain = cleaned_data.get('firewall_chain')
rule_action = cleaned_data.get('rule_action')
in_interface = cleaned_data.get('in_interface')
if firewall_chain == 'forward' and rule_action not in ['accept', 'drop', 'reject']:
raise forms.ValidationError("Invalid rule action for firewall chain 'forward'. Allowed actions are 'accept', 'drop', and 'reject'.")
if firewall_chain == 'postrouting':
if rule_action not in ['masquerade', 'accept']:
raise forms.ValidationError("Invalid rule action for firewall chain 'postrouting'. Allowed actions are 'masquerade' and 'accept'.")
if in_interface:
raise forms.ValidationError("In Interface cannot be used with firewall chain 'postrouting'.")
return cleaned_data

View File

@ -1,4 +1,5 @@
from django.shortcuts import render, get_object_or_404, redirect from django.shortcuts import render, get_object_or_404, redirect
from django.db.models import Max
from firewall.models import RedirectRule, FirewallRule, FirewallSettings from firewall.models import RedirectRule, FirewallRule, FirewallSettings
from firewall.forms import RedirectRuleForm, FirewallRuleForm from firewall.forms import RedirectRuleForm, FirewallRuleForm
from django.contrib import messages from django.contrib import messages
@ -82,6 +83,7 @@ def manage_firewall_rule(request):
uuid = request.GET.get('uuid', None) uuid = request.GET.get('uuid', None)
if uuid: if uuid:
instance = get_object_or_404(FirewallRule, uuid=uuid) instance = get_object_or_404(FirewallRule, uuid=uuid)
current_chain = instance.firewall_chain
if request.GET.get('action') == 'delete': if request.GET.get('action') == 'delete':
if request.GET.get('confirmation') == 'delete': if request.GET.get('confirmation') == 'delete':
firewall_settings, firewall_settings_created = FirewallSettings.objects.get_or_create(name='global') firewall_settings, firewall_settings_created = FirewallSettings.objects.get_or_create(name='global')
@ -91,19 +93,32 @@ def manage_firewall_rule(request):
else: else:
messages.warning(request, 'Error deleting Firewall rule|Confirmation did not match. Firewall rule was not deleted.') messages.warning(request, 'Error deleting Firewall rule|Confirmation did not match. Firewall rule was not deleted.')
return redirect('/firewall/rule_list/') return redirect('/firewall/rule_list/')
else:
current_chain = request.GET.get('chain', 'forward')
if request.method == 'POST': if request.method == 'POST':
form = FirewallRuleForm(request.POST, instance=instance) form = FirewallRuleForm(request.POST, instance=instance, current_chain=current_chain)
if form.is_valid(): if form.is_valid():
firewall_settings, firewall_settings_created = FirewallSettings.objects.get_or_create(name='global') firewall_settings, firewall_settings_created = FirewallSettings.objects.get_or_create(name='global')
firewall_settings.pending_changes = True firewall_settings.pending_changes = True
firewall_settings.save() firewall_settings.save()
form.save() form.save()
messages.success(request, 'Firewall rule saved successfully') messages.success(request, 'Firewall rule saved successfully')
return redirect('/firewall/rule_list/') return redirect('/firewall/rule_list/?chain=' + current_chain)
else: else:
form = FirewallRuleForm(instance=instance) form = FirewallRuleForm(instance=instance, current_chain=current_chain)
context['form'] = form context['form'] = form
context['instance'] = instance context['instance'] = instance
highest_forward_sort_order = FirewallRule.objects.filter(firewall_chain='forward').aggregate(Max('sort_order'))['sort_order__max']
if highest_forward_sort_order is None:
highest_forward_sort_order = 0
highest_postrouting_sort_order = FirewallRule.objects.filter(firewall_chain='postrouting').aggregate(Max('sort_order'))['sort_order__max']
if highest_postrouting_sort_order is None:
highest_postrouting_sort_order = 0
context['forward_sort_order'] = highest_forward_sort_order + 10
context['postrouting_sort_order'] = highest_postrouting_sort_order + 10
return render(request, 'firewall/manage_firewall_rule.html', context=context) return render(request, 'firewall/manage_firewall_rule.html', context=context)

View File

@ -87,7 +87,7 @@
</tbody> </tbody>
</table> </table>
<a href="/firewall/manage_firewall_rule/" class='btn btn-primary'>Create Firewall Rule</a> <a href="/firewall/manage_firewall_rule/?chain={{ current_chain }}" class='btn btn-primary'>Create Firewall Rule</a>
</div> </div>
</div> </div>
</div> </div>
@ -102,42 +102,6 @@
{% endblock %} {% endblock %}
{% block custom_page_scripts %} {% block custom_page_scripts %}
{% comment %}
<script>
document.addEventListener("DOMContentLoaded", function() {
document.querySelectorAll('td').forEach(function(td) {
// Conta o número de <br> na célula
let brCount = (td.innerHTML.match(/<br>/g) || []).length;
// Aplica a lógica de mostrar/esconder apenas se houver 2 ou mais <br>
if (brCount >= 2) {
let contentParts = td.innerHTML.split('<br>');
// Assume que queremos manter a primeira linha visível, adiciona explicitamente uma quebra de linha antes do conteúdo escondido
td.innerHTML = contentParts[0] + '<br>' +
'<span style="display: none;">' +
contentParts.slice(1).join('<br>') + '</span>' +
'<button class="more-btn">Mais</button>';
}
});
// Adiciona evento de clique para botões "Mais"
document.querySelectorAll('.more-btn').forEach(function(button) {
button.addEventListener('click', function() {
let moreText = this.previousElementSibling; // O span com o texto extra
if (moreText.style.display === "none") {
moreText.style.display = "inline";
this.textContent = "Menos";
} else {
moreText.style.display = "none";
this.textContent = "Mais";
}
});
});
});
</script>
{% endcomment %}
<script> <script>
document.addEventListener("DOMContentLoaded", function() { document.addEventListener("DOMContentLoaded", function() {
document.querySelectorAll('td').forEach(function(td) { document.querySelectorAll('td').forEach(function(td) {
@ -168,8 +132,6 @@
}); });
}); });
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@ -431,4 +431,29 @@
</script> </script>
<script>
document.addEventListener("DOMContentLoaded", function() {
var forward_sort_order = {{ forward_sort_order }};
var postrouting_sort_order = {{ postrouting_sort_order }};
function updateSortOrder() {
var chainSelected = document.getElementById('firewall_chain').value;
var sortOrderField = document.getElementById('sort_order');
if (chainSelected === 'forward') {
sortOrderField.value = forward_sort_order;
} else if (chainSelected === 'postrouting') {
sortOrderField.value = postrouting_sort_order;
}
}
document.getElementById('firewall_chain').addEventListener('change', updateSortOrder);
{% if not instance %}
updateSortOrder();
{% endif %}
});
</script>
{% endblock %} {% endblock %}