Jádro implementace view na doregistraci
This commit is contained in:
		
							parent
							
								
									a0edc1e0a1
								
							
						
					
					
						commit
						c9f0d60e7c
					
				
					 1 changed files with 120 additions and 22 deletions
				
			
		|  | @ -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 = ... | ||||
| 	form = RegistraceUzivateleForm | ||||
| 
 | ||||
| 	def form_valid(self, form): | ||||
| 		pass | ||||
| 	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 … | ||||
| 
 | ||||
| 
 | ||||
| # Pozor, tokeny existují dva: jeden do URL do mailu, druhý do hidden položky ve formuláři. | ||||
| 
 | ||||
| def _gen_token(usecase: str, data: dict[str, object]): | ||||
| 	... | ||||
| 
 | ||||
| def _verify_token(usecase: str, data: dict[str, object]): | ||||
| 	... | ||||
| 
 | ||||
| def _invite(osoba: Osoba): | ||||
| 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' | ||||
| 
 | ||||
| # Token kóduje všechno v sobě | ||||
| def gen_token(usecase: _UseCase, data: dict[str, str]) -> str: | ||||
| 	... | ||||
| 
 | ||||
| def verify_token(usecase: _UseCase, token: str) -> Optional[dict[str, str]]: | ||||
| 	""" | ||||
| 	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…
	
		Reference in a new issue
	
	 Pavel "LEdoian" Turinsky
						Pavel "LEdoian" Turinsky