Add config identity_min_length and apply non-admin validation

This commit is contained in:
Michael Manfre 2022-11-20 13:13:44 -05:00 committed by GitHub
parent f9ee3ef69d
commit 6b7082a194
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 126 additions and 1 deletions

View File

@ -165,6 +165,7 @@ class Config(models.Model):
signup_text: str = ""
post_length: int = 500
identity_min_length: int = 2
identity_max_per_user: int = 5
identity_max_age: int = 24 * 60 * 60

View File

@ -0,0 +1,94 @@
import pytest
from core.models import Config
from users.models import Domain, Identity, User
from users.views.identity import CreateIdentity
@pytest.mark.django_db
def test_create_identity_form(client):
""" """
# Make a user
user = User.objects.create(email="test@example.com")
admin = User.objects.create(email="admin@example.com", admin=True)
# Make a domain
domain = Domain.objects.create(domain="example.com", local=True)
domain.users.add(user)
domain.users.add(admin)
# Test identity_min_length
data = {
"username": "a",
"domain": domain.domain,
"name": "The User",
}
form = CreateIdentity.form_class(user=user, data=data)
assert not form.is_valid()
assert "username" in form.errors
assert "value has at least" in form.errors["username"][0]
form = CreateIdentity.form_class(user=admin, data=data)
assert form.errors == {}
# Test restricted_usernames
data = {
"username": "@root",
"domain": domain.domain,
"name": "The User",
}
form = CreateIdentity.form_class(user=user, data=data)
assert not form.is_valid()
assert "username" in form.errors
assert "restricted to administrators" in form.errors["username"][0]
form = CreateIdentity.form_class(user=admin, data=data)
assert form.errors == {}
# Test valid chars
data = {
"username": "@someval!!!!",
"domain": domain.domain,
"name": "The User",
}
for u in (user, admin):
form = CreateIdentity.form_class(user=u, data=data)
assert not form.is_valid()
assert "username" in form.errors
assert form.errors["username"][0].startswith("Only the letters")
@pytest.mark.django_db
def test_identity_max_per_user(client):
"""
Ensures the webfinger and actor URLs are working properly
"""
# Make a user
user = User.objects.create(email="test@example.com")
# Make a domain
domain = Domain.objects.create(domain="example.com", local=True)
domain.users.add(user)
# Make an identity for them
for i in range(Config.system.identity_max_per_user):
identity = Identity.objects.create(
actor_uri=f"https://example.com/@test{i}@example.com/actor/",
username=f"test{i}",
domain=domain,
name=f"Test User{i}",
local=True,
)
identity.users.add(user)
data = {
"username": "toomany",
"domain": domain.domain,
"name": "Too Many",
}
form = CreateIdentity.form_class(user=user, data=data)
assert form.errors["__all__"][0].startswith("You are not allowed more than")
user.admin = True
form = CreateIdentity.form_class(user=user, data=data)
assert form.is_valid()

View File

@ -54,6 +54,10 @@ class BasicSettings(AdminSettingsPage):
"title": "Maximum Identities Per User",
"help_text": "Non-admins will be blocked from creating more than this",
},
"identity_min_length": {
"title": "Minimum Length For User Identities",
"help_text": "Non-admins will be blocked from creating identities shorter than this",
},
"signup_allowed": {
"title": "Signups Allowed",
"help_text": "If signups are allowed at all",
@ -84,5 +88,9 @@ class BasicSettings(AdminSettingsPage):
],
"Signups": ["signup_allowed", "signup_invite_only", "signup_text"],
"Posts": ["post_length"],
"Identities": ["identity_max_per_user", "restricted_usernames"],
"Identities": [
"identity_max_per_user",
"identity_min_length",
"restricted_usernames",
],
}

View File

@ -2,6 +2,7 @@ import string
from django import forms
from django.contrib.auth.decorators import login_required
from django.core import validators
from django.http import Http404, JsonResponse
from django.shortcuts import redirect
from django.utils.decorators import method_decorator
@ -144,10 +145,23 @@ class CreateIdentity(FormView):
(domain.domain, domain.domain)
for domain in Domain.available_for_user(user)
]
self.user = user
def clean_username(self):
# Remove any leading @ and force it lowercase
value = self.cleaned_data["username"].lstrip("@").lower()
if not self.user.admin:
# Apply username min length
limit = int(Config.system.identity_min_length)
validators.MinLengthValidator(limit)(value)
# Apply username restrictions
if value in Config.system.restricted_usernames.split():
raise forms.ValidationError(
"This username is restricted to administrators only."
)
# Validate it's all ascii characters
for character in value:
if character not in string.ascii_letters + string.digits + "_-":
@ -167,6 +181,14 @@ class CreateIdentity(FormView):
):
raise forms.ValidationError(f"{username}@{domain} is already taken")
if not self.user.admin and (
Identity.objects.filter(users=self.user).count()
>= Config.system.identity_max_per_user
):
raise forms.ValidationError(
f"You are not allowed more than {Config.system.identity_max_per_user} identities"
)
def get_form(self):
form_class = self.get_form_class()
return form_class(user=self.request.user, **self.get_form_kwargs())