Commit 54bba1fea25b9317c79e3c7f9882aa2d9b43030a

Authored by Andrew Buss
1 parent 1cc32d8b00
Exists in master

enforce enrollment on things

Showing 4 changed files with 57 additions and 25 deletions Side-by-side Diff

flashcards/api.py View file @ 54bba1f
  1 +from flashcards.models import Flashcard
1 2 from rest_framework.pagination import PageNumberPagination
2 3 from rest_framework.permissions import BasePermission
3 4  
4 5  
... ... @@ -12,8 +13,15 @@
12 13 """
13 14 Permissions for the user detail view. Anonymous users may only POST.
14 15 """
  16 +
15 17 def has_object_permission(self, request, view, obj):
16 18 if request.method == 'POST':
17 19 return True
18 20 return request.user.is_authenticated()
  21 +
  22 +
  23 +class IsEnrolledInAssociatedSection(BasePermission):
  24 + def has_object_permission(self, request, view, obj):
  25 + assert type(obj) is Flashcard
  26 + return request.user.is_in_section(obj.section)
flashcards/models.py View file @ 54bba1f
1 1 from django.contrib.auth.models import AbstractUser, UserManager
2   -from django.core.exceptions import PermissionDenied
  2 +from django.core.exceptions import PermissionDenied, ValidationError
3 3 from django.db.models import *
4 4 from django.utils.timezone import now
5 5 from simple_email_confirmation import SimpleEmailConfirmationUserMixin
... ... @@ -54,7 +54,7 @@
54 54 if not self.is_in_section(flashcard.section):
55 55 raise ValueError("User not in the section this flashcard belongs to")
56 56 user_card = UserFlashcard.objects.create(user=self, flashcard=flashcard)
57   - user_card.pulled = datetime.now()
  57 + user_card.pulled = now()
58 58 user_card.save()
59 59  
60 60 def unpull(self, flashcard):
... ... @@ -250,6 +250,19 @@
250 250 :return: whether the user is on the waitlist for this section
251 251 """
252 252 return self.whitelist.filter(email=user.email).exists()
  253 +
  254 +
  255 + def enroll(self, user):
  256 + if user.sections.filter(pk=self.pk).exists():
  257 + raise ValidationError('User is already enrolled in this section')
  258 + if self.is_whitelisted and not self.is_user_on_whitelist(user):
  259 + raise PermissionDenied("User must be on the whitelist to add this section.")
  260 + self.user_set.add(user)
  261 +
  262 + def drop(self, user):
  263 + if not user.sections.filter(pk=self.pk).exists():
  264 + raise ValidationError("User is not enrolled in the section.")
  265 + self.user_set.remove(user)
253 266  
254 267 class Meta:
255 268 ordering = ['-course_title']
flashcards/tests/test_api.py View file @ 54bba1f
1 1 from django.core import mail
2 2 from flashcards.models import *
3   -from rest_framework.status import HTTP_204_NO_CONTENT, HTTP_201_CREATED, HTTP_200_OK, HTTP_403_FORBIDDEN, HTTP_404_NOT_FOUND
  3 +from rest_framework.status import HTTP_204_NO_CONTENT, HTTP_201_CREATED, HTTP_200_OK, HTTP_403_FORBIDDEN
4 4 from rest_framework.test import APITestCase
5 5 from re import search
6 6 from django.utils.timezone import now
... ... @@ -183,7 +183,10 @@
183 183 def setUp(self):
184 184 section = Section.objects.get(pk=1)
185 185 user = User.objects.get(email='none@none.com')
186   -
  186 + section.enroll(user)
  187 + self.inaccessible_flashcard = Flashcard(text="you can't see me!", section=Section.objects.get(pk=2),
  188 + material_date=now(), author=user)
  189 + self.inaccessible_flashcard.save()
187 190 self.flashcard = Flashcard(text="jason", section=section, material_date=now(), author=user)
188 191 self.flashcard.save()
189 192  
... ... @@ -221,7 +224,8 @@
221 224 def setUp(self):
222 225 self.client.login(email='none@none.com', password='1234')
223 226 self.user = User.objects.get(email='none@none.com')
224   - self.flashcard = Flashcard(text="jason", section=Section.objects.get(pk=1), material_date=now(), author=self.user)
  227 + self.flashcard = Flashcard(text="jason", section=Section.objects.get(pk=1), material_date=now(),
  228 + author=self.user)
225 229 self.flashcard.save()
226 230 self.section = Section.objects.get(pk=1)
227 231  
228 232  
229 233  
230 234  
231 235  
... ... @@ -308,17 +312,25 @@
308 312 def setUp(self):
309 313 self.client.login(email='none@none.com', password='1234')
310 314 self.user = User.objects.get(email='none@none.com')
311   - self.flashcard = Flashcard(text="jason", section=Section.objects.get(pk=1), material_date=now(),
  315 + self.section = Section.objects.get(pk=1)
  316 + self.section.enroll(self.user)
  317 + self.flashcard = Flashcard(text="jason", section=self.section, material_date=now(),
312 318 author=self.user)
313 319 self.flashcard.save()
314   - self.section = Section.objects.get(pk=1)
  320 + self.inaccessible_flashcard = Flashcard(text="can't touch this", section=Section.objects.get(pk=2),
  321 + material_date=now(), author=self.user)
  322 + self.inaccessible_flashcard.save()
315 323  
  324 +
316 325 def test_hide_flashcard(self):
317 326 url = '/api/flashcards/1/hide/'
318   - data = {1, ''}
319   - response = self.client.post(url, data, format='json')
  327 + response = self.client.post(url, format='json')
320 328 self.assertEqual(response.status_code, HTTP_204_NO_CONTENT)
321 329  
  330 + response = self.client.post('/api/flashcards/%d/hide/' % self.inaccessible_flashcard.pk, format='json')
  331 + # This should fail because the user is not enrolled in section id 2
  332 + self.assertEqual(response.status_code, HTTP_403_FORBIDDEN)
  333 +
322 334 def test_unhide_flashcard(self):
323 335 url = '/api/flashcards/1/unhide/'
324 336 flashcard_hide = FlashcardHide(user=self.user, flashcard=self.flashcard)
... ... @@ -326,4 +338,9 @@
326 338  
327 339 response = self.client.post(url, format='json')
328 340 self.assertEqual(response.status_code, HTTP_204_NO_CONTENT)
  341 +
  342 + response = self.client.post('/api/flashcards/%d/unhide/' % self.inaccessible_flashcard.pk, format='json')
  343 +
  344 + # This should fail because the user is not enrolled in section id 2
  345 + self.assertEqual(response.status_code, HTTP_403_FORBIDDEN)
flashcards/views.py View file @ 54bba1f
  1 +import django
  2 +
1 3 from django.contrib import auth
2 4 from django.shortcuts import get_object_or_404
3   -from flashcards.api import StandardResultsSetPagination
  5 +from flashcards.api import StandardResultsSetPagination, IsEnrolledInAssociatedSection
4 6 from flashcards.models import Section, User, Flashcard, FlashcardHide, UserFlashcard
5 7 from flashcards.serializers import SectionSerializer, UserUpdateSerializer, RegistrationSerializer, UserSerializer, \
6 8 PasswordResetSerializer, PasswordResetRequestSerializer, EmailPasswordSerializer, FlashcardSerializer, \
... ... @@ -31,8 +33,7 @@
31 33 Gets flashcards for a section, excluding hidden cards.
32 34 Returned in strictly chronological order (material date).
33 35 """
34   - flashcards = Flashcard.cards_visible_to(request.user).filter( \
35   - section=self.get_object()).all()
  36 + flashcards = Flashcard.cards_visible_to(request.user).filter(section=self.get_object()).all()
