nalodeni.pirati.cz/nalodeni/views.py

715 lines
26 KiB
Python

# -*- 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 '
+ '<a href="/prihlaseni/">přihlašovací odkaz</a>.', 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 '
+ '<a href="/prihlaseni/">přihlašovací odkaz</a>.', 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):
template = 'paluby.html'
context = {
}
return render(request, template, context)
def posadky(request):
template = 'posadky.html'
context = {
}
return render(request, template, context)
@login_required(login_url="/prihlaseni")
def ja_pirat(request):
template = 'ja_pirat.html'
context = {
}
return render(request, template, context)
@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 '
+ '<a href="/jdu-do-toho/">zde</a>.', 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.")
template = 'prihlaseni.html'
context = {
'AUTH_AVAIL_IDP' : appSettings.AUTH_AVAIL_IDP,
'fs_email' : fs_email,
}
return render(request, template, 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
def save_and_redirect(page):
request.user.save()
return HttpResponseRedirect(page)
# TODO :: check and enforce DB transaction to prevent race-condition attacks
if request.method == "GET":
# udeleni souhlasu se zpracovanim osobnich udaju
if request.GET.get('doConsent', None) is not None:
request.user.dc_stamp = datetime.now()
request.user.dc_undo_stamp = None
return save_and_redirect('/ja-pirat/profil/')
# odvolani souhlasu se zpracovanim osobnich udaju
if request.GET.get('undoConsent', None) is not None:
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 {approxdate}".format(approxdate=datetime.now()),
"nalodeni@pirati.cz", [appSettings.EMAIL_RECIPIENT_GDPR],
)
return save_and_redirect('/ja-pirat/profil/')
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)
@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":
# create edit form
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":
# create edit form
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":
# create edit form
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)