|
|
@ -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í. |
|
|
|
""" |
|
|
|
... |
|
|
|
|
|
|
|