diff --git a/jabberfr/settings.py b/jabberfr/settings.py index d412e93..858496c 100644 --- a/jabberfr/settings.py +++ b/jabberfr/settings.py @@ -31,6 +31,7 @@ ALLOWED_HOSTS = [ 'jabberfr.org', 'irc.jabberfr.org', 'chat.jabberfr.org', + 'localhost', ] DEFAULT_HOST = 'jabberfr' diff --git a/jabberfr/templates/hebergement.html b/jabberfr/templates/hebergement.html new file mode 100644 index 0000000..15cb5b8 --- /dev/null +++ b/jabberfr/templates/hebergement.html @@ -0,0 +1,31 @@ +{% extends "page_model.html" %} + +{% block content %} +
+

Hébergement

+

Vous disposez d’un nom de domaine et vous souhaitez l’utiliser pour communiquer avec XMPP, mais sans gérer vous même votre serveur ?

+

Vous pouvez désormais nous déléguer l’administration de votre service XMPP !

+

Ajoutez un enregistrement CNAME pointant vers jabberfr.org. dans votre zone DNS (sans oublier le point à la fin !), et remplissez le formulaire ci-dessous :

+
+ +
+ +
+{% if errors %} +

Des erreurs ont été rencontrées lors de la validation de votre demande :

+ {% for error in errors %} +{{ error | safe }} + {% endfor %} +

Si vous avez du mal à faire ça, n’hésitez pas à nous demander de l’aide sur jabberfr@chat.jabberfr.org (webchat) !

+{% endif %} +
+ {% csrf_token %} + {{ form.as_p }} +

Vous pouvez à tout moment récupérer l’intégralité des données de votre serveur, par exemple si vous souhaitez vous auto-héberger, ou passer chez un autre hébergeur.

+

N’hésitez pas à adhérer à l’association pour décider de l’évolution du service avec nous.

+
+

+ +
+ +{% endblock %} diff --git a/jabberfr/templates/hebergement_success.html b/jabberfr/templates/hebergement_success.html new file mode 100644 index 0000000..555d09e --- /dev/null +++ b/jabberfr/templates/hebergement_success.html @@ -0,0 +1,9 @@ +{% extends "page_model.html" %} + +{% block content %} + +

Et voilà !

+ +

Nous allons configurer ce qu’il faut pour gérer votre domaine, nous vous enverrons un email dès que tout est en place de notre côté.

+ +{% endblock %} diff --git a/jabberfr/urls.py b/jabberfr/urls.py index 054aa02..b6bbc56 100644 --- a/jabberfr/urls.py +++ b/jabberfr/urls.py @@ -16,7 +16,7 @@ Including another URLconf from django.urls import path from django.views.generic import TemplateView -from jabberfr.views import get_root_index +from jabberfr.views import get_root_index, hebergement PAGES = [ @@ -26,7 +26,8 @@ PAGES = [ ] urlpatterns = [ - path('', get_root_index) + path('', get_root_index), + path('hébergement', hebergement), ] urlpatterns += [ diff --git a/jabberfr/views.py b/jabberfr/views.py index 96bebdf..9605b6e 100644 --- a/jabberfr/views.py +++ b/jabberfr/views.py @@ -1,5 +1,9 @@ +from asyncio import gather, get_event_loop from aiohttp import ClientSession, ClientError, ClientTimeout +from aiodns import DNSResolver from django.shortcuts import render +from django import forms +from django.core.validators import RegexValidator, URLValidator BASE_MUC_URL = 'http://[::1]:5280/muc_list/?' @@ -10,6 +14,7 @@ TABLE_END = """ """ + async def get_chatrooms(*, limit: int = 25, order: str = 'users') -> str: params = { 'limit': str(limit), @@ -47,3 +52,127 @@ async def get_chat_index(request): } 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 n’est pas valide' + ) + ], + ) + email = forms.EmailField( + required=True, + label='Votre adresse email', + ) + type = forms.ChoiceField( + choices=[ + ('users', 'Comptes utilisateurs'), + ('muc', 'Salons'), + ], + label='Type d’hébergement', + required=True, + ) + message = forms.CharField( + widget=forms.Textarea, + max_length=3000, + label='Un petit mot ?' + ) + cname = forms.BooleanField( + required=True, + label='J’ai effectué la redirection CNAME', + ) + cgu = forms.BooleanField( + required=True, + label=( + 'J’accepte 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 = ( + '

Vous avez des enregistrements SRV qui ne pointent pas ' + 'vers JabberFR:

\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 = '
'
+
+    for rec in records:
+        text += (
+            f'{rec.host}. {rec.type} {rec.ttl} {rec.priority} '
+            f'{rec.weight} {rec.port}\n'
+        )
+    text += '
' + + 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'

Vous n’avez pas d’enregistrement CNAME pour ce' + f' domaine, ajoutez-en un qui pointe vers JabberFR :\n' + f'

{data["domain"]} IN CNAME jabberfr.org
' + ) + elif cname.cname != 'jabberfr.org': + errors.append( + f'

Votre enregistrement CNAME ne pointe pas ' + f'vers jabberfr.org mais vers ' + f'{cname.cname}, vous devriez changer ça dans ' + f'votre interface de gestion DNS.

' + ) + 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)