mirror of
https://github.com/pretix/pretix.git
synced 2026-08-18 12:06:26 +00:00
Compare commits
8
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
806b4d1cc5 | ||
|
|
5de7824b66 | ||
|
|
ed432fefad | ||
|
|
c36bf71403 | ||
|
|
fbf0034d04 | ||
|
|
ec4f4f81b4 | ||
|
|
f461f82f36 | ||
|
|
a6f31df0d4 |
@@ -53,7 +53,6 @@ from django.db.models import QuerySet
|
||||
from django.forms import Select, widgets
|
||||
from django.forms.widgets import FILE_INPUT_CONTRADICTION
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.functional import lazy
|
||||
from django.utils.html import escape
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.text import format_lazy
|
||||
@@ -325,21 +324,16 @@ class WrappedPhonePrefixSelect(Select):
|
||||
initial = None
|
||||
|
||||
def __init__(self, initial=None):
|
||||
def _get_choices():
|
||||
choices = [("", "---------")]
|
||||
|
||||
if initial:
|
||||
for prefix, values in COUNTRY_CODE_TO_REGION_CODE.items():
|
||||
if all(v == REGION_CODE_FOR_NON_GEO_ENTITY for v in values):
|
||||
continue
|
||||
if initial in values:
|
||||
self.initial = "+%d" % prefix
|
||||
break
|
||||
choices += get_phone_prefixes_sorted_and_localized()
|
||||
return choices
|
||||
|
||||
choices = lazy(_get_choices, list)()
|
||||
choices = [("", "---------")]
|
||||
|
||||
if initial:
|
||||
for prefix, values in COUNTRY_CODE_TO_REGION_CODE.items():
|
||||
if all(v == REGION_CODE_FOR_NON_GEO_ENTITY for v in values):
|
||||
continue
|
||||
if initial in values:
|
||||
self.initial = "+%d" % prefix
|
||||
break
|
||||
choices += get_phone_prefixes_sorted_and_localized()
|
||||
super().__init__(choices=choices, attrs={
|
||||
'aria-label': pgettext_lazy('phonenumber', 'International area code'),
|
||||
'autocomplete': 'tel-country-code',
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-today pretix GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# 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 django.core.management.base import BaseCommand
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Do nothing. Useful for startup performance testing."
|
||||
|
||||
def handle(self, *args, **options):
|
||||
pass
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2.12 on 2026-07-01 08:34
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("pretixbase", "0301_reusablemedium_remove_orderposition"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="customerssoprovider",
|
||||
name="allow_convert_to_sso",
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
||||
@@ -73,6 +73,13 @@ class CustomerSSOProvider(LoggedModel):
|
||||
null=False, blank=False,
|
||||
choices=METHODS,
|
||||
)
|
||||
allow_convert_to_sso = models.BooleanField(
|
||||
default=False,
|
||||
verbose_name=_("Convert existing customers to single-sign-on accounts on login"),
|
||||
help_text=_(
|
||||
"If enabled, when an existing customer registered with email and password tries to login through this SSO provider, pretix changes the account to single-sign-on. Otherwise pretix does not allow to log in."
|
||||
),
|
||||
)
|
||||
configuration = models.JSONField()
|
||||
|
||||
def allow_delete(self):
|
||||
|
||||
@@ -899,7 +899,7 @@ class Event(EventMixin, LoggedModel):
|
||||
self.save()
|
||||
self.log_action('pretix.object.cloned', data={'source': other.slug, 'source_id': other.pk})
|
||||
|
||||
if hasattr(other, 'alternative_domain_assignment'):
|
||||
if hasattr(other, 'alternative_domain_assignment') and not is_cross_organizer:
|
||||
other.alternative_domain_assignment.domain.event_assignments.create(event=self)
|
||||
|
||||
if not self.all_sales_channels:
|
||||
|
||||
@@ -1673,7 +1673,7 @@ class CountriesAndEUAndStates(CountriesAndEU):
|
||||
|
||||
class TaxRuleLineForm(I18nForm):
|
||||
country = LazyTypedChoiceField(
|
||||
choices=lazy(lambda: CountriesAndEUAndStates(), CountriesAndEUAndStates),
|
||||
choices=CountriesAndEUAndStates(),
|
||||
required=False
|
||||
)
|
||||
address_type = forms.ChoiceField(
|
||||
|
||||
@@ -1244,7 +1244,7 @@ class SSOProviderForm(I18nModelForm):
|
||||
|
||||
class Meta:
|
||||
model = CustomerSSOProvider
|
||||
fields = ['is_active', 'name', 'button_label', 'method']
|
||||
fields = ['is_active', 'name', 'button_label', 'method', 'allow_convert_to_sso']
|
||||
widgets = {
|
||||
'method': forms.RadioSelect,
|
||||
}
|
||||
|
||||
@@ -132,6 +132,7 @@
|
||||
<legend>{% trans "Customer accounts" %}</legend>
|
||||
{% bootstrap_field sform.customer_accounts layout="control" %}
|
||||
{% bootstrap_field sform.customer_accounts_native layout="control" %}
|
||||
{% bootstrap_field sform.customer_accounts_to_oidc layout="control" %}
|
||||
{% bootstrap_field sform.customer_accounts_require_login_for_order_access layout="control" %}
|
||||
{% bootstrap_field sform.customer_accounts_link_by_email layout="control" %}
|
||||
{% bootstrap_field sform.name_scheme layout="control" %}
|
||||
|
||||
@@ -864,11 +864,33 @@ class SSOLoginReturnView(RedirectBackMixin, View):
|
||||
identifier=identifier,
|
||||
)
|
||||
except Customer.DoesNotExist:
|
||||
return self._fail(
|
||||
_('We were unable to use your login since the email address {email} is already used for a '
|
||||
'different account in this system.').format(email=profile['email']),
|
||||
popup_origin,
|
||||
)
|
||||
# no race-condition, try to convert to oidc?
|
||||
if self.provider.allow_convert_to_sso:
|
||||
try:
|
||||
customer = self.request.organizer.customers.get(
|
||||
email=profile['email'],
|
||||
)
|
||||
customer.set_unusable_password()
|
||||
customer.provider = self.provider
|
||||
customer.external_identifier = str(profile['uid'])
|
||||
customer.is_verified = True
|
||||
if name_parts:
|
||||
customer.name_parts = name_parts
|
||||
if profile.get('phone'):
|
||||
customer.phone = profile.get('phone')
|
||||
customer.save()
|
||||
except Customer.DoesNotExist:
|
||||
# should actually never happen
|
||||
return self._fail(
|
||||
_('We were unable to use your login since either the email address is unknown in this system.'),
|
||||
popup_origin,
|
||||
)
|
||||
else:
|
||||
return self._fail(
|
||||
_('We were unable to use your login since the email address {email} is already used for a '
|
||||
'different account in this system.').format(email=profile['email']),
|
||||
popup_origin,
|
||||
)
|
||||
else:
|
||||
if customer.is_active and customer.email != profile['email']:
|
||||
customer.email = profile['email']
|
||||
|
||||
@@ -438,6 +438,24 @@ def test_org_sso_login_new_customer_email_conflict(env, client, provider):
|
||||
_sso_login(client, provider, 'new@example.net', expect_fail=True)
|
||||
|
||||
|
||||
@pytest.mark.django_db(transaction=True)
|
||||
def test_org_sso_convert_customer_on_login(env, client, provider):
|
||||
organizer = env[0]
|
||||
provider.allow_convert_to_sso = True
|
||||
provider.save()
|
||||
with scopes_disabled():
|
||||
customer = organizer.customers.create(email='new@example.net', is_verified=True, is_active=False)
|
||||
customer.set_password('foo')
|
||||
customer.save()
|
||||
|
||||
_sso_login(client, provider, 'new@example.net')
|
||||
|
||||
customer.refresh_from_db()
|
||||
assert customer.provider
|
||||
assert customer.identifier
|
||||
assert not customer.has_usable_password()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize("url", [
|
||||
"account/change",
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
#
|
||||
# This file is part of pretix (Community Edition).
|
||||
#
|
||||
# Copyright (C) 2014-2020 Raphael Michel and contributors
|
||||
# Copyright (C) 2020-today pretix GmbH and contributors
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
|
||||
# Public License as published by the Free Software Foundation in version 3 of the License.
|
||||
#
|
||||
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
|
||||
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
|
||||
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
|
||||
# this file, see <https://pretix.eu/about/en/license>.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
|
||||
def test_start_with_redis_down():
|
||||
"""
|
||||
This is a test that ensures that pretix is able to start without a running redis server,
|
||||
even if one is configured.
|
||||
"""
|
||||
with tempfile.NamedTemporaryFile(suffix="cfg") as f:
|
||||
f.write(b"[redis]\nlocation=redis://127.0.0.99:65534/2\n")
|
||||
f.flush()
|
||||
|
||||
assert subprocess.check_call(
|
||||
[
|
||||
sys.executable,
|
||||
os.path.join(os.path.dirname(__file__), '../manage.py'),
|
||||
"noop",
|
||||
],
|
||||
env={
|
||||
"PRETIX_CONFIG_FILE": f.name,
|
||||
}
|
||||
) == 0
|
||||
Reference in New Issue
Block a user