mirror of
https://github.com/pretix/pretix.git
synced 2026-05-04 15:04:03 +00:00
Added password reset to control.auth
This commit is contained in:
@@ -140,8 +140,10 @@ class PasswordForgotForm(forms.Form):
|
||||
label=_('E-mail'),
|
||||
)
|
||||
|
||||
def __init__(self, event, *args, **kwargs):
|
||||
self.event = event
|
||||
def __init__(self, *args, **kwargs):
|
||||
if 'event' in kwargs:
|
||||
# Backwards compatibility
|
||||
del kwargs['event']
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def clean_email(self):
|
||||
|
||||
@@ -42,26 +42,27 @@ def mail(email: str, subject: str, template: str, context: dict=None, event: Eve
|
||||
sender = event.settings.get('mail_from') if event else settings.MAIL_FROM
|
||||
|
||||
subject = str(subject)
|
||||
prefix = event.settings.get('mail_prefix')
|
||||
if prefix:
|
||||
subject = "[%s] %s" % (prefix, subject)
|
||||
if event:
|
||||
prefix = event.settings.get('mail_prefix')
|
||||
if prefix:
|
||||
subject = "[%s] %s" % (prefix, subject)
|
||||
|
||||
body += "\r\n\r\n----\r\n"
|
||||
body += _(
|
||||
"You are receiving this e-mail because you placed an order for %s." % event.name
|
||||
)
|
||||
body += "\r\n"
|
||||
body += _(
|
||||
"You can view all of your orders at the following URL:"
|
||||
)
|
||||
body += "\r\n"
|
||||
body += build_absolute_uri(
|
||||
'presale:event.orders', kwargs={
|
||||
'event': event.slug,
|
||||
'organizer': event.organizer.slug
|
||||
}
|
||||
)
|
||||
body += "\r\n"
|
||||
body += "\r\n\r\n----\r\n"
|
||||
body += _(
|
||||
"You are receiving this e-mail because you placed an order for %s." % event.name
|
||||
)
|
||||
body += "\r\n"
|
||||
body += _(
|
||||
"You can view all of your orders at the following URL:"
|
||||
)
|
||||
body += "\r\n"
|
||||
body += build_absolute_uri(
|
||||
'presale:event.orders', kwargs={
|
||||
'event': event.slug,
|
||||
'organizer': event.organizer.slug
|
||||
}
|
||||
)
|
||||
body += "\r\n"
|
||||
try:
|
||||
return mail_send([email], subject, body, sender)
|
||||
finally:
|
||||
|
||||
@@ -20,7 +20,9 @@ class PermissionMiddleware:
|
||||
|
||||
EXCEPTIONS = (
|
||||
"auth.login",
|
||||
"auth.register"
|
||||
"auth.register",
|
||||
"auth.forgot",
|
||||
"auth.forgot.recover"
|
||||
)
|
||||
|
||||
def process_request(self, request):
|
||||
|
||||
@@ -3,24 +3,34 @@
|
||||
{% load staticfiles %}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{ settings.PRETIX_INSTANCE_NAME }}</title>
|
||||
{% compress css %}
|
||||
<link rel="stylesheet" type="text/less" href="{% static "pretixcontrol/less/auth.less" %}" />
|
||||
{% endcompress %}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
<footer>
|
||||
{% with "href='http://pretix.eu'" as a_attr %}
|
||||
{% blocktrans trimmed %}
|
||||
powered by <a {{ a_attr }}>pretix</a>
|
||||
{% endblocktrans %}
|
||||
{% endwith %}
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
<head>
|
||||
<title>{{ settings.PRETIX_INSTANCE_NAME }}</title>
|
||||
{% compress css %}
|
||||
<link rel="stylesheet" type="text/less" href="{% static "pretixcontrol/less/auth.less" %}"/>
|
||||
{% endcompress %}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<a href="{% url "control:auth.login" %}">
|
||||
<img src="{% static "pretixbase/img/pretix-logo.svg" %}" class="logo"/>
|
||||
</a>
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
<div class="alert {{ message.tags }}">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
<footer>
|
||||
{% with "href='http://pretix.eu'" as a_attr %}
|
||||
{% blocktrans trimmed %}
|
||||
powered by <a {{ a_attr }}>pretix</a>
|
||||
{% endblocktrans %}
|
||||
{% endwith %}
|
||||
</footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
18
src/pretix/control/templates/pretixcontrol/auth/forgot.html
Normal file
18
src/pretix/control/templates/pretixcontrol/auth/forgot.html
Normal file
@@ -0,0 +1,18 @@
|
||||
{% extends "pretixcontrol/auth/base.html" %}
|
||||
{% load bootstrap3 %}
|
||||
{% load staticfiles %}
|
||||
{% load i18n %}
|
||||
{% block content %}
|
||||
<form class="form-signin" action="" method="post">
|
||||
<h3>{% trans "Password recovery" %}</h3>
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form_errors form type='all' layout='inline' %}
|
||||
{% bootstrap_field form.email %}
|
||||
<div class="form-group buttons">
|
||||
|
||||
<button type="submit" class="btn btn-primary">
|
||||
{% trans "Send recovery information" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -3,20 +3,25 @@
|
||||
{% load i18n %}
|
||||
{% load staticfiles %}
|
||||
{% block content %}
|
||||
<img src="{% static "pretixbase/img/pretix-logo.svg" %}" class="logo" />
|
||||
<form class="form-signin" action="" method="post">
|
||||
{% bootstrap_form_errors form type='all' layout='inline' %}
|
||||
{% csrf_token %}
|
||||
{% bootstrap_field form.email %}
|
||||
{% bootstrap_field form.password %}
|
||||
<div class="form-group buttons">
|
||||
<form class="form-signin" action="" method="post">
|
||||
{% bootstrap_form_errors form type='all' layout='inline' %}
|
||||
{% csrf_token %}
|
||||
{% bootstrap_field form.email %}
|
||||
{% bootstrap_field form.password %}
|
||||
<div class="form-group buttons">
|
||||
<a href="{% url "control:auth.forgot" %}" class="btn btn-link">
|
||||
{% trans "Lost password?" %}
|
||||
</a>
|
||||
|
||||
<button type="submit" class="btn btn-primary">
|
||||
{% trans "Log in" %}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="form-group buttons">
|
||||
<a href="{% url "control:auth.register" %}" class="btn btn-link">
|
||||
{% trans "Register" %}
|
||||
</a>
|
||||
|
||||
<button type="submit" class="btn btn-primary">
|
||||
{% trans "Log in" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
19
src/pretix/control/templates/pretixcontrol/auth/recover.html
Normal file
19
src/pretix/control/templates/pretixcontrol/auth/recover.html
Normal file
@@ -0,0 +1,19 @@
|
||||
{% extends "pretixcontrol/auth/base.html" %}
|
||||
{% load bootstrap3 %}
|
||||
{% load staticfiles %}
|
||||
{% load i18n %}
|
||||
{% block content %}
|
||||
<form class="form-signin" action="" method="post">
|
||||
<h3>{% trans "Set new password" %}</h3>
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form_errors form type='all' layout='inline' %}
|
||||
{% bootstrap_field form.password %}
|
||||
{% bootstrap_field form.password_repeat %}
|
||||
<div class="form-group buttons">
|
||||
|
||||
<button type="submit" class="btn btn-primary">
|
||||
{% trans "Save" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -3,7 +3,6 @@
|
||||
{% load staticfiles %}
|
||||
{% load i18n %}
|
||||
{% block content %}
|
||||
<img src="{% static "pretixbase/img/pretix-logo.svg" %}" class="logo" />
|
||||
<form class="form-signin" action="" method="post">
|
||||
<h3>{% trans "Create a new account" %}</h3>
|
||||
{% bootstrap_form_errors form type='all' layout='inline' %}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{% load i18n %}{% blocktrans with url=url|safe %}Hello,
|
||||
|
||||
you requested a new password. Please go to the following page to reset your password:
|
||||
|
||||
{{ url }}
|
||||
|
||||
Best regards,
|
||||
Your pretix team
|
||||
{% endblocktrans %}
|
||||
@@ -8,6 +8,8 @@ urlpatterns = [
|
||||
url(r'^logout$', auth.logout, name='auth.logout'),
|
||||
url(r'^login$', auth.login, name='auth.login'),
|
||||
url(r'^register$', auth.register, name='auth.register'),
|
||||
url(r'^forgot$', auth.Forgot.as_view(), name='auth.forgot'),
|
||||
url(r'^forgot/recover$', auth.Recover.as_view(), name='auth.forgot.recover'),
|
||||
url(r'^$', main.index, name='index'),
|
||||
url(r'^settings$', user.UserSettings.as_view(), name='user.settings'),
|
||||
url(r'^organizers/$', organizer.OrganizerList.as_view(), name='organizers'),
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import (
|
||||
authenticate, login as auth_login, logout as auth_logout,
|
||||
)
|
||||
from django.contrib.auth.tokens import default_token_generator
|
||||
from django.shortcuts import redirect, render
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from pretix.base.forms.auth import LoginForm, RegistrationForm
|
||||
from pretix.base.forms.auth import (
|
||||
LoginForm, PasswordForgotForm, PasswordRecoverForm, RegistrationForm,
|
||||
)
|
||||
from pretix.base.models import User
|
||||
from pretix.base.services.mail import mail
|
||||
from pretix.helpers.urls import build_absolute_uri
|
||||
|
||||
|
||||
def login(request):
|
||||
@@ -15,9 +24,7 @@ def login(request):
|
||||
"""
|
||||
ctx = {}
|
||||
if request.user.is_authenticated():
|
||||
if "next" in request.GET:
|
||||
return redirect(request.GET.get("next", 'control:index'))
|
||||
return redirect('control:index')
|
||||
return redirect(request.GET.get("next", 'control:index'))
|
||||
if request.method == 'POST':
|
||||
form = LoginForm(data=request.POST)
|
||||
if form.is_valid() and form.user_cache:
|
||||
@@ -45,9 +52,7 @@ def register(request):
|
||||
"""
|
||||
ctx = {}
|
||||
if request.user.is_authenticated():
|
||||
if "next" in request.GET:
|
||||
return redirect(request.GET.get("next", 'control:index'))
|
||||
return redirect('control:index')
|
||||
return redirect(request.GET.get("next", 'control:index'))
|
||||
if request.method == 'POST':
|
||||
form = RegistrationForm(data=request.POST)
|
||||
if form.is_valid():
|
||||
@@ -63,3 +68,88 @@ def register(request):
|
||||
form = RegistrationForm()
|
||||
ctx['form'] = form
|
||||
return render(request, 'pretixcontrol/auth/register.html', ctx)
|
||||
|
||||
|
||||
class Forgot(TemplateView):
|
||||
template_name = 'pretixcontrol/auth/forgot.html'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
if request.user.is_authenticated():
|
||||
return redirect(request.GET.get("next", 'control:index'))
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
if self.form.is_valid():
|
||||
user = self.form.cleaned_data['user']
|
||||
mail(
|
||||
user.email, _('Password recovery'), 'pretixcontrol/email/forgot.txt',
|
||||
{
|
||||
'user': user,
|
||||
'url': (build_absolute_uri('control:auth.forgot.recover')
|
||||
+ '?id=%d&token=%s' % (user.id, default_token_generator.make_token(user)))
|
||||
},
|
||||
None, locale=user.locale
|
||||
)
|
||||
messages.success(request, _('We sent you an e-mail containing further instructions.'))
|
||||
return redirect('control:auth.forgot')
|
||||
else:
|
||||
return self.get(request, *args, **kwargs)
|
||||
|
||||
@cached_property
|
||||
def form(self):
|
||||
return PasswordForgotForm(data=self.request.POST if self.request.method == 'POST' else None)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['form'] = self.form
|
||||
return context
|
||||
|
||||
|
||||
class Recover(TemplateView):
|
||||
template_name = 'pretixcontrol/auth/recover.html'
|
||||
|
||||
error_messages = {
|
||||
'invalid': _('You clicked on an invalid link. Please check that you copied the full '
|
||||
'web address into your address bar. Please note that the link is only valid '
|
||||
'for three days and that the link can only be used once.'),
|
||||
'unknownuser': _('We were unable to find the user you requested a new password for.')
|
||||
}
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
if request.user.is_authenticated():
|
||||
return redirect(request.GET.get("next", 'control:index'))
|
||||
try:
|
||||
user = User.objects.get(id=self.request.GET.get('id'))
|
||||
except User.DoesNotExist:
|
||||
return self.invalid('unknownuser')
|
||||
if not default_token_generator.check_token(user, self.request.GET.get('token')):
|
||||
return self.invalid('invalid')
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def invalid(self, msg):
|
||||
messages.error(self.request, self.error_messages[msg])
|
||||
return redirect('control:auth.forgot')
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
if self.form.is_valid():
|
||||
try:
|
||||
user = User.objects.get(id=self.request.GET.get('id'))
|
||||
except User.DoesNotExist:
|
||||
return self.invalid('unknownuser')
|
||||
if not default_token_generator.check_token(user, self.request.GET.get('token')):
|
||||
return self.invalid('invalid')
|
||||
user.set_password(self.form.cleaned_data['password'])
|
||||
user.save()
|
||||
messages.success(request, _('You can now login using your new password.'))
|
||||
return redirect('control:auth.login')
|
||||
else:
|
||||
return self.get(request, *args, **kwargs)
|
||||
|
||||
@cached_property
|
||||
def form(self):
|
||||
return PasswordRecoverForm(data=self.request.POST if self.request.method == 'POST' else None)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['form'] = self.form
|
||||
return context
|
||||
|
||||
@@ -6,8 +6,6 @@ from django.contrib.auth import (
|
||||
authenticate, login, logout, update_session_auth_hash,
|
||||
)
|
||||
from django.contrib.auth.tokens import default_token_generator
|
||||
from django.core import signing
|
||||
from django.core.signing import BadSignature, SignatureExpired
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db.models import Count
|
||||
from django.shortcuts import redirect
|
||||
@@ -27,7 +25,6 @@ from pretix.presale.forms.checkout import GuestForm
|
||||
from pretix.presale.views import (
|
||||
CartDisplayMixin, EventViewMixin, LoginRequiredMixin,
|
||||
)
|
||||
from pretix.presale.views.cart import CartAdd
|
||||
|
||||
|
||||
class EventIndex(EventViewMixin, CartDisplayMixin, TemplateView):
|
||||
|
||||
Reference in New Issue
Block a user