Add csrf token checks to team join/leave as well as event attending views. Fixes #96

This commit is contained in:
Michael Hall 2018-07-07 12:06:01 -04:00
commit f720f626eb
5 changed files with 36 additions and 11 deletions

View file

@ -1,5 +1,8 @@
import re import re
import unicodedata import unicodedata
from django.middleware.csrf import _sanitize_token, _compare_salted_tokens
from django.conf import settings
from django.core.exceptions import PermissionDenied
SLUG_OK = '-_~' SLUG_OK = '-_~'
@ -19,3 +22,18 @@ def slugify(s, ok=SLUG_OK, lower=True, spaces=False):
new = re.sub('[-\s]+', '-', new) new = re.sub('[-\s]+', '-', new)
return new.lower() if lower else new return new.lower() if lower else new
def verify_csrf(token_key='csrftoken'):
def wrap_view(view_func):
def check_csrf_token(request, *args, **kwargs):
csrf_token = _sanitize_token(request.GET.get(token_key, ''))
match = _compare_salted_tokens(csrf_token, request.COOKIES[settings.CSRF_COOKIE_NAME])
if not match:
raise PermissionDenied
else:
return view_func(request, *args, **kwargs)
return check_csrf_token
return wrap_view

View file

@ -11,6 +11,7 @@ from .models.events import Event, EventComment, Place, PlaceSerializer, Attendee
from .models.locale import Country ,CountrySerializer, SPR, SPRSerializer, City, CitySerializer from .models.locale import Country ,CountrySerializer, SPR, SPRSerializer, City, CitySerializer
from .models.profiles import Team, UserProfile, Member, Sponsor, SponsorSerializer from .models.profiles import Team, UserProfile, Member, Sponsor, SponsorSerializer
from .forms import EventCommentForm from .forms import EventCommentForm
from .utils import verify_csrf
import simplejson import simplejson
@ -104,7 +105,9 @@ def sponsor_list(request):
return Response(serializer.data) return Response(serializer.data)
@verify_csrf(token_key='csrftoken')
def join_team(request, team_id): def join_team(request, team_id):
if request.user.is_anonymous: if request.user.is_anonymous:
messages.add_message(request, messages.WARNING, message=_('You must be logged in to join a team.')) messages.add_message(request, messages.WARNING, message=_('You must be logged in to join a team.'))
return redirect('show-team', team_id=team_id) return redirect('show-team', team_id=team_id)
@ -116,6 +119,8 @@ def join_team(request, team_id):
messages.add_message(request, messages.SUCCESS, message=_('Welcome to the team!')) messages.add_message(request, messages.SUCCESS, message=_('Welcome to the team!'))
return redirect('show-team', team_id=team_id) return redirect('show-team', team_id=team_id)
@verify_csrf(token_key='csrftoken')
def leave_team(request, team_id): def leave_team(request, team_id):
if request.user.is_anonymous: if request.user.is_anonymous:
messages.add_message(request, messages.WARNING, message=_('You must be logged in to leave a team.')) messages.add_message(request, messages.WARNING, message=_('You must be logged in to leave a team.'))

View file

