Use mod_register_json to register with prosody

(untested)
This commit is contained in:
Mathieu Pasquet 2021-04-18 16:22:35 +02:00
parent 9a51821e42
commit 44a56385f8
5 changed files with 113 additions and 24 deletions

View File

@ -2,6 +2,7 @@ from django import forms
from django.utils.safestring import mark_safe
from django.core.validators import RegexValidator, URLValidator
from jabberfr.validators import validate_jid_userpart, validate_jid
from jabberfr.utils import pick_captcha, validate_captcha
USERNAME_HELP = """
<ul>
@ -116,6 +117,13 @@ class InscriptionForm(forms.Form):
if choices:
self.fields['server'].widget.choices = choices
def clean(self):
cleaned_data = super().clean()
if cleaned_data['password'] != cleaned_data['confirm_password']:
self.add_error(
'confirm_password', 'Les mots de passe ne correspondent pas.'
)
class AdhesionForm(forms.Form):
surname = forms.CharField(
@ -157,3 +165,9 @@ class AdhesionForm(forms.Form):
self.fields['captcha'].help_text = mark_safe(
f'\n<br/>Répondez à cette question: {question}'
)
def clean(self):
cleaned_data = super().clean()
if not validate_captcha(cleaned_data):
self.add_error('captcha', 'Êtes-vous un robot?')
self.reset_captcha(*pick_captcha())

View File

@ -117,6 +117,9 @@ STATICFILES_DIRS = [
BASE_DIR / 'static'
]
# Prosody mod_register_json token
REGISTRATION_TOKEN = 'CHANGEME'
# To override in local settings
CAPTCHA_QUESTIONS = [
('1 + 1?', '2'),

View File

@ -0,0 +1,9 @@
{% extends "page_model.html" %}
{% block content %}
<main>
<h2 class="center">Inscription à un serveur Jabber par JabberFR</h2>
<p>Votre compte <code>{{ jid }}</code> a été créé avec succès !</p>
<hr />
</main>
{% endblock %}

View File

@ -2,11 +2,16 @@ import logging
import xml.etree.ElementTree as ET
from asyncio import gather, get_event_loop
from random import randint
from uuid import uuid4
from aiodns import DNSResolver
from aiohttp import ClientSession, ClientTimeout
from django import forms
from aiohttp.web import (
HTTPConflict,
HTTPServiceUnavailable,
)
from django.conf import settings
from slixmpp import JID, InvalidJID
log = logging.getLogger(__name__)
@ -14,6 +19,9 @@ BASE_MUC_URL = 'http://[::1]:5280/muc_list/?'
DOMAINS_URL = 'https://jabberfr.org/domains.xml'
REGISTER_URL = 'http://[::1]:5280/register_account/'
VERIFY_URL = REGISTER_URL + 'verify'
async def get_chatrooms(*, limit: int = 25, order: str = 'users') -> str:
params = {
@ -108,37 +116,76 @@ async def check_dns_config(data: dict) -> list[str]:
return errors
def check_passwords(form: forms.Form) -> bool:
if form.cleaned_data['password'] != form.cleaned_data['confirm_password']:
form.add_error(
'confirm_password', 'Les mots de passe ne correspondent pas'
)
return False
return True
def pick_captcha() -> tuple[str, str]:
choice = randint(0, len(settings.CAPTCHA_QUESTIONS) - 1)
question = settings.CAPTCHA_QUESTIONS[choice][0]
return str(choice), question
def validate_captcha(form: forms.Form) -> bool:
data = form.cleaned_data
print(data)
def validate_captcha(data: dict) -> bool:
try:
captcha_id = data['captcha_id']
if captcha_id < 0 or captcha_id >= len(settings.CAPTCHA_QUESTIONS):
raise ValueError
except ValueError:
form.add_error('captcha', 'Êtes-vous un robot?')
return False
question_tup = settings.CAPTCHA_QUESTIONS[captcha_id]
if question_tup is None:
form.add_error('captcha', 'Êtes-vous un robot?')
return False
if question_tup[1] != data['captcha']:
form.add_error('captcha', 'Êtes-vous un robot?')
return False
return True
class RegistrationError(Exception):
def __init__(self, text):
super().__init__()
self.text = text
async def register_account(username: str, host: str, password: str, ip: str):
"""Register an account using prosodys mod_register_json"""
try:
jid = JID(f'{username}@{host}')
except InvalidJID:
return
headers = {'Host': jid.server}
# we dont care about the email feature
email = uuid4().hex + '@example.com'
payload = {
'username': jid.user,
'password': password,
'ip': ip,
'auth_token': settings.REGISTRATION_TOKEN,
'mail': email,
}
tmout = ClientTimeout(total=10)
async with ClientSession(raise_for_status=True, timeout=tmout) as session:
try:
async with session.post(REGISTER_URL, json=payload, headers=headers) as resp:
uid = await resp.text()
async with session.post(VERIFY_URL, data={'uid': uid}, headers=headers):
pass
except HTTPConflict:
raise RegistrationError(
'Ce nom dutilisateur nest pas disponible.'
)
except HTTPServiceUnavailable:
raise RegistrationError(
'Vous avez fait trop de demandes, veuillez revenir plus tard.'
)
except BaseException:
raise RegistrationError(
'Erreur inconnue. (serveur indisponible?)'
)
def get_client_ip(request) -> str:
"""Get a client IP"""
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
if x_forwarded_for:
ip = x_forwarded_for.split(',')[0]
else:
ip = request.META.get('REMOTE_ADDR')
return ip

View File

@ -6,9 +6,11 @@ from jabberfr.utils import (
get_chatrooms,
get_custom_domains,
check_dns_config,
check_passwords,
pick_captcha,
validate_captcha
validate_captcha,
register_account,
RegistrationError,
get_client_ip,
)
log = logging.getLogger(__name__)
@ -79,8 +81,24 @@ async def inscription(request, domain=None):
if request.method == 'POST':
form = InscriptionForm(request.POST, custom_choices=choices)
context['form'] = form
if form.is_valid() and check_passwords(form):
pass
if form.is_valid():
try:
data = form.cleaned_data
await register_account(
data['username'],
data['server'],
data['password'],
get_client_ip(request),
)
except RegistrationError as exc:
form.add_error('__all__', exc.text)
else:
context['jid'] = data['username'] + '@' + data['server']
return render(
request,
'inscription_success.html',
context=context
)
else:
form = InscriptionForm(custom_choices=choices)
context['form'] = form
@ -92,10 +110,8 @@ async def adhesion(request):
if request.method == 'POST':
form = AdhesionForm(request.POST)
context['form'] = form
if form.is_valid() and validate_captcha(form):
if form.is_valid():
return render(request, 'adhesion_success.html', context=context)
else:
form.reset_captcha(*pick_captcha())
else:
form = AdhesionForm(captcha=pick_captcha())
context['form'] = form