mirror of
https://github.com/pretix/pretix.git
synced 2026-08-26 13:14:40 +00:00
Handle permissions on event copying
This commit is contained in:
@@ -445,7 +445,7 @@ class CloneEventSerializer(EventSerializer):
|
||||
date_admission = validated_data.pop('date_admission', None)
|
||||
new_event = super().create({**validated_data, 'plugins': None})
|
||||
|
||||
event = Event.objects.filter(slug=self.context['event'], organizer=self.context['organizer'].pk).first()
|
||||
event = self.context['event']
|
||||
new_event.copy_data_from(event, skip_meta_data='meta_data' in validated_data)
|
||||
|
||||
if plugins is not None:
|
||||
|
||||
@@ -281,6 +281,11 @@ class EventViewSet(viewsets.ModelViewSet):
|
||||
new_event = serializer.save(organizer=self.request.organizer)
|
||||
|
||||
if copy_from:
|
||||
perm_holder = (self.request.auth if isinstance(self.request.auth, (Device, TeamAPIToken))
|
||||
else self.request.user)
|
||||
if not copy_from.allow_copy_data(self.request.organizer, perm_holder):
|
||||
raise PermissionDenied("Not sufficient permission on source event to copy")
|
||||
|
||||
new_event.copy_data_from(copy_from, skip_meta_data='meta_data' in serializer.validated_data)
|
||||
|
||||
if plugins is not None:
|
||||
@@ -345,7 +350,7 @@ class CloneEventViewSet(viewsets.ModelViewSet):
|
||||
|
||||
def get_serializer_context(self):
|
||||
ctx = super().get_serializer_context()
|
||||
ctx['event'] = self.kwargs['event']
|
||||
ctx['event'] = Event.objects.get(slug=self.kwargs['event'], organizer=self.request.organizer)
|
||||
ctx['organizer'] = self.request.organizer
|
||||
return ctx
|
||||
|
||||
@@ -356,6 +361,9 @@ class CloneEventViewSet(viewsets.ModelViewSet):
|
||||
if not perm_holder.has_organizer_permission(self.request.organizer, "organizer.events:create", request=self.request):
|
||||
raise PermissionDenied("No permission to create events")
|
||||
|
||||
if not serializer.context['event'].allow_copy_data(self.request.organizer, perm_holder):
|
||||
raise PermissionDenied("Not sufficient permission on source event to copy")
|
||||
|
||||
serializer.save(organizer=self.request.organizer)
|
||||
|
||||
serializer.instance.log_action(
|
||||
|
||||
@@ -843,6 +843,33 @@ class Event(EventMixin, LoggedModel):
|
||||
time(hour=23, minute=59, second=59)
|
||||
), tz)
|
||||
|
||||
def allow_copy_data(self, new_organizer, auth) -> bool:
|
||||
"""
|
||||
Returns whether it is allowed to copy the event to the target organizer. Auth can be TeamAPIToken or User.
|
||||
"""
|
||||
from ..permissions import get_all_event_permissions
|
||||
from .auth import User
|
||||
|
||||
if self.organizer == new_organizer:
|
||||
# Copying in the same organizer is always okay with any read access, we just need to ensure it does not
|
||||
# grant more permissions than I had before, but that is handled by the view logic
|
||||
return auth.has_event_permission(self.organizer, self, None)
|
||||
|
||||
if isinstance(auth, User):
|
||||
# Cross-organizer copying requires almost full permission of source to prevent settings extraction
|
||||
required_permissions = get_all_event_permissions() - {
|
||||
# We do not require these, as this data is not copied
|
||||
"event:orders.read", "event:orders.write", "event:vouchers.read", "event:vouchers.write",
|
||||
"event:subevents.write",
|
||||
}
|
||||
given_permission = auth.get_event_permission_set(self.organizer, self)
|
||||
return all(p in given_permission for p in required_permissions if ":" not in p)
|
||||
|
||||
else:
|
||||
# Tokens or devices can never copy between organizers, as they are organizer-bound. Kept for future
|
||||
# compatibility and easier calling
|
||||
return False
|
||||
|
||||
def copy_data_from(self, other, skip_meta_data=False):
|
||||
from ..signals import event_copy_data
|
||||
from . import (
|
||||
|
||||
@@ -101,6 +101,7 @@ class EventWizardFoundationForm(forms.Form):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.user = kwargs.pop('user')
|
||||
self.session = kwargs.pop('session')
|
||||
self.clone_from = kwargs.pop('clone_from')
|
||||
super().__init__(*args, **kwargs)
|
||||
qs = Organizer.objects.all()
|
||||
if not self.user.has_active_staff_session(self.session.session_key):
|
||||
@@ -126,6 +127,16 @@ class EventWizardFoundationForm(forms.Form):
|
||||
self.fields['organizer'].initial = organizer
|
||||
self.fields['locales'].initial = organizer.settings.locales
|
||||
|
||||
def clean(self):
|
||||
d = super().clean()
|
||||
if d.get('organizer') and self.clone_from and not self.user.has_active_staff_session(self.session.session_key):
|
||||
if not self.clone_from.allow_copy_data(d['organizer'], self.user):
|
||||
raise ValidationError({
|
||||
"organizer": _("You do not have a sufficient level of access on the event you selected "
|
||||
"to copy it to the desired organizer.")
|
||||
})
|
||||
return d
|
||||
|
||||
|
||||
class EventWizardBasicsForm(I18nModelForm):
|
||||
error_messages = {
|
||||
@@ -199,6 +210,7 @@ class EventWizardBasicsForm(I18nModelForm):
|
||||
self.has_subevents = kwargs.pop('has_subevents')
|
||||
self.user = kwargs.pop('user')
|
||||
self.session = kwargs.pop('session')
|
||||
self.clone_from = kwargs.pop('clone_from')
|
||||
super().__init__(*args, **kwargs)
|
||||
if 'timezone' not in self.initial:
|
||||
self.initial['timezone'] = get_current_timezone_name()
|
||||
@@ -239,6 +251,18 @@ class EventWizardBasicsForm(I18nModelForm):
|
||||
'check "{field}" above.').format(field=self.fields["no_taxes"].label)
|
||||
})
|
||||
|
||||
if self.clone_from and not self.user.has_active_staff_session(self.session.session_key):
|
||||
if data.get("team"):
|
||||
source_event_perms = self.user.get_event_permission_set(self.organizer, self.clone_from)
|
||||
team_perms = data["team"].event_permission_set(include_legacy=False)
|
||||
print(source_event_perms)
|
||||
print(team_perms)
|
||||
if any(t not in source_event_perms for t in team_perms if ":" in t):
|
||||
raise ValidationError({
|
||||
"team": _("You cannot choose a team that would give you more access than you have on "
|
||||
"the event you are copying.")
|
||||
})
|
||||
|
||||
# change timezone
|
||||
zone = ZoneInfo(data.get('timezone'))
|
||||
data['date_from'] = self.reset_timezone(zone, data.get('date_from'))
|
||||
@@ -262,10 +286,9 @@ class EventWizardBasicsForm(I18nModelForm):
|
||||
|
||||
@staticmethod
|
||||
def has_control_rights(user, organizer, session):
|
||||
# It's mostly pointless to let a user create an event where they can't event change the name or create products,
|
||||
# so we detect if the user has sufficient access for that on a new event.
|
||||
return user.teams.filter(
|
||||
TeamQuerySet.event_permission_q("event.items:write"),
|
||||
TeamQuerySet.event_permission_q("event.orders:write"),
|
||||
TeamQuerySet.event_permission_q("event.vouchers:write"),
|
||||
TeamQuerySet.event_permission_q("event.settings.general:write"),
|
||||
organizer=organizer,
|
||||
all_events=True,
|
||||
@@ -298,23 +321,24 @@ class EventWizardCopyForm(forms.Form):
|
||||
if user.has_active_staff_session(session.session_key):
|
||||
return Event.objects.all()
|
||||
return Event.objects.filter(
|
||||
# It is generally pointless to let users copy events when they would not even be able to change the
|
||||
# date of the event they have just created. Therefore, even if it looks wrong, we're checking a write
|
||||
# permission for read access.
|
||||
Q(organizer_id__in=user.teams.filter(
|
||||
# TODO: review these!
|
||||
# Restrict cross-organizer copying further than same-organizer copying?
|
||||
TeamQuerySet.event_permission_q("event.settings.general:write"),
|
||||
TeamQuerySet.event_permission_q("event.items:write"),
|
||||
all_events=True,
|
||||
).values_list('organizer', flat=True)) | Q(id__in=user.teams.filter(
|
||||
TeamQuerySet.event_permission_q("event.settings.general:write"),
|
||||
TeamQuerySet.event_permission_q("event.items:write"),
|
||||
).values_list('limit_events__id', flat=True))
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs.pop('organizer')
|
||||
self.organizer = kwargs.pop('organizer')
|
||||
kwargs.pop('locales')
|
||||
self.session = kwargs.pop('session')
|
||||
self.team = kwargs.pop('team')
|
||||
kwargs.pop('has_subevents')
|
||||
kwargs.pop('clone_from')
|
||||
self.user = kwargs.pop('user')
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -333,6 +357,24 @@ class EventWizardCopyForm(forms.Form):
|
||||
)
|
||||
self.fields['copy_from_event'].widget.choices = self.fields['copy_from_event'].choices
|
||||
|
||||
def clean(self):
|
||||
d = super().clean()
|
||||
if d.get('copy_from_event') and not self.user.has_active_staff_session(self.session.session_key):
|
||||
if not d['copy_from_event'].allow_copy_data(self.organizer, self.user):
|
||||
raise ValidationError({
|
||||
"copy_from_event": _("You do not have a sufficient level of access on the event you selected "
|
||||
"to copy it to the desired organizer.")
|
||||
})
|
||||
if self.team:
|
||||
source_event_perms = self.user.get_event_permission_set(self.organizer, d['copy_from_event'])
|
||||
team_perms = self.team.event_permission_set(include_legacy=False)
|
||||
if any(t not in source_event_perms for t in team_perms if ":" in t):
|
||||
raise ValidationError({
|
||||
"copy_from_event": _("You cannot choose an event on which you have less access than the "
|
||||
"team you selected in the previous step.")
|
||||
})
|
||||
return d
|
||||
|
||||
|
||||
class EventMetaValueForm(forms.ModelForm):
|
||||
|
||||
|
||||
@@ -211,12 +211,7 @@ class EventWizard(SafeSessionWizardView):
|
||||
except Event.DoesNotExist:
|
||||
allow = False
|
||||
else:
|
||||
allow = (
|
||||
request.user.has_event_permission(clone_from.organizer, clone_from,
|
||||
'event.settings.general:write', request)
|
||||
and request.user.has_event_permission(clone_from.organizer, clone_from,
|
||||
'event.items:write', request)
|
||||
)
|
||||
allow = request.user.has_event_permission(clone_from.organizer, clone_from, None, request)
|
||||
if not allow:
|
||||
messages.error(self.request, _('You do not have permission to clone this event.'))
|
||||
else:
|
||||
@@ -242,6 +237,7 @@ class EventWizard(SafeSessionWizardView):
|
||||
kwargs = {
|
||||
'user': self.request.user,
|
||||
'session': self.request.session,
|
||||
'clone_from': self.clone_from,
|
||||
}
|
||||
if step != 'foundation':
|
||||
fdata = self.get_cleaned_data_for_step('foundation')
|
||||
@@ -253,6 +249,13 @@ class EventWizard(SafeSessionWizardView):
|
||||
}
|
||||
# The show must go on, we catch this error in render()
|
||||
kwargs.update(fdata)
|
||||
if step == 'copy':
|
||||
bdata = self.get_cleaned_data_for_step('basics')
|
||||
if bdata:
|
||||
bdata = {
|
||||
'team': bdata.get('team'),
|
||||
}
|
||||
kwargs.update(bdata)
|
||||
return kwargs
|
||||
|
||||
def get_template_names(self):
|
||||
@@ -277,18 +280,41 @@ class EventWizard(SafeSessionWizardView):
|
||||
user=self.request.user,
|
||||
)
|
||||
|
||||
if not EventWizardBasicsForm.has_control_rights(self.request.user, event.organizer, self.request.session):
|
||||
if copy_data and copy_data['copy_from_event']:
|
||||
copy_from_event = copy_data['copy_from_event']
|
||||
elif self.clone_from:
|
||||
copy_from_event = self.clone_from
|
||||
else:
|
||||
copy_from_event = None
|
||||
|
||||
if not EventWizardBasicsForm.has_control_rights(
|
||||
self.request.user, event.organizer, self.request.session
|
||||
):
|
||||
if basics_data["team"] is not None:
|
||||
t = basics_data["team"]
|
||||
t.limit_events.add(event)
|
||||
elif event.organizer.settings.event_team_provisioning:
|
||||
# Create a new team for new events with full access, but for copied events with the same access
|
||||
# as the source
|
||||
limit_event_permissions = {}
|
||||
if copy_from_event and copy_from_event.organizer == event.organizer:
|
||||
source_teams = self.request.user._get_teams_for_event(copy_from_event.organizer, copy_from_event)
|
||||
all_event_permissions = any(t.all_event_permissions for t in source_teams)
|
||||
if not all_event_permissions:
|
||||
for t in source_teams:
|
||||
limit_event_permissions.update(t.limit_event_permissions)
|
||||
else:
|
||||
# The cross-organizer case is protected through allow_copy_data
|
||||
all_event_permissions = True
|
||||
|
||||
t = Team.objects.create(
|
||||
organizer=event.organizer,
|
||||
name=_('Team {event}').format(
|
||||
event=str(event.name)[:100] + "…" if len(str(event.name)) > 100 else str(event.name)
|
||||
),
|
||||
all_organizer_permissions=False,
|
||||
all_event_permissions=True,
|
||||
all_event_permissions=all_event_permissions,
|
||||
limit_event_permissions=limit_event_permissions,
|
||||
)
|
||||
t.members.add(self.request.user)
|
||||
t.limit_events.add(event)
|
||||
@@ -296,7 +322,8 @@ class EventWizard(SafeSessionWizardView):
|
||||
'_created_by_event_wizard': True,
|
||||
'name': t.name,
|
||||
'all_organizer_permissions': False,
|
||||
'all_event_permissions': True,
|
||||
'all_event_permissions': all_event_permissions,
|
||||
'limit_event_permissions': limit_event_permissions,
|
||||
'limit_events': [event.pk],
|
||||
})
|
||||
|
||||
@@ -307,11 +334,8 @@ class EventWizard(SafeSessionWizardView):
|
||||
})
|
||||
event.log_action('pretix.event.settings', user=self.request.user, data=logdata)
|
||||
|
||||
if copy_data and copy_data['copy_from_event']:
|
||||
from_event = copy_data['copy_from_event']
|
||||
event.copy_data_from(from_event)
|
||||
elif self.clone_from:
|
||||
event.copy_data_from(self.clone_from)
|
||||
if copy_from_event:
|
||||
event.copy_data_from(copy_from_event)
|
||||
else:
|
||||
event.set_active_plugins(settings.PRETIX_PLUGINS_DEFAULT.split(","),
|
||||
allow_restricted=settings.PRETIX_PLUGINS_DEFAULT.split(","))
|
||||
@@ -324,10 +348,8 @@ class EventWizard(SafeSessionWizardView):
|
||||
event.set_defaults()
|
||||
|
||||
if basics_data['tax_rate'] is not None:
|
||||
if self.clone_from:
|
||||
default_tax_rule = self.clone_from.cached_default_tax_rule
|
||||
elif copy_data and copy_data['copy_from_event']:
|
||||
default_tax_rule = from_event.cached_default_tax_rule
|
||||
if copy_from_event:
|
||||
default_tax_rule = copy_from_event.cached_default_tax_rule
|
||||
else:
|
||||
default_tax_rule = None
|
||||
if not default_tax_rule or default_tax_rule.rate != basics_data['tax_rate']:
|
||||
@@ -341,7 +363,7 @@ class EventWizard(SafeSessionWizardView):
|
||||
event.settings.set('locale', basics_data['locale'])
|
||||
event.settings.set('locales', foundation_data['locales'])
|
||||
|
||||
if (copy_data and copy_data['copy_from_event']) or self.clone_from or event.has_subevents:
|
||||
if copy_from_event or event.has_subevents:
|
||||
return redirect(reverse('control:event.settings', kwargs={
|
||||
'organizer': event.organizer.slug,
|
||||
'event': event.slug,
|
||||
|
||||
@@ -577,7 +577,9 @@ def test_event_create_with_clone_unknown_source(user, user_client, organizer, ev
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_create_with_clone_across_organizers(user, user_client, organizer, event, taxrule):
|
||||
def test_event_create_with_clone_across_organizers(user, user_client, organizer, event, taxrule, team):
|
||||
team.all_event_permissions = True
|
||||
team.save()
|
||||
with scopes_disabled():
|
||||
target_org = Organizer.objects.create(name='Dummy', slug='dummy2')
|
||||
team = target_org.teams.create(
|
||||
@@ -622,6 +624,51 @@ def test_event_create_with_clone_across_organizers(user, user_client, organizer,
|
||||
assert cloned_event.tax_rules.exists()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_create_with_clone_across_organizers_lack_of_permission_on_source(user, user_client, team, organizer, event, taxrule):
|
||||
team.all_event_permissions = False
|
||||
team.limit_event_permissions = {
|
||||
"event.settings.general:write": True,
|
||||
}
|
||||
team.save()
|
||||
with scopes_disabled():
|
||||
target_org = Organizer.objects.create(name='Dummy', slug='dummy2')
|
||||
team = target_org.teams.create(
|
||||
name="Test-Team",
|
||||
all_event_permissions=True,
|
||||
all_organizer_permissions=True,
|
||||
)
|
||||
team.members.add(user)
|
||||
|
||||
resp = user_client.post(
|
||||
'/api/v1/organizers/{}/events/?clone_from={}/{}'.format(target_org.slug, organizer.slug, event.slug),
|
||||
{
|
||||
"name": {
|
||||
"de": "Demo Konference 2020 Test",
|
||||
"en": "Demo Conference 2020 Test"
|
||||
},
|
||||
"live": False,
|
||||
"testmode": True,
|
||||
"currency": "EUR",
|
||||
"date_from": "2018-12-27T10:00:00Z",
|
||||
"date_to": "2018-12-28T10:00:00Z",
|
||||
"date_admission": None,
|
||||
"is_public": False,
|
||||
"presale_start": None,
|
||||
"presale_end": None,
|
||||
"location": None,
|
||||
"slug": "2030",
|
||||
"plugins": [
|
||||
"pretix.plugins.ticketoutputpdf"
|
||||
],
|
||||
"timezone": "Europe/Vienna"
|
||||
},
|
||||
format='json'
|
||||
)
|
||||
assert resp.status_code == 403
|
||||
assert resp.data["detail"] == "Not sufficient permission on source event to copy"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_put_with_clone(token_client, organizer, event, meta_prop):
|
||||
resp = token_client.put(
|
||||
|
||||
@@ -76,11 +76,16 @@ class EventsTest(SoupTest):
|
||||
date_from=datetime.datetime(2014, 9, 5, tzinfo=datetime.timezone.utc),
|
||||
)
|
||||
|
||||
self.team1 = Team.objects.create(organizer=self.orga1, all_event_permissions=True, limit_organizer_permissions={"organizer.events:create": True})
|
||||
self.team1 = Team.objects.create(
|
||||
organizer=self.orga1,
|
||||
name="T1",
|
||||
all_event_permissions=True,
|
||||
limit_organizer_permissions={"organizer.events:create": True}
|
||||
)
|
||||
self.team1.members.add(self.user)
|
||||
self.team1.limit_events.add(self.event1)
|
||||
|
||||
self.team2 = Team.objects.create(organizer=self.orga1, all_event_permissions=True)
|
||||
self.team2 = Team.objects.create(organizer=self.orga1, name="T2", all_event_permissions=True)
|
||||
self.team2.members.add(self.user)
|
||||
|
||||
self.client.login(email='dummy@dummy.dummy', password='dummy')
|
||||
@@ -1260,6 +1265,265 @@ class EventsTest(SoupTest):
|
||||
})
|
||||
assert doc.select(".has-error")
|
||||
|
||||
def test_create_event_copy_from_other_org_validates_source_permissions(self):
|
||||
# To prevent leaks of e.g. settings contents, a user may only copy from one organizer to the other
|
||||
# if they have basically all permissions on the old event for all data that may be copied.
|
||||
self.team1.all_event_permissions = False
|
||||
self.team1.limit_event_permissions = {"event.settings.general:write": True, "event.orders:read": True}
|
||||
self.team1.save()
|
||||
team3 = Team.objects.create(organizer=self.orga2, all_event_permissions=True, all_organizer_permissions=True)
|
||||
team3.members.add(self.user)
|
||||
|
||||
doc = self.post_doc('/control/events/add', {
|
||||
'event_wizard-current_step': 'foundation',
|
||||
'event_wizard-prefix': 'event_wizard',
|
||||
'foundation-organizer': self.orga2.pk,
|
||||
'foundation-locales': ('en', 'de')
|
||||
})
|
||||
assert doc.select("#id_basics-name_0")
|
||||
|
||||
doc = self.post_doc('/control/events/add', {
|
||||
'event_wizard-current_step': 'basics',
|
||||
'event_wizard-prefix': 'event_wizard',
|
||||
'basics-name_0': '33C3',
|
||||
'basics-name_1': '33C3',
|
||||
'basics-slug': '33c3',
|
||||
'basics-date_from_0': '2016-12-27',
|
||||
'basics-date_from_1': '10:00:00',
|
||||
'basics-date_to_0': '2016-12-30',
|
||||
'basics-date_to_1': '19:00:00',
|
||||
'basics-location_0': 'Hamburg',
|
||||
'basics-location_1': 'Hamburg',
|
||||
'basics-currency': 'EUR',
|
||||
'basics-tax_rate': '19.00',
|
||||
'basics-locale': 'en',
|
||||
'basics-timezone': 'Europe/Berlin',
|
||||
'basics-presale_start_0': '2016-11-01',
|
||||
'basics-presale_start_1': '10:00:00',
|
||||
'basics-presale_end_0': '2016-11-30',
|
||||
'basics-presale_end_1': '18:00:00',
|
||||
})
|
||||
assert doc.select("#id_copy-copy_from_event")
|
||||
|
||||
doc = self.post_doc('/control/events/add', {
|
||||
'event_wizard-current_step': 'copy',
|
||||
'event_wizard-prefix': 'event_wizard',
|
||||
'copy-copy_from_event': self.event1.pk
|
||||
})
|
||||
assert doc.select(".alert-danger")
|
||||
assert "sufficient level of access" in doc.select(".has-error")[0].text
|
||||
|
||||
def test_create_event_clone_from_other_org_validates_source_permissions(self):
|
||||
# To prevent leaks of e.g. settings contents, a user may only copy from one organizer to the other
|
||||
# if they have basically all permissions on the old event for all data that may be copied.
|
||||
self.team1.all_event_permissions = False
|
||||
self.team1.limit_event_permissions = {"event.settings.general:write": True, "event.orders:read": True}
|
||||
self.team1.save()
|
||||
team3 = Team.objects.create(organizer=self.orga2, all_event_permissions=True, all_organizer_permissions=True)
|
||||
team3.members.add(self.user)
|
||||
|
||||
doc = self.post_doc(f'/control/events/add?clone={self.event1.pk}', {
|
||||
'event_wizard-current_step': 'foundation',
|
||||
'event_wizard-prefix': 'event_wizard',
|
||||
'foundation-organizer': self.orga2.pk,
|
||||
'foundation-locales': ('en', 'de')
|
||||
})
|
||||
assert doc.select(".alert-danger")
|
||||
assert "sufficient level of access" in doc.select(".has-error")[0].text
|
||||
|
||||
def test_create_event_copy_from_same_org_creates_new_team_with_same_permissions(self):
|
||||
# To prevent unwanted permission escalations, when a user copies an event and a new team is created to make
|
||||
# sure they can access the new event, the new event must be created with the same level of access they have
|
||||
# on the old event.
|
||||
self.team1.all_event_permissions = False
|
||||
self.team1.limit_event_permissions = {"event.settings.general:write": True, "event.orders:read": True}
|
||||
self.team1.save()
|
||||
|
||||
doc = self.post_doc('/control/events/add', {
|
||||
'event_wizard-current_step': 'foundation',
|
||||
'event_wizard-prefix': 'event_wizard',
|
||||
'foundation-organizer': self.orga1.pk,
|
||||
'foundation-locales': ('en', 'de')
|
||||
})
|
||||
assert doc.select("#id_basics-name_0")
|
||||
|
||||
doc = self.post_doc('/control/events/add', {
|
||||
'event_wizard-current_step': 'basics',
|
||||
'event_wizard-prefix': 'event_wizard',
|
||||
'basics-name_0': '33C3',
|
||||
'basics-name_1': '33C3',
|
||||
'basics-slug': '33c3',
|
||||
'basics-date_from_0': '2016-12-27',
|
||||
'basics-date_from_1': '10:00:00',
|
||||
'basics-date_to_0': '2016-12-30',
|
||||
'basics-date_to_1': '19:00:00',
|
||||
'basics-location_0': 'Hamburg',
|
||||
'basics-location_1': 'Hamburg',
|
||||
'basics-currency': 'EUR',
|
||||
'basics-tax_rate': '19.00',
|
||||
'basics-locale': 'en',
|
||||
'basics-timezone': 'Europe/Berlin',
|
||||
'basics-presale_start_0': '2016-11-01',
|
||||
'basics-presale_start_1': '10:00:00',
|
||||
'basics-presale_end_0': '2016-11-30',
|
||||
'basics-presale_end_1': '18:00:00',
|
||||
})
|
||||
assert doc.select("#id_copy-copy_from_event")
|
||||
|
||||
self.post_doc('/control/events/add', {
|
||||
'event_wizard-current_step': 'copy',
|
||||
'event_wizard-prefix': 'event_wizard',
|
||||
'copy-copy_from_event': self.event1.pk
|
||||
})
|
||||
with scopes_disabled():
|
||||
ev = Event.objects.get(slug='33c3')
|
||||
new_team = Team.objects.get(limit_events=ev, members=self.user)
|
||||
assert new_team.pk > self.team2.pk
|
||||
assert new_team.all_event_permissions is False
|
||||
assert new_team.all_organizer_permissions is False
|
||||
assert new_team.limit_event_permissions == {"event.settings.general:write": True, "event.orders:read": True}
|
||||
assert new_team.limit_organizer_permissions == {}
|
||||
assert new_team.all_events is False
|
||||
assert new_team.limit_events.get() == ev
|
||||
|
||||
def test_create_event_clone_from_same_org_creates_new_team_with_same_permissions(self):
|
||||
# To prevent unwanted permission escalations, when a user copies an event and a new team is created to make
|
||||
# sure they can access the new event, the new event must be created with the same level of access they have
|
||||
# on the old event.
|
||||
self.team1.all_event_permissions = False
|
||||
self.team1.limit_event_permissions = {"event.settings.general:write": True, "event.orders:read": True}
|
||||
self.team1.save()
|
||||
|
||||
doc = self.post_doc(f'/control/events/add?clone={self.event1.pk}', {
|
||||
'event_wizard-current_step': 'foundation',
|
||||
'event_wizard-prefix': 'event_wizard',
|
||||
'foundation-organizer': self.orga1.pk,
|
||||
'foundation-locales': ('en', 'de')
|
||||
})
|
||||
assert doc.select("#id_basics-name_0")
|
||||
|
||||
self.post_doc(f'/control/events/add?clone={self.event1.pk}', {
|
||||
'event_wizard-current_step': 'basics',
|
||||
'event_wizard-prefix': 'event_wizard',
|
||||
'basics-name_0': '33C3',
|
||||
'basics-name_1': '33C3',
|
||||
'basics-slug': '33c3',
|
||||
'basics-date_from_0': '2016-12-27',
|
||||
'basics-date_from_1': '10:00:00',
|
||||
'basics-date_to_0': '2016-12-30',
|
||||
'basics-date_to_1': '19:00:00',
|
||||
'basics-location_0': 'Hamburg',
|
||||
'basics-location_1': 'Hamburg',
|
||||
'basics-currency': 'EUR',
|
||||
'basics-tax_rate': '19.00',
|
||||
'basics-locale': 'en',
|
||||
'basics-timezone': 'Europe/Berlin',
|
||||
'basics-presale_start_0': '2016-11-01',
|
||||
'basics-presale_start_1': '10:00:00',
|
||||
'basics-presale_end_0': '2016-11-30',
|
||||
'basics-presale_end_1': '18:00:00',
|
||||
})
|
||||
with scopes_disabled():
|
||||
ev = Event.objects.get(slug='33c3')
|
||||
new_team = Team.objects.get(limit_events=ev, members=self.user)
|
||||
assert new_team.pk > self.team2.pk
|
||||
assert new_team.all_event_permissions is False
|
||||
assert new_team.all_organizer_permissions is False
|
||||
assert new_team.limit_event_permissions == {"event.settings.general:write": True, "event.orders:read": True}
|
||||
assert new_team.limit_organizer_permissions == {}
|
||||
assert new_team.all_events is False
|
||||
assert new_team.limit_events.get() == ev
|
||||
|
||||
def test_create_event_copy_from_same_org_validates_selected_team_permissions(self):
|
||||
# To prevent unwanted permission escalations, when a user copies an event and selects the team the new event
|
||||
# should be attached to, this new team may not have higher permissions than the permissions the user holds for
|
||||
# the event that is copied from.
|
||||
self.team1.all_event_permissions = False
|
||||
self.team1.limit_event_permissions = {"event.settings.general:write": True, "event.orders:read": True}
|
||||
self.team1.save()
|
||||
|
||||
doc = self.post_doc('/control/events/add', {
|
||||
'event_wizard-current_step': 'foundation',
|
||||
'event_wizard-prefix': 'event_wizard',
|
||||
'foundation-organizer': self.orga1.pk,
|
||||
'foundation-locales': ('en', 'de')
|
||||
})
|
||||
assert doc.select("#id_basics-name_0")
|
||||
assert doc.select("#id_basics-team")
|
||||
|
||||
doc = self.post_doc('/control/events/add', {
|
||||
'event_wizard-current_step': 'basics',
|
||||
'event_wizard-prefix': 'event_wizard',
|
||||
'basics-name_0': '33C3',
|
||||
'basics-name_1': '33C3',
|
||||
'basics-slug': '33c3',
|
||||
'basics-date_from_0': '2016-12-27',
|
||||
'basics-date_from_1': '10:00:00',
|
||||
'basics-date_to_0': '2016-12-30',
|
||||
'basics-date_to_1': '19:00:00',
|
||||
'basics-location_0': 'Hamburg',
|
||||
'basics-location_1': 'Hamburg',
|
||||
'basics-currency': 'EUR',
|
||||
'basics-tax_rate': '19.00',
|
||||
'basics-locale': 'en',
|
||||
'basics-team': self.team2.pk,
|
||||
'basics-timezone': 'Europe/Berlin',
|
||||
'basics-presale_start_0': '2016-11-01',
|
||||
'basics-presale_start_1': '10:00:00',
|
||||
'basics-presale_end_0': '2016-11-30',
|
||||
'basics-presale_end_1': '18:00:00',
|
||||
})
|
||||
assert doc.select("#id_copy-copy_from_event")
|
||||
|
||||
doc = self.post_doc('/control/events/add', {
|
||||
'event_wizard-current_step': 'copy',
|
||||
'event_wizard-prefix': 'event_wizard',
|
||||
'copy-copy_from_event': self.event1.pk
|
||||
})
|
||||
assert doc.select(".alert-danger")
|
||||
assert "less access than" in doc.select(".has-error")[0].text
|
||||
|
||||
def test_create_event_clone_from_same_org_validates_selected_team_permissions(self):
|
||||
# To prevent unwanted permission escalations, when a user copies an event and selects the team the new event
|
||||
# should be attached to, this new team may not have higher permissions than the permissions the user holds for
|
||||
# the event that is copied from.
|
||||
self.team1.all_event_permissions = False
|
||||
self.team1.limit_event_permissions = {"event.settings.general:write": True, "event.orders:read": True}
|
||||
self.team1.save()
|
||||
|
||||
doc = self.post_doc(f'/control/events/add?clone={self.event1.pk}', {
|
||||
'event_wizard-current_step': 'foundation',
|
||||
'event_wizard-prefix': 'event_wizard',
|
||||
'foundation-organizer': self.orga1.pk,
|
||||
'foundation-locales': ('en', 'de')
|
||||
})
|
||||
assert doc.select("#id_basics-name_0")
|
||||
assert doc.select("#id_basics-team")
|
||||
|
||||
doc = self.post_doc(f'/control/events/add?clone={self.event1.pk}', {
|
||||
'event_wizard-current_step': 'basics',
|
||||
'event_wizard-prefix': 'event_wizard',
|
||||
'basics-name_0': '33C3',
|
||||
'basics-name_1': '33C3',
|
||||
'basics-slug': '33c3',
|
||||
'basics-date_from_0': '2016-12-27',
|
||||
'basics-date_from_1': '10:00:00',
|
||||
'basics-date_to_0': '2016-12-30',
|
||||
'basics-date_to_1': '19:00:00',
|
||||
'basics-location_0': 'Hamburg',
|
||||
'basics-location_1': 'Hamburg',
|
||||
'basics-currency': 'EUR',
|
||||
'basics-tax_rate': '19.00',
|
||||
'basics-locale': 'en',
|
||||
'basics-team': self.team2.pk,
|
||||
'basics-timezone': 'Europe/Berlin',
|
||||
'basics-presale_start_0': '2016-11-01',
|
||||
'basics-presale_start_1': '10:00:00',
|
||||
'basics-presale_end_0': '2016-11-30',
|
||||
'basics-presale_end_1': '18:00:00',
|
||||
})
|
||||
assert "would give you more access than" in doc.select(".has-error")[0].text
|
||||
|
||||
|
||||
class EventDeletionTest(SoupTest):
|
||||
@scopes_disabled()
|
||||
|
||||
Reference in New Issue
Block a user