@ -124,13 +124,13 @@
{% endif %} {% endif %}
{% if not is_attending %} {% if not is_attending %}
<div class="btn-group"> <div class="btn-group">
<a href="{% url 'attend-event' event.id %}" class="btn btn-success btn-sm"><i class="fa fa-check-square-o"></i> Attend</a> <a href="{% url 'attend-event' event.id %}?csrftoken={{csrf_token}}" class="btn btn-success btn-sm"><i class="fa fa-check-square-o"></i> Attend</a>
<button type="button" class="btn btn-success btn-sm dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <button type="button" class="btn btn-success btn-sm dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="sr-only">Attendance options</span> <span class="sr-only">Attendance options</span>
</button> </button>
<div class="dropdown-menu"> <div class="dropdown-menu">
<a class="dropdown-item" href="{% url 'attend-event' event.id %}?response=maybe">Maybe</a> <a class="dropdown-item" href="{% url 'attend-event' event.id %}?response=maybe&csrftoken={{csrf_token}}">Maybe</a>
<a class="dropdown-item" href="{% url 'attend-event' event.id %}?response=no">No</a> <a class="dropdown-item" href="{% url 'attend-event' event.id %}?response=no&csrftoken={{csrf_token}}">No</a>
</div> </div>
</div> </div>
{% endif %} {% endif %}
@ -287,20 +287,20 @@
{% if attendee.status == attendee.YES %} {% if attendee.status == attendee.YES %}
<span class="badge badge-success dropdown-toggle align-top" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{ attendee.status_name }}</span> <span class="badge badge-success dropdown-toggle align-top" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{ attendee.status_name }}</span>
<div class="dropdown-menu"> <div class="dropdown-menu">
<a class="dropdown-item" href="{% url 'attend-event' event.id %}?response=maybe">Maybe</a> <a class="dropdown-item" href="{% url 'attend-event' event.id %}?response=maybe&csrftoken={{csrf_token}}">Maybe</a>
<a class="dropdown-item" href="{% url 'attend-event' event.id %}?response=no">No</a> <a class="dropdown-item" href="{% url 'attend-event' event.id %}?response=no&csrftoken={{csrf_token}}">No</a>
</div> </div>
{% elif attendee.status == attendee.MAYBE %} {% elif attendee.status == attendee.MAYBE %}
<span class="badge badge-default dropdown-toggle align-top" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{ attendee.status_name }}</span> <span class="badge badge-default dropdown-toggle align-top" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{ attendee.status_name }}</span>
<div class="dropdown-menu"> <div class="dropdown-menu">
<a class="dropdown-item" href="{% url 'attend-event' event.id %}?response=yes">Yes</a> <a class="dropdown-item" href="{% url 'attend-event' event.id %}?response=yes&csrftoken={{csrf_token}}">Yes</a>
<a class="dropdown-item" href="{% url 'attend-event' event.id %}?response=no">No</a> <a class="dropdown-item" href="{% url 'attend-event' event.id %}?response=no&csrftoken={{csrf_token}}">No</a>
</div> </div>
{% elif attendee.status == attendee.NO %} {% elif attendee.status == attendee.NO %}
<span class="badge badge-danger dropdown-toggle align-top" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{ attendee.status_name }}</span> <span class="badge badge-danger dropdown-toggle align-top" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{ attendee.status_name }}</span>
<div class="dropdown-menu"> <div class="dropdown-menu">
<a class="dropdown-item" href="{% url 'attend-event' event.id %}?response=yes">Yes</a> <a class="dropdown-item" href="{% url 'attend-event' event.id %}?response=yes&csrftoken={{csrf_token}}">Yes</a>
<a class="dropdown-item" href="{% url 'attend-event' event.id %}?response=maybe">Maybe</a> <a class="dropdown-item" href="{% url 'attend-event' event.id %}?response=maybe&csrftoken={{csrf_token}}">Maybe</a>
</div> </div>
{% endif %} {% endif %}
{% else %} {% else %}

View file

@ -35,9 +35,9 @@
<a href="{% url 'manage-members' team.id %}" class="btn btn-secondary btn-sm"><i class="fa fa-users"></i> Manage Members</a> <a href="{% url 'manage-members' team.id %}" class="btn btn-secondary btn-sm"><i class="fa fa-users"></i> Manage Members</a>
{% endif %} {% endif %}
{% if is_member %} {% if is_member %}
{% if not team.owner_profile == request.user.profile %}<a href="{% url 'leave-team' team.id %}" class="btn btn-danger btn-sm">Leave Team</a>{% endif %} {% if not team.owner_profile == request.user.profile %}<a href="{% url 'leave-team' team.id %}?csrftoken={{csrf_token}}" class="btn btn-danger btn-sm">Leave Team</a>{% endif %}
{% else %} {% else %}
<a href="{% url 'join-team' team.id %}" class="btn btn-success btn-sm">Join Team</a> <a href="{% url 'join-team' team.id %}?csrftoken={{csrf_token}}" class="btn btn-success btn-sm">Join Team</a>
{% endif %} {% endif %}
<hr/> <hr/>

View file

@ -43,6 +43,7 @@ from events.forms import (
SponsorForm, SponsorForm,
) )
from events import location from events import location
from events.utils import verify_csrf
from accounts.models import EmailRecord from accounts.models import EmailRecord
@ -421,6 +422,7 @@ def contact_attendee(attendee, body, sender):
) )
@verify_csrf(token_key='csrftoken')
def attend_event(request, event_id): def attend_event(request, event_id):
event = get_object_or_404(Event, id=event_id) event = get_object_or_404(Event, id=event_id)
if request.user.is_anonymous: if request.user.is_anonymous: