Refs #96 -- Completely removed local users

This commit is contained in:
Raphael Michel
2015-09-17 00:52:36 +02:00
parent 0dccdcb0f7
commit 7def097dcd
37 changed files with 367 additions and 707 deletions

View File

@@ -25,7 +25,6 @@ Example::
[pretix]
instance_name=pretix.de
global_registration=off
url=http://localhost
currency=EUR
cookiedomain=.pretix.de
@@ -36,10 +35,6 @@ Example::
``instance_name``
The name of this installation. Default: ``pretix.de``
``global_registration``
Whether or not this installation supports global user accounts (in addition to
event-bound accounts). Defaults to ``True``.
``url``
The installation's full URL, without a trailing slash.

View File

@@ -30,28 +30,7 @@ Every event is managed by the **organizer**, an abstract entity running the even
Pretix is used by **users**. We want to enable global users who can just login into
pretix and buy tickets for as many events as they like but at the same time it
should be possible to create some kind of local user to have a temporary account
just to buy tickets for one single event.
The problem is, we cannot use usernames as primary keys for our users, as we
do not want one username to be blocked forever just because of one temporary
account using it (people would have to think of a new username for every temporary
account they create). On the other hand, we can not use e-mail addresses either,
as those are not unique (imagine one person having multiple temporary accounts)
and they should not be required for temporary account (to enable anonymity).
Therefore, we split our users into two groups and use an internal **identifier**
as our primary key:
**Local users**
Local users do only exist inside the scope of one event. They are identified by
usernames, which are only valid for exactly one event. Internally, their identifier
is "{username}@{event.id}.event.pretix"
**Global users**
Global users exist everywhere in the installation of Tixl. They can buy tickets
for multiple events and they can be managers of one or more Organizers/Events.
Global users are identified by e-mail addresses.
should be possible to order products **without** needing an user account.
Items and variations

View File

