Compare commits

...

5 Commits

Author SHA1 Message Date
Raphael Michel
9c44df125c GitLab config 2017-11-25 19:15:00 +01:00
Raphael Michel
cbc5b86af7 Release 1.7.2 2017-11-25 19:05:29 +01:00
Raphael Michel
5e71fe09ea [SECURITY] Fix handling of session timeouts 2017-11-25 19:03:51 +01:00
Raphael Michel
dfa30e0a49 Bump version 2017-09-05 15:35:38 +02:00
Raphael Michel
5ee0c84a46 Re-do squashed migration 2017-09-05 15:35:19 +02:00
6 changed files with 273 additions and 489 deletions

View File

@@ -8,6 +8,8 @@ tests:
- XDG_CACHE_HOME=/cache bash .travis.sh tests
tags:
- python3
except:
- pypi
pypi:
stage: release
script:
@@ -22,7 +24,7 @@ pypi:
tags:
- python3
only:
- release
- pypi
artifacts:
paths:
- src/dist/

View File

@@ -1 +1 @@
__version__ = "1.7.0"
__version__ = "1.7.2"

File diff suppressed because one or more lines are too long

View File

@@ -1,484 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.4 on 2017-09-05 10:20
from __future__ import unicode_literals
from decimal import Decimal
import django.core.validators
import django.db.migrations.operations.special
import django.db.models.deletion
import django_countries.fields
import i18nfield.fields
from django.core.cache import cache
from django.db import migrations, models
from i18nfield.strings import LazyI18nString
import pretix.base.models.base
import pretix.base.models.vouchers
def tax_rate_converter(app, schema_editor):
EventSettingsStore = app.get_model('pretixbase', 'Event_SettingsStore')
Item = app.get_model('pretixbase', 'Item')
TaxRule = app.get_model('pretixbase', 'TaxRule')
Order = app.get_model('pretixbase', 'Order')
OrderPosition = app.get_model('pretixbase', 'OrderPosition')
InvoiceLine = app.get_model('pretixbase', 'InvoiceLine')
n = LazyI18nString({
'en': 'VAT',
'de': 'MwSt.',
'de-informal': 'MwSt.'
})
for i in Item.objects.select_related('event').exclude(tax_rate=0):
try:
i.tax_rule = i.event.tax_rules.get(rate=i.tax_rate)
except TaxRule.DoesNotExist:
tr = i.event.tax_rules.create(rate=i.tax_rate, name=n)
i.tax_rule = tr
i.save()
for o in Order.objects.select_related('event').exclude(payment_fee_tax_rate=0):
try:
o.payment_fee_tax_rule = o.event.tax_rules.get(rate=o.payment_fee_tax_rate)
except TaxRule.DoesNotExist:
tr = o.event.tax_rules.create(rate=o.payment_fee_tax_rate, name=n)
o.tax_rule = tr
o.save()
for op in OrderPosition.objects.select_related('order', 'order__event').exclude(tax_rate=0):
try:
op.tax_rule = op.order.event.tax_rules.get(rate=op.tax_rate)
except TaxRule.DoesNotExist:
tr = op.order.event.tax_rules.create(rate=op.tax_rate, name=n)
op.tax_rule = tr
op.save()
for il in InvoiceLine.objects.select_related('invoice', 'invoice__event').exclude(tax_rate=0):
try:
il.tax_name = il.invoice.event.tax_rules.get(rate=op.tax_rate).name
except TaxRule.DoesNotExist:
tr = il.invoice.event.tax_rules.create(rate=op.tax_rate, name=n)
il.tax_name = tr.name
il.save()
for setting in EventSettingsStore.objects.filter(key='tax_rate_default'):
try:
tr = setting.object.tax_rules.get(rate=setting.value)
except TaxRule.DoesNotExist:
tr = setting.object.tax_rules.create(rate=setting.value, name=n)
setting.value = tr.pk
setting.save()
cache.delete('hierarkey_{}_{}'.format('event', setting.object.pk))
def fee_converter(app, schema_editor):
OrderFee = app.get_model('pretixbase', 'OrderFee')
Order = app.get_model('pretixbase', 'Order')
of = []
for o in Order.objects.exclude(payment_fee=Decimal('0.00')).iterator():
of.append(OrderFee(
order=o,
value=o.payment_fee,
fee_type='payment',
tax_rate=o.payment_fee_tax_rate,
tax_rule=o.payment_fee_tax_rule,
tax_value=o.payment_fee_tax_value,
internal_type=o.payment_provider
))
if len(of) > 900:
OrderFee.objects.bulk_create(of)
of = []
OrderFee.objects.bulk_create(of)
def assign_positions(app, schema_editor):
Invoice = app.get_model('pretixbase', 'Invoice')
for i in Invoice.objects.iterator():
for j, l in enumerate(i.lines.all()):
l.position = j
l.save()
class Migration(migrations.Migration):
replaces = [('pretixbase', '0071_auto_20170729_1616'), ('pretixbase', '0072_order_download_reminder_sent'),
('pretixbase', '0073_auto_20170716_1333'), ('pretixbase', '0074_auto_20170825_1258'),
('pretixbase', '0075_auto_20170828_0901'), ('pretixbase', '0076_orderfee'),
('pretixbase', '0077_auto_20170829_1126')]
dependencies = [
('pretixbase', '0070_auto_20170719_0910'),
]
operations = [
migrations.AddField(
model_name='question',
name='help_text',
field=i18nfield.fields.I18nTextField(blank=True,
help_text='If the question needs to be explained or clarified, '
'do it here!',
null=True, verbose_name='Help text'),
),
migrations.AlterField(
model_name='invoiceaddress',
name='vat_id',
field=models.CharField(blank=True, help_text='Only for business customers within the EU.', max_length=255,
verbose_name='VAT ID'),
),
migrations.AddField(
model_name='order',
name='download_reminder_sent',
field=models.BooleanField(default=False),
),
migrations.CreateModel(
name='TaxRule',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', i18nfield.fields.I18nCharField(help_text='Should be short, e.g. "VAT"', max_length=190,
verbose_name='Name')),
('rate', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Tax rate')),
('price_includes_tax', models.BooleanField(default=True,
verbose_name='The configured product prices includes the '
'tax amount')),
('eu_reverse_charge', models.BooleanField(default=False,
help_text='Not recommended. Most events will NOT be '
'qualified for reverse charge since the place of '
'taxation is the location of the event. This '
'option only enables reverse charge for business '
'customers who entered a valid EU VAT ID. Only '
'enable this option after consulting a tax '
'counsel. No warranty given for correct tax '
'calculation.',
verbose_name='Use EU reverse charge taxation')),
('home_country', models.CharField(blank=True,
choices=[('AT', 'Austria'), ('BE', 'Belgium'), ('BG', 'Bulgaria'),
('HR', 'Croatia'), ('CY', 'Cyprus'),
('CZ', 'Czech Republic'), ('DK', 'Denmark'),
('EE', 'Estonia'), ('FI', 'Finland'), ('FR', 'France'),
('DE', 'Germany'), ('GR', 'Greece'), ('HU', 'Hungary'),
('IE', 'Ireland'), ('IT', 'Italy'), ('LV', 'Latvia'),
('LT', 'Lithuania'), ('LU', 'Luxembourg'), ('MT', 'Malta'),
('NL', 'Netherlands'), ('PL', 'Poland'), ('PT', 'Portugal'),
('RO', 'Romania'), ('SK', 'Slovakia'), ('SI', 'Slovenia'),
('ES', 'Spain'), ('SE', 'Sweden'), ('UJ', 'United Kingdom')],
help_text='Your country. Only relevant for EU reverse charge.',
max_length=2, verbose_name='Merchant country')),
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tax_rules',
to='pretixbase.Event')),
],
),
migrations.AddField(
model_name='item',
name='tax_rule',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT,
to='pretixbase.TaxRule', verbose_name='Sales tax'),
),
migrations.AddField(
model_name='order',
name='payment_fee_tax_rule',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT,
to='pretixbase.TaxRule'),
),
migrations.AddField(
model_name='orderposition',
name='tax_rule',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT,
to='pretixbase.TaxRule'),
),
migrations.RunPython(
code=tax_rate_converter,
reverse_code=django.db.migrations.operations.special.RunPython.noop,
),
migrations.RemoveField(
model_name='item',
name='tax_rate',
),
migrations.AddField(
model_name='invoiceaddress',
name='vat_id_validated',
field=models.BooleanField(default=False),
),
migrations.AlterField(
model_name='invoiceaddress',
name='vat_id',
field=models.CharField(blank=True, help_text='Only for business customers within the EU.', max_length=255,
verbose_name='VAT ID'),
),
migrations.AlterField(
model_name='taxrule',
name='home_country',
field=django_countries.fields.CountryField(blank=True,
help_text='Your country of residence. This is the country the '
'EU reverse charge rule will not apply in, '
'if configured above.',
max_length=2, verbose_name='Merchant country'),
),
migrations.AddField(
model_name='cartposition',
name='includes_tax',
field=models.BooleanField(default=True),
),
migrations.AddField(
model_name='invoiceline',
name='tax_name',
field=models.CharField(default='', max_length=190),
preserve_default=False,
),
migrations.AlterField(
model_name='taxrule',
name='eu_reverse_charge',
field=models.BooleanField(default=False,
help_text='Not recommended. Most events will NOT be qualified for reverse '
'charge since the place of taxation is the location of the event. '
'This option disables charging VAT for all customers outside the EU '
'and for business customers in different EU countries that do not '
'customers who entered a valid EU VAT ID. Only enable this option '
'after consulting a tax counsel. No warranty given for correct tax '
'calculation. USE AT YOUR OWN RISK.',
verbose_name='Use EU reverse charge taxation rules'),
),
migrations.AddField(
model_name='invoice',
name='foreign_currency_display',
field=models.CharField(blank=True, max_length=50, null=True),
),
migrations.AddField(
model_name='invoice',
name='foreign_currency_rate',
field=models.DecimalField(blank=True, decimal_places=4, max_digits=10, null=True),
),
migrations.AddField(
model_name='invoice',
name='foreign_currency_rate_date',
field=models.DateField(blank=True, null=True),
),
migrations.AddField(
model_name='item',
name='checkin_attention',
field=models.BooleanField(default=False,
help_text='If you set this, the check-in app will show a visible warning that '
'this ticket requires special attention. You can use this for example '
'for student tickets to indicate to the person at check-in that the '
'student ID card still needs to be checked.',
verbose_name='Requires special attention'),
),
migrations.AlterField(
model_name='event',
name='currency',
field=models.CharField(choices=[('AED', 'AED - UAE Dirham'), ('AFN', 'AFN - Afghani'), ('ALL', 'ALL - Lek'),
('AMD', 'AMD - Armenian Dram'),
('ANG', 'ANG - Netherlands Antillean Guilder'), ('AOA', 'AOA - Kwanza'),
('ARS', 'ARS - Argentine Peso'), ('AUD', 'AUD - Australian Dollar'),
('AWG', 'AWG - Aruban Florin'), ('AZN', 'AZN - Azerbaijanian Manat'),
('BAM', 'BAM - Convertible Mark'), ('BBD', 'BBD - Barbados Dollar'),
('BDT', 'BDT - Taka'), ('BGN', 'BGN - Bulgarian Lev'),
('BHD', 'BHD - Bahraini Dinar'), ('BIF', 'BIF - Burundi Franc'),
('BMD', 'BMD - Bermudian Dollar'), ('BND', 'BND - Brunei Dollar'),
('BOB', 'BOB - Boliviano'), ('BRL', 'BRL - Brazilian Real'),
('BSD', 'BSD - Bahamian Dollar'), ('BTN', 'BTN - Ngultrum'),
('BWP', 'BWP - Pula'), ('BYN', 'BYN - Belarusian Ruble'),
('BZD', 'BZD - Belize Dollar'), ('CAD', 'CAD - Canadian Dollar'),
('CDF', 'CDF - Congolese Franc'), ('CHF', 'CHF - Swiss Franc'),
('CLP', 'CLP - Chilean Peso'), ('CNY', 'CNY - Yuan Renminbi'),
('COP', 'COP - Colombian Peso'), ('CRC', 'CRC - Costa Rican Colon'),
('CUC', 'CUC - Peso Convertible'), ('CUP', 'CUP - Cuban Peso'),
('CVE', 'CVE - Cabo Verde Escudo'), ('CZK', 'CZK - Czech Koruna'),
('DJF', 'DJF - Djibouti Franc'), ('DKK', 'DKK - Danish Krone'),
('DOP', 'DOP - Dominican Peso'), ('DZD', 'DZD - Algerian Dinar'),
('EGP', 'EGP - Egyptian Pound'), ('ERN', 'ERN - Nakfa'),
('ETB', 'ETB - Ethiopian Birr'), ('EUR', 'EUR - Euro'),
('FJD', 'FJD - Fiji Dollar'), ('FKP', 'FKP - Falkland Islands Pound'),
('GBP', 'GBP - Pound Sterling'), ('GEL', 'GEL - Lari'),
('GHS', 'GHS - Ghana Cedi'), ('GIP', 'GIP - Gibraltar Pound'),
('GMD', 'GMD - Dalasi'), ('GNF', 'GNF - Guinea Franc'),
('GTQ', 'GTQ - Quetzal'), ('GYD', 'GYD - Guyana Dollar'),
('HKD', 'HKD - Hong Kong Dollar'), ('HNL', 'HNL - Lempira'),
('HRK', 'HRK - Kuna'), ('HTG', 'HTG - Gourde'), ('HUF', 'HUF - Forint'),
('IDR', 'IDR - Rupiah'), ('ILS', 'ILS - New Israeli Sheqel'),
('INR', 'INR - Indian Rupee'), ('IQD', 'IQD - Iraqi Dinar'),
('IRR', 'IRR - Iranian Rial'), ('ISK', 'ISK - Iceland Krona'),
('JMD', 'JMD - Jamaican Dollar'), ('JOD', 'JOD - Jordanian Dinar'),
('JPY', 'JPY - Yen'), ('KES', 'KES - Kenyan Shilling'),
('KGS', 'KGS - Som'), ('KHR', 'KHR - Riel'), ('KMF', 'KMF - Comoro Franc'),
('KPW', 'KPW - North Korean Won'), ('KRW', 'KRW - Won'),
('KWD', 'KWD - Kuwaiti Dinar'), ('KYD', 'KYD - Cayman Islands Dollar'),
('KZT', 'KZT - Tenge'), ('LAK', 'LAK - Kip'),
('LBP', 'LBP - Lebanese Pound'), ('LKR', 'LKR - Sri Lanka Rupee'),
('LRD', 'LRD - Liberian Dollar'), ('LSL', 'LSL - Loti'),
('LYD', 'LYD - Libyan Dinar'), ('MAD', 'MAD - Moroccan Dirham'),
('MDL', 'MDL - Moldovan Leu'), ('MGA', 'MGA - Malagasy Ariary'),
('MKD', 'MKD - Denar'), ('MMK', 'MMK - Kyat'), ('MNT', 'MNT - Tugrik'),
('MOP', 'MOP - Pataca'), ('MRO', 'MRO - Ouguiya'),
('MUR', 'MUR - Mauritius Rupee'), ('MVR', 'MVR - Rufiyaa'),
('MWK', 'MWK - Malawi Kwacha'), ('MXN', 'MXN - Mexican Peso'),
('MYR', 'MYR - Malaysian Ringgit'), ('MZN', 'MZN - Mozambique Metical'),
('NAD', 'NAD - Namibia Dollar'), ('NGN', 'NGN - Naira'),
('NIO', 'NIO - Cordoba Oro'), ('NOK', 'NOK - Norwegian Krone'),
('NPR', 'NPR - Nepalese Rupee'), ('NZD', 'NZD - New Zealand Dollar'),
('OMR', 'OMR - Rial Omani'), ('PAB', 'PAB - Balboa'), ('PEN', 'PEN - Sol'),
('PGK', 'PGK - Kina'), ('PHP', 'PHP - Philippine Peso'),
('PKR', 'PKR - Pakistan Rupee'), ('PLN', 'PLN - Zloty'),
('PYG', 'PYG - Guarani'), ('QAR', 'QAR - Qatari Rial'),
('RON', 'RON - Romanian Leu'), ('RSD', 'RSD - Serbian Dinar'),
('RUB', 'RUB - Russian Ruble'), ('RWF', 'RWF - Rwanda Franc'),
('SAR', 'SAR - Saudi Riyal'), ('SBD', 'SBD - Solomon Islands Dollar'),
('SCR', 'SCR - Seychelles Rupee'), ('SDG', 'SDG - Sudanese Pound'),
('SEK', 'SEK - Swedish Krona'), ('SGD', 'SGD - Singapore Dollar'),
('SHP', 'SHP - Saint Helena Pound'), ('SLL', 'SLL - Leone'),
('SOS', 'SOS - Somali Shilling'), ('SRD', 'SRD - Surinam Dollar'),
('SSP', 'SSP - South Sudanese Pound'), ('STD', 'STD - Dobra'),
('SVC', 'SVC - El Salvador Colon'), ('SYP', 'SYP - Syrian Pound'),
('SZL', 'SZL - Lilangeni'), ('THB', 'THB - Baht'), ('TJS', 'TJS - Somoni'),
('TMT', 'TMT - Turkmenistan New Manat'), ('TND', 'TND - Tunisian Dinar'),
('TOP', 'TOP - Paanga'), ('TRY', 'TRY - Turkish Lira'),
('TTD', 'TTD - Trinidad and Tobago Dollar'),
('TWD', 'TWD - New Taiwan Dollar'), ('TZS', 'TZS - Tanzanian Shilling'),
('UAH', 'UAH - Hryvnia'), ('UGX', 'UGX - Uganda Shilling'),
('USD', 'USD - US Dollar'), ('UYU', 'UYU - Peso Uruguayo'),
('UZS', 'UZS - Uzbekistan Sum'), ('VEF', 'VEF - Bolívar'),
('VND', 'VND - Dong'), ('VUV', 'VUV - Vatu'), ('WST', 'WST - Tala'),
('XAF', 'XAF - CFA Franc BEAC'), ('XAG', 'XAG - Silver'),
('XAU', 'XAU - Gold'),
('XBA', 'XBA - Bond Markets Unit European Composite Unit (EURCO)'),
('XBB', 'XBB - Bond Markets Unit European Monetary Unit (E.M.U.-6)'),
('XBC', 'XBC - Bond Markets Unit European Unit of Account 9 (E.U.A.-9)'),
('XBD', 'XBD - Bond Markets Unit European Unit of Account 17 (E.U.A.-17)'),
('XCD', 'XCD - East Caribbean Dollar'),
('XDR', 'XDR - SDR (Special Drawing Right)'),
('XOF', 'XOF - CFA Franc BCEAO'), ('XPD', 'XPD - Palladium'),
('XPF', 'XPF - CFP Franc'), ('XPT', 'XPT - Platinum'),
('XSU', 'XSU - Sucre'),
('XTS', 'XTS - Codes specifically reserved for testing purposes'),
('XUA', 'XUA - ADB Unit of Account'), ('XXX',
'XXX - The codes assigned for '
'transactions where no currency is '
'involved'),
('YER', 'YER - Yemeni Rial'), ('ZAR', 'ZAR - Rand'),
('ZMW', 'ZMW - Zambian Kwacha'), ('ZWL', 'ZWL - Zimbabwe Dollar')],
default='EUR', max_length=10, verbose_name='Event currency'),
),
migrations.AlterField(
model_name='taxrule',
name='price_includes_tax',
field=models.BooleanField(default=True,
verbose_name='The configured product prices include the tax amount'),
),
migrations.AlterField(
model_name='voucher',
name='code',
field=models.CharField(db_index=True, default=pretix.base.models.vouchers.generate_code, max_length=255,
validators=[django.core.validators.MinLengthValidator(5)],
verbose_name='Voucher code'),
),
migrations.CreateModel(
name='EventMetaProperty',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(db_index=True,
help_text='Can not contain spaces or special characters execpt underscores',
max_length=50, validators=[django.core.validators.RegexValidator(
message='The property name may only contain letters, numbers and underscores.',
regex='^[a-zA-Z0-9_]+$')], verbose_name='Name')),
('default', models.TextField()),
('organizer',
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='meta_properties',
to='pretixbase.Organizer')),
],
options={
'abstract': False,
},
bases=(models.Model, pretix.base.models.base.LoggingMixin),
),
migrations.CreateModel(
name='EventMetaValue',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('value', models.TextField()),
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='meta_values',
to='pretixbase.Event')),
('property', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='event_values',
to='pretixbase.EventMetaProperty')),
],
bases=(models.Model, pretix.base.models.base.LoggingMixin),
),
migrations.CreateModel(
name='SubEventMetaValue',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('value', models.TextField()),
('property',
models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='subevent_values',
to='pretixbase.EventMetaProperty')),
('subevent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='meta_values',
to='pretixbase.SubEvent')),
],
bases=(models.Model, pretix.base.models.base.LoggingMixin),
),
migrations.AlterUniqueTogether(
name='subeventmetavalue',
unique_together=set([('subevent', 'property')]),
),
migrations.AlterUniqueTogether(
name='eventmetavalue',
unique_together=set([('event', 'property')]),
),
migrations.CreateModel(
name='OrderFee',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('value', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Value')),
('description', models.CharField(blank=True, max_length=190)),
('internal_type', models.CharField(blank=True, max_length=255)),
('fee_type', models.CharField(choices=[('payment', 'Payment method fee'), ('shipping', 'Shipping fee')],
max_length=100)),
('tax_rate', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='Tax rate')),
('tax_value', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Tax value')),
('order', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='fees',
to='pretixbase.Order', verbose_name='Order')),
('tax_rule', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT,
to='pretixbase.TaxRule')),
],
),
migrations.RunPython(
code=fee_converter,
reverse_code=django.db.migrations.operations.special.RunPython.noop,
),
migrations.RemoveField(
model_name='order',
name='payment_fee',
),
migrations.RemoveField(
model_name='order',
name='payment_fee_tax_rate',
),
migrations.RemoveField(
model_name='order',
name='payment_fee_tax_rule',
),
migrations.RemoveField(
model_name='order',
name='payment_fee_tax_value',
),
migrations.AddField(
model_name='invoiceline',
name='position',
field=models.PositiveIntegerField(default=0),
),
migrations.AlterField(
model_name='orderfee',
name='fee_type',
field=models.CharField(
choices=[('payment', 'Payment fee'), ('shipping', 'Shipping fee'), ('other', 'Other fees')],
max_length=100),
),
migrations.RunPython(
code=assign_positions,
reverse_code=django.db.migrations.operations.special.RunPython.noop,
),
migrations.AlterModelOptions(
name='invoiceline',
options={'ordering': ('position', 'pk')},
),
]

