diff --git a/fedcode/forms.py b/fedcode/forms.py
index 98550b9..261c384 100644
--- a/fedcode/forms.py
+++ b/fedcode/forms.py
@@ -8,8 +8,12 @@
#
from django import forms
+from django.contrib.admin.forms import AdminAuthenticationForm
+from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
+from django_recaptcha.fields import ReCaptchaField
+from django_recaptcha.widgets import ReCaptchaV2Checkbox
from .models import Note
from .models import Repository
@@ -58,6 +62,9 @@ def __init__(self, *args, **kwargs):
class PersonSignUpForm(UserCreationForm):
+ captcha = ReCaptchaField(
+ error_messages={"required": ("Captcha is required")}, widget=ReCaptchaV2Checkbox
+ )
email = forms.EmailField(max_length=254)
class Meta:
@@ -141,3 +148,21 @@ class SearchRepositoryForm(forms.Form):
},
),
)
+
+
+class UserLoginForm(AuthenticationForm):
+ captcha = ReCaptchaField(
+ error_messages={
+ "required": ("Captcha is required"),
+ },
+ widget=ReCaptchaV2Checkbox,
+ )
+
+
+class AdminLoginForm(AdminAuthenticationForm):
+ captcha = ReCaptchaField(
+ error_messages={
+ "required": ("Captcha is required"),
+ },
+ widget=ReCaptchaV2Checkbox(),
+ )
diff --git a/fedcode/templates/admin_login.html b/fedcode/templates/admin_login.html
new file mode 100644
index 0000000..074ca6e
--- /dev/null
+++ b/fedcode/templates/admin_login.html
@@ -0,0 +1,66 @@
+{% extends "admin/base_site.html" %}
+{% load i18n static %}
+
+{% block extrastyle %}{{ block.super }}
+{{ form.media }}
+{% endblock %}
+
+{% block bodyclass %}{{ block.super }} login{% endblock %}
+
+{% block usertools %}{% endblock %}
+
+{% block nav-global %}{% endblock %}
+
+{% block nav-sidebar %}{% endblock %}
+
+{% block content_title %}{% endblock %}
+
+{% block nav-breadcrumbs %}{% endblock %}
+
+{% block content %}
+
+{% if form.errors %}
+{% for error in form.errors.values %}
+
{{ error }}
+{% endfor %}
+{% endif %}
+
+
+
+
+ {% if user.is_authenticated %}
+
+ {% blocktranslate trimmed %}
+ You are authenticated as {{ username }}, but are not authorized to
+ access this page. Would you like to login to a different account?
+ {% endblocktranslate %}
+
+ {% endif %}
+
+
+
+{% endblock %}
diff --git a/fedcode/templates/login.html b/fedcode/templates/login.html
index 6e3d27a..834312d 100644
--- a/fedcode/templates/login.html
+++ b/fedcode/templates/login.html
@@ -10,7 +10,10 @@
{% if form.errors %}
- Error! Your username and password didn't match. Please try again.
+ Error!
+ {% for error in form.errors.values %}
+ {{ error }}
+ {% endfor %}
{% endif %}
@@ -32,7 +35,11 @@ User Login
autocomplete="current-password" required id="id_password">
-
+
+
+ {{ form.captcha }}
+
+
- {{ form.errors }}
+ {% for error in form.errors.values %}
+ {{ error }}
+ {% endfor %}
@@ -61,6 +63,11 @@ User Signup
autocomplete="new-password" required id="id_password2">
+
+
+ {{ form.captcha }}
+
+
diff --git a/fedcode/views.py b/fedcode/views.py
index de95a09..de129b6 100644
--- a/fedcode/views.py
+++ b/fedcode/views.py
@@ -10,7 +10,6 @@
import json
import logging
import os.path
-from urllib.parse import urlparse
import requests
from django.contrib import messages
@@ -47,6 +46,7 @@
from fedcode.activitypub import AP_CONTEXT
from fedcode.activitypub import create_activity_obj
from fedcode.activitypub import has_valid_header
+from fedcode.forms import AdminLoginForm
from fedcode.forms import CreateGitRepoForm
from fedcode.forms import CreateNoteForm
from fedcode.forms import CreateReviewForm
@@ -57,6 +57,7 @@
from fedcode.forms import SearchRepositoryForm
from fedcode.forms import SearchReviewForm
from fedcode.forms import SubscribePackageForm
+from fedcode.forms import UserLoginForm
from fedcode.models import Follow
from fedcode.models import Note
from fedcode.models import Package
@@ -277,6 +278,7 @@ def post(self, request, repository_id):
class UserLogin(LoginView):
template_name = "login.html"
next_page = "/"
+ form_class = UserLoginForm
def dispatch(self, request, *args, **kwargs):
# If user is already logged in, redirect to the next_page.
@@ -887,3 +889,8 @@ def revoke_token(request):
},
)
return JsonResponse(json.loads(r.content), status=r.status_code, content_type=AP_CONTENT_TYPE)
+
+
+class AdminLoginView(LoginView):
+ template_name = "admin_login.html"
+ authentication_form = AdminLoginForm
diff --git a/federatedcode/settings.py b/federatedcode/settings.py
index bc7285b..07d36e5 100644
--- a/federatedcode/settings.py
+++ b/federatedcode/settings.py
@@ -61,6 +61,10 @@
FEDERATEDCODE_CLIENT_ID = env.str("FEDERATEDCODE_CLIENT_ID")
FEDERATEDCODE_CLIENT_SECRET = env.str("FEDERATEDCODE_CLIENT_SECRET")
+RECAPTCHA_PUBLIC_KEY = env.str("RECAPTCHA_PUBLIC_KEY", "")
+RECAPTCHA_PRIVATE_KEY = env.str("RECAPTCHA_PRIVATE_KEY", "")
+SILENCED_SYSTEM_CHECKS = ["captcha.recaptcha_test_key_error"]
+RECAPTCHA_DOMAIN = env.str("RECAPTCHA_DOMAIN", "www.recaptcha.net")
# Application definition
@@ -78,6 +82,7 @@
"django.contrib.humanize",
# Third-party apps
"oauth2_provider",
+ "django_recaptcha",
]
MIDDLEWARE = [
diff --git a/federatedcode/urls.py b/federatedcode/urls.py
index 5457cd9..934528b 100644
--- a/federatedcode/urls.py
+++ b/federatedcode/urls.py
@@ -13,6 +13,7 @@
from django.urls import path
from fedcode import views
+from fedcode.views import AdminLoginView
from fedcode.views import CreateReview
from fedcode.views import CreateSync
from fedcode.views import CreatGitView
@@ -45,6 +46,7 @@
from fedcode.views import redirect_vulnerability
urlpatterns = [
+ path("admin/login/", AdminLoginView.as_view(), name="admin-login"),
path("admin/", admin.site.urls),
path(".well-known/webfinger", WebfingerView.as_view(), name="web-finger"),
path("", HomeView.as_view(), name="home-page"),
diff --git a/requirements.txt b/requirements.txt
index 28cd1cb..e617873 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -16,6 +16,7 @@ Django==5.1.2
django-environ==0.11.2
django-ninja==1.3.0
django-oauth-toolkit==3.0.1
+django-recaptcha==4.0.0
djangorestframework==3.15.2
doc8==1.1.2
docutils==0.21.2
diff --git a/setup.cfg b/setup.cfg
index 7285ae8..a2368bd 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -76,6 +76,9 @@ install_requires =
python-dotenv==1.0.1
click==8.1.7
+ # Captcha
+ django-recaptcha==4.0.0
+
[options.extras_require]
dev =
# Validation