# -*- encoding: utf-8 -*- import sys # zjistovani jmen funkci import urllib import time import re import random import json import hashlib from datetime import date, datetime # timeSlices from collections import OrderedDict import django from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import redirect, resolve_url from django.template import Template, RequestContext, loader from django.shortcuts import render import django.contrib.auth as auth from django.contrib.auth.decorators import login_required from django.urls import reverse from django.views.decorators.csrf import ensure_csrf_cookie from django import forms from django.forms import ModelForm from django.contrib import messages from django.utils.translation import ugettext_lazy as _ from django.utils.translation import pgettext, pgettext_lazy from django.db import transaction from django.db.models import F from django.core.validators import validate_email from django.core.exceptions import ValidationError from django.core.mail import send_mail from django.core.cache import cache import logging from django.conf import settings as appSettings from . import models from . import forms from . import auth as nalodeni_auth # Logger instance logger = logging.getLogger(__name__) def about(request): if not request.user.is_anonymous: return HttpResponseRedirect('/ja-pirat/') return render(request, 'about.html') def page_eurovolby_2019(request, reg_ok=False): if request.method == "POST" and not reg_ok: form = forms.Euro2019InterestForm(request.POST) f_email = request.POST.get('email', None) try: validate_email(f_email) f_email = f_email.lower().strip() # force lowercase except ValidationError as e: f_email = None messages.error(request, "Zadali jste neplatnou emailovou adresu.") if f_email: try: ## user exists, send login eui = models.Euro2019Interest.objects.get(email__iexact=f_email) messages.warning(request, 'Zadaná e-mailová adresa již byla registrována.') except models.Euro2019Interest.DoesNotExist: ## user not found eui = None if f_email and not eui: if form.is_valid(): form.save() return redirect("nalodeni:page_eurovolby_2019_ok") else: messages.error(request, "Formulář s kontakními údaji níže obsahuje chyby, prosím, opravte je.") elif request.method == "GET": form = forms.Euro2019InterestForm() context = { 'form' : form, 'reg_ok' : reg_ok, } return render(request, 'page_eurovolby_2019.html', context) @ensure_csrf_cookie @transaction.atomic def join_pirates(request): """ Zpracovani vyplneneho formulare 'Pridej se k piratum' """ template = 'join_pirates.html' form = forms.AppRegEmailForm() if request.method == "POST": form = forms.AppRegEmailForm(request.POST) f_email = request.POST.get('email', None) try: validate_email(f_email) f_email = f_email.lower().strip() # force lowercase except ValidationError as e: f_email = None messages.error(request, "Zadali jste neplatnou emailovou adresu.") if f_email: try: ## user exists, send login user = models.AppUser.objects.get(email__iexact=f_email) request.session['page_prihlaseni'] = { 'email' : f_email } request.session.save() messages.error(request, 'Zadaná e-mailová adresa již existuje, nelze ji registrovat opakovaně. ') messages.error(request, 'Pokud je tato adresa vaše, můžete si nechat zaslat ' + 'přihlašovací odkaz.', extra_tags='safe' ) except models.AppUser.DoesNotExist: ## user not found user = None if f_email and not user: cnt = models.AppRegEmail.objects.all().count() try: ## user does not exist, try registration appReg = models.AppRegEmail.objects.get(email__iexact=f_email) form = forms.AppRegEmailForm(initial={'email':f_email}) if appReg.emailToken is None: # neni odeslana zprava messages.info(request, 'Registrace pro tuto e-mailovou adresu již existuje. Přihlašovací údaje ' + 'zašleme e-mailem po potvrzení koordinátorem.') else: # registracni zprava jiz odeslana if cnt >= int(appSettings.APP_REG_LIMIT_SOFT) or cnt >= int(appSettings.APP_REG_LIMIT_HARD): messages.info(request, 'Registrační zpráva již byla odeslána. Pokud nedorazila,' + 'koordinátor vám po vypršení její platnosti zašle novou.') else: nalodeni_auth.sendRegisterTokenReg(appReg) messages.info(request, 'Na zadanou emailovou adresu jsme vám zaslali potvrzovací odkaz.') except models.AppRegEmail.DoesNotExist: # create new registration if form.is_valid(): f_email = form.cleaned_data['email'] # attack defense system if cnt >= int(appSettings.APP_REG_LIMIT_HARD): messages.error(request, 'Vaše registrace NEBYLA přijata z důvodu velkého počtu nezpracovaných ' + 'registrací. Jedná se o ochranu před zneužitím registračního systému. ' + 'Opakujte Vaši registraci později, nebo využijte registraci přes ' + 'centrální pirátskou identitu (SSO) níže.' ) logger.critical('AppRegEmail hard limit reached - too many ' + 'unconfirmed email registrations. ' + 'Not accepting any more registrations now.') elif cnt >= int(appSettings.APP_REG_LIMIT_SOFT): messages.info(request, 'Vaše registrace byla úspěšna. Přihlašovací údaje ' + 'Vám zašleme emailem po potvrzení koordinátorem. ' ) logger.critical('AppRegEmail soft limit reached - approve registrations to send. ' + 'login links to the users.' ) # create registration without sending email rt = form.instance rt.dc_stamp = datetime.now() rt.save() request.session['fillFormFor'] = rt.id request.session.save() return redirect("nalodeni:dotaznik2") else: rt = form.instance rt.dc_stamp = datetime.now() rt.save() nalodeni_auth.sendRegisterTokenReg(rt) messages.info(request, 'Registrace byla úspěšná. Na Vaši emailovou adresu ' + 'jsme Vám zaslali potvrzovací odkaz.') request.session['fillFormFor'] = rt.id request.session.save() return redirect("nalodeni:dotaznik2") elif request.method == "GET": if 'page_join_pirates' in request.session and 'email' in request.session['page_join_pirates']: fs_email = request.session['page_join_pirates']['email'] del request.session['page_join_pirates']['email'] request.session.save() try: validate_email(fs_email) fs_email = fs_email.lower().strip() # force lowercase except ValidationError as e: fs_email = '' messages.error(request, "Zadali jste neplatnou emailovou adresu.") form.fields['email'].initial = fs_email context = { 'AUTH_AVAIL_IDP' : appSettings.AUTH_AVAIL_IDP, 'form' : form, } return render(request, template, context) @ensure_csrf_cookie @transaction.atomic def follow_pirates(request): template = 'follow_pirates.html' if request.method == "POST": form = forms.AppRegFollowEmailForm(request.POST) f_email = request.POST.get('email', None) try: validate_email(f_email) f_email = f_email.lower().strip() # force lowercase except ValidationError as e: f_email = None messages.error(request, "Zadali jste neplatnou emailovou adresu.") if f_email: try: ## user exists, send login user = models.AppUser.objects.get(email__iexact=f_email) request.session['page_prihlaseni'] = { 'email' : f_email } request.session.save() messages.error(request, 'Zadaná e-mailová adresa již existuje, nelze ji registrovat opakovaně. ') messages.error(request, 'Pokud je tato adresa vaše, můžete si nechat zaslat ' + 'přihlašovací odkaz.', extra_tags='safe' ) except models.AppUser.DoesNotExist: ## user not found user = None if f_email and not user: cnt = models.AppRegEmail.objects.all().count() print(cnt, user) try: ## user does not exist, try registration appReg = models.AppRegEmail.objects.get(email__iexact=f_email) form = forms.AppRegFollowEmailForm(initial={'email':f_email}) if appReg.emailToken is None: # neni odeslana zprava messages.info(request, 'Registrace pro tuto e-mailovou adresu již existuje. Přihlašovací údaje ' + 'zašleme e-mailem po potvrzení koordinátorem.') else: # registracni zprava jiz odeslana if cnt >= int(appSettings.APP_REG_LIMIT_SOFT) or cnt >= int(appSettings.APP_REG_LIMIT_HARD): messages.info(request, 'Registrační zpráva již byla odeslána. Pokud nedorazila,' + 'koordinátor vám po vypršení její platnosti zašle novou.') else: nalodeni_auth.sendRegisterTokenReg(appReg) messages.info(request, 'Na zadanou emailovou adresu jsme vám zaslali potvrzovací odkaz.') except models.AppRegEmail.DoesNotExist: # create new registration if form.is_valid(): f_email = form.cleaned_data['email'] # attack defense system if cnt >= int(appSettings.APP_REG_LIMIT_HARD): messages.error(request, 'Vaše registrace NEBYLA přijata z důvodu velkého počtu nezpracovaných ' + 'registrací. Jedná se o ochranu před zneužitím registračního systému. ' + 'Opakujte Vaši registraci později, nebo využijte registraci přes ' + 'centrální pirátskou identitu (SSO) níže.' ) logger.critical('AppRegEmail hard limit reached - too many ' + 'unconfirmed email registrations. ' + 'Not accepting any more registrations now.') elif cnt >= int(appSettings.APP_REG_LIMIT_SOFT): messages.info(request, 'Vaše registrace byla úspěšna. Přihlašovací údaje ' + 'Vám zašleme emailem po potvrzení koordinátorem. ' ) logger.critical('AppRegEmail soft limit reached - approve registrations to send. ' + 'login links to the users.' ) # create registration without sending email rt = form.instance rt.dc_stamp = datetime.now() rt.save() request.session['fillFormFor'] = rt.id request.session.save() return redirect("nalodeni:dotaznik_follow") else: rt = form.instance rt.dc_stamp = datetime.now() rt.save() nalodeni_auth.sendRegisterTokenReg(rt) messages.info(request, 'Registrace byla úspěšná. Na Vaši emailovou adresu ' + 'jsme Vám zaslali potvrzovací odkaz.') request.session['fillFormFor'] = rt.id request.session.save() return redirect("nalodeni:dotaznik_follow") elif request.method == "GET": form = forms.AppRegFollowEmailForm() context = { 'form' : form, } return render(request, template, context) def paluby(request): return render(request, 'paluby.html') def posadky(request): return render(request, 'posadky.html') @login_required(login_url="/prihlaseni") def ja_pirat(request): return render(request, 'ja_pirat.html') @ensure_csrf_cookie @transaction.atomic def prihlaseni(request): """ Uživatel zadá email. Pokud ještě není registrován, tak: 1. je přihlášen a má možnost vyplnit profil, 2. po vyplnění profilu mu bude zaslán ověřovací email. 3. v DB bude poznačeno, kdy bylo věření emailem provedeno, případně jestli vůbec. 4. do doby, kdy se odhlásí (tj. první login, max. 30 minut), má možnost ještě profil editovat, případně se věnovat dalším činnostem. Některé činnosti budou přístupné pouze uživatelům s ověřeným emailem. Pokud je uživatel již registrován (s nebo bez ověřeného emailu), je mu zaslán přihlašovací odkaz na email. Použitím odkazu bude uživatel přihlášen. """ if not request.user.is_anonymous: return HttpResponseRedirect('/ja-pirat/') fs_email = '' # user wants to register or login if request.method == "POST": f_email = request.POST.get('f_adresa',None) try: validate_email(f_email) f_email = f_email.lower().strip() except ValidationError as e: f_email = None messages.error(request, "Zadali jste neplatnou emailovou adresu.") if f_email: try: ## user exists, send login user = models.AppUser.objects.get(email__iexact=f_email) nalodeni_auth.sendLoginToken(user) messages.info(request, 'Poslali jsme Vám email s přihlašovacím odkazem. ' + 'Mrkněte do poštovní schránky ... ') except models.AppUser.DoesNotExist: ## user not found, register new user ? request.session['page_join_pirates'] = { 'email' : f_email } request.session.save() messages.error(request, 'Zadaná e-mailová adresa neexistuje. Můžete se zaregistrovat ' + 'zde.', extra_tags='safe' ) elif request.method == "GET": emailToken = request.GET.get('t', None) user = None # process the link from email if emailToken: try: user = auth.authenticate(request, emailToken=emailToken) except ValidationError as e: messages.error(request, e.message) except Exception as e: messages.error(request, "Proces se nezdařil, chyba byla oznámena správci.") logger.error("Nastala chyba pri zpracovani registracniho / login tokenu: %s " % e) if user is not None: # A backend authenticated the credentials auth.login(request, user) if user.postcode is None or user.district is None: messages.info(request, "Prosím, doplňte údaje v profilu.") return HttpResponseRedirect('/ja-pirat/profil/') return HttpResponseRedirect('/ja-pirat/') else: # No backend authenticated the credentials pass # predvyplnit emailovou adresu, pokud se uzivatel proklikl z registrace if 'page_prihlaseni' in request.session and 'email' in request.session['page_prihlaseni']: fs_email = request.session['page_prihlaseni']['email'] del request.session['page_prihlaseni']['email'] request.session.save() try: validate_email(fs_email) fs_email = fs_email.lower().strip() # force lowercase except ValidationError as e: fs_email = '' messages.error(request, "Zadali jste neplatnou emailovou adresu.") context = { 'AUTH_AVAIL_IDP' : appSettings.AUTH_AVAIL_IDP, 'fs_email' : fs_email, } return render(request, 'prihlaseni.html', context) @login_required(login_url="/prihlaseni") def email_vizitka(request): """ HTML vizitka pro emaily """ if request.method == "POST": form = forms.EmailVizitkaForm(request.POST) else: form = forms.EmailVizitkaForm( initial={'name': '%s %s' % (request.user.firstName, request.user.lastName) } ) context = { 'form' : form, } return render(request, 'nastenka/email_vizitka.html', context) @login_required(login_url="/prihlaseni") @transaction.atomic def profil(request): _form = forms.AppUserSsoForm if request.user.ssoUid else forms.AppUserForm # TODO :: check and enforce DB transaction to prevent race-condition attacks if request.method == "GET": emailToken = request.GET.get('t', None) if emailToken: # user token from DB ut = request.user.email_contact_token if ut is None: messages.error(request, "Špatný ověřovací token.") return redirect('nalodeni:profil') ut = ut.split('-') if ( (datetime.now() - datetime.utcfromtimestamp(int(ut[0])) ).total_seconds() ) > 24*3600: messages.error(request, "Potvrzovací token kontaktního emailu vypršel.") request.user.email_contact_token = None request.user.email_contact_verified = False else: # hash the verified email_contact with token hash_tae = hashlib.sha256() hash_tae.update(ut[1].encode('utf-8')) hash_tae.update(request.user.email_contact.encode('utf-8')) if emailToken.strip() == hash_tae.hexdigest(): messages.info(request, "Kontaktní e-mailová adresa úspěšně ověřena.") request.user.email_contact_token = None request.user.email_contact_verified = True request.user.ts_for_ldap_sync = datetime.now() request.user.save() return redirect('nalodeni:ja_pirat') else: messages.error(request, "Špatný ověřovací token.") request.user.email_contact_token = None request.user.email_contact_verified = False request.user.save() return redirect('nalodeni:profil') # create edit form form = _form(instance=request.user) elif request.method == "POST": form = _form(request.POST, instance=request.user) email_contact_orig = request.user.email_contact with request.user.audit_context(request.user) as audit: if form.is_valid(): email_contact_changed = email_contact_orig != form.instance.email_contact if email_contact_changed: form.instance.email_contact_verified = False form.save() if (form.instance.email_contact != None and not form.instance.email_contact_verified): try: nalodeni_auth.sendEmailContactVerificationToken(form.instance) messages.info(request, "Potvrzovací email pro nastavení kontaktní adresy byl odeslán.") except ValidationError as e: messages.error(request, "Kontaktní email není správně vyplněn.") except Exception as e: logger.error("Chyba pri odesilani email_contact potvrzeni: %s" % e) messages.error(request, "Potvrzovací email pro změnu kontaktní adresy " + "se nepodařilo odeslat.") messages.info(request, "Údaje byly uloženy.") return redirect('nalodeni:ja_pirat') else: messages.error(request, "Opravte prosím chyby v zadání.") raise audit.DataNotSavedException else: form = None context = { 'form' : form, 'AUTH_SERVER' : appSettings.AUTH_SERVER, } return render(request, 'profil.html', context) @ensure_csrf_cookie @login_required(login_url="/prihlaseni") @transaction.atomic def souhlas(request): """ Udeleni ci odvolani souhlasu se zpracovanim osobnich udaju """ if request.method == "POST": if request.POST.get("action", None) == "yes": messages.info(request, "Souhlasil/a jste se zpracováním osobních údajů. Děkujeme.") request.user.dc_stamp = datetime.now() request.user.dc_undo_stamp = None request.user.save() if request.POST.get("action", None) == "no": request.user.dc_stamp = None request.user.dc_undo_stamp = datetime.now() messages.info(request, "Odvolal/a jste souhlas se zpracováním osobních údajů.") send_mail( "Nalodeni: %s odvolal souhlas se zpracovanim osobnich udaju" % request.user.email, "Stalo se tak {mydate}".format(mydate=request.user.dc_undo_stamp), "nalodeni@pirati.cz", [appSettings.EMAIL_RECIPIENT_GDPR], ) request.user.save() return HttpResponseRedirect('/ja-pirat/profil/') @login_required(login_url="/prihlaseni") @transaction.atomic def dotaznik(request): _form = forms.UserFormForm if request.user.userform is None: f = models.UserForm() f.save() request.user.userform = f request.user.save() # TODO :: check and enforce DB transaction to prevent race-condition attacks if request.method == "GET": form = _form(instance=request.user.userform) elif request.method == "POST": with request.user.userform.audit_context(request.user) as audit: form = _form(request.POST, instance=request.user.userform) if form.is_valid(): form.save() messages.info(request, "Údaje byly uloženy.") return redirect('nalodeni:ja_pirat') else: messages.error(request, "Opravte prosím chyby v zadání.") raise audit.DataNotSavedException else: form = None context = { 'form' : form, } return render(request, 'dotaznik.html', context) @transaction.atomic def dotaznik2(request): _form = forms.UserFormForm if 'fillFormFor' in request.session: f = models.AppRegEmail.objects.get(pk=request.session['fillFormFor']) uf = f.userform if uf is None: uf = models.UserForm() uf.save() f.userform = uf f.save() else: return redirect('nalodeni:join_pirates') # TODO :: check and enforce DB transaction to prevent race-condition attacks if request.method == "GET": form = _form(instance=uf) elif request.method == "POST": form = _form(request.POST, instance=uf) if form.is_valid(): form.save() messages.info(request, "Údaje byly uloženy.") del request.session['fillFormFor'] return redirect('nalodeni:join_pirates') else: messages.error(request, "Opravte prosím chyby v zadání.") else: form = None context = { 'form' : form, } return render(request, 'dotaznik.html', context) @transaction.atomic def dotaznik_follow(request): _form = forms.UserFollowFormForm if 'fillFormFor' in request.session: f = models.AppRegEmail.objects.get(pk=request.session['fillFormFor']) uf = f.userform if uf is None: uf = models.UserForm() uf.save() f.userform = uf f.save() else: return redirect('nalodeni:follow_pirates') # TODO :: check and enforce DB transaction to prevent race-condition attacks if request.method == "GET": form = _form(instance=uf) elif request.method == "POST": form = _form(request.POST, instance=uf) if form.is_valid(): form.save() messages.info(request, "Údaje byly uloženy.") del request.session['fillFormFor'] return redirect('nalodeni:follow_pirates') else: messages.error(request, "Opravte prosím chyby v zadání.") else: form = None context = { 'form' : form, } return render(request, 'dotaznik_follow.html', context)