Add config identity_min_length and apply non-admin validation
This commit is contained in:
parent
f9ee3ef69d
commit
6b7082a194
@ -165,6 +165,7 @@ class Config(models.Model):
|
|||||||
signup_text: str = ""
|
signup_text: str = ""
|
||||||
|
|
||||||
post_length: int = 500
|
post_length: int = 500
|
||||||
|
identity_min_length: int = 2
|
||||||
identity_max_per_user: int = 5
|
identity_max_per_user: int = 5
|
||||||
identity_max_age: int = 24 * 60 * 60
|
identity_max_age: int = 24 * 60 * 60
|
||||||
|
|
||||||
|
94
users/tests/test_identity.py
Normal file
94
users/tests/test_identity.py
Normal 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()
|
@ -54,6 +54,10 @@ class BasicSettings(AdminSettingsPage):
|
|||||||
"title": "Maximum Identities Per User",
|
"title": "Maximum Identities Per User",
|
||||||
"help_text": "Non-admins will be blocked from creating more than this",
|
"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": {
|
"signup_allowed": {
|
||||||
"title": "Signups Allowed",
|
"title": "Signups Allowed",
|
||||||
"help_text": "If signups are allowed at all",
|
"help_text": "If signups are allowed at all",
|
||||||
@ -84,5 +88,9 @@ class BasicSettings(AdminSettingsPage):
|
|||||||
],
|
],
|
||||||
"Signups": ["signup_allowed", "signup_invite_only", "signup_text"],
|
"Signups": ["signup_allowed", "signup_invite_only", "signup_text"],
|
||||||
"Posts": ["post_length"],
|
"Posts": ["post_length"],
|
||||||
"Identities": ["identity_max_per_user", "restricted_usernames"],
|
"Identities": [
|
||||||
|
"identity_max_per_user",
|
||||||
|
"identity_min_length",
|
||||||
|
"restricted_usernames",
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import string
|
|||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.core import validators
|
||||||
from django.http import Http404, JsonResponse
|
from django.http import Http404, JsonResponse
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
@ -144,10 +145,23 @@ class CreateIdentity(FormView):
|
|||||||
(domain.domain, domain.domain)
|
(domain.domain, domain.domain)
|
||||||
for domain in Domain.available_for_user(user)
|
for domain in Domain.available_for_user(user)
|
||||||
]
|
]
|
||||||
|
self.user = user
|
||||||
|
|
||||||
def clean_username(self):
|
def clean_username(self):
|
||||||
# Remove any leading @ and force it lowercase
|
# Remove any leading @ and force it lowercase
|
||||||
value = self.cleaned_data["username"].lstrip("@").lower()
|
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
|
# Validate it's all ascii characters
|
||||||
for character in value:
|
for character in value:
|
||||||
if character not in string.ascii_letters + string.digits + "_-":
|
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")
|
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):
|
def get_form(self):
|
||||||
form_class = self.get_form_class()
|
form_class = self.get_form_class()
|
||||||
return form_class(user=self.request.user, **self.get_form_kwargs())
|
return form_class(user=self.request.user, **self.get_form_kwargs())
|
||||||
|
Reference in New Issue
Block a user