Compare commits

...
4 Commits
Author SHA1 Message Date
Raphael Michel e188796a81 Bump Pillow to 13.0.* (#6694) 2026-10-22 15:44:57 +02:00
Lukas Bockstaller 1ca41269cd test: unset EMAIL_HOST_USER and EMAIL_HOST_PASSWORD for smtp tests (#6688)
Harden tests so that they don't fail locally when the config values are set in pretix.cfg
2026-10-22 12:03:57 +02:00
Lukas Bockstaller 9581c08949 put last_seen attribute into the defaults parameter of the get_or_create (#6689) 2026-10-22 12:00:35 +02:00
luelista 5393aff5f0 Attempt to make customer account SSO configuration a bit less confusing (#6586)
* More explanation text, better titles
* Direct links to docs
* Add all information to copy/paste in one place
2026-10-22 10:57:37 +02:00
9 changed files with 87 additions and 20 deletions

No files matched your search

+1 -1
View File
@@ -77,7 +77,7 @@ dependencies = [
"paypal-checkout-serversdk==1.0.*",
"PyJWT==2.15.*",
"phonenumberslite==9.0.*",
"Pillow==12.3.*",
"Pillow==13.0.*",
"pretix-plugin-build",
"protobuf==7.36.*",
"psycopg2-binary",
+1 -1
View File
@@ -76,7 +76,7 @@ class DeviceTokenAuthentication(TokenAuthentication):
except DeviceLastSeen.DoesNotExist:
# First request from device, create model, ignore result. Use get_or_create to be safe
# against concurrent create requests
DeviceLastSeen.objects.get_or_create(device=device, last_seen=now())
DeviceLastSeen.objects.get_or_create(device=device, defaults={"last_seen": now()})
else:
if now() - last_seen_obj.last_seen < timedelta(seconds=10):
# We don't need to know the last seen info of a device to more precision than this,
+16 -5
View File
@@ -1198,7 +1198,7 @@ OrganizerFooterLinkFormset = inlineformset_factory(
class SSOProviderForm(I18nModelForm):
config_oidc_base_url = forms.URLField(
label=pgettext_lazy('sso_oidc', 'Base URL'),
label=pgettext_lazy('sso_oidc', 'Base URL / issuer'),
required=False,
)
config_oidc_client_id = forms.CharField(
@@ -1295,6 +1295,14 @@ class SSOProviderForm(I18nModelForm):
class SSOClientForm(I18nModelForm):
disp_base_url = forms.CharField(
label=_('Base URL / issuer'),
disabled=True,
)
disp_client_id = forms.CharField(
label=_('Client ID'),
disabled=True,
)
regenerate_client_secret = forms.BooleanField(
label=_('Invalidate old client secret and generate a new one'),
required=False,
@@ -1302,7 +1310,8 @@ class SSOClientForm(I18nModelForm):
class Meta:
model = CustomerSSOClient
fields = ['is_active', 'name', 'client_id', 'client_type', 'authorization_grant_type', 'redirect_uris',
fields = ['is_active', 'name', 'disp_base_url', 'disp_client_id', 'regenerate_client_secret',
'client_type', 'authorization_grant_type', 'redirect_uris',
'allowed_scopes', 'require_pkce']
widgets = {
'authorization_grant_type': forms.RadioSelect,
@@ -1317,13 +1326,15 @@ class SSOClientForm(I18nModelForm):
help_text=self.fields['allowed_scopes'].help_text,
required=self.fields['allowed_scopes'].required,
initial=self.fields['allowed_scopes'].initial,
choices=CustomerSSOClient.SCOPE_CHOICES,
choices=[(val, format_html('<code>{}</code> &ndash; {}', val, label)) for val, label in CustomerSSOClient.SCOPE_CHOICES],
widget=forms.CheckboxSelectMultiple
)
organizer = kwargs['event']
self.initial['disp_base_url'] = eventreverse_absolute(organizer, 'presale:organizer.index', {}).strip('/')
if self.instance and self.instance.pk:
self.fields['client_id'].disabled = True
self.initial['disp_client_id'] = self.instance.client_id
else:
del self.fields['client_id']
self.initial['disp_client_id'] = _('(will be generated)')
del self.fields['regenerate_client_secret']
@@ -1,12 +1,29 @@
{% extends "pretixcontrol/organizers/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% block title %}
{% if client %}
{% trans "SSO client:" %} {{ client.name }}
{% else %}
{% trans "Create credentials for a new SSO client" %}
{% endif %}
{% endblock %}
{% block inner %}
{% if client %}
<h1>{% trans "SSO client:" %} {{ client.name }}</h1>
{% else %}
<h1>{% trans "Create a new SSO client" %}</h1>
<h1>{% trans "Create credentials for a new SSO client" %}</h1>
<p>
{% blocktrans trimmed %}
After submitting this form, client credentials for your SSO client will be generated.
{% endblocktrans %}
</p>
{% endif %}
<p>
{% blocktrans trimmed with url="https://docs.pretix.eu/guides/customer-accounts/#using-pretix-as-an-sso-provider" %}
For more information, refer to our documentation on <a href="{{ url }}">Using pretix as an SSO provider with external SSO clients</a>.
{% endblocktrans %}
</p>
<form class="form-horizontal" action="" method="post">
{% csrf_token %}
{% bootstrap_form form layout="control" %}
@@ -3,16 +3,23 @@
{% load bootstrap3 %}
{% block title %}{% trans "SSO clients" %}{% endblock %}
{% block inner %}
<h1>{% trans "SSO clients" %}</h1>
<h1>{% trans "SSO clients" %} <small> &mdash; {% blocktrans %}
"Login with pretix" in external systems
{% endblocktrans %}</small></h1>
<p>
{% blocktrans trimmed %}
You can allow your customers to log into other systems using their customer account credentials by setting up
your other systems as a Single-Sign-On (SSO) client based on OpenID Connect.
{% endblocktrans %}
</p>
<p>
{% blocktrans trimmed with url="https://docs.pretix.eu/guides/customer-accounts/#using-pretix-as-an-sso-provider" %}
For more information, refer to our documentation on <a href="{{ url }}">Using pretix as an SSO provider with external SSO clients</a>.
{% endblocktrans %}
</p>
<a href="{% url "control:organizer.ssoclient.add" organizer=request.organizer.slug %}" class="btn btn-default">
<span class="fa fa-plus"></span>
{% trans "Create a new SSO client" %}
{% trans "Create credentials for a new SSO client" %}
</a>
<table class="table table-condensed table-hover">
<thead>
@@ -1,12 +1,24 @@
{% extends "pretixcontrol/organizers/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% block title %}
{% if provider %}
{% trans "External SSO provider:" %} {{ provider.name }}
{% else %}
{% trans "Connect to an external SSO provider" %}
{% endif %}
{% endblock %}
{% block inner %}
{% if provider %}
<h1>{% trans "SSO provider:" %} {{ provider.name }}</h1>
<h1>{% trans "External SSO provider:" %} {{ provider.name }}</h1>
{% else %}
<h1>{% trans "Create a new SSO provider" %}</h1>
<h1>{% trans "Connect to an external SSO provider" %}</h1>
{% endif %}
<p>
{% blocktrans trimmed with url="https://docs.pretix.eu/guides/customer-accounts/#using-pretix-as-an-sso-client" %}
For more information, refer to our documentation on <a href="{{ url }}" target="_blank">Using pretix as an SSO client</a>.
{% endblocktrans %}
</p>
<form class="form-horizontal" action="" method="post">
{% csrf_token %}
{% bootstrap_form form layout="control" %}
@@ -3,16 +3,23 @@
{% load bootstrap3 %}
{% block title %}{% trans "SSO providers" %}{% endblock %}
{% block inner %}
<h1>{% trans "SSO providers" %}</h1>
<h1>{% trans "SSO providers" %} <small> &mdash; {% blocktrans %}
Connect to an external login provider
{% endblocktrans %}</small></h1>
<p>
{% blocktrans trimmed %}
You can connect existing Single-Sign-On (SSO) providers to allow your customers to log in using your own
account system.
{% endblocktrans %}
</p>
<p>
{% blocktrans trimmed with url="https://docs.pretix.eu/guides/customer-accounts/#using-pretix-as-an-sso-client" %}
For more information, refer to our documentation on <a href="{{ url }}" target="_blank">Using pretix as an SSO client</a>.
{% endblocktrans %}
</p>
<a href="{% url "control:organizer.ssoprovider.add" organizer=request.organizer.slug %}" class="btn btn-default">
<span class="fa fa-plus"></span>
{% trans "Create a new SSO provider" %}
{% trans "Connect to an external SSO provider" %}
</a>
<table class="table table-condensed table-hover">
<thead>
+15 -6
View File
@@ -2817,10 +2817,15 @@ class SSOProviderCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequire
return get_object_or_404(CustomerSSOProvider, organizer=self.request.organizer, pk=self.kwargs.get('provider'))
def get_success_url(self):
return reverse('control:organizer.ssoproviders', kwargs={
'organizer': self.request.organizer.slug,
return reverse('control:organizer.ssoprovider.edit', kwargs={
'organizer': self.request.organizer.slug, 'provider': self.object.pk,
})
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['redirect_uri'] = _('(will be generated)')
return ctx
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['event'] = self.request.organizer
@@ -2950,8 +2955,11 @@ class SSOClientCreateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredM
secret = form.instance.set_client_secret()
messages.success(
self.request,
_('The SSO client has been created. Please note down the following client secret, it will never be shown '
'again: {secret}').format(secret=secret)
_('The SSO client has been created.') + ' ' +
_('Please note down the following client secret, it will never be shown again.') + '\n\n' +
_('Base URL / issuer') + f': {form.initial["disp_base_url"]}\n\n' +
_('Client ID') + f': {form.instance.client_id}\n\n' +
_('Client secret') + f': {secret}'
)
form.instance.organizer = self.request.organizer
ret = super().form_valid(form)
@@ -3000,8 +3008,9 @@ class SSOClientUpdateView(OrganizerDetailViewMixin, OrganizerPermissionRequiredM
secret = form.instance.set_client_secret()
messages.success(
self.request,
_('Your changes have been saved. Please note down the following client secret, it will never be shown '
'again: {secret}').format(secret=secret)
_('Your changes have been saved.') + ' ' +
_('Please note down the following client secret, it will never be shown again.') + '\n\n' +
_('Client secret') + f': {secret}'
)
else:
messages.success(
+4
View File
@@ -642,6 +642,8 @@ def assert_mail_connection(res, should_connect, use_ssl):
True, False
])
def test_private_smtp_ip(res, use_ssl, settings):
settings.EMAIL_HOST_USER = ''
settings.EMAIL_HOST_PASSWORD = ''
settings.EMAIL_CUSTOM_SMTP_BACKEND = 'pretix.base.email.CheckPrivateNetworkSmtpBackend'
settings.MAIL_CUSTOM_SMTP_ALLOW_PRIVATE_NETWORKS = False
with assert_mail_connection(res=res, should_connect=False, use_ssl=use_ssl), pytest.raises(match="Request to .* blocked"):
@@ -665,6 +667,8 @@ def test_private_smtp_ip(res, use_ssl, settings):
True, False
])
def test_public_smtp_ip(use_ssl, allow_private, settings):
settings.EMAIL_HOST_USER = ''
settings.EMAIL_HOST_PASSWORD = ''
settings.EMAIL_CUSTOM_SMTP_BACKEND = 'pretix.base.email.CheckPrivateNetworkSmtpBackend'
settings.MAIL_CUSTOM_SMTP_ALLOW_PRIVATE_NETWORKS = allow_private