Reduce confusion about customer login with event level domains (#2380)

This commit is contained in:
Raphael Michel
2021-12-15 16:47:08 +01:00
committed by GitHub
parent 0e9600a7bf
commit 5210ac3a78
7 changed files with 337 additions and 50 deletions

View File

@@ -343,31 +343,45 @@ def get_or_create_cart_id(request, create=True):
if current_id and current_id in request.session.get('carts', {}):
if current_id != orig_current_id:
request.session[session_keyname] = current_id
return current_id
else:
cart_data = {}
if prefix and 'take_cart_id' in request.GET and current_id:
new_id = current_id
cached_widget_data = widget_data_cache.get('widget_data_{}'.format(current_id))
if cached_widget_data:
cart_data['widget_data'] = cached_widget_data
cart_invalidated = (
request.session['carts'][current_id].get('customer_cart_tied_to_login', False) and
request.session['carts'][current_id].get('customer') and
(not request.customer or request.session['carts'][current_id].get('customer') != request.customer.pk)
)
if cart_invalidated:
# This cart was created with a login but the person is now logged out.
# Destroy the cart for privacy protection.
request.session['carts'][current_id] = {}
else:
if not create:
return None
new_id = generate_cart_id(request, prefix=prefix)
return current_id
if 'widget_data' not in cart_data and 'widget_data' in request.GET:
try:
cart_data['widget_data'] = json.loads(request.GET.get('widget_data'))
except ValueError:
pass
cart_data = {}
if prefix and 'take_cart_id' in request.GET and current_id:
new_id = current_id
cached_widget_data = widget_data_cache.get('widget_data_{}'.format(current_id))
if cached_widget_data:
cart_data['widget_data'] = cached_widget_data
else:
if not create:
return None
new_id = generate_cart_id(request, prefix=prefix)
if 'carts' not in request.session:
request.session['carts'] = {}
if new_id not in request.session['carts']:
request.session['carts'][new_id] = cart_data
request.session[session_keyname] = new_id
return new_id
if 'widget_data' not in cart_data and 'widget_data' in request.GET:
try:
cart_data['widget_data'] = json.loads(request.GET.get('widget_data'))
except ValueError:
pass
if 'carts' not in request.session:
request.session['carts'] = {}
if new_id not in request.session['carts']:
request.session['carts'][new_id] = cart_data
request.session[session_keyname] = new_id
return new_id
def cart_session(request):

View File

@@ -19,8 +19,12 @@
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
from urllib.parse import quote
from importlib import import_module
from urllib.parse import (
parse_qs, quote, urlencode, urljoin, urlparse, urlsplit, urlunparse,
)
from django.conf import settings
from django.contrib import messages
from django.core.signing import BadSignature, dumps, loads
from django.db import transaction
@@ -38,6 +42,7 @@ from django.views.generic import DeleteView, FormView, ListView, View
from pretix.base.models import Customer, InvoiceAddress, Order, OrderPosition
from pretix.base.services.mail import mail
from pretix.multidomain.models import KnownDomain
from pretix.multidomain.urlreverse import build_absolute_uri, eventreverse
from pretix.presale.forms.customer import (
AuthenticationForm, ChangeInfoForm, ChangePasswordForm, RegistrationForm,
@@ -47,6 +52,8 @@ from pretix.presale.utils import (
customer_login, customer_logout, update_customer_session_auth_hash,
)
SessionStore = import_module(settings.SESSION_ENGINE).SessionStore
class RedirectBackMixin:
redirect_field_name = 'next'
@@ -57,9 +64,14 @@ class RedirectBackMixin:
self.redirect_field_name,
self.request.GET.get(self.redirect_field_name, '')
)
hosts = list(KnownDomain.objects.filter(event__organizer=self.request.organizer).values_list('domainname', flat=True))
siteurlsplit = urlsplit(settings.SITE_URL)
if siteurlsplit.port and siteurlsplit.port not in (80, 443):
hosts = ['%s:%d' % (h, siteurlsplit.port) for h in hosts]
url_is_safe = url_has_allowed_host_and_scheme(
url=redirect_to,
allowed_hosts=None,
allowed_hosts=hosts,
require_https=self.request.is_secure(),
)
return redirect_to if url_is_safe else ''
@@ -96,11 +108,23 @@ class LoginView(RedirectBackMixin, FormView):
def get_success_url(self):
url = self.get_redirect_url()
if getattr(self.request, 'event_domain', False):
default_url = '/'
else:
default_url = eventreverse(self.request.organizer, 'presale:organizer.customer.profile', kwargs={})
return url or default_url
if not url:
return eventreverse(self.request.organizer, 'presale:organizer.customer.profile', kwargs={})
if self.request.GET.get("request_cross_domain_customer_auth") == "true":
otpstore = SessionStore()
otpstore[f'customer_cross_domain_auth_{self.request.organizer.pk}'] = self.request.session.session_key
otpstore.set_expiry(60)
otpstore.save(must_create=True)
otp = otpstore.session_key
u = urlparse(url)
qsl = parse_qs(u.query)
qsl['cross_domain_customer_auth'] = otp
url = urlunparse((u.scheme, u.netloc, u.path, u.params, urlencode(qsl, doseq=True), u.fragment))
return url
def form_valid(self, form):
"""Security check complete. Log the user in."""
@@ -119,25 +143,39 @@ class LogoutView(View):
def get_next_page(self):
if getattr(self.request, 'event_domain', False):
next_page = '/'
# After we cleared the cookies on this domain, redirect to the parent domain to clear cookies as well
next_page = eventreverse(self.request.organizer, 'presale:organizer.customer.logout', kwargs={})
if self.redirect_field_name in self.request.POST or self.redirect_field_name in self.request.GET:
after_next_page = self.request.POST.get(
self.redirect_field_name,
self.request.GET.get(self.redirect_field_name)
)
next_page += '?' + urlencode({
'next': urljoin(f'{self.request.scheme}://{self.request.get_host()}', after_next_page)
})
else:
next_page = eventreverse(self.request.organizer, 'presale:organizer.index', kwargs={})
if (self.redirect_field_name in self.request.POST or
self.redirect_field_name in self.request.GET):
next_page = self.request.POST.get(
self.redirect_field_name,
self.request.GET.get(self.redirect_field_name)
)
url_is_safe = url_has_allowed_host_and_scheme(
url=next_page,
allowed_hosts=None,
require_https=self.request.is_secure(),
)
# Security check -- Ensure the user-originating redirection URL is
# safe.
if not url_is_safe:
next_page = self.request.path
if (self.redirect_field_name in self.request.POST or
self.redirect_field_name in self.request.GET):
next_page = self.request.POST.get(
self.redirect_field_name,
self.request.GET.get(self.redirect_field_name)
)
hosts = list(KnownDomain.objects.filter(event__organizer=self.request.organizer).values_list('domainname', flat=True))
siteurlsplit = urlsplit(settings.SITE_URL)
if siteurlsplit.port and siteurlsplit.port not in (80, 443):
hosts = ['%s:%d' % (h, siteurlsplit.port) for h in hosts]
url_is_safe = url_has_allowed_host_and_scheme(
url=next_page,
allowed_hosts=hosts,
require_https=self.request.is_secure(),
)
# Security check -- Ensure the user-originating redirection URL is
# safe.
if not url_is_safe:
next_page = self.request.path
return next_page