feat(accout): Add a view to self-create an account
Various checks are in place to make sure that the user does not already have an account, and has accepted the rules of the association
This commit is contained in:
parent
6c18ec3855
commit
5f0dfff4ae
6 changed files with 141 additions and 11 deletions
|
@ -33,3 +33,11 @@ class CreateKanidmAccountForm(forms.Form):
|
|||
help_text=_("Si selectionné, la personne sera ajoutée au groupe dgnum_members"),
|
||||
required=False,
|
||||
)
|
||||
|
||||
|
||||
class CreateSelfAccountForm(forms.Form):
|
||||
displayname = CharField(label=_("Nom d'usage"))
|
||||
mail = EmailField(
|
||||
label=_("Adresse e-mail"),
|
||||
help_text=_("De préférence l'adresse '@ens.psl.eu'"),
|
||||
)
|
||||
|
|
15
src/dgsi/templates/dgsi/create_self_account.html
Normal file
15
src/dgsi/templates/dgsi/create_self_account.html
Normal file
|
@ -0,0 +1,15 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<h2 class="subtitle">{% trans "Création d'un compte DGNum" %}</h2>
|
||||
<hr>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{% include "bulma/form.html" with form=form %}
|
||||
|
||||
<button class="button is-fullwidth mt-6">{% trans "Enregistrer" %}</button>
|
||||
</form>
|
||||
{% endblock content %}
|
|
@ -6,6 +6,21 @@
|
|||
<h2 class="subtitle">Documents Légaux</h2>
|
||||
<hr>
|
||||
|
||||
{% if user.kanidm is None %}
|
||||
{% if show_message %}
|
||||
<div class="notification is-warning is-light has-text-centered">
|
||||
<b>{% trans "Vous devez accepter les Statuts et le Règlement Intérieur de la DGNum avant de pouvoir créer un compte." %}</b>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="notification is-primary is-light has-text-centered">
|
||||
<b>{% trans "Vous n'avez pas encore de compte DGNum, mais vous pouvez désormais en créer un." %}</b>
|
||||
<br>
|
||||
<a class="button mt-5 is-light"
|
||||
href="{% url "dgsi:dgn-create_self_account" %}">{% trans "Poursuivre la création d'un compte DGNum" %}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<br class="my-5">
|
||||
|
||||
{% include "_legal_document.html" with document=statutes user_document=user.accepted_statutes title=_("Statuts") accept_question=_("Accepter les statuts") %}
|
||||
|
|
|
@ -47,8 +47,11 @@
|
|||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="notification px-5 py-5 is-warning is-light has-text-centered is-size-4 mt-6">
|
||||
{% trans "Pas de compte DGNum répertorié." %}
|
||||
<div class="notification is-primary is-light has-text-centered mt-6">
|
||||
<b>{% trans "Pas de compte DGNum répertorié." %}</b>
|
||||
<br>
|
||||
<a class="button mt-5 is-light"
|
||||
href="{% url "dgsi:dgn-create_self_account" %}">{% trans "Créer un compte DGNum" %}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock content %}
|
||||
|
|
|
@ -20,10 +20,15 @@ urlpatterns = [
|
|||
),
|
||||
# Account views
|
||||
path("accounts/profile/", views.ProfileView.as_view(), name="dgn-profile"),
|
||||
path(
|
||||
"accounts/create/",
|
||||
views.CreateSelfAccountView.as_view(),
|
||||
name="dgn-create_self_account",
|
||||
),
|
||||
path(
|
||||
"accounts/create-kanidm/",
|
||||
views.CreateKanidmAccountView.as_view(),
|
||||
name="dgn-create_user",
|
||||
name="dgn-create_kanidm_user",
|
||||
),
|
||||
path(
|
||||
"accounts/forbidden/",
|
||||
|
@ -33,7 +38,7 @@ urlpatterns = [
|
|||
# Services views
|
||||
path("services/", views.ServiceListView.as_view(), name="dgn-services"),
|
||||
path(
|
||||
"services/redirect/<int:pk>",
|
||||
"services/redirect/<int:pk>/",
|
||||
views.ServiceRedirectView.as_view(),
|
||||
name="dgn-services_redirect",
|
||||
),
|
||||
|
|
|
@ -2,10 +2,10 @@ from typing import Any, NamedTuple
|
|||
|
||||
from asgiref.sync import async_to_sync
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.mixins import AccessMixin, LoginRequiredMixin
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.core.mail import EmailMessage
|
||||
from django.http import HttpRequest, HttpResponseBase
|
||||
from django.http import HttpRequest, HttpResponseBase, HttpResponseRedirect
|
||||
from django.template.loader import render_to_string
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.functional import Promise
|
||||
|
@ -13,7 +13,7 @@ from django.utils.translation import gettext_lazy as _
|
|||
from django.views.generic import FormView, ListView, RedirectView, TemplateView
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
|
||||
from dgsi.forms import CreateKanidmAccountForm
|
||||
from dgsi.forms import CreateKanidmAccountForm, CreateSelfAccountForm
|
||||
from dgsi.mixins import StaffRequiredMixin
|
||||
from dgsi.models import Bylaws, Service, Statutes, User
|
||||
from shared.kanidm import client
|
||||
|
@ -34,7 +34,7 @@ AUTHENTICATED_LINKS: list[Link] = [
|
|||
ADMIN_LINKS: list[Link] = [
|
||||
Link(
|
||||
"is-danger",
|
||||
"dgsi:dgn-create_user",
|
||||
"dgsi:dgn-create_kanidm_user",
|
||||
_("Créer un nouveau compte Kanidm"),
|
||||
"user-plus",
|
||||
),
|
||||
|
@ -69,13 +69,97 @@ class ProfileView(LoginRequiredMixin, TemplateView):
|
|||
)
|
||||
|
||||
|
||||
# INFO: We subclass AccessMixin and not LoginRequiredMixin because the way we want to
|
||||
# use dispatch means that we need to execute the login check anyways.
|
||||
class CreateSelfAccountView(AccessMixin, SuccessMessageMixin, FormView):
|
||||
template_name = "dgsi/create_self_account.html"
|
||||
form_class = CreateSelfAccountForm
|
||||
success_message = _("Compte DGNum créé avec succès")
|
||||
success_url = reverse_lazy("dgsi:dgn-profile")
|
||||
|
||||
def dispatch(
|
||||
self, request: HttpRequest, *args: Any, **kwargs: Any
|
||||
) -> HttpResponseBase:
|
||||
if not request.user.is_authenticated:
|
||||
return self.handle_no_permission()
|
||||
|
||||
u = User.from_request(request)
|
||||
|
||||
# Check that the user does not already exist
|
||||
if u.kanidm is not None:
|
||||
messages.add_message(
|
||||
request,
|
||||
messages.WARNING,
|
||||
_("<b>Vous possédez déjà un compte DGNum !</b>"),
|
||||
)
|
||||
return HttpResponseRedirect(reverse_lazy("dgsi:dgn-profile"))
|
||||
|
||||
# Check that the Statutes and Bylaws have been accepted
|
||||
if (
|
||||
u.accepted_statutes != Statutes.latest()
|
||||
or u.accepted_bylaws != Bylaws.latest()
|
||||
):
|
||||
messages.add_message(
|
||||
request,
|
||||
messages.WARNING,
|
||||
_("Vous devez accepter les Statuts et le Règlement Intérieur."),
|
||||
)
|
||||
return HttpResponseRedirect(reverse_lazy("dgsi:dgn-legal_documents"))
|
||||
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
@async_to_sync
|
||||
async def form_valid(self, form):
|
||||
ttl = 86400 # 24h
|
||||
|
||||
d = form.cleaned_data
|
||||
u = User.from_request(self.request)
|
||||
|
||||
# Create the base account
|
||||
await client.person_account_create(u.username, d["displayname"])
|
||||
|
||||
# Update the information
|
||||
await client.person_account_update(u.username, mail=[d["mail"]])
|
||||
|
||||
# FIXME: Will maybe change when kanidm gets its shit together and switches to POST
|
||||
r = await client.call_get(
|
||||
f"/v1/person/{u.username}/_credential/_update_intent/{ttl}"
|
||||
)
|
||||
|
||||
assert r.data is not None
|
||||
|
||||
token: str = r.data["token"]
|
||||
link = f"https://sso.dgnum.eu/ui/reset?token={token}"
|
||||
|
||||
# Send an email to the new user with the given email address
|
||||
EmailMessage(
|
||||
subject="Réinitialisation de mot de passe DGNum -- DGNum password reset",
|
||||
body=render_to_string(
|
||||
"mail/credentials_reset.txt",
|
||||
context={"link": link},
|
||||
),
|
||||
from_email="To Be Determined <dgsi@infra.dgnum.eu>",
|
||||
to=[d["mail"]],
|
||||
headers={"Reply-To": "contact@dgnum.eu"},
|
||||
).send()
|
||||
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class LegalDocumentsView(LoginRequiredMixin, TemplateView):
|
||||
template_name = "dgsi/legal_documents.html"
|
||||
|
||||
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
|
||||
u = User.from_request(self.request)
|
||||
statutes = Statutes.latest()
|
||||
bylaws = Bylaws.latest()
|
||||
|
||||
return super().get_context_data(
|
||||
statutes=Statutes.latest(),
|
||||
bylaws=Bylaws.latest(),
|
||||
statutes=statutes,
|
||||
bylaws=bylaws,
|
||||
show_message=(
|
||||
(u.accepted_bylaws != bylaws) or (u.accepted_statutes != statutes)
|
||||
),
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
@ -129,7 +213,7 @@ class CreateKanidmAccountView(StaffRequiredMixin, SuccessMessageMixin, FormView)
|
|||
template_name = "dgsi/create_kanidm_account.html"
|
||||
success_message = _("Compte DGNum pour %(displayname)s [%(name)s] créé.")
|
||||
|
||||
success_url = reverse_lazy("dgsi:dgn-create_user")
|
||||
success_url = reverse_lazy("dgsi:dgn-create_kanidm_user")
|
||||
|
||||
@async_to_sync
|
||||
async def form_valid(self, form):
|
||||
|
|
Loading…
Add table
Reference in a new issue