mirror of
https://github.com/eduardogsilva/wireguard_webadmin.git
synced 2026-03-15 13:36:18 +00:00
add policy type selection for access policies
This commit is contained in:
@@ -104,29 +104,72 @@ class AccessPolicyForm(forms.ModelForm):
|
|||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
cancel_url = kwargs.pop('cancel_url', '#')
|
cancel_url = kwargs.pop('cancel_url', '#')
|
||||||
|
policy_type = kwargs.pop('policy_type', None)
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
if self.instance and self.instance.pk:
|
||||||
|
policy_type = self.instance.policy_type
|
||||||
|
|
||||||
|
if policy_type and not self.initial.get('policy_type'):
|
||||||
|
self.initial['policy_type'] = policy_type
|
||||||
|
|
||||||
|
self.fields['policy_type'].widget = forms.HiddenInput()
|
||||||
|
|
||||||
self.helper = FormHelper()
|
self.helper = FormHelper()
|
||||||
self.helper.layout = Layout(
|
|
||||||
Div(
|
if policy_type in ['public', 'deny']:
|
||||||
Div('name', css_class='col-md-6'),
|
self.helper.layout = Layout(
|
||||||
Div('policy_type', css_class='col-md-6'),
|
|
||||||
css_class='row'
|
|
||||||
),
|
|
||||||
Div(
|
|
||||||
Div('groups', css_class='col-md-6'),
|
|
||||||
Div('methods', css_class='col-md-6'),
|
|
||||||
css_class='row'
|
|
||||||
),
|
|
||||||
Div(
|
|
||||||
Div(
|
Div(
|
||||||
Submit('submit', _('Save'), css_class='btn btn-primary'),
|
Div('name', css_class='col-md-12'),
|
||||||
HTML(f'<a href="{cancel_url}" class="btn btn-secondary">{_("Cancel")}</a>'),
|
'policy_type',
|
||||||
css_class='col-md-12'
|
css_class='row'
|
||||||
),
|
),
|
||||||
css_class='row'
|
Div(
|
||||||
|
Div(
|
||||||
|
Submit('submit', _('Save'), css_class='btn btn-primary'),
|
||||||
|
HTML(f'<a href="{cancel_url}" class="btn btn-secondary">{_("Cancel")}</a>'),
|
||||||
|
css_class='col-md-12'
|
||||||
|
),
|
||||||
|
css_class='row'
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
else:
|
||||||
|
self.helper.layout = Layout(
|
||||||
|
Div(
|
||||||
|
Div('name', css_class='col-md-12'),
|
||||||
|
'policy_type',
|
||||||
|
css_class='row'
|
||||||
|
),
|
||||||
|
Div(Div('methods', css_class='col-md-12'), css_class='row'),
|
||||||
|
Div(Div('groups', css_class='col-md-12'), css_class='row'),
|
||||||
|
Div(
|
||||||
|
Div(
|
||||||
|
Submit('submit', _('Save'), css_class='btn btn-primary'),
|
||||||
|
HTML(f'<a href="{cancel_url}" class="btn btn-secondary">{_("Cancel")}</a>'),
|
||||||
|
css_class='col-md-12'
|
||||||
|
),
|
||||||
|
css_class='row'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
cleaned_data = super().clean()
|
||||||
|
policy_type = cleaned_data.get('policy_type')
|
||||||
|
groups = cleaned_data.get('groups')
|
||||||
|
methods = cleaned_data.get('methods')
|
||||||
|
|
||||||
|
if policy_type == 'protected':
|
||||||
|
if groups and len(groups) > 0:
|
||||||
|
has_local_password = False
|
||||||
|
if methods:
|
||||||
|
for method in methods:
|
||||||
|
if method.auth_type == 'local_password':
|
||||||
|
has_local_password = True
|
||||||
|
break
|
||||||
|
if not has_local_password:
|
||||||
|
self.add_error(None, _("User groups can only be used with local user authentication."))
|
||||||
|
|
||||||
|
return cleaned_data
|
||||||
|
|
||||||
|
|
||||||
class ApplicationPolicyForm(forms.ModelForm):
|
class ApplicationPolicyForm(forms.ModelForm):
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ urlpatterns = [
|
|||||||
path('host/delete/', views.view_delete_application_host, name='delete_application_host'),
|
path('host/delete/', views.view_delete_application_host, name='delete_application_host'),
|
||||||
|
|
||||||
# Access Policies
|
# Access Policies
|
||||||
|
path('policy/select-type/', views.view_select_policy_type, name='select_policy_type'),
|
||||||
path('policy/manage/', views.view_manage_access_policy, name='manage_access_policy'),
|
path('policy/manage/', views.view_manage_access_policy, name='manage_access_policy'),
|
||||||
path('policy/delete/', views.view_delete_access_policy, name='delete_access_policy'),
|
path('policy/delete/', views.view_delete_access_policy, name='delete_access_policy'),
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.db.models import ProtectedError
|
||||||
from django.shortcuts import render, get_object_or_404, redirect
|
from django.shortcuts import render, get_object_or_404, redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
@@ -78,10 +79,24 @@ def view_manage_application(request):
|
|||||||
messages.success(request, _('Application saved successfully.'))
|
messages.success(request, _('Application saved successfully.'))
|
||||||
return redirect(cancel_url)
|
return redirect(cancel_url)
|
||||||
|
|
||||||
|
form_description = {
|
||||||
|
'size': 'col-lg-6',
|
||||||
|
'content': _('''
|
||||||
|
<h5>Application</h5>
|
||||||
|
<p>Define the main details of the application you want to expose through the gateway.</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Name</strong>: A unique internal identifier for this application (e.g., "wiki", "crm"). Contains only letters, numbers, hyphens, or underscores.</li>
|
||||||
|
<li><strong>Display Name</strong>: A friendly, human-readable name for display purposes.</li>
|
||||||
|
<li><strong>Upstream</strong>: The destination URL where requests will be forwarded (e.g., <code>http://10.188.18.27:3000</code>). Must start with <code>http://</code> or <code>https://</code>.</li>
|
||||||
|
</ul>
|
||||||
|
''')
|
||||||
|
}
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'form': form,
|
'form': form,
|
||||||
'title': title,
|
'title': title,
|
||||||
'page_title': title,
|
'page_title': title,
|
||||||
|
'form_description': form_description,
|
||||||
}
|
}
|
||||||
return render(request, 'generic_form.html', context)
|
return render(request, 'generic_form.html', context)
|
||||||
|
|
||||||
@@ -168,34 +183,77 @@ def view_delete_application_host(request):
|
|||||||
return render(request, 'generic_delete_confirmation.html', context)
|
return render(request, 'generic_delete_confirmation.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def view_select_policy_type(request):
|
||||||
|
if not UserAcl.objects.filter(user=request.user).filter(user_level__gte=50).exists():
|
||||||
|
return render(request, 'access_denied.html', {'page_title': _('Access Denied')})
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'page_title': _('Select Access Policy Type'),
|
||||||
|
}
|
||||||
|
return render(request, 'app_gateway/access_policy_type_select.html', context)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def view_manage_access_policy(request):
|
def view_manage_access_policy(request):
|
||||||
if not UserAcl.objects.filter(user=request.user).filter(user_level__gte=50).exists():
|
if not UserAcl.objects.filter(user=request.user).filter(user_level__gte=50).exists():
|
||||||
return render(request, 'access_denied.html', {'page_title': _('Access Denied')})
|
return render(request, 'access_denied.html', {'page_title': _('Access Denied')})
|
||||||
|
|
||||||
access_policy_uuid = request.GET.get('uuid')
|
access_policy_uuid = request.GET.get('uuid')
|
||||||
|
policy_type = request.GET.get('policy_type')
|
||||||
|
|
||||||
if access_policy_uuid:
|
if access_policy_uuid:
|
||||||
access_policy = get_object_or_404(AccessPolicy, uuid=access_policy_uuid)
|
access_policy = get_object_or_404(AccessPolicy, uuid=access_policy_uuid)
|
||||||
title = _('Edit Access Policy')
|
title = _('Edit Access Policy')
|
||||||
|
policy_type = access_policy.policy_type
|
||||||
else:
|
else:
|
||||||
access_policy = None
|
access_policy = None
|
||||||
title = _('Create Access Policy')
|
title = _('Create Access Policy')
|
||||||
|
|
||||||
cancel_url = reverse('app_gateway_list') + '?tab=policies'
|
cancel_url = reverse('app_gateway_list') + '?tab=policies'
|
||||||
|
|
||||||
form = AccessPolicyForm(request.POST or None, instance=access_policy, cancel_url=cancel_url)
|
form = AccessPolicyForm(request.POST or None, instance=access_policy, cancel_url=cancel_url, policy_type=policy_type)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
form.save()
|
form.save()
|
||||||
messages.success(request, _('Access Policy saved successfully.'))
|
messages.success(request, _('Access Policy saved successfully.'))
|
||||||
return redirect(cancel_url)
|
return redirect(cancel_url)
|
||||||
|
|
||||||
|
if policy_type == 'public':
|
||||||
|
form_description = {
|
||||||
|
'size': 'col-lg-6',
|
||||||
|
'content': _('''
|
||||||
|
<h5>Public Policy</h5>
|
||||||
|
<p>A Public policy allows access to the application without requiring any authentication.</p>
|
||||||
|
''')
|
||||||
|
}
|
||||||
|
elif policy_type == 'deny':
|
||||||
|
form_description = {
|
||||||
|
'size': 'col-lg-6',
|
||||||
|
'content': _('''
|
||||||
|
<h5>Deny Policy</h5>
|
||||||
|
<p>A Deny policy blocks all access to the matched routes.</p>
|
||||||
|
''')
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
form_description = {
|
||||||
|
'size': 'col-lg-6',
|
||||||
|
'content': _('''
|
||||||
|
<h5>Protected Policy</h5>
|
||||||
|
<p>A Protected policy requires users to authenticate before accessing the application.</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Allowed Groups</strong>: Limits access to specific user groups. Note: Using groups requires selecting an Authentication Method of type "Local Password".</li>
|
||||||
|
<li><strong>Authentication Methods</strong>: Specify which methods users can use to authenticate (e.g., Local Password, TOTP, OIDC).</li>
|
||||||
|
</ul>
|
||||||
|
''')
|
||||||
|
}
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'form': form,
|
'form': form,
|
||||||
'title': title,
|
'title': title,
|
||||||
'page_title': title,
|
'page_title': title,
|
||||||
|
'form_description': form_description,
|
||||||
}
|
}
|
||||||
return render(request, 'app_gateway/app_gateway_policy_form.html', context)
|
return render(request, 'generic_form.html', context)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@@ -208,8 +266,11 @@ def view_delete_access_policy(request):
|
|||||||
cancel_url = reverse('app_gateway_list') + '?tab=policies'
|
cancel_url = reverse('app_gateway_list') + '?tab=policies'
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
access_policy.delete()
|
try:
|
||||||
messages.success(request, _('Access Policy deleted successfully.'))
|
access_policy.delete()
|
||||||
|
messages.success(request, _('Access Policy deleted successfully.'))
|
||||||
|
except ProtectedError:
|
||||||
|
messages.error(request, _('Cannot delete this Access Policy because it is currently in use by an Application Route or Application Default Policy.'))
|
||||||
return redirect(cancel_url)
|
return redirect(cancel_url)
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
|
|||||||
55
templates/app_gateway/access_policy_type_select.html
Normal file
55
templates/app_gateway/access_policy_type_select.html
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card card-primary card-outline">
|
||||||
|
<div class="card-header">
|
||||||
|
<h3 class="card-title">{% trans 'Select Access Policy Type' %}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="card-body text-center">
|
||||||
|
<p class="mb-4">{% trans 'Choose the type of access policy you want to create.' %}</p>
|
||||||
|
<div class="row">
|
||||||
|
<!-- Public -->
|
||||||
|
<div class="col-md-4 mb-3">
|
||||||
|
<div class="card h-100 bg-light">
|
||||||
|
<div class="card-body d-flex flex-column align-items-center justify-content-center">
|
||||||
|
<i class="fas fa-globe fa-3x mb-3 text-success"></i>
|
||||||
|
<h5>{% trans 'Public' %}</h5>
|
||||||
|
<p class="text-muted small">{% trans 'Allow access to everyone without authentication.' %}</p>
|
||||||
|
<a href="{% url 'manage_access_policy' %}?policy_type=public" class="btn btn-success mt-auto">{% trans 'Select' %}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Protected -->
|
||||||
|
<div class="col-md-4 mb-3">
|
||||||
|
<div class="card h-100 bg-light">
|
||||||
|
<div class="card-body d-flex flex-column align-items-center justify-content-center">
|
||||||
|
<i class="fas fa-shield-alt fa-3x mb-3 text-primary"></i>
|
||||||
|
<h5>{% trans 'Protected' %}</h5>
|
||||||
|
<p class="text-muted small">{% trans 'Require authentication using specified methods or groups.' %}</p>
|
||||||
|
<a href="{% url 'manage_access_policy' %}?policy_type=protected" class="btn btn-primary mt-auto">{% trans 'Select' %}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Deny -->
|
||||||
|
<div class="col-md-4 mb-3">
|
||||||
|
<div class="card h-100 bg-light">
|
||||||
|
<div class="card-body d-flex flex-column align-items-center justify-content-center">
|
||||||
|
<i class="fas fa-ban fa-3x mb-3 text-danger"></i>
|
||||||
|
<h5>{% trans 'Deny' %}</h5>
|
||||||
|
<p class="text-muted small">{% trans 'Deny all access.' %}</p>
|
||||||
|
<a href="{% url 'manage_access_policy' %}?policy_type=deny" class="btn btn-danger mt-auto">{% trans 'Select' %}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer">
|
||||||
|
<a href="{% url 'app_gateway_list' %}?tab=policies" class="btn btn-secondary">{% trans 'Cancel' %}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -119,7 +119,7 @@
|
|||||||
|
|
||||||
{% elif active_tab == 'policies' %}
|
{% elif active_tab == 'policies' %}
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<a href="{% url 'manage_access_policy' %}" class="btn btn-outline-primary btn-sm">
|
<a href="{% url 'select_policy_type' %}" class="btn btn-outline-primary btn-sm">
|
||||||
<i class="fas fa-plus"></i> {% trans 'Add Access Policy' %}
|
<i class="fas fa-plus"></i> {% trans 'Add Access Policy' %}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
{% extends 'base.html' %}
|
|
||||||
{% load crispy_forms_tags %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class='row'>
|
|
||||||
<div class='{% if form_size %}{{ form_size }}{% else %}col-lg-6{% endif %}'>
|
|
||||||
<div class="card card-primary card-outline">
|
|
||||||
{% if page_title %}
|
|
||||||
<div class="card-header">
|
|
||||||
<h3 class="card-title">{{ page_title }}</h3>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
<div class="card-body row">
|
|
||||||
<div class="col-lg-12">
|
|
||||||
{% csrf_token %}
|
|
||||||
{% crispy form %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if form_description %}
|
|
||||||
<div class='{% if form_description.size %}{{ form_description.size }}{% else %}col-lg-6{% endif %}'>
|
|
||||||
<div class="card card-primary card-outline">
|
|
||||||
|
|
||||||
<div class="card-body row">
|
|
||||||
<div class="col-lg-12">
|
|
||||||
{{ form_description.content|safe }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block custom_page_scripts %}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
Reference in New Issue
Block a user