From d67c48f427c9cab3a1fc6b269883101dc7b0bfd5 Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Thu, 12 Mar 2026 10:26:47 -0300 Subject: [PATCH] improve authentication method form --- gatekeeper/forms.py | 8 ++-- gatekeeper/urls.py | 1 + gatekeeper/views.py | 41 +++++++++++++++++++ .../gatekeeper_auth_method_form.html | 40 ++++++++++++++++++ 4 files changed, 86 insertions(+), 4 deletions(-) diff --git a/gatekeeper/forms.py b/gatekeeper/forms.py index 19b1383..d5fa3b1 100644 --- a/gatekeeper/forms.py +++ b/gatekeeper/forms.py @@ -115,21 +115,21 @@ class AuthMethodForm(forms.ModelForm): Div( Div('name', css_class='col-md-6'), Div('auth_type', css_class='col-md-6'), - css_class='row' + css_class='row auth-type-group' ), Div( Div('totp_secret', css_class='col-md-6'), Div('totp_pin', css_class='col-md-6'), - css_class='row' + css_class='row totp-group' ), Div( Div('oidc_provider', css_class='col-md-12'), - css_class='row' + css_class='row oidc-group' ), Div( Div('oidc_client_id', css_class='col-md-6'), Div('oidc_client_secret', css_class='col-md-6'), - css_class='row' + css_class='row oidc-group' ), Div( Div( diff --git a/gatekeeper/urls.py b/gatekeeper/urls.py index 3ad1dbc..b762dd6 100644 --- a/gatekeeper/urls.py +++ b/gatekeeper/urls.py @@ -17,6 +17,7 @@ urlpatterns = [ # Auth Methods path('auth_method/manage/', views.view_manage_auth_method, name='manage_gatekeeper_auth_method'), path('auth_method/delete/', views.view_delete_auth_method, name='delete_gatekeeper_auth_method'), + path('auth_method/qr/', views.view_generate_totp_qr, name='generate_totp_qr'), # Auth Method Allowed Domains path('domain/manage/', views.view_manage_auth_domain, name='manage_gatekeeper_domain'), diff --git a/gatekeeper/views.py b/gatekeeper/views.py index e7877ab..f85f539 100644 --- a/gatekeeper/views.py +++ b/gatekeeper/views.py @@ -1,5 +1,10 @@ +import io + +import pyotp +import qrcode from django.contrib import messages from django.contrib.auth.decorators import login_required +from django.http import HttpResponse from django.shortcuts import render, get_object_or_404, redirect from django.urls import reverse from django.utils.translation import gettext as _ @@ -223,6 +228,42 @@ def view_delete_auth_method(request): return render(request, 'generic_delete_confirmation.html', context) +@login_required +def view_generate_totp_qr(request): + if not UserAcl.objects.filter(user=request.user).filter(user_level__gte=50).exists(): + return HttpResponse("Access Denied", status=403) + + totp_secret = request.GET.get('secret') + issuer = request.GET.get('issuer', 'wireguard_webadmin') + name = request.GET.get('name', 'Gatekeeper') + + if not totp_secret: + return HttpResponse("No secret provided", status=400) + + try: + totp = pyotp.TOTP(totp_secret) + uri = totp.provisioning_uri(name=name, issuer_name=issuer) + + qr = qrcode.QRCode( + version=1, + error_correction=qrcode.constants.ERROR_CORRECT_L, + box_size=10, + border=4, + ) + qr.add_data(uri) + qr.make(fit=True) + img = qr.make_image(fill_color="black", back_color="white") + + response = HttpResponse(content_type="image/png") + img_io = io.BytesIO() + img.save(img_io, format='PNG') + img_io.seek(0) + response.write(img_io.getvalue()) + return response + except Exception: + return HttpResponse("Error generating QR code", status=500) + + @login_required def view_manage_auth_domain(request): if not UserAcl.objects.filter(user=request.user).filter(user_level__gte=50).exists(): diff --git a/templates/gatekeeper/gatekeeper_auth_method_form.html b/templates/gatekeeper/gatekeeper_auth_method_form.html index df995b1..17c0dd7 100644 --- a/templates/gatekeeper/gatekeeper_auth_method_form.html +++ b/templates/gatekeeper/gatekeeper_auth_method_form.html @@ -36,5 +36,45 @@ {% endblock %} {% block custom_page_scripts %} + {% endblock %} \ No newline at end of file