Browse Source

Jádro implementace view na doregistraci

export_seznamu_prednasek
Pavel "LEdoian" Turinsky 3 years ago
parent
commit
c9f0d60e7c
  1. 136
      seminar/views/registrace.py

136
seminar/views/registrace.py

@ -4,32 +4,119 @@ Registrace uživatelů k existujícím osobám
V tomto souboru bude asi všechno, co je relevantní (kromě template), protože to
je dostatečně malá a jednorázová věc.
Proto všechno kromě view začíná podtržítkem, aby se to nenatáhlo jako součást
seminar.views
Importovat prosím jen ty dva Views, ve výjimečných případech invite, nic dalšího.
"""
#TODO: Logování (tohle logovat chce skoro určitě)
#TODO: Omezení počtu pokusů (per token -- města / údaje se bruteforcit dají, na rozdíl od tokenů)
from enum import Enum
from django import forms
from django.contrib.auth.models import User, Permission
from django.forms import Form
from django.views.generic.edit import FormView
from django.views import View
from django.views.generic.base import TemplateResponseMixin
import hmac
from typing import Optional
from django.conf.settings import AUTH_USER_MODEL, SECRET_KEY
from django.conf.settings import SECRET_KEY
from seminar.models import Osoba
class DodatecnaRegistraceUzivateleView(FormView):
form = _RegistraceUzivateleForm
# Složitější class-based views mi neumožňují vracet chyby.
class DodatecnaRegistraceUzivateleView(TemplateResponseMixin, View):
template_name = ...
success_url_pattern = ...
def form_valid(self, form):
pass
form = RegistraceUzivateleForm
def get(self, request, url_token):
# Ověřit token
tok_data = verify_token(UseCase.email, url_token)
if tok_data is None:
return render_to_response(
context={
'error': 'Token není platný',
},
status_code=400,
)
# Zkontrolovat, že to není moc staré poslání
now = datetime.now()
token_generation = datetime.fromisoformat(tok_data['timestamp'])
delta = now - token_generation
if delta >= timedelta(weeks=10):
return render_to_response(
context={
'error': 'Vypršela časová platnost tokenu',
},
status_code=400,
)
# Najít osobu
osoba_id = int(tok_data['osoba']) # Pokud tam není, tak jsme vygenerovali špatný token my (byl validní).
osoba = m.Osoba.objects.get(id=osoba_id)
if osoba.user is not None:
return render_to_response(
context={
'error': 'Už máte uživatele',
},
status_code=400,
)
# Vrátit view s formulářem a formulářovým tokenem
form_token = gen_token(UseCase.form, data={
'osoba': str(osoba_id),
'timestamp': datetime.now().isoformat(),
})
return render_to_response(
context={
'form': self.form(initial={
'token': form_token,
}),
}
)
def post(self, request, url_token):
# Zkontrolovat formulář
form = self.form(self.request.POST)
if not form.is_valid():
return render_to_response(
context={
'error': 'Chyba ve formuláři', # TODO: Umíme dostat konkrétní detaily?
# TODO: Formulář pro zkusení znovu?
},
status_code=400,
)
form_data = form.cleaned_data
# Zkontrolovat token
token_data = verify_token(UseCase.form, form_data['token'])
if token_data is None:
return render_to_response(
context={
'error': 'Neplatný token',
},
status_code=400,
)
osoba_id = int(token_data['osoba'])
osoba = m.Osoba.objects.get(id=osoba_id)
# Zkontrolovat verifikační field
...
# Vyrobit uživatele
u = User.objects.create_user(
username=form_data['username'],
password=form_data['password'],
email=osoba.email,
#first_name=o.jmeno,
#last_name=o.prijmeni,
)
u.user_permissions.add(Permission.objects.get(codename__exact='resitel'))
# Přesměrovat na kontrolu údajů
return ...
class KontrolaUdajuASouhlasyView(FormView):
...
class _RegistraceUzivateleForm(Form):
#model = AUTH_USER_MODEL
class RegistraceUzivateleForm(Form):
#model = User
# Zkopírováno z přihlášky :-)
username = forms.CharField(label='Přihlašovací jméno',
max_length=256,
@ -47,21 +134,32 @@ class _RegistraceUzivateleForm(Form):
widget=forms.PasswordInput())
# Dodatečné fieldy + token…
token = ...
token = forms.CharField(widget=forms.HiddenInput(), required=True)
verifikace_TODO = ... # TODO: Co verifikovat
# TODO: clean_username, verifikace …
def invite(osoba: Osoba):
"""
Pošle dané osobě e-mail s odkazem na registraci.
"""
...
# Pozor, tokeny existují dva: jeden do URL do mailu, druhý do hidden položky ve formuláři.
class UseCase(Enum):
email = 'email'
form = 'form'
def _gen_token(usecase: str, data: dict[str, object]):
# Token kóduje všechno v sobě
def gen_token(usecase: _UseCase, data: dict[str, str]) -> str:
...
def _verify_token(usecase: str, data: dict[str, object]):
...
def _invite(osoba: Osoba):
def verify_token(usecase: _UseCase, token: str) -> Optional[dict[str, str]]:
"""
Pošle dané osobě e-mail s odkazem na registraci.
Vrací slovník dat, pokud je token validní, jinak None.
Inspirováno OSMO, je díky tomu jednoduché zároveň předat dekódovaná data a
výsledek ověření.
"""
...

Loading…
Cancel
Save