View File

@@ -64,15 +64,17 @@ class PermissionMiddleware(MiddlewareMixin):
return self._login_redirect(request)
if not settings.PRETIX_LONG_SESSIONS or not request.session.get('pretix_auth_long_session', False):
# If this logic is updated, make sure to also update the logic in pretix/api/auth/permission.py
last_used = request.session.get('pretix_auth_last_used', time.time())
if time.time() - request.session.get('pretix_auth_login_time', time.time()) > settings.PRETIX_SESSION_TIMEOUT_ABSOLUTE:
logout(request)
request.session['pretix_auth_login_time'] = 0
return self._login_redirect(request)
if time.time() - last_used > settings.PRETIX_SESSION_TIMEOUT_RELATIVE and url_name != 'user.reauth':
return redirect(reverse('control:user.reauth') + '?next=' + quote(request.get_full_path()))
if url_name != 'user.reauth':
if time.time() - last_used > settings.PRETIX_SESSION_TIMEOUT_RELATIVE:
return redirect(reverse('control:user.reauth') + '?next=' + quote(request.get_full_path()))
request.session['pretix_auth_last_used'] = int(time.time())
request.session['pretix_auth_last_used'] = int(time.time())
if 'event' in url.kwargs and 'organizer' in url.kwargs:
request.event = Event.objects.filter(

View File

@@ -566,6 +566,26 @@ class SessionTimeOutTest(TestCase):
response = self.client.get('/control/')
self.assertEqual(response.status_code, 200)
def test_log_out_after_relative_timeout_really_enforced(self):
# Regression test added after a security problem in 1.9.1
# The problem was that, once the relative timeout happened, the user was redirected
# to /control/reauth/, but loading /control/reauth/ was already considered to be
# "session activitiy". Therefore, after loding /control/reauth/, the session was no longer
# in the timeout state and the user was able to access pages again without re-entering the
# password.
session = self.client.session
session['pretix_auth_long_session'] = False
session['pretix_auth_login_time'] = int(time.time()) - 3600 * 6
session['pretix_auth_last_used'] = int(time.time()) - 3600 * 3 - 60
session.save()
response = self.client.get('/control/')
self.assertEqual(response.status_code, 302)
self.assertRedirects(response, '/control/reauth/?next=/control/')
self.client.get('/control/reauth/?next=/control/')
response = self.client.get('/control/')
self.assertEqual(response.status_code, 302)
def test_update_session_activity(self):
t1 = int(time.time()) - 5
session = self.client.session