Use mod_register_json to register with prosody
(untested)
This commit is contained in:
parent
9a51821e42
commit
44a56385f8
@ -2,6 +2,7 @@ from django import forms
|
|||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.core.validators import RegexValidator, URLValidator
|
from django.core.validators import RegexValidator, URLValidator
|
||||||
from jabberfr.validators import validate_jid_userpart, validate_jid
|
from jabberfr.validators import validate_jid_userpart, validate_jid
|
||||||
|
from jabberfr.utils import pick_captcha, validate_captcha
|
||||||
|
|
||||||
USERNAME_HELP = """
|
USERNAME_HELP = """
|
||||||
<ul>
|
<ul>
|
||||||
@ -116,6 +117,13 @@ class InscriptionForm(forms.Form):
|
|||||||
if choices:
|
if choices:
|
||||||
self.fields['server'].widget.choices = 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):
|
class AdhesionForm(forms.Form):
|
||||||
surname = forms.CharField(
|
surname = forms.CharField(
|
||||||
@ -157,3 +165,9 @@ class AdhesionForm(forms.Form):
|
|||||||
self.fields['captcha'].help_text = mark_safe(
|
self.fields['captcha'].help_text = mark_safe(
|
||||||
f'\n<br/>Répondez à cette question : {question}'
|
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())
|
||||||
|
@ -117,6 +117,9 @@ STATICFILES_DIRS = [
|
|||||||
BASE_DIR / 'static'
|
BASE_DIR / 'static'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Prosody mod_register_json token
|
||||||
|
REGISTRATION_TOKEN = 'CHANGEME'
|
||||||
|
|
||||||
# To override in local settings
|
# To override in local settings
|
||||||
CAPTCHA_QUESTIONS = [
|
CAPTCHA_QUESTIONS = [
|
||||||
('1 + 1 ?', '2'),
|
('1 + 1 ?', '2'),
|
||||||
|
9
jabberfr/templates/inscription_success.html
Normal file
9
jabberfr/templates/inscription_success.html
Normal 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 %}
|
@ -2,11 +2,16 @@ import logging
|
|||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
from asyncio import gather, get_event_loop
|
from asyncio import gather, get_event_loop
|
||||||
from random import randint
|
from random import randint
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
from aiodns import DNSResolver
|
from aiodns import DNSResolver
|
||||||
from aiohttp import ClientSession, ClientTimeout
|
from aiohttp import ClientSession, ClientTimeout
|
||||||
from django import forms
|
from aiohttp.web import (
|
||||||
|
HTTPConflict,
|
||||||
|
HTTPServiceUnavailable,
|
||||||
|
)
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from slixmpp import JID, InvalidJID
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -14,6 +19,9 @@ BASE_MUC_URL = 'http://[::1]:5280/muc_list/?'
|
|||||||
|
|
||||||
DOMAINS_URL = 'https://jabberfr.org/domains.xml'
|
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:
|
async def get_chatrooms(*, limit: int = 25, order: str = 'users') -> str:
|
||||||
params = {
|
params = {
|
||||||
@ -108,37 +116,76 @@ async def check_dns_config(data: dict) -> list[str]:
|
|||||||
return errors
|
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]:
|
def pick_captcha() -> tuple[str, str]:
|
||||||
choice = randint(0, len(settings.CAPTCHA_QUESTIONS) - 1)
|
choice = randint(0, len(settings.CAPTCHA_QUESTIONS) - 1)
|
||||||
question = settings.CAPTCHA_QUESTIONS[choice][0]
|
question = settings.CAPTCHA_QUESTIONS[choice][0]
|
||||||
return str(choice), question
|
return str(choice), question
|
||||||
|
|
||||||
|
|
||||||
def validate_captcha(form: forms.Form) -> bool:
|
def validate_captcha(data: dict) -> bool:
|
||||||
data = form.cleaned_data
|
|
||||||
print(data)
|
|
||||||
try:
|
try:
|
||||||
captcha_id = data['captcha_id']
|
captcha_id = data['captcha_id']
|
||||||
if captcha_id < 0 or captcha_id >= len(settings.CAPTCHA_QUESTIONS):
|
if captcha_id < 0 or captcha_id >= len(settings.CAPTCHA_QUESTIONS):
|
||||||
raise ValueError
|
raise ValueError
|
||||||
except ValueError:
|
except ValueError:
|
||||||
form.add_error('captcha', 'Êtes-vous un robot ?')
|
|
||||||
return False
|
return False
|
||||||
question_tup = settings.CAPTCHA_QUESTIONS[captcha_id]
|
question_tup = settings.CAPTCHA_QUESTIONS[captcha_id]
|
||||||
if question_tup is None:
|
if question_tup is None:
|
||||||
form.add_error('captcha', 'Êtes-vous un robot ?')
|
|
||||||
return False
|
return False
|
||||||
if question_tup[1] != data['captcha']:
|
if question_tup[1] != data['captcha']:
|
||||||
form.add_error('captcha', 'Êtes-vous un robot ?')
|
|
||||||
return False
|
return False
|
||||||
return True
|
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 prosody’s mod_register_json"""
|
||||||
|
try:
|
||||||
|
jid = JID(f'{username}@{host}')
|
||||||
|
except InvalidJID:
|
||||||
|
return
|
||||||
|
headers = {'Host': jid.server}
|
||||||
|
# we don’t 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 d’utilisateur n’est 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
|
||||||
|
@ -6,9 +6,11 @@ from jabberfr.utils import (
|
|||||||
get_chatrooms,
|
get_chatrooms,
|
||||||
get_custom_domains,
|
get_custom_domains,
|
||||||
check_dns_config,
|
check_dns_config,
|
||||||
check_passwords,
|
|
||||||
pick_captcha,
|
pick_captcha,
|
||||||
validate_captcha
|
validate_captcha,
|
||||||
|
register_account,
|
||||||
|
RegistrationError,
|
||||||
|
get_client_ip,
|
||||||
)
|
)
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -79,8 +81,24 @@ async def inscription(request, domain=None):
|
|||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form = InscriptionForm(request.POST, custom_choices=choices)
|
form = InscriptionForm(request.POST, custom_choices=choices)
|
||||||
context['form'] = form
|
context['form'] = form
|
||||||
if form.is_valid() and check_passwords(form):
|
if form.is_valid():
|
||||||
pass
|
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:
|
else:
|
||||||
form = InscriptionForm(custom_choices=choices)
|
form = InscriptionForm(custom_choices=choices)
|
||||||
context['form'] = form
|
context['form'] = form
|
||||||
@ -92,10 +110,8 @@ async def adhesion(request):
|
|||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form = AdhesionForm(request.POST)
|
form = AdhesionForm(request.POST)
|
||||||
context['form'] = form
|
context['form'] = form
|
||||||
if form.is_valid() and validate_captcha(form):
|
if form.is_valid():
|
||||||
return render(request, 'adhesion_success.html', context=context)
|
return render(request, 'adhesion_success.html', context=context)
|
||||||
else:
|
|
||||||
form.reset_captcha(*pick_captcha())
|
|
||||||
else:
|
else:
|
||||||
form = AdhesionForm(captcha=pick_captcha())
|
form = AdhesionForm(captcha=pick_captcha())
|
||||||
context['form'] = form
|
context['form'] = form
|
||||||
|
Loading…
Reference in New Issue
Block a user