@@ -121,7 +121,7 @@ class JSONExporter(BaseExporter):
{
'code': o.code,
'status': o.status,
'user': o.user.identifier,
'user': o.user.email,
'datetime': o.datetime,
'payment_fee': o.payment_fee,
'total': o.total,

View File

@@ -0,0 +1,158 @@
from django import forms
from django.contrib.auth import authenticate
from django.utils.translation import ugettext_lazy as _
from pretix.base.models import User
class LoginForm(forms.Form):
"""
Base class for authenticating users. Extend this to get a form that accepts
username/password logins.
"""
email = forms.EmailField(label=_("E-mail"), max_length=254)
password = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
error_messages = {
'invalid_login': _("Please enter a correct e-mail address and password."),
'inactive': _("This account is inactive.")
}
def __init__(self, request=None, *args, **kwargs):
"""
The 'request' parameter is set for custom auth use by subclasses.
The form data comes in via the standard 'data' kwarg.
"""
self.request = request
self.user_cache = None
super().__init__(*args, **kwargs)
def clean(self):
email = self.cleaned_data.get('email')
password = self.cleaned_data.get('password')
if email and password:
self.user_cache = authenticate(email=email.lower(), password=password)
if self.user_cache is None:
raise forms.ValidationError(
self.error_messages['invalid_login'],
code='invalid_login'
)
else:
self.confirm_login_allowed(self.user_cache)
return self.cleaned_data
def confirm_login_allowed(self, user):
"""
Controls whether the given User may log in. This is a policy setting,
independent of end-user authentication. This default behavior is to
allow login by active users, and reject login by inactive users.
If the given user cannot log in, this method should raise a
``forms.ValidationError``.
If the given user may log in, this method should return None.
"""
if not user.is_active:
raise forms.ValidationError(
self.error_messages['inactive'],
code='inactive',
)
def get_user(self):
return self.user_cache
class RegistrationForm(forms.Form):
error_messages = {
'duplicate_email': _("You already registered with that e-mail address, please use the login form."),
'pw_mismatch': _("Please enter the same password twice"),
}
email = forms.EmailField(
label=_('Email address'),
required=True
)
password = forms.CharField(
label=_('Password'),
widget=forms.PasswordInput,
required=True
)
password_repeat = forms.CharField(
label=_('Repeat password'),
widget=forms.PasswordInput
)
def clean(self):
password1 = self.cleaned_data.get('password')
password2 = self.cleaned_data.get('password_repeat')
if password1 and password1 != password2:
raise forms.ValidationError(
self.error_messages['pw_mismatch'],
code='pw_mismatch'
)
return self.cleaned_data
def clean_email(self):
email = self.cleaned_data['email']
if User.objects.filter(email=email).exists():
raise forms.ValidationError(
self.error_messages['duplicate_email'],
code='duplicate_email'
)
return email
class PasswordRecoverForm(forms.Form):
error_messages = {
'pw_mismatch': _("Please enter the same password twice")
}
password = forms.CharField(
label=_('Password'),
widget=forms.PasswordInput,
required=True
)
password_repeat = forms.CharField(
label=_('Repeat password'),
widget=forms.PasswordInput
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def clean(self):
password1 = self.cleaned_data.get('password')
password2 = self.cleaned_data.get('password_repeat')
if password1 and password1 != password2:
raise forms.ValidationError(
self.error_messages['pw_mismatch'],
code='pw_mismatch'
)
return self.cleaned_data
class PasswordForgotForm(forms.Form):
email = forms.EmailField(
label=_('E-mail'),
)
def __init__(self, event, *args, **kwargs):
self.event = event
super().__init__(*args, **kwargs)
def clean_email(self):
email = self.cleaned_data['email']
try:
self.cleaned_data['user'] = User.objects.get(
email=email, event__isnull=True
)
return email
except User.DoesNotExist:
raise forms.ValidationError(
_("We are unable to find a user matching the data you provided."),
code='unknown_user'
)

View File

@@ -59,7 +59,7 @@ class UserSettingsForm(forms.ModelForm):
def clean_email(self):
email = self.cleaned_data['email']
if User.objects.filter(Q(identifier=email) & ~Q(pk=self.instance.pk)).exists():
if User.objects.filter(Q(email=email) & ~Q(pk=self.instance.pk)).exists():
raise forms.ValidationError(
self.error_messages['duplicate_identifier'],
code='duplicate_identifier',
@@ -88,6 +88,5 @@ class UserSettingsForm(forms.ModelForm):
if password1:
self.instance.set_password(password1)
self.instance.identifier = email
return self.cleaned_data

View File

@@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import pretix.base.models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0014_auto_20150916_1319'),
]
operations = [
migrations.AddField(
model_name='cartposition',
name='session',
field=models.CharField(max_length=255, verbose_name='Session', null=True, blank=True),
),
migrations.AddField(
model_name='order',
name='secret',
field=models.CharField(max_length=32, default=pretix.base.models.generate_secret),
),
migrations.AlterField(
model_name='user',
name='email',
field=models.EmailField(max_length=254, verbose_name='E-mail', blank=True, db_index=True, unique=True, null=True),
),
migrations.AlterUniqueTogether(
name='user',
unique_together=set([]),
),
migrations.RemoveField(
model_name='user',
name='event',
),
migrations.RemoveField(
model_name='user',
name='identifier',
),
migrations.RemoveField(
model_name='user',
name='username',
),
]

View File

@@ -1,5 +1,6 @@
import copy
import random
import string
import uuid
from datetime import datetime
from itertools import product
@@ -88,34 +89,17 @@ class UserManager(BaseUserManager):
model documentation to see what's so special about our user model.
"""
def create_user(self, identifier, username, password=None):
user = self.model(identifier=identifier)
def create_user(self, email, password=None, **kwargs):
user = self.model(email=email, **kwargs)
user.set_password(password)
user.save()
return user
def create_global_user(self, email, password=None, **kwargs):
user = self.model(**kwargs)
user.identifier = email
user.email = email
user.set_password(password)
user.save()
return user
def create_local_user(self, event, username, password=None, **kwargs):
user = self.model(**kwargs)
user.identifier = '%s@%s.event.pretix' % (username, event.identity)
user.username = username
user.event = event
user.set_password(password)
user.save()
return user
def create_superuser(self, identifier, password=None): # NOQA
def create_superuser(self, email, password=None): # NOQA
# Not used in the software but required by Django
if password is None:
raise Exception("You must provide a password")
user = self.model(identifier=identifier, email=identifier)
user = self.model(email=email)
user.is_staff = True
user.is_superuser = True
user.set_password(password)
@@ -126,44 +110,8 @@ class UserManager(BaseUserManager):
class User(AbstractBaseUser, PermissionsMixin):
"""
This is the user model used by pretix for authentication.
Handling users is somehow complicated, as we try to have two
classes of users in one system:
(1) We want *global* users who can just login into pretix and
buy tickets for multiple events -- we also need those
global users for event organizers who should not need
multiple users for managing multiple events.
(2) We want *local* users who exist only in the scope of a
certain event
The hard part is to find a primary key to identify all of these
users. Letting the users choose usernames is a bad idea, as
the primary key needs to be unique and there is no reason for a
local user to block a name for all time. Using e-mail addresses
is not a good idea either, for two reasons: First, a user might
have multiple local users (so they are not unique), and second,
it should be possible to create anonymous users without having
to supply an e-mail address.
Therefore, we use an abstract "identifier" field as the primary
key. The identifier is:
(1) the e-mail address for global users. An e-mail address
is and should be required for them and global users use
their e-mail address for login.
(2) "{username}@{event.identity}.event.pretix" for local users, who
use their username to login on the event page.
The model's save() method automatically fills the identifier field
according to this scheme when it is empty. The __str__() method
returns the identifier.
:param identifier: The identifier of the user, as described above
:type identifier: str
:param username: The username, null for global users.
:type username: str
:param event: The event the user belongs to, null for global users
:type event: Event
:param email: The user's e-mail address. May be empty or null for local users
:param email: The user's e-mail address, used for identification.
:type email: str
:param givenname: The user's given name. May be empty or null.
:type givenname: str
@@ -183,24 +131,14 @@ class User(AbstractBaseUser, PermissionsMixin):
:type timezone: str
"""
USERNAME_FIELD = 'identifier'
REQUIRED_FIELDS = ['username']
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
identifier = models.CharField(max_length=255, unique=True)
username = models.CharField(max_length=120, blank=True,
null=True,
help_text=_('Letters, digits and ./+/-/_ only.'))
event = models.ForeignKey('Event', related_name="users",
null=True, blank=True,
on_delete=models.PROTECT)
email = models.EmailField(unique=False, db_index=True,
null=True, blank=True,
email = models.EmailField(unique=True, db_index=True, null=True, blank=True,
verbose_name=_('E-mail'))
givenname = models.CharField(max_length=255, blank=True,
null=True,
givenname = models.CharField(max_length=255, blank=True, null=True,
verbose_name=_('Given name'))
familyname = models.CharField(max_length=255, blank=True,
null=True,
familyname = models.CharField(max_length=255, blank=True, null=True,
verbose_name=_('Family name'))
is_active = models.BooleanField(default=True,
verbose_name=_('Is active'))
@@ -221,32 +159,20 @@ class User(AbstractBaseUser, PermissionsMixin):
class Meta:
verbose_name = _("User")
verbose_name_plural = _("Users")
unique_together = (("event", "username"),)
def __str__(self):
return self.identifier
def save(self, *args, **kwargs):
"""
Before passing the call to the default ``save()`` method, this will fill the ``identifier``
field if it is empty, according to the scheme descriped in the model docstring.
"""
if not self.identifier:
if self.event is None:
self.identifier = self.email.lower()
else:
self.identifier = "%s@%s.event.pretix" % (self.username.lower(), self.event.id)
if not self.pk:
self.identifier = self.identifier.lower()
self.email = self.email.lower()
super().save(*args, **kwargs)
def __str__(self):
return self.email
def get_short_name(self) -> str:
"""
Returns the first of the following user properties that is found to exist:
* Given name
* Family name
* User name
* E-mail address
"""
if self.givenname:
@@ -254,7 +180,7 @@ class User(AbstractBaseUser, PermissionsMixin):
elif self.familyname:
return self.familyname
else:
return self.get_local_name()
return self.email
def get_full_name(self) -> str:
"""
@@ -264,7 +190,6 @@ class User(AbstractBaseUser, PermissionsMixin):
* Given name
* Family name
* User name
* E-mail address
"""
if self.givenname and not self.familyname:
return self.givenname
@@ -276,18 +201,7 @@ class User(AbstractBaseUser, PermissionsMixin):
'given': self.givenname
}
else:
return self.get_local_name()
def get_local_name(self) -> str:
"""
Returns the username for local users and the e-mail address for global
users.
"""
if self.username:
return self.username
if self.email:
return self.email
return self.identifier # NOQA
def cachedfile_name(instance, filename):
@@ -1450,6 +1364,10 @@ class Quota(Versionable):
pass
def generate_secret():
return ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(32))
class Order(Versionable):
"""
An order is created when a user clicks 'buy' on his cart. It holds
@@ -1524,6 +1442,7 @@ class Order(Versionable):
verbose_name=_("User"),
related_name="orders"
)
secret = models.CharField(max_length=32, default=generate_secret)
datetime = models.DateTimeField(
verbose_name=_("Date")
)
@@ -1824,6 +1743,10 @@ class CartPosition(ObjectWithAnswers, Versionable):
User, null=True, blank=True,
verbose_name=_("User")
)
session = models.CharField(
max_length=255, null=True, blank=True,
verbose_name=_("Session")
)
item = VersionedForeignKey(
Item,
verbose_name=_("Item")

View File

@@ -1,85 +0,0 @@
from django import forms
from django.contrib.auth import authenticate
from django.contrib.auth.forms import \
AuthenticationForm as BaseAuthenticationForm
from django.utils.translation import ugettext as _
from pretix.base.models import User
class AuthenticationForm(BaseAuthenticationForm):
"""
The login form, providing an email and password field. The form already implements
validation for correct user data.
"""
email = forms.EmailField(label=_("Email address"), max_length=254)
password = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
username = None
error_messages = {
'invalid_login': _("Please enter a correct e-mail address and password."),
'inactive': _("This account is inactive.")
}
def __init__(self, request=None, *args, **kwargs):
self.request = request
self.user_cache = None
super(forms.Form, self).__init__(*args, **kwargs)
def clean(self):
email = self.cleaned_data.get('email')
password = self.cleaned_data.get('password')
if email and password:
self.user_cache = authenticate(identifier=email.lower(),
password=password)
if self.user_cache is None:
raise forms.ValidationError(
self.error_messages['invalid_login'],
code='invalid_login'
)
else:
self.confirm_login_allowed(self.user_cache)
return self.cleaned_data
class GlobalRegistrationForm(forms.Form):
error_messages = {
'duplicate_email': _("You already registered with that e-mail address, please use the login form."),
'pw_mismatch': _("Please enter the same password twice")
}
email = forms.EmailField(
label=_('Email address'),
required=True
)
password = forms.CharField(
label=_('Password'),
widget=forms.PasswordInput,
required=True
)
password_repeat = forms.CharField(
label=_('Repeat password'),
widget=forms.PasswordInput
)
def clean(self):
password1 = self.cleaned_data.get('password')
password2 = self.cleaned_data.get('password_repeat')
if password1 and password1 != password2:
raise forms.ValidationError(
self.error_messages['pw_mismatch'],
code='pw_mismatch',
)
return self.cleaned_data
def clean_email(self):
email = self.cleaned_data['email']
if User.objects.filter(identifier=email).exists():
raise forms.ValidationError(
self.error_messages['duplicate_email'],
code='duplicate_email',
)
return email

View File

@@ -4,10 +4,8 @@ from django.contrib.auth import (
)
from django.shortcuts import redirect, render
from pretix.base.forms.auth import LoginForm, RegistrationForm
from pretix.base.models import User
from pretix.control.forms.auth import (
AuthenticationForm, GlobalRegistrationForm,
)
def login(request):
@@ -21,14 +19,14 @@ def login(request):
return redirect(request.GET.get("next", 'control:index'))
return redirect('control:index')
if request.method == 'POST':
form = AuthenticationForm(data=request.POST)
form = LoginForm(data=request.POST)
if form.is_valid() and form.user_cache:
auth_login(request, form.user_cache)
if "next" in request.GET:
return redirect(request.GET.get("next", 'control:index'))
return redirect('control:index')
else:
form = AuthenticationForm()
form = LoginForm()
ctx['form'] = form
return render(request, 'pretixcontrol/auth/login.html', ctx)
@@ -51,17 +49,17 @@ def register(request):
return redirect(request.GET.get("next", 'control:index'))
return redirect('control:index')
if request.method == 'POST':
form = GlobalRegistrationForm(data=request.POST)
form = RegistrationForm(data=request.POST)
if form.is_valid():
user = User.objects.create_global_user(
user = User.objects.create_user(
form.cleaned_data['email'], form.cleaned_data['password'],
locale=request.LANGUAGE_CODE,
timezone=request.timezone if hasattr(request, 'timezone') else settings.TIME_ZONE
)
user = authenticate(identifier=user.identifier, password=form.cleaned_data['password'])
user = authenticate(email=user.email, password=form.cleaned_data['password'])
auth_login(request, user)
return redirect('control:index')
else:
form = GlobalRegistrationForm()
form = RegistrationForm()
ctx['form'] = form
return render(request, 'pretixcontrol/auth/register.html', ctx)

View File

@@ -314,7 +314,7 @@ class EventPermissions(EventPermissionRequiredMixin, TemplateView):
if self.formset.is_valid() and self.add_form.is_valid():
if self.add_form.has_changed():
try:
self.add_form.instance.user = User.objects.get(identifier=self.add_form.cleaned_data['user'])
self.add_form.instance.user = User.objects.get(email=self.add_form.cleaned_data['user'])
self.add_form.instance.user_id = self.add_form.instance.user.id
self.add_form.instance.event = self.request.event
self.add_form.instance.event_id = self.request.event.identity

View File

@@ -39,8 +39,7 @@ class OrderList(EventPermissionRequiredMixin, ListView):
if self.request.GET.get("user", "") != "":
u = self.request.GET.get("user", "")
qs = qs.filter(
Q(user__identifier__icontains=u) | Q(user__email__icontains=u)
| Q(user__givenname__icontains=u) | Q(user__familyname__icontains=u)
Q(user__email__icontains=u) | Q(user__givenname__icontains=u) | Q(user__familyname__icontains=u)
)
if self.request.GET.get("status", "") != "":
s = self.request.GET.get("status", "")

View File

@@ -1,227 +0,0 @@
from django import forms
from django.conf import settings
from django.contrib.auth import authenticate
from django.contrib.auth.forms import \
AuthenticationForm as BaseAuthenticationForm
from django.core.validators import RegexValidator
from django.forms import Form
from django.utils.translation import ugettext_lazy as _
from pretix.base.models import User
class LoginForm(BaseAuthenticationForm):
username = forms.CharField(
label=_('Username'),
help_text=(
_('If you registered for multiple events, your username is your email address.')
if settings.PRETIX_GLOBAL_REGISTRATION
else None
)
)
password = forms.CharField(
label=_('Password'),
widget=forms.PasswordInput
)
error_messages = {
'invalid_login': _("Please enter a correct username and password."),
'inactive': _("This account is inactive."),
}
def __init__(self, request=None, *args, **kwargs):
self.request = request
self.user_cache = None
super(forms.Form, self).__init__(*args, **kwargs)
def clean(self):
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
if username and password:
if '@' in username:
identifier = username.lower()
else:
identifier = "%s@%s.event.pretix" % (username, self.request.event.identity)
self.user_cache = authenticate(identifier=identifier,
password=password)
if self.user_cache is None:
raise forms.ValidationError(
self.error_messages['invalid_login'],
code='invalid_login',
)
else:
self.confirm_login_allowed(self.user_cache)
return self.cleaned_data
class GlobalRegistrationForm(forms.Form):
error_messages = {
'duplicate_email': _("You already registered with that e-mail address, please use the login form."),
'pw_mismatch': _("Please enter the same password twice"),
}
email = forms.EmailField(
label=_('Email address'),
required=True
)
password = forms.CharField(
label=_('Password'),
widget=forms.PasswordInput,
required=True
)
password_repeat = forms.CharField(
label=_('Repeat password'),
widget=forms.PasswordInput
)
def clean(self):
password1 = self.cleaned_data.get('password')
password2 = self.cleaned_data.get('password_repeat')
if password1 and password1 != password2:
raise forms.ValidationError(
self.error_messages['pw_mismatch'],
code='pw_mismatch',
)
return self.cleaned_data
def clean_email(self):
email = self.cleaned_data['email']
if User.objects.filter(identifier=email).exists():
raise forms.ValidationError(
self.error_messages['duplicate_email'],
code='duplicate_email',
)
return email
class LocalRegistrationForm(forms.Form):
error_messages = {
'invalid_username': _("Please only use characters, numbers or ./+/-/_ in your username."),
'duplicate_username': _("This username is already taken. Please choose a different one."),
'pw_mismatch': _("Please enter the same password twice"),
}
username = forms.CharField(
label=_('Username'),
validators=[
RegexValidator(
regex='^[a-zA-Z0-9\.+\-_]*$',
code='invalid_username',
message=error_messages['invalid_username']
),
],
required=True
)
email = forms.EmailField(
label=_('E-mail address'),
required=False
)
password = forms.CharField(
label=_('Password'),
widget=forms.PasswordInput,
required=True
)
password_repeat = forms.CharField(
label=_('Repeat password'),
widget=forms.PasswordInput
)
def __init__(self, request, *args, **kwargs):
super().__init__(*args, **kwargs)
self.request = request
self.fields['email'].required = request.event.settings.user_mail_required
def clean(self):
password1 = self.cleaned_data.get('password')
password2 = self.cleaned_data.get('password_repeat')
if password1 and password1 != password2:
raise forms.ValidationError(
self.error_messages['pw_mismatch'],
code='pw_mismatch',
)
return self.cleaned_data
def clean_username(self):
username = self.cleaned_data['username']
if User.objects.filter(event=self.request.event, username=username).exists():
raise forms.ValidationError(
self.error_messages['duplicate_username'],
code='duplicate_username',
)
return username
class PasswordRecoverForm(Form):
error_messages = {
'pw_mismatch': _("Please enter the same password twice"),
}
password = forms.CharField(
label=_('Password'),
widget=forms.PasswordInput,
required=True
)
password_repeat = forms.CharField(
label=_('Repeat password'),
widget=forms.PasswordInput
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def clean(self):
password1 = self.cleaned_data.get('password')
password2 = self.cleaned_data.get('password_repeat')
if password1 and password1 != password2:
raise forms.ValidationError(
self.error_messages['pw_mismatch'],
code='pw_mismatch',
)
return self.cleaned_data
class PasswordForgotForm(Form):
username = forms.CharField(
label=_('Username or E-mail'),
)
def __init__(self, event, *args, **kwargs):
self.event = event
super().__init__(*args, **kwargs)
def clean_username(self):
username = self.cleaned_data['username']
try:
self.cleaned_data['user'] = User.objects.get(
identifier=username, event__isnull=True
)
return username
except User.DoesNotExist:
pass
try:
self.cleaned_data['user'] = User.objects.get(
username=username, event=self.event
)
return username
except User.DoesNotExist:
pass
try:
self.cleaned_data['user'] = User.objects.get(
email=username, event=self.event
)
return username
except User.MultipleObjectsReturned:
raise forms.ValidationError(
_("We found multiple users with that e-mail address. Please specify the username instead"),
code='unknown_user',
)
except User.DoesNotExist:
raise forms.ValidationError(
_("We are unable to find a user matching the data you provided."),
code='unknown_user',
)

View File

@@ -7,7 +7,7 @@
<form class="form-horizontal" method="post">
{% csrf_token %}
{% bootstrap_form_errors form type='all' layout='inline' %}
{% bootstrap_field form.username layout="horizontal" %}
{% bootstrap_field form.email layout="horizontal" %}
<input type="hidden" name="form" value="login" />
<div class="form-group">
<div class="submit-group col-md-offset-2 col-md-4 text-right">

View File

@@ -19,7 +19,7 @@
<form class="form-horizontal" method="post">
{% csrf_token %}
{% bootstrap_form_errors login_form type='all' layout='inline' %}
{% bootstrap_field login_form.username layout="horizontal" %}
{% bootstrap_field login_form.email layout="horizontal" %}
{% bootstrap_field login_form.password layout="horizontal" %}
<input type="hidden" name="form" value="login" />
<div class="form-group">
@@ -39,67 +39,47 @@
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingOne">
<h4 class="panel-title">
<a data-toggle="collapse" href="#localRegistrationForm" data-parent="#login_accordion">
{% if global_registration_form %}
{% trans "I want to create a new account just for this event" %}
{% else %}
{% trans "I want to create a new account" %}
{% endif %}
<a data-toggle="collapse" href="#guestForm" data-parent="#login_accordion">
{% trans "I want to order as a guest" %}
</a>
</h4>
</div>
<div id="localRegistrationForm" class="panel-collapse collapsed {% if request.POST.form == 'local_registration' %}in{% endif %}">
<div id="guestForm" class="panel-collapse collapsed {% if request.POST.form == 'guest' %}in{% endif %}">
<div class="panel-body">
<div class="panel-body">
<form class="form-horizontal" method="post">
{% csrf_token %}
{% bootstrap_form_errors local_registration_form type='all' layout='inline' %}
{% bootstrap_field local_registration_form.username layout="horizontal" %}
{% bootstrap_field local_registration_form.email layout="horizontal" %}
{% bootstrap_field local_registration_form.password layout="horizontal" %}
{% bootstrap_field local_registration_form.password_repeat layout="horizontal" %}
<input type="hidden" name="form" value="local_registration" />
<div class="form-group">
<div class="submit-group col-md-offset-2 col-md-4 text-right">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Register" %}
</button>
</div>
</div>
</form>
Coming soon.
</div>
</div>
</div>
</div>
{% if global_registration_form %}
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingOne">
<h4 class="panel-title">
<a data-toggle="collapse" href="#globalRegistrationForm" data-parent="#login_accordion">
{% trans "I want to create a permanent account" %}
</a>
</h4>
</div>
<div id="globalRegistrationForm" class="panel-collapse collapsed {% if request.POST.form == 'global_registration' %}in{% endif %}">
<div class="panel-body">
<form class="form-horizontal" method="post">
{% csrf_token %}
{% bootstrap_form_errors global_registration_form type='all' layout='inline' %}
{% bootstrap_field global_registration_form.email layout="horizontal" %}
{% bootstrap_field global_registration_form.password layout="horizontal" %}
{% bootstrap_field global_registration_form.password_repeat layout="horizontal" %}
<input type="hidden" name="form" value="global_registration" />
<div class="form-group">
<div class="submit-group col-md-offset-2 col-md-4 text-right">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Register" %}
</button>
</div>
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingOne">
<h4 class="panel-title">
<a data-toggle="collapse" href="#registrationForm" data-parent="#login_accordion">
{% trans "I want to create a permanent account" %}
</a>
</h4>
</div>
<div id="registrationForm" class="panel-collapse collapsed
{% if request.POST.form == 'registration' %}in{% endif %}">
<div class="panel-body">
<form class="form-horizontal" method="post">
{% csrf_token %}
{% bootstrap_form_errors registration_form type='all' layout='inline' %}
{% bootstrap_field registration_form.email layout="horizontal" %}
{% bootstrap_field registration_form.password layout="horizontal" %}
{% bootstrap_field registration_form.password_repeat layout="horizontal" %}
<input type="hidden" name="form" value="registration" />
<div class="form-group">
<div class="submit-group col-md-offset-2 col-md-4 text-right">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Register" %}
</button>
</div>
</form>
</div>
</div>
</form>
</div>
</div>
{% endif %}
</div>
</div>
{% endblock %}

View File

@@ -1,6 +1,7 @@
from datetime import timedelta
from itertools import groupby
from django.contrib.auth.decorators import login_required
from django.contrib.auth.views import redirect_to_login
from django.core.urlresolvers import reverse
from django.db.models import Q
@@ -11,26 +12,12 @@ from pretix.base.models import CartPosition
from pretix.base.signals import register_payment_providers
class EventLoginRequiredMixin:
class LoginRequiredMixin:
@classmethod
def as_view(cls, **initkwargs):
view = super(EventLoginRequiredMixin, cls).as_view(**initkwargs)
def decorator(view_func):
def _wrapped_view(request, *args, **kwargs):
if request.user.is_authenticated() and \
(request.user.event is None or request.user.event == request.event):
return view_func(request, *args, **kwargs)
path = request.path
return redirect_to_login(
path, reverse('presale:event.checkout.login', kwargs={
'organizer': request.event.organizer.slug,
'event': request.event.slug,
}), 'next'
)
return _wrapped_view
return decorator(view)
view = super().as_view(**initkwargs)
return login_required(view)
class CartDisplayMixin:

View File

@@ -13,7 +13,7 @@ from django.views.generic import View
from pretix.base.models import (
CartPosition, EventLock, Item, ItemVariation, Quota,
)
from pretix.presale.views import EventLoginRequiredMixin, EventViewMixin
from pretix.presale.views import EventViewMixin, LoginRequiredMixin
class CartActionMixin:
@@ -62,7 +62,7 @@ class CartActionMixin:
return items
class CartRemove(EventViewMixin, CartActionMixin, EventLoginRequiredMixin, View):
class CartRemove(EventViewMixin, CartActionMixin, LoginRequiredMixin, View):
def post(self, *args, **kwargs):
items = self._items_from_post_data()
@@ -110,10 +110,9 @@ class CartAdd(EventViewMixin, CartActionMixin, View):
self.items = self._items_from_post_data()
# We do not use EventLoginRequiredMixin here, as we want to store stuff into the
# We do not use LoginRequiredMixin here, as we want to store stuff into the
# session before redirecting to login
if not request.user.is_authenticated() or \
(request.user.event is not None and request.user.event != request.event):
if not request.user.is_authenticated():
request.session['cart_tmp'] = json.dumps(self.items)
return redirect_to_login(
self.get_success_url(), reverse('presale:event.checkout.login', kwargs={

View File

@@ -12,7 +12,7 @@ from pretix.base.services.orders import OrderError, perform_order
from pretix.base.signals import register_payment_providers
from pretix.presale.forms.checkout import QuestionsForm
from pretix.presale.views import (
CartDisplayMixin, EventLoginRequiredMixin, EventViewMixin,
CartDisplayMixin, EventViewMixin, LoginRequiredMixin,
)
@@ -106,7 +106,7 @@ class QuestionsViewMixin:
return not failed
class CheckoutStart(EventViewMixin, CartDisplayMixin, EventLoginRequiredMixin,
class CheckoutStart(EventViewMixin, CartDisplayMixin, LoginRequiredMixin,
QuestionsViewMixin, CheckoutView):
template_name = "pretixpresale/event/checkout_questions.html"
@@ -138,7 +138,7 @@ class CheckoutStart(EventViewMixin, CartDisplayMixin, EventLoginRequiredMixin,
return ctx
class PaymentDetails(EventViewMixin, CartDisplayMixin, EventLoginRequiredMixin, CheckoutView):
class PaymentDetails(EventViewMixin, CartDisplayMixin, LoginRequiredMixin, CheckoutView):
template_name = "pretixpresale/event/checkout_payment.html"
@cached_property
@@ -194,7 +194,7 @@ class PaymentDetails(EventViewMixin, CartDisplayMixin, EventLoginRequiredMixin,
return self.get_questions_url() + "?back=true"
class OrderConfirm(EventViewMixin, CartDisplayMixin, EventLoginRequiredMixin, CheckoutView):
class OrderConfirm(EventViewMixin, CartDisplayMixin, LoginRequiredMixin, CheckoutView):
template_name = "pretixpresale/event/checkout_confirm.html"
def __init__(self, *args, **kwargs):

View File

@@ -14,16 +14,15 @@ from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
from django.views.generic import TemplateView, UpdateView, View
from pretix.base.forms.auth import (
LoginForm, PasswordForgotForm, PasswordRecoverForm, RegistrationForm,
)
from pretix.base.forms.user import UserSettingsForm
from pretix.base.models import User
from pretix.base.services.mail import mail
from pretix.helpers.urls import build_absolute_uri
from pretix.presale.forms.auth import (
GlobalRegistrationForm, LocalRegistrationForm, LoginForm,
PasswordForgotForm, PasswordRecoverForm,
)
from pretix.presale.views import (
CartDisplayMixin, EventLoginRequiredMixin, EventViewMixin,
CartDisplayMixin, EventViewMixin, LoginRequiredMixin,
)
from pretix.presale.views.cart import CartAdd
@@ -102,8 +101,7 @@ class EventLogin(EventViewMixin, TemplateView):
event=self.request.event.slug)
def get(self, request, *args, **kwargs):
if request.user.is_authenticated() and \
(request.user.event is None or request.user.event == request.event):
if request.user.is_authenticated():
return self.redirect_to_next()
return super().get(request, *args, **kwargs)
@@ -113,27 +111,15 @@ class EventLogin(EventViewMixin, TemplateView):
if form.is_valid() and form.user_cache:
login(request, form.user_cache)
return self.redirect_to_next()
elif request.POST.get('form') == 'local_registration':
form = self.local_registration_form
elif request.POST.get('form') == 'registration':
form = self.registration_form
if form.is_valid():
user = User.objects.create_local_user(
request.event, form.cleaned_data['username'], form.cleaned_data['password'],
email=form.cleaned_data['email'] if form.cleaned_data['email'] != '' else None,
locale=request.LANGUAGE_CODE,
timezone=request.timezone if hasattr(request, 'timezone') else settings.TIME_ZONE
)
user = authenticate(identifier=user.identifier, password=form.cleaned_data['password'])
login(request, user)
return self.redirect_to_next()
elif request.POST.get('form') == 'global_registration' and settings.PRETIX_GLOBAL_REGISTRATION:
form = self.global_registration_form
if form.is_valid():
user = User.objects.create_global_user(
user = User.objects.create_user(
form.cleaned_data['email'], form.cleaned_data['password'],
locale=request.LANGUAGE_CODE,
timezone=request.timezone if hasattr(request, 'timezone') else settings.TIME_ZONE
)
user = authenticate(identifier=user.identifier, password=form.cleaned_data['password'])
user = authenticate(email=user.email, password=form.cleaned_data['password'])
login(request, user)
return self.redirect_to_next()
return super().get(request, *args, **kwargs)
@@ -146,26 +132,15 @@ class EventLogin(EventViewMixin, TemplateView):
)
@cached_property
def global_registration_form(self):
if settings.PRETIX_GLOBAL_REGISTRATION:
return GlobalRegistrationForm(
data=self.request.POST if self.request.POST.get('form', '') == 'global_registration' else None
)
else:
return None
@cached_property
def local_registration_form(self):
return LocalRegistrationForm(
self.request,
data=self.request.POST if self.request.POST.get('form', '') == 'local_registration' else None
def registration_form(self):
return RegistrationForm(
data=self.request.POST if self.request.POST.get('form', '') == 'registration' else None
)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['login_form'] = self.login_form
context['global_registration_form'] = self.global_registration_form
context['local_registration_form'] = self.local_registration_form
context['registration_form'] = self.registration_form
return context
@@ -173,8 +148,7 @@ class EventForgot(EventViewMixin, TemplateView):
template_name = 'pretixpresale/event/forgot.html'
def get(self, request, *args, **kwargs):
if request.user.is_authenticated() and \
(request.user.event is None or request.user.event == request.event):
if request.user.is_authenticated():
return redirect('presale:event.orders',
organizer=self.request.event.organizer.slug,
event=self.request.event.slug)
@@ -238,8 +212,7 @@ class EventRecover(EventViewMixin, TemplateView):
}
def get(self, request, *args, **kwargs):
if request.user.is_authenticated() and \
(request.user.event is None or request.user.event == request.event):
if request.user.is_authenticated():
return redirect('presale:event.orders',
organizer=self.request.event.organizer.slug,
event=self.request.event.slug)
@@ -306,7 +279,7 @@ class EventLogout(EventViewMixin, View):
event=self.request.event.slug)
class EventAccount(EventLoginRequiredMixin, EventViewMixin, TemplateView):
class EventAccount(LoginRequiredMixin, EventViewMixin, TemplateView):
template_name = 'pretixpresale/event/account.html'
def get_context_data(self, **kwargs):
@@ -315,7 +288,7 @@ class EventAccount(EventLoginRequiredMixin, EventViewMixin, TemplateView):
return context
class EventOrders(EventLoginRequiredMixin, EventViewMixin, TemplateView):
class EventOrders(LoginRequiredMixin, EventViewMixin, TemplateView):
template_name = 'pretixpresale/event/orders.html'
def get_context_data(self, **kwargs):
@@ -324,7 +297,7 @@ class EventOrders(EventLoginRequiredMixin, EventViewMixin, TemplateView):
return context
class EventAccountSettings(EventLoginRequiredMixin, EventViewMixin, UpdateView):
class EventAccountSettings(LoginRequiredMixin, EventViewMixin, UpdateView):
model = User
form_class = UserSettingsForm
template_name = 'pretixpresale/event/account_settings.html'

View File

@@ -15,7 +15,7 @@ from pretix.base.signals import (
register_payment_providers, register_ticket_outputs,
)
from pretix.presale.views import (
CartDisplayMixin, EventLoginRequiredMixin, EventViewMixin,
CartDisplayMixin, EventViewMixin, LoginRequiredMixin,
)
from pretix.presale.views.checkout import QuestionsViewMixin
@@ -49,7 +49,7 @@ class OrderDetailMixin:
})
class OrderDetails(EventViewMixin, EventLoginRequiredMixin, OrderDetailMixin,
class OrderDetails(EventViewMixin, LoginRequiredMixin, OrderDetailMixin,
CartDisplayMixin, TemplateView):
template_name = "pretixpresale/event/order.html"
@@ -102,7 +102,7 @@ class OrderDetails(EventViewMixin, EventLoginRequiredMixin, OrderDetailMixin,
return ctx
class OrderPay(EventViewMixin, EventLoginRequiredMixin, OrderDetailMixin, TemplateView):
class OrderPay(EventViewMixin, LoginRequiredMixin, OrderDetailMixin, TemplateView):
template_name = "pretixpresale/event/order_pay.html"
def dispatch(self, request, *args, **kwargs):
@@ -145,7 +145,7 @@ class OrderPay(EventViewMixin, EventLoginRequiredMixin, OrderDetailMixin, Templa
})
class OrderPayDo(EventViewMixin, EventLoginRequiredMixin, OrderDetailMixin, TemplateView):
class OrderPayDo(EventViewMixin, LoginRequiredMixin, OrderDetailMixin, TemplateView):
template_name = "pretixpresale/event/order_pay_confirm.html"
def dispatch(self, request, *args, **kwargs):
@@ -185,7 +185,7 @@ class OrderPayDo(EventViewMixin, EventLoginRequiredMixin, OrderDetailMixin, Temp
})
class OrderModify(EventViewMixin, EventLoginRequiredMixin, OrderDetailMixin,
class OrderModify(EventViewMixin, LoginRequiredMixin, OrderDetailMixin,
QuestionsViewMixin, TemplateView):
template_name = "pretixpresale/event/order_modify.html"
@@ -227,7 +227,7 @@ class OrderModify(EventViewMixin, EventLoginRequiredMixin, OrderDetailMixin,
return ctx
class OrderCancel(EventViewMixin, EventLoginRequiredMixin, OrderDetailMixin,
class OrderCancel(EventViewMixin, LoginRequiredMixin, OrderDetailMixin,
TemplateView):
template_name = "pretixpresale/event/order_cancel.html"
@@ -255,7 +255,7 @@ class OrderCancel(EventViewMixin, EventLoginRequiredMixin, OrderDetailMixin,
return ctx
class OrderDownload(EventViewMixin, EventLoginRequiredMixin, OrderDetailMixin,
class OrderDownload(EventViewMixin, LoginRequiredMixin, OrderDetailMixin,
View):
@cached_property

View File

@@ -57,7 +57,6 @@ STATIC_URL = config.get('urls', 'static', fallback='/static/')
MEDIA_URL = config.get('urls', 'media', fallback='/media/')
PRETIX_INSTANCE_NAME = config.get('pretix', 'instance_name', fallback='pretix.de')
PRETIX_GLOBAL_REGISTRATION = config.getboolean('pretix', 'global_registration', fallback=True)
SITE_URL = config.get('pretix', 'url', fallback='http://localhost')

View File

@@ -15,7 +15,7 @@ def env():
organizer=o, name='Dummy', slug='dummy',
date_from=now(), plugins='pretix.plugins.banktransfer'
)
user = User.objects.create_user('dummy@dummy.dummy', 'dummy@dummy.dummy', 'dummy')
user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
user.email = 'dummy@dummy.dummy'
user.save()
return event, user, o

View File

@@ -19,7 +19,7 @@ class LocaleDeterminationTest(TestCase):
)
cls.TEST_LOCALE = 'de' if settings.LANGUAGE_CODE == 'en' else 'en'
cls.TEST_LOCALE_LONG = 'de-AT' if settings.LANGUAGE_CODE == 'en' else 'en-NZ'
cls.user = User.objects.create_user('dummy@dummy.dummy', 'dummy@dummy.dummy', 'dummy')
cls.user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
def test_global_default(self):
c = Client()

View File

@@ -160,31 +160,8 @@ class VersionableTestCase(TestCase):
class UserTestCase(TestCase):
def test_identifier_local(self):
o = Organizer.objects.create(name='Dummy', slug='dummy')
event = Event.objects.create(
organizer=o, name='Dummy', slug='dummy',
date_from=now(),
)
u = User(event=event, username='tester')
u.set_password("test")
u.save()
self.assertEqual(u.identifier, "%s@%s.event.pretix" % (u.username.lower(), event.id))
def test_identifier_global(self):
u = User(email='test@example.com')
u.set_password("test")
u.save()
self.assertEqual(u.identifier, "test@example.com")
def test_name(self):
o = Organizer.objects.create(name='Dummy', slug='dummy')
event = Event.objects.create(
organizer=o, name='Dummy', slug='dummy',
date_from=now(),
)
u = User.objects.create_local_user(event, 'test', 'test')
self.assertEqual(u.get_local_name(), 'test')
u = User.objects.create_user('test@foo.bar', 'test')
u.givenname = "Christopher"
u.familyname = "Nolan"
u.set_password("test")
@@ -202,8 +179,8 @@ class UserTestCase(TestCase):
self.assertEqual(u.get_short_name(), 'Christopher')
u.givenname = None
u.save()
self.assertEqual(u.get_full_name(), 'test')
self.assertEqual(u.get_short_name(), 'test')
self.assertEqual(u.get_full_name(), 'test@foo.bar')
self.assertEqual(u.get_short_name(), 'test@foo.bar')
class BaseQuotaTestCase(TestCase):
@@ -330,7 +307,7 @@ class OrderTestCase(BaseQuotaTestCase):
def setUp(self):
super().setUp()
self.user = User.objects.create_local_user(self.event, 'dummy', 'dummy')
self.user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
self.order = Order.objects.create(
status=Order.STATUS_PENDING, event=self.event,
user=self.user, datetime=now() - timedelta(days=5),

View File

@@ -152,7 +152,7 @@ class SettingsTestCase(TestCase):
self._test_serialization(self.event, Event)
def test_serialize_model(self):
self._test_serialization(User.objects.create_local_user(self.event, 'dummy', 'dummy'), User)
self._test_serialization(User.objects.create_user('dummy@dummy.dummy', 'dummy'), User)
def test_serialize_unknown(self):
class Type:

View File

@@ -8,7 +8,7 @@ class LoginFormBrowserTest(BrowserTest):
def setUp(self):
super().setUp()
self.user = User.objects.create_user('dummy@dummy.dummy', 'dummy@dummy.dummy', 'dummy')
self.user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
def test_login(self):
self.driver.implicitly_wait(10)
@@ -38,7 +38,7 @@ class LoginFormTest(TestCase):
"""
def setUp(self):
self.user = User.objects.create_user('dummy@dummy.dummy', 'dummy@dummy.dummy', 'dummy')
self.user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
def test_wrong_credentials(self):
c = Client()

View File

@@ -11,7 +11,7 @@ class EventsTest(BrowserTest):
def setUp(self):
super().setUp()
self.user = User.objects.create_user('dummy@dummy.dummy', 'dummy@dummy.dummy', 'dummy')
self.user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
self.orga1 = Organizer.objects.create(name='CCC', slug='ccc')
self.orga2 = Organizer.objects.create(name='MRM', slug='mrm')
self.event1 = Event.objects.create(

View File

@@ -16,7 +16,7 @@ from pretix.base.models import (
class ItemFormTest(BrowserTest):
def setUp(self):
super().setUp()
self.user = User.objects.create_user('dummy@dummy.dummy', 'dummy@dummy.dummy', 'dummy')
self.user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
self.orga1 = Organizer.objects.create(name='CCC', slug='ccc')
self.orga2 = Organizer.objects.create(name='MRM', slug='mrm')
self.event1 = Event.objects.create(

View File

@@ -17,7 +17,7 @@ def env():
organizer=o, name='Dummy', slug='dummy',
date_from=now(), plugins='pretix.plugins.banktransfer'
)
user = User.objects.create_user('dummy@dummy.dummy', 'dummy@dummy.dummy', 'dummy')
user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
EventPermission.objects.create(
event=event,
user=user,
@@ -46,7 +46,7 @@ def env():
@pytest.mark.django_db
def test_order_list(client, env):
client.login(identifier='dummy@dummy.dummy', password='dummy')
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.get('/control/event/dummy/dummy/orders/')
assert 'FOO' in response.rendered_content
response = client.get('/control/event/dummy/dummy/orders/?user=peter')
@@ -61,7 +61,7 @@ def test_order_list(client, env):
@pytest.mark.django_db
def test_order_detail(client, env):
client.login(identifier='dummy@dummy.dummy', password='dummy')
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.get('/control/event/dummy/dummy/orders/FOO/')
assert 'Early-bird' in response.rendered_content
assert 'Peter' in response.rendered_content
@@ -69,7 +69,7 @@ def test_order_detail(client, env):
@pytest.mark.django_db
def test_order_transition_cancel(client, env):
client.login(identifier='dummy@dummy.dummy', password='dummy')
client.login(email='dummy@dummy.dummy', password='dummy')
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
'status': 'c'
})
@@ -79,7 +79,7 @@ def test_order_transition_cancel(client, env):
@pytest.mark.django_db
def test_order_transition_to_paid_success(client, env):
client.login(identifier='dummy@dummy.dummy', password='dummy')
client.login(email='dummy@dummy.dummy', password='dummy')
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
'status': 'p'
})
@@ -92,7 +92,7 @@ def test_order_transition_to_unpaid_success(client, env):
o = Order.objects.current.get(identity=env[2].identity)
o.status = Order.STATUS_PAID
o.save()
client.login(identifier='dummy@dummy.dummy', password='dummy')
client.login(email='dummy@dummy.dummy', password='dummy')
client.post('/control/event/dummy/dummy/orders/FOO/transition', {
'status': 'n'
})

View File

@@ -15,7 +15,7 @@ def env():
organizer=o, name='Dummy', slug='dummy',
date_from=now(), plugins='pretix.plugins.banktransfer'
)
user = User.objects.create_user('dummy@dummy.dummy', 'dummy@dummy.dummy', 'dummy')
user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
Order.objects.create(
code='FOO', event=event,
user=user, status=Order.STATUS_PENDING,
@@ -86,7 +86,7 @@ def test_logged_out(client, env, url):
@pytest.mark.django_db
@pytest.mark.parametrize("url", event_urls)
def test_wrong_event(client, env, url):
client.login(identifier='dummy@dummy.dummy', password='dummy')
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.get('/control/event/dummy/dummy/' + url)
# These permission violations do not yield a 403 error, but
# a 404 error to prevent information leakage
@@ -136,7 +136,7 @@ def test_wrong_event_permission(client, env, perm, url, code):
)
setattr(ep, perm, False)
ep.save()
client.login(identifier='dummy@dummy.dummy', password='dummy')
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.get('/control/event/dummy/dummy/' + url)
assert response.status_code == 403
@@ -148,7 +148,7 @@ def test_current_permission(client, env):
)
setattr(ep, 'can_change_settings', True)
ep.save()
client.login(identifier='dummy@dummy.dummy', password='dummy')
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.get('/control/event/dummy/dummy/settings/')
assert response.status_code == 200
ep = ep.clone()
@@ -166,7 +166,7 @@ def test_correct_event_permission(client, env, perm, url, code):
)
setattr(ep, perm, True)
ep.save()
client.login(identifier='dummy@dummy.dummy', password='dummy')
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.get('/control/event/dummy/dummy/' + url)
assert response.status_code == code
@@ -174,7 +174,7 @@ def test_correct_event_permission(client, env, perm, url, code):
@pytest.mark.django_db
@pytest.mark.parametrize("url", organizer_urls)
def test_wrong_organizer(client, env, url):
client.login(identifier='dummy@dummy.dummy', password='dummy')
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.get('/control/' + url)
# These permission violations do not yield a 403 error, but
# a 404 error to prevent information leakage
@@ -195,7 +195,7 @@ def test_wrong_organizer_permission(client, env, perm, url, code):
)
setattr(op, perm, False)
op.save()
client.login(identifier='dummy@dummy.dummy', password='dummy')
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.get('/control/' + url)
assert response.status_code == 403
@@ -209,6 +209,6 @@ def test_correct_organizer_permission(client, env, perm, url, code):
if perm:
setattr(op, perm, True)
op.save()
client.login(identifier='dummy@dummy.dummy', password='dummy')
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.get('/control/' + url)
assert response.status_code == code

View File

@@ -6,7 +6,7 @@ from pretix.base.models import User
class UserSettingsTest(BrowserTest):
def setUp(self):
super().setUp()
self.user = User.objects.create_global_user('dummy@dummy.dummy', 'dummy')
self.user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
self.driver.implicitly_wait(10)
self.driver.get('%s%s' % (self.live_server_url, '/control/login'))
username_input = self.driver.find_element_by_name("email")
@@ -47,7 +47,7 @@ class UserSettingsTest(BrowserTest):
assert self.user.email == 'foo@example.com'
def test_change_email_no_duplicates(self):
User.objects.create_global_user('foo@example.com', 'foo')
User.objects.create_user('foo@example.com', 'foo')
self.driver.find_element_by_name("email").clear()
self.driver.find_element_by_name("email").send_keys("foo@example.com")
self.driver.find_element_by_name("old_pw").clear()

View File

@@ -17,7 +17,7 @@ def env():
organizer=o, name='Dummy', slug='dummy',
date_from=now(), plugins='pretix.plugins.banktransfer'
)
user = User.objects.create_user('dummy@dummy.dummy', 'dummy@dummy.dummy', 'dummy')
user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
EventPermission.objects.create(user=user, event=event)
o1 = Order.objects.create(
code='1234S', event=event,
@@ -40,7 +40,7 @@ def env():
@pytest.mark.django_db
def test_import_csv_file(client, env):
client.login(identifier='dummy@dummy.dummy', password='dummy')
client.login(email='dummy@dummy.dummy', password='dummy')
r = client.get('/control/event/dummy/dummy/banktransfer/import/')
assert r.status_code == 200

View File

@@ -17,7 +17,7 @@ def env():
organizer=o, name='Dummy', slug='dummy',
date_from=now(), plugins='pretix.plugins.banktransfer'
)
user = User.objects.create_user('dummy@dummy.dummy', 'dummy@dummy.dummy', 'dummy')
user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
EventPermission.objects.create(user=user, event=event)
shirt = Item.objects.create(event=event, name='T-Shirt', default_price=12)
prop1 = Property.objects.create(event=event, name="Color")
@@ -48,7 +48,7 @@ def env():
@pytest.mark.django_db
def test_pretixdroid(client, env):
client.login(identifier='dummy@dummy.dummy', password='dummy')
client.login(email='dummy@dummy.dummy', password='dummy')
client.get('/control/event/%s/%s/pretixdroid/' % (env[0].organizer.slug, env[0].slug))
key1 = env[0].settings.get('pretixdroid_key')
assert key1

View File

@@ -9,14 +9,14 @@ from pretix.base.models import User
class UserSettingsTest(EventTestMixin, BrowserTest):
def setUp(self):
super().setUp()
self.user = User.objects.create_global_user('dummy@dummy.dummy', 'dummy')
self.user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
self.driver.implicitly_wait(10)
self.driver.get('%s/%s/%s/login' % (self.live_server_url, self.orga.slug, self.event.slug))
# open the login accordion
self.scroll_and_click(self.driver.find_element_by_css_selector('a[href*=loginForm]'))
time.sleep(1)
# enter login details
self.driver.find_element_by_css_selector('#loginForm input[name=username]').send_keys('dummy@dummy.dummy')
self.driver.find_element_by_css_selector('#loginForm input[name=email]').send_keys('dummy@dummy.dummy')
self.driver.find_element_by_css_selector('#loginForm input[name=password]').send_keys('dummy')
self.scroll_and_click(self.driver.find_element_by_css_selector('#loginForm button.btn-primary'))
self.driver.find_element_by_partial_link_text('Your account')
@@ -51,19 +51,8 @@ class UserSettingsTest(EventTestMixin, BrowserTest):
self.user = User.objects.get(pk=self.user.pk)
assert self.user.email == 'foo@example.com'
def test_change_email_allow_local_duplicates(self):
User.objects.create_local_user(event=self.event, username='test', email='foo@example.com', password='foo')
self.driver.find_element_by_name("email").clear()
self.driver.find_element_by_name("email").send_keys("foo@example.com")
self.driver.find_element_by_name("old_pw").clear()
self.driver.find_element_by_name("old_pw").send_keys("dummy")
self.scroll_and_click(self.driver.find_element_by_class_name('btn-save'))
self.driver.find_element_by_class_name("alert-success")
self.user = User.objects.get(pk=self.user.pk)
assert self.user.email == 'foo@example.com'
def test_change_email_no_global_duplicates(self):
User.objects.create_global_user('foo@example.com', 'foo')
def test_change_email_no_duplicates(self):
User.objects.create_user('foo@example.com', 'foo')
self.driver.find_element_by_name("email").clear()
self.driver.find_element_by_name("email").send_keys("foo@example.com")
self.driver.find_element_by_name("old_pw").clear()

View File

@@ -22,7 +22,7 @@ class CartTestMixin:
organizer=self.orga, name='30C3', slug='30c3',
date_from=datetime.datetime(2013, 12, 26, tzinfo=datetime.timezone.utc)
)
self.user = User.objects.create_local_user(self.event, 'demo', 'demo')
self.user = User.objects.create_user('dummy@dummy.dummy', 'demo')
self.category = ItemCategory.objects.create(event=self.event, name="Everything", position=0)
self.quota_shirts = Quota.objects.create(event=self.event, name='Shirts', size=2)
self.shirt = Item.objects.create(event=self.event, name='T-Shirt', category=self.category, default_price=12)
@@ -51,7 +51,7 @@ class CartBrowserTest(CartTestMixin, BrowserTest):
self.driver.find_element_by_css_selector('input[type=number][name=item_%s]' % self.ticket.identity).send_keys('1')
self.scroll_and_click(self.driver.find_element_by_css_selector('.checkout-button-row button'))
# should redirect to login page
self.driver.find_element_by_name('username')
self.driver.find_element_by_name('email')
def test_simple_login(self):
self.driver.get('%s/%s/%s/' % (self.live_server_url, self.orga.slug, self.event.slug))
@@ -63,44 +63,26 @@ class CartBrowserTest(CartTestMixin, BrowserTest):
self.scroll_and_click(self.driver.find_element_by_css_selector('a[href*=loginForm]'))
time.sleep(1)
# enter login details
self.driver.find_element_by_css_selector('#loginForm input[name=username]').send_keys('demo')
self.driver.find_element_by_css_selector('#loginForm input[name=email]').send_keys('dummy@dummy.dummy')
self.driver.find_element_by_css_selector('#loginForm input[name=password]').send_keys('demo')
self.scroll_and_click(self.driver.find_element_by_css_selector('#loginForm button.btn-primary'))
# should display our ticket
self.assertIn('Early-bird', self.driver.find_element_by_css_selector('.cart-row:first-child').text)
def test_local_registration(self):
def test_registration(self):
self.driver.get('%s/%s/%s/' % (self.live_server_url, self.orga.slug, self.event.slug))
# add the entry ticket to cart
self.driver.find_element_by_css_selector('input[type=number][name=item_%s]' % self.ticket.identity).send_keys('1')
self.scroll_and_click(self.driver.find_element_by_css_selector('.checkout-button-row button'))
# should redirect to login page
# open the login accordion
self.scroll_and_click(self.driver.find_element_by_css_selector('a[href*=localRegistrationForm]'))
self.scroll_and_click(self.driver.find_element_by_css_selector('a[href*=registrationForm]'))
time.sleep(1)
# enter login details
self.driver.find_element_by_css_selector('#localRegistrationForm input[name=username]').send_keys('demo2')
self.driver.find_element_by_css_selector('#localRegistrationForm input[name=email]').send_keys('demo@demo.demo')
self.driver.find_element_by_css_selector('#localRegistrationForm input[name=password]').send_keys('demo')
self.driver.find_element_by_css_selector('#localRegistrationForm input[name=password_repeat]').send_keys('demo')
self.scroll_and_click(self.driver.find_element_by_css_selector('#localRegistrationForm button.btn-primary'))
# should display our ticket
self.assertIn('Early-bird', self.driver.find_element_by_css_selector('.cart-row:first-child').text)
def test_global_registration(self):
self.driver.get('%s/%s/%s/' % (self.live_server_url, self.orga.slug, self.event.slug))
# add the entry ticket to cart
self.driver.find_element_by_css_selector('input[type=number][name=item_%s]' % self.ticket.identity).send_keys('1')
self.scroll_and_click(self.driver.find_element_by_css_selector('.checkout-button-row button'))
# should redirect to login page
# open the login accordion
self.scroll_and_click(self.driver.find_element_by_css_selector('a[href*=globalRegistrationForm]'))
time.sleep(1)
# enter login details
self.driver.find_element_by_css_selector('#globalRegistrationForm input[name=email]').send_keys('demo@example.com')
self.driver.find_element_by_css_selector('#globalRegistrationForm input[name=password]').send_keys('demo')
self.driver.find_element_by_css_selector('#globalRegistrationForm input[name=password_repeat]').send_keys('demo')
self.scroll_and_click(self.driver.find_element_by_css_selector('#globalRegistrationForm button.btn-primary'))
self.driver.find_element_by_css_selector('#registrationForm input[name=email]').send_keys('demo@example.com')
self.driver.find_element_by_css_selector('#registrationForm input[name=password]').send_keys('demo')
self.driver.find_element_by_css_selector('#registrationForm input[name=password_repeat]').send_keys('demo')
self.scroll_and_click(self.driver.find_element_by_css_selector('#registrationForm button.btn-primary'))
# should display our ticket
self.assertIn('Early-bird', self.driver.find_element_by_css_selector('.cart-row:first-child').text)
@@ -109,7 +91,7 @@ class CartTest(CartTestMixin, TestCase):
def setUp(self):
super().setUp()
self.assertTrue(self.client.login(username='demo@%s.event.pretix' % self.event.identity, password='demo'))
self.assertTrue(self.client.login(email='dummy@dummy.dummy', password='demo'))
def test_simple(self):
response = self.client.post('/%s/%s/cart/add' % (self.orga.slug, self.event.slug), {

View File

@@ -21,13 +21,13 @@ class CheckoutTestCase(TestCase):
date_from=datetime.datetime(2013, 12, 26, tzinfo=datetime.timezone.utc),
plugins='pretix.plugins.stripe,pretix.plugins.banktransfer'
)
self.user = User.objects.create_local_user(self.event, 'demo', 'demo')
self.user = User.objects.create_user('dummy@dummy.dummy', 'demo')
self.category = ItemCategory.objects.create(event=self.event, name="Everything", position=0)
self.quota_tickets = Quota.objects.create(event=self.event, name='Tickets', size=5)
self.ticket = Item.objects.create(event=self.event, name='Early-bird ticket',
category=self.category, default_price=23, admission=True)
self.quota_tickets.items.add(self.ticket)
self.assertTrue(self.client.login(username='demo@%s.event.pretix' % self.event.identity, password='demo'))
self.assertTrue(self.client.login(email='dummy@dummy.dummy', password='demo'))
self.event.settings.set('attendee_names_asked', False)
self.event.settings.set('payment_banktransfer__enabled', True)

View File

@@ -144,52 +144,40 @@ class LoginTest(EventTestMixin, TestCase):
def setUp(self):
super().setUp()
self.local_user = User.objects.create_local_user(self.event, 'demo', 'foo')
self.global_user = User.objects.create_global_user('demo@demo.dummy', 'demo')
self.user = User.objects.create_user('demo@demo.dummy', 'demo')
def test_login_invalid(self):
response = self.client.post(
'/%s/%s/login' % (self.orga.slug, self.event.slug),
{
'form': 'login',
'username': 'demo',
'email': 'demo@demo.foo',
'password': 'bar'
}
)
self.assertEqual(response.status_code, 200)
self.assertIn('alert-danger', response.rendered_content)
def test_login_local(self):
def test_login_valid(self):
response = self.client.post(
'/%s/%s/login' % (self.orga.slug, self.event.slug),
{
'form': 'login',
'username': 'demo',
'password': 'foo'
}
)
self.assertEqual(response.status_code, 302)
def test_login_global(self):
response = self.client.post(
'/%s/%s/login' % (self.orga.slug, self.event.slug),
{
'form': 'login',
'username': 'demo@demo.dummy',
'email': 'demo@demo.dummy',
'password': 'demo'
}
)
self.assertEqual(response.status_code, 302)
def test_login_already_logged_in(self):
self.assertTrue(self.client.login(username='demo@%s.event.pretix' % self.event.identity, password='foo'))
self.assertTrue(self.client.login(email='demo@demo.dummy', password='demo'))
response = self.client.get(
'/%s/%s/login' % (self.orga.slug, self.event.slug),
)
self.assertEqual(response.status_code, 302)
def test_logout(self):
self.assertTrue(self.client.login(username='demo@%s.event.pretix' % self.event.identity, password='foo'))
self.assertTrue(self.client.login(email='demo@demo.dummy', password='demo'))
response = self.client.get(
'/%s/%s/logout' % (self.orga.slug, self.event.slug),
)

View File

@@ -23,9 +23,9 @@ class OrdersTest(TestCase):
)
self.event.settings.set('payment_banktransfer__enabled', True)
self.event.settings.set('ticketoutput_testdummy__enabled', True)
self.user = User.objects.create_local_user(self.event, 'demo', 'foo')
self.user2 = User.objects.create_local_user(self.event, 'bar', 'foo')
self.assertTrue(self.client.login(username='demo@%s.event.pretix' % self.event.identity, password='foo'))
self.user = User.objects.create_user('dummy@dummy.dummy', 'foo')
self.user2 = User.objects.create_user('bar@dummy.dummy', 'foo')
self.assertTrue(self.client.login(email='dummy@dummy.dummy', password='foo'))
self.category = ItemCategory.objects.create(event=self.event, name="Everything", position=0)
self.quota_shirts = Quota.objects.create(event=self.event, name='Shirts', size=2)