36 37 return Response(FlashcardSerializer(flashcards, many=True).data)
37 38  
38 39 @detail_route(methods=['post'])
... ... @@ -47,12 +48,8 @@
47 48 parameters_strategy:
48 49 form: replace
49 50 """
50   - section = self.get_object()
51   - if request.user.sections.filter(pk=section.pk).exists():
52   - raise ValidationError("You are already in this section.")
53   - if section.is_whitelisted and not section.is_user_on_whitelist(request.user):
54   - raise PermissionDenied("You must be on the whitelist to add this section.")
55   - request.user.sections.add(section)
  51 +
  52 + self.get_object().enroll(request.user)
56 53 return Response(status=HTTP_204_NO_CONTENT)
57 54  
58 55 @detail_route(methods=['post'])
... ... @@ -67,10 +64,10 @@
67 64 parameters_strategy:
68 65 form: replace
69 66 """
70   - section = self.get_object()
71   - if not section.user_set.filter(pk=request.user.pk).exists():
72   - raise ValidationError("You are not in the section.")
73   - section.user_set.remove(request.user)
  67 + try:
  68 + self.get_object().drop(request.user)
  69 + except django.core.exceptions.PermissionDenied as e: raise PermissionDenied(e)
  70 + except django.core.exceptions.ValidationError as e: raise ValidationError(e)
74 71 return Response(status=HTTP_204_NO_CONTENT)
75 72  
76 73 @list_route(methods=['GET'])
... ... @@ -286,7 +283,7 @@
286 283 class FlashcardViewSet(GenericViewSet, UpdateModelMixin, CreateModelMixin, RetrieveModelMixin):
287 284 queryset = Flashcard.objects.all()
288 285 serializer_class = FlashcardSerializer
289   - permission_classes = [IsAuthenticated]
  286 + permission_classes = [IsAuthenticated, IsEnrolledInAssociatedSection]
290 287  
291 288 # Override create in CreateModelMixin
292 289 def create(self, request, *args, **kwargs):
... ... @@ -331,7 +328,7 @@
331 328 parameters_strategy:
332 329 form: replace
333 330 """
334   - hide = get_object_or_404(FlashcardHide ,user=request.user, flashcard=self.get_object())
  331 + hide = get_object_or_404(FlashcardHide, user=request.user, flashcard=self.get_object())
335 332 hide.delete()
336 333 return Response(status=HTTP_204_NO_CONTENT)
337 334