hébergement form, with DNS checks and form field validation

This commit is contained in:
Mathieu Pasquet 2021-04-17 22:25:35 +02:00
parent ac491fd571
commit 84d7d6334e
5 changed files with 173 additions and 2 deletions

View File

@ -31,6 +31,7 @@ ALLOWED_HOSTS = [
'jabberfr.org', 'jabberfr.org',
'irc.jabberfr.org', 'irc.jabberfr.org',
'chat.jabberfr.org', 'chat.jabberfr.org',
'localhost',
] ]
DEFAULT_HOST = 'jabberfr' DEFAULT_HOST = 'jabberfr'

View File

@ -0,0 +1,31 @@
{% extends "page_model.html" %}
{% block content %}
<div>
<h2>Hébergement</h2>
<p>Vous disposez dun nom de domaine et vous souhaitez lutiliser pour communiquer avec XMPP, mais sans gérer vous même votre serveur?</p>
<p>Vous pouvez désormais nous déléguer ladministration de votre service XMPP!</p>
<p>Ajoutez un enregistrement <code>CNAME</code> pointant vers <code>jabberfr.org.</code> dans votre zone DNS (sans oublier le point à la fin!), et remplissez le formulaire ci-dessous:</p>
</div>
<div>
<div>
{% if errors %}
<p class="error">Des erreurs ont été rencontrées lors de la validation de votre demande:</p>
{% for error in errors %}
{{ error | safe }}
{% endfor %}
<p>Si vous avez du mal à faire ça, nhésitez pas à nous demander de laide sur <a href="xmpp:jabberfr@chat.jabberfr.org?join">jabberfr@chat.jabberfr.org</a> (<a href="https://chat.jabberfr.org/converse.js/jabberfr@chat.jabberfr.org">webchat</a>)!</p>
{% endif %}
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<p>Vous pouvez à tout moment récupérer lintégralité des données de votre serveur, par exemple si vous souhaitez vous auto-héberger, ou passer chez un autre hébergeur.</p>
<p>Nhésitez pas à <a href="/adhesion/">adhérer à lassociation</a> pour décider de lévolution du service avec nous.</p>
</div>
<p><input type="submit" value="Envoyer!"/></p>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,9 @@
{% extends "page_model.html" %}
{% block content %}
<p>Et voilà!</p>
<p>Nous allons configurer ce quil faut pour gérer votre domaine, nous vous enverrons un email dès que tout est en place de notre côté.</p>
{% endblock %}

View File

@ -16,7 +16,7 @@ Including another URLconf
from django.urls import path from django.urls import path
from django.views.generic import TemplateView from django.views.generic import TemplateView
from jabberfr.views import get_root_index from jabberfr.views import get_root_index, hebergement
PAGES = [ PAGES = [
@ -26,7 +26,8 @@ PAGES = [
] ]
urlpatterns = [ urlpatterns = [
path('', get_root_index) path('', get_root_index),
path('hébergement', hebergement),
] ]
urlpatterns += [ urlpatterns += [

View File

@ -1,5 +1,9 @@
from asyncio import gather, get_event_loop
from aiohttp import ClientSession, ClientError, ClientTimeout from aiohttp import ClientSession, ClientError, ClientTimeout
from aiodns import DNSResolver
from django.shortcuts import render from django.shortcuts import render
from django import forms
from django.core.validators import RegexValidator, URLValidator
BASE_MUC_URL = 'http://[::1]:5280/muc_list/?' BASE_MUC_URL = 'http://[::1]:5280/muc_list/?'
@ -10,6 +14,7 @@ TABLE_END = """
</table> </table>
""" """
async def get_chatrooms(*, limit: int = 25, order: str = 'users') -> str: async def get_chatrooms(*, limit: int = 25, order: str = 'users') -> str:
params = { params = {
'limit': str(limit), 'limit': str(limit),
@ -47,3 +52,127 @@ async def get_chat_index(request):
} }
return render(request, 'chat/index.html', context=context) return render(request, 'chat/index.html', context=context)
class HebergForm(forms.Form):
"""Form for hosted domains"""
domain = forms.CharField(
max_length=100,
required=True,
label='Votre nom de domaine',
validators=[
RegexValidator(
regex=URLValidator.host_re,
message='Ce nom de domaine nest pas valide'
)
],
)
email = forms.EmailField(
required=True,
label='Votre adresse email',
)
type = forms.ChoiceField(
choices=[
('users', 'Comptes utilisateurs'),
('muc', 'Salons'),
],
label='Type dhébergement',
required=True,
)
message = forms.CharField(
widget=forms.Textarea,
max_length=3000,
label='Un petit mot?'
)
cname = forms.BooleanField(
required=True,
label='Jai effectué la redirection CNAME',
)
cgu = forms.BooleanField(
required=True,
label=(
'Jaccepte les CGU'
)
)
def check_srv_contents(srv_client, srv_server) -> bool:
if not isinstance(srv_client, Exception): # No SRV
for srv in srv_client:
if srv.host != 'jabberfr.org' or srv.port != 5222:
return True
if not isinstance(srv_server, Exception): # No SRV
for srv in srv_server:
if srv.host != 'jabberfr.org' or srv.port != 5269:
return True
return False
def format_srv_errors(srv_client, srv_server) -> str:
msg = (
'<p>Vous avez des enregistrements SRV qui ne pointent pas '
'vers JabberFR:</p>\n'
)
records = []
if not isinstance(srv_client, Exception):
for srv in srv_client:
records.append(srv)
if not isinstance(srv_server, Exception):
for srv in srv_server:
records.append(srv)
text = '<pre>'
for rec in records:
text += (
f'{rec.host}. {rec.type} {rec.ttl} {rec.priority} '
f'{rec.weight} {rec.port}\n'
)
text += '</pre>'
return msg + text
async def check_dns_config(form: HebergForm) -> list[str]:
errors = []
data = form.cleaned_data
resolver = DNSResolver(loop=get_event_loop())
cname, srv_client, srv_server = await gather(
resolver.query(data['domain'], 'CNAME'),
resolver.query(f'_xmpp-server._tcp.{data["domain"]}', 'SRV'),
resolver.query(f'_xmpp-client._tcp.{data["domain"]}', 'SRV'),
return_exceptions=True,
)
if check_srv_contents(srv_client, srv_server):
errors.append(format_srv_errors(srv_client, srv_server))
if isinstance(cname, BaseException):
errors.append(
f'<p>Vous navez pas denregistrement <code>CNAME</code> pour ce'
f' domaine, ajoutez-en un qui pointe vers JabberFR:\n'
f'<pre><code>{data["domain"]} IN CNAME jabberfr.org</code></pre>'
)
elif cname.cname != 'jabberfr.org':
errors.append(
f'<p>Votre enregistrement <code>CNAME</code> ne pointe pas '
f'vers <code>jabberfr.org</code> mais vers '
f'<code>{cname.cname}</code>, vous devriez changer ça dans '
f'votre interface de gestion DNS.</p>'
)
return errors
async def hebergement(request):
context = {
'title': 'Hébergement chez JabberFR',
}
if request.method == 'POST':
form = HebergForm(request.POST)
context['form'] = form
if form.is_valid():
errors = await check_dns_config(form)
if not errors:
return render(request, 'hebergement_success.html', context)
context['errors'] = errors
else:
form = HebergForm()
context['form'] = form
return render(request, 'hebergement.html', context=context)