diff --git a/integration_manager/__init__.py b/integration_manager/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/integration_manager/admin.py b/integration_manager/admin.py new file mode 100644 index 0000000..2c7f687 --- /dev/null +++ b/integration_manager/admin.py @@ -0,0 +1,9 @@ +from django.contrib import admin +from .models import ExternalIntegration + +class ExternalIntegrationAdmin(admin.ModelAdmin): + list_display = ('id', 'name', 'integration_type', 'integration_url', 'wireguard_webadmin_default_user_level', 'token', 'created_at', 'updated_at') + search_fields = ('name', 'integration_type', 'integration_url', 'wireguard_webadmin_default_user_level', 'token', 'created_at', 'updated_at') + readonly_fields = ('created_at', 'updated_at') + +admin.site.register(ExternalIntegration, ExternalIntegrationAdmin) \ No newline at end of file diff --git a/integration_manager/apps.py b/integration_manager/apps.py new file mode 100644 index 0000000..3eced48 --- /dev/null +++ b/integration_manager/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class IntegrationManagerConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'integration_manager' diff --git a/integration_manager/forms.py b/integration_manager/forms.py new file mode 100644 index 0000000..ed693bf --- /dev/null +++ b/integration_manager/forms.py @@ -0,0 +1,88 @@ +from django import forms +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Layout, Submit, Row, Column, HTML +from .models import ExternalIntegration +import requests + + +class WireGuardWebAdminForm(forms.ModelForm): + token = forms.CharField(widget=forms.PasswordInput, required=False) + + class Meta: + model = ExternalIntegration + fields = ['integration_url', 'wireguard_webadmin_default_user_level', 'token'] + + def __init__(self, *args, **kwargs): + self.user = kwargs.pop('user', None) + super(WireGuardWebAdminForm, self).__init__(*args, **kwargs) + self.helper = FormHelper() + self.helper.form_method = 'post' + if self.instance.pk: + delete_html = "Delete" + if self.instance.token: + self.fields['token'].widget.attrs['placeholder'] = '************' + else: + delete_html = '' + self.helper.layout = Layout( + Row( + Column('integration_url', css_class='col-md-12'), + ), + Row( + Column('wireguard_webadmin_default_user_level', css_class='col-md-12'), + ), + Row( + Column('token', css_class='col-md-12'), + ), + Row( + Column( + Submit('submit', 'Salvar', css_class='btn btn-success'), + HTML(' Back '), + HTML(delete_html), + css_class='col-md-12'), + css_class='form-row' + ) + ) + + def clean(self): + cleaned_data = super().clean() + integration_url = cleaned_data.get('integration_url') + wireguard_webadmin_default_user_level = cleaned_data.get('wireguard_webadmin_default_user_level') + token = cleaned_data.get('token') + if integration_url.endswith('/'): + cleaned_data['integration_url'] = integration_url[:-1] + if not token and self.instance.token: + cleaned_data['token'] = self.instance.token + + if not integration_url.startswith('https://'): + raise forms.ValidationError('Please use https://') + + api_test_url = f"{cleaned_data['integration_url']}/api/routerfleet_get_user_token/" + api_test_url += f"?key={cleaned_data['token']}" + api_test_url += f"&username={self.user.username}&action=test" + + try: + api_test = requests.get(api_test_url) + except: + raise forms.ValidationError('Error connecting to API') + + try: + if api_test.status_code == 403: + api_response = {} + else: + api_response = api_test.json() + except: + raise forms.ValidationError('Error parsing API response') + + if api_test.status_code == 403: + raise forms.ValidationError('Invalid token') + elif api_test.status_code == 400: + if api_response.get('message'): + raise forms.ValidationError(api_response.get('message')) + else: + raise forms.ValidationError(f'Error authenticating with API. Status Code: {api_test.status_code}') + elif api_test.status_code != 200: + raise forms.ValidationError(f'Error connecting to API. Status Code: {api_test.status_code}') + + return cleaned_data + + diff --git a/integration_manager/migrations/0001_initial.py b/integration_manager/migrations/0001_initial.py new file mode 100644 index 0000000..fe9ffa2 --- /dev/null +++ b/integration_manager/migrations/0001_initial.py @@ -0,0 +1,27 @@ +# Generated by Django 5.0.3 on 2024-04-03 19:21 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='ExternalIntegration', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100, unique=True)), + ('integration_type', models.CharField(choices=[('wireguard_webadmin', 'WireGuard WebAdmin')], max_length=100)), + ('integration_url', models.URLField()), + ('wireguard_webadmin_default_user_level', models.PositiveIntegerField(choices=[(10, 'Debugging Analyst'), (20, 'View Only User'), (30, 'Peer Manager'), (40, 'Manager'), (50, 'Administrator')], default=0)), + ('token', models.CharField(max_length=100)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ], + ), + ] diff --git a/integration_manager/migrations/__init__.py b/integration_manager/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/integration_manager/models.py b/integration_manager/models.py new file mode 100644 index 0000000..2144058 --- /dev/null +++ b/integration_manager/models.py @@ -0,0 +1,14 @@ +from django.db import models + + +class ExternalIntegration(models.Model): + name = models.CharField(max_length=100, unique=True) + integration_type = models.CharField(max_length=100, choices=(('wireguard_webadmin', 'WireGuard WebAdmin'), )) + integration_url = models.URLField() + wireguard_webadmin_default_user_level = models.PositiveIntegerField(default=0, choices=((0, 'Do not create users'), (10, 'Debugging Analyst'), (20, 'View Only User'), (30, 'Peer Manager'), (40, 'Manager'), (50, 'Administrator'))) + token = models.CharField(max_length=100) + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + + def __str__(self): + return self.name diff --git a/integration_manager/tests.py b/integration_manager/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/integration_manager/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/integration_manager/views.py b/integration_manager/views.py new file mode 100644 index 0000000..acfaafc --- /dev/null +++ b/integration_manager/views.py @@ -0,0 +1,76 @@ +from django.contrib.auth.decorators import login_required +from django.shortcuts import render, redirect, get_object_or_404 +from .models import ExternalIntegration +from .forms import WireGuardWebAdminForm +from django.contrib import messages +import requests + + +@login_required() +def view_wireguard_webadmin_launcher(request): + context = { + 'page_title': 'WireGuard WebAdmin Launcher', + 'wireguard_integration': ExternalIntegration.objects.filter(name='wireguard_webadmin', integration_type='wireguard_webadmin').first() + } + return render(request, 'integration_manager/wireguard_webadmin_launcher.html', context=context) + + +@login_required() +def view_launch_wireguard_webadmin(request): + wireguard_integration = get_object_or_404(ExternalIntegration, name='wireguard_webadmin', integration_type='wireguard_webadmin') + api_url = f"{wireguard_integration.integration_url}/api/routerfleet_get_user_token/" + api_url += f"?key={wireguard_integration.token}" + api_url += f"&username={request.user.username}&action=login" + api_url += f"&default_user_level={wireguard_integration.wireguard_webadmin_default_user_level}" + try: + api_response = requests.get(api_url) + except: + messages.warning(request, 'Error connecting to API') + return redirect('/wireguard_webadmin/') + + try: + if api_response.status_code == 200: + api_json = api_response.json() + else: + api_json = {} + except: + messages.warning(request, 'Error parsing API response') + return redirect('/wireguard_webadmin/') + + if api_response.status_code == 200: + redirect_url = f"{wireguard_integration.integration_url}/accounts/routerfleet_authenticate_session/" + redirect_url += f"?token={api_json.get('authentication_token')}" + return redirect(redirect_url) + else: + messages.warning(request, f'Error authenticating with API. Status Code: {api_response.status_code}') + return redirect('/wireguard_webadmin/') + + +@login_required() +def view_manage_wireguard_integration(request): + context = { + 'page_title': 'Manage WireGuard Integration', + 'delete_confirmation_message': 'Are you sure you want to delete this integration? This action cannot be undone. Type delete in the box below to confirm.' + } + wireguard_integration = ExternalIntegration.objects.filter(name='wireguard_webadmin', integration_type='wireguard_webadmin').first() + if request.GET.get('action') == 'delete': + if request.GET.get('confirmation') == 'delete': + wireguard_integration.delete() + messages.success(request, 'WireGuard WebAdmin integration deleted') + return redirect('/wireguard_webadmin/') + else: + messages.warning(request, 'Invalid confirmation. Integration not deleted') + return redirect('/wireguard_webadmin/') + + form = WireGuardWebAdminForm(request.POST or None, instance=wireguard_integration, user=request.user) + + if form.is_valid(): + this_form = form.save(commit=False) + this_form.name = 'wireguard_webadmin' + this_form.integration_type = 'wireguard_webadmin' + this_form.save() + messages.success(request, 'WireGuard WebAdmin integration saved') + return redirect('/wireguard_webadmin/') + + context['form'] = form + return render(request, 'generic_form.html', context=context) diff --git a/routerfleet/settings.py b/routerfleet/settings.py index af27b22..533647c 100644 --- a/routerfleet/settings.py +++ b/routerfleet/settings.py @@ -44,7 +44,8 @@ INSTALLED_APPS = [ 'router_manager', 'monitoring', 'backup', - 'backup_data' + 'backup_data', + 'integration_manager', ] MIDDLEWARE = [ diff --git a/routerfleet/urls.py b/routerfleet/urls.py index e179819..8b9658f 100644 --- a/routerfleet/urls.py +++ b/routerfleet/urls.py @@ -1,6 +1,7 @@ from django.contrib import admin from django.urls import path from dashboard.views import view_dashboard, view_status +from integration_manager.views import view_wireguard_webadmin_launcher, view_manage_wireguard_integration, view_launch_wireguard_webadmin from user_manager.views import view_manage_user, view_user_list from accounts.views import view_login, view_logout, view_create_first_user from router_manager.views import view_router_list, view_manage_router, view_router_group_list, view_ssh_key_list, view_manage_router_group, view_manage_sshkey, view_router_details, view_create_instant_backup_task @@ -40,4 +41,7 @@ urlpatterns = [ path('cron/create_backup_tasks/', view_create_backup_tasks, name='create_backup_tasks'), path('cron/perform_backup_tasks/', view_perform_backup_tasks, name='perform_backup_tasks'), path('cron/housekeeping/', view_housekeeping, name='housekeeping'), + path('wireguard_webadmin/', view_wireguard_webadmin_launcher, name='wireguard_webadmin_launcher'), + path('wireguard_webadmin/manage/', view_manage_wireguard_integration, name='manage_wireguard_integration'), + path('wireguard_webadmin/launch/', view_launch_wireguard_webadmin, name='launch_wireguard_webadmin') ] diff --git a/templates/base.html b/templates/base.html index c2fa579..6d279f0 100644 --- a/templates/base.html +++ b/templates/base.html @@ -131,6 +131,15 @@ + + diff --git a/templates/integration_manager/wireguard_webadmin_launcher.html b/templates/integration_manager/wireguard_webadmin_launcher.html new file mode 100644 index 0000000..ac95207 --- /dev/null +++ b/templates/integration_manager/wireguard_webadmin_launcher.html @@ -0,0 +1,36 @@ +{% extends 'base.html' %} + +{% block content %} +
+
+
+ +
+

wireguard_webadmin

+
+ +
+
+
+

+ wireguard_webadmin is a full-featured yet easy-to-configure web interface for managing WireGuard VPN instances. Designed to simplify the administration of WireGuard networks, it provides a user-friendly interface that supports multiple users with varying access levels, multiple WireGuard instances with individual peer management, and support for crypto key routing for site-to-site interconnections.

+ For more information, please visit the project's GitHub repository
eduardogsilva/wireguard_webadmin +

+ +
+
+ {% if wireguard_integration %} + Launch wireguard_webadmin + {% else %} + Launch wireguard_webadmin + {% endif %} +
+ +
+
+
+
+
+{% endblock %} \ No newline at end of file