Select Git revision
tailwind.config.js
security.py 5.79 KiB
"""
Helios Security -- mostly access control
Ben Adida (ben@adida.net)
"""
import urllib.parse
# nicely update the wrapper function
from functools import update_wrapper
from django.conf import settings
from django.core.exceptions import PermissionDenied
from django.http import Http404
from django.http import HttpResponseRedirect
from django.urls import reverse
import helios
from helios_auth.security import get_user
from .models import Voter, Trustee, Election
class HSTSMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
# Code to be executed for each request/response after
# the view is called.
if settings.STS:
response['Strict-Transport-Security'] = "max-age=31536000; includeSubDomains; preload"
return response
# current voter
def get_voter(request, user, election):
"""
return the current voter
"""
voter = None
if 'CURRENT_VOTER_ID' in request.session:
voter = Voter.objects.get(id=request.session['CURRENT_VOTER_ID'])
if voter.election != election:
voter = None
if not voter:
if user:
voter = Voter.get_by_election_and_user(election, user)
return voter
# a function to check if the current user is a trustee
HELIOS_TRUSTEE_UUID = 'helios_trustee_uuid'
def get_logged_in_trustee(request):
if HELIOS_TRUSTEE_UUID in request.session:
return Trustee.get_by_uuid(request.session[HELIOS_TRUSTEE_UUID])
else:
return None
def set_logged_in_trustee(request, trustee):
request.session[HELIOS_TRUSTEE_UUID] = trustee.uuid
#
# some common election checks
#
def do_election_checks(election, props):
# frozen
if 'frozen' in props:
frozen = props['frozen']
else:
frozen = None
# newvoters (open for registration)
if 'newvoters' in props:
newvoters = props['newvoters']
else:
newvoters = None
# frozen check
if frozen is not None:
if frozen and not election.frozen_at:
raise PermissionDenied()
if not frozen and election.frozen_at:
raise PermissionDenied()
# open for new voters check
if newvoters is not None:
if election.can_add_voters() != newvoters:
raise PermissionDenied()
def get_election_by_uuid(uuid):
if not uuid:
raise Exception("no election ID")
return Election.get_by_uuid(uuid)
# decorator for views that pertain to an election
# takes parameters:
# frozen - is the election frozen
# newvoters - does the election accept new voters
def election_view(**checks):
def election_view_decorator(func):
def election_view_wrapper(request, election_uuid=None, *args, **kw):
election = get_election_by_uuid(election_uuid)
if not election:
raise Http404
# do checks
do_election_checks(election, checks)
# if private election, only logged in voters
if election.private_p and not checks.get('allow_logins',False):
from .views import password_voter_login
if not user_can_see_election(request, election):
return_url = request.get_full_path()
return HttpResponseRedirect("%s?%s" % (reverse(password_voter_login, args=[election.uuid]), urllib.parse.urlencode({
'return_url' : return_url
})))
return func(request, election, *args, **kw)
return update_wrapper(election_view_wrapper, func)
return election_view_decorator
def user_can_admin_election(user, election):
if not user:
return False
# election or site administrator
return election.admin == user or user.admin_p
def user_can_see_election(request, election):
user = get_user(request)
if not election.private_p:
return True
# election is private
# but maybe this user is the administrator?
if user_can_admin_election(user, election):
return True
# or maybe this is a trustee of the election?
trustee = get_logged_in_trustee(request)
if trustee and trustee.election.uuid == election.uuid:
return True
# then this user has to be a voter
return get_voter(request, user, election) is not None
def api_client_can_admin_election(api_client, election):
return election.api_client == api_client and api_client is not None
# decorator for checking election admin access, and some properties of the election
# frozen - is the election frozen
# newvoters - does the election accept new voters
def election_admin(**checks):
def election_admin_decorator(func):
def election_admin_wrapper(request, election_uuid=None, *args, **kw):
election = get_election_by_uuid(election_uuid)
user = get_user(request)
if not user_can_admin_election(user, election):
raise PermissionDenied()
# do checks
do_election_checks(election, checks)
return func(request, election, *args, **kw)
return update_wrapper(election_admin_wrapper, func)
return election_admin_decorator
def trustee_check(func):
def trustee_check_wrapper(request, election_uuid, trustee_uuid, *args, **kwargs):
election = get_election_by_uuid(election_uuid)
trustee = Trustee.get_by_election_and_uuid(election, trustee_uuid)
if trustee == get_logged_in_trustee(request):
return func(request, election, trustee, *args, **kwargs)
else:
raise PermissionDenied()
return update_wrapper(trustee_check_wrapper, func)
def can_create_election(request):
user = get_user(request)
if not user:
return False
if helios.ADMIN_ONLY:
return user.admin_p
else:
return user.can_create_election()
def user_can_feature_election(user, election):
if not user:
return False
return user.admin_p