conference-website/conference_main/permissions.py

202 lines
6.7 KiB
Python

"""
The functions in this file are used to check *user* permissions.
There are two kinds of permission systems in use. One of them is defined in this
file, the other is the Django permission system (accessed by `User.has_perm`).
The system in this file describe the permissions for the user of the website and
thus we call this one the *user* permission system. And the Django permission
system is used to describe the permissions of the admins/staff/moderators and
thus we call this system the *admin* permission system.
The reason for this distinction is that the Django permission system is used by
the admin interface and consequently is designed for global permissions like
"This user can update Events", and not per se for granular permissions of the
form "This user can update Events that he/she owns". To have the best of both
worlds we basically extend the Django permission system with more granular
fallbacks.
So per *user* permissions we define a function in this file. If there is a
relevant permission in the Django/admin permission system we check that first,
and failing that defer to more granular logic.
"""
from django.contrib.auth.models import User
from conference_main.models import Edition, Event, FestivalEntry, Profile, FestivalEntryFinalVotes
import tickets.queries
def can_view_event(event: Event, user: User) -> bool:
if user.has_perm('can_view_event'):
# User has Model-level permission
return True
elif event.user == user:
# User is the submitter of the event
return True
elif event.status in ('accepted', 'cancelled') and event.edition.speakers_viewable:
# The event is 'public' and the speaker list is 'public'
return True
else:
return False
def can_add_event(edition: Edition, user: User) -> bool:
if user.has_perm('can_add_event'):
return True
else:
# NOTE: We ignore `edition.presentation_submissions_open` because from past experience we
# know that being able to send users who were too late with submissions a link to the
# submission form is really handy.
return not edition.is_archived
def can_change_event(event: Event, user: User) -> bool:
if user.has_perm('can_change_event'):
return True
elif event.user == user:
return not event.edition.is_archived
else:
return False
def can_view_messages_on_event(event: Event, user: User) -> bool:
if user.has_perm('can_view_message'):
return True
elif event.user == user:
return True
else:
return False
def can_add_message_to_event(event: Event, user: User) -> bool:
if user.has_perm('can_add_message'):
return True
elif event.user == user:
return not event.edition.is_archived
else:
return False
def can_view_festival_entry(event: FestivalEntry, user: User) -> bool:
if user.has_perm('can_view_festival_entry'):
return True
elif event.user == user:
return True
elif event.status in ('accepted', 'winner'):
return True
else:
return False
def can_add_festival_entry(edition: Edition, user: User) -> bool:
if user.has_perm('can_add_festival_entry'):
return True
else:
# NOTE: We ignore `edition.presentation_submissions_open` because from past experience we
# know that being able to send users who were too late with submissions a link to the
# submission form is really handy.
return not edition.is_archived
def can_change_festival_entry(festival_entry: FestivalEntry, user: User) -> bool:
if user.has_perm('can_change_festival_entry'):
return True
elif festival_entry.user == user:
return not festival_entry.edition.is_archived
else:
return False
def can_view_messages_on_festival_entry(festival_entry: FestivalEntry, user: User) -> bool:
if user.has_perm('can_view_message'):
return True
elif festival_entry.user == user:
return True
else:
return False
def can_add_message_to_festival_entry(festival_entry: FestivalEntry, user: User) -> bool:
if user.has_perm('can_add_message'):
return True
elif festival_entry.user == user:
return not festival_entry.edition.is_archived
else:
return False
def can_vote_on_festival_entry(festival_entry: FestivalEntry, user: User) -> bool:
if user.has_perm('can_add_festival_entry_votes'):
return True
elif festival_entry.user.is_anonymous:
# Users need to be logged in to be able to vote.
return False
elif festival_entry.user == user:
# Users cannot vote on their own entry.
return False
elif festival_entry.status not in ('accepted', 'winner'):
# Users cannot vote on non-accepted entries.
return False
else:
return (
not festival_entry.edition.is_archived and festival_entry.edition.festival_voting_open
)
def can_vote_on_festival_entry_final(festival_entry: FestivalEntry, user: User) -> bool:
if not can_vote_on_festival_entry(festival_entry, user):
return False
if festival_entry.status not in {'nominated'}:
# Users cannot vote on entries which weren't nominated for finals.
return False
# Cannot vote if already voted for this edition
if FestivalEntryFinalVotes.objects.filter(
user=user, festival__edition_id=festival_entry.edition_id
).exists():
return False
return True
def can_view_schedule(edition: Edition, user: User) -> bool:
if user.is_superuser:
return True
else:
return edition.schedule_status in (Edition.SCHEDULE_PROPOSED, Edition.SCHEDULE_FINAL)
def can_view_speakers(edition: Edition, user: User) -> bool:
if user.is_superuser:
return True
else:
return edition.speakers_viewable
def can_view_attendees(edition: Edition, user: User) -> bool:
"""Allow attendees and admins view attendees list."""
if user.is_superuser:
return True
if tickets.queries.is_attending_edition(user, edition) and edition.attendees_viewable:
return True
return False
def can_view_profile(profile: Profile, user: User) -> bool:
if user.is_superuser:
return True
elif profile.user == user:
return True
else:
return profile.events.filter(status='accepted', edition__speakers_viewable=True).exists()
def can_add_flatfile(user: User) -> bool:
return user.has_perm('can_add_flatfile')
def can_upload_photos(album, user: User) -> bool:
"""Allow attendees and admins upload new photos."""
if user.is_superuser:
return True
if album.is_upload_open and tickets.queries.is_attending_edition(user, album.edition):
return True
return False