Limit page access to users which can access course
This commit is contained in:
parent
d8148158a1
commit
d8577c70d5
|
|
@ -515,7 +515,7 @@ if "django_redis.cache.RedisCache" in env("IT_DJANGO_CACHE_BACKEND", default="")
|
|||
|
||||
CACHES["api_page_cache"] = {
|
||||
"BACKEND": "django.core.cache.backends.db.DatabaseCache",
|
||||
"LOCATION": "django_cache_learning_path",
|
||||
"LOCATION": "django_cache_table_api_page",
|
||||
}
|
||||
|
||||
# OAuth/OpenId Connect
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ from vbv_lernwelt.core.views import (
|
|||
)
|
||||
from vbv_lernwelt.course.views import (
|
||||
mark_course_completion,
|
||||
page_api_view,
|
||||
course_page_api_view,
|
||||
request_course_completion,
|
||||
)
|
||||
from wagtail import urls as wagtail_urls
|
||||
|
|
@ -59,7 +59,8 @@ urlpatterns = [
|
|||
name="generate_web_component_icons"),
|
||||
|
||||
# course
|
||||
path(r"api/course/page/<slug:slug>/", page_api_view, name="page_api_view"),
|
||||
path(r"api/course/page/<slug:slug>/", course_page_api_view,
|
||||
name="course_page_api_view"),
|
||||
path(r"api/course/completion/mark/", mark_course_completion,
|
||||
name="mark_course_completion"),
|
||||
path(r"api/course/completion/<course_id>/", request_course_completion,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import logging
|
|||
|
||||
import structlog
|
||||
from django.conf import settings
|
||||
from django.core.cache import caches
|
||||
from rest_framework.throttling import UserRateThrottle
|
||||
from structlog.types import EventDict
|
||||
|
||||
|
|
@ -50,3 +51,16 @@ def first_true(iterable, default=False, pred=None):
|
|||
# first_true([a,b,c], x) --> a or b or c or x
|
||||
# first_true([a,b], x, f) --> a if f(a) else b if f(b) else x
|
||||
return next(filter(pred, iterable), default)
|
||||
|
||||
|
||||
def get_api_page_cache():
|
||||
return caches["api_page_cache"]
|
||||
|
||||
|
||||
def api_page_cache_get_or_set(key, func, timeout=60 * 60 * 8):
|
||||
cache = get_api_page_cache()
|
||||
value = cache.get(key)
|
||||
if value is None:
|
||||
value = func()
|
||||
cache.set(key, value, timeout=timeout)
|
||||
return value
|
||||
|
|
|
|||
|
|
@ -1,12 +1,21 @@
|
|||
from rest_framework import permissions
|
||||
from vbv_lernwelt.course.models import CourseSessionUser
|
||||
|
||||
|
||||
class CourseAccessPermission(permissions.BasePermission):
|
||||
def has_object_permission(self, request, view, obj):
|
||||
# Read permissions are allowed to any request,
|
||||
# so we'll always allow GET, HEAD or OPTIONS requests.
|
||||
if request.method in permissions.SAFE_METHODS:
|
||||
return True
|
||||
def has_course_access(request, obj):
|
||||
if request.user.is_superuser:
|
||||
return True
|
||||
|
||||
# Instance must have an attribute named `owner`.
|
||||
return obj.owner == request.user
|
||||
course = obj.specific.get_course()
|
||||
|
||||
# attached to CourseSession
|
||||
course_session = CourseSessionUser.objects.filter(
|
||||
course_session__course_id=course.id, user=request.user
|
||||
).exists()
|
||||
|
||||
if course_session:
|
||||
return True
|
||||
|
||||
# TODO is trainer/expert of session
|
||||
|
||||
# TODO check school class access
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ class CourseCompletionApiTestCase(APITestCase):
|
|||
def setUp(self) -> None:
|
||||
create_default_users()
|
||||
create_test_course()
|
||||
self.user = User.objects.get(username="student")
|
||||
self.client.login(username="student", password="test")
|
||||
self.user = User.objects.get(username="admin")
|
||||
self.client.login(username="admin", password="test")
|
||||
|
||||
def test_completeLearningContent_works(self):
|
||||
learning_content = LearningContent.objects.get(title="Fachcheck Fahrzeug")
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import structlog
|
||||
from django.views.decorators.cache import cache_page
|
||||
from rest_framework.decorators import api_view
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
from rest_framework.response import Response
|
||||
from wagtail.models import Page
|
||||
|
||||
from vbv_lernwelt.course.models import CourseCompletion, CoursePage
|
||||
from vbv_lernwelt.core.utils import api_page_cache_get_or_set
|
||||
from vbv_lernwelt.course.models import CourseCompletion
|
||||
from vbv_lernwelt.course.permissions import has_course_access
|
||||
from vbv_lernwelt.course.serializers import CourseCompletionSerializer
|
||||
from vbv_lernwelt.learnpath.utils import get_wagtail_type
|
||||
|
||||
|
|
@ -12,12 +14,20 @@ logger = structlog.get_logger(__name__)
|
|||
|
||||
|
||||
@api_view(["GET"])
|
||||
@cache_page(60 * 60 * 8, cache="api_page_cache")
|
||||
def page_api_view(request, slug):
|
||||
def course_page_api_view(request, slug):
|
||||
try:
|
||||
page = Page.objects.get(slug=slug, locale__language_code="de-CH")
|
||||
serializer = page.specific.get_serializer_class()(page.specific)
|
||||
return Response(serializer.data)
|
||||
if not has_course_access(request, page):
|
||||
raise PermissionDenied()
|
||||
|
||||
data = api_page_cache_get_or_set(
|
||||
key=request.get_full_path(),
|
||||
func=lambda: page.specific.get_serializer_class()(page.specific).data,
|
||||
)
|
||||
|
||||
return Response(data)
|
||||
except PermissionDenied as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
return Response({"error": str(e)}, status=404)
|
||||
|
|
@ -25,48 +35,63 @@ def page_api_view(request, slug):
|
|||
|
||||
@api_view(["GET"])
|
||||
def request_course_completion(request, course_id):
|
||||
response_data = CourseCompletionSerializer(
|
||||
CourseCompletion.objects.filter(user=request.user, course_id=course_id),
|
||||
many=True,
|
||||
).data
|
||||
try:
|
||||
response_data = CourseCompletionSerializer(
|
||||
CourseCompletion.objects.filter(user=request.user, course_id=course_id),
|
||||
many=True,
|
||||
).data
|
||||
|
||||
return Response(status=200, data=response_data)
|
||||
return Response(status=200, data=response_data)
|
||||
except PermissionDenied as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
return Response({"error": str(e)}, status=404)
|
||||
|
||||
|
||||
@api_view(["POST"])
|
||||
def mark_course_completion(request):
|
||||
page_key = request.data.get("page_key")
|
||||
completion_status = request.data.get("completion_status", "success")
|
||||
try:
|
||||
page_key = request.data.get("page_key")
|
||||
completion_status = request.data.get("completion_status", "success")
|
||||
|
||||
page = Page.objects.get(translation_key=page_key, locale__language_code="de-CH")
|
||||
page_type = get_wagtail_type(page.specific)
|
||||
course = CoursePage.objects.ancestor_of(page).first().specific.course
|
||||
page = Page.objects.get(translation_key=page_key, locale__language_code="de-CH")
|
||||
if not has_course_access(request, page):
|
||||
raise PermissionDenied()
|
||||
|
||||
cc, created = CourseCompletion.objects.get_or_create(
|
||||
user=request.user,
|
||||
page_key=page_key,
|
||||
course_id=course.id,
|
||||
)
|
||||
cc.page_slug = page.slug
|
||||
cc.page_type = page_type
|
||||
cc.completion_status = completion_status
|
||||
cc.save()
|
||||
page_type = get_wagtail_type(page.specific)
|
||||
course = page.specific.get_course()
|
||||
|
||||
response_data = CourseCompletionSerializer(
|
||||
CourseCompletion.objects.filter(user=request.user, course_id=course.id),
|
||||
many=True,
|
||||
).data
|
||||
cc, created = CourseCompletion.objects.get_or_create(
|
||||
user=request.user,
|
||||
page_key=page_key,
|
||||
course_id=course.id,
|
||||
)
|
||||
cc.page_slug = page.slug
|
||||
cc.page_type = page_type
|
||||
cc.completion_status = completion_status
|
||||
cc.save()
|
||||
|
||||
logger.debug(
|
||||
"mark_course_completion successful",
|
||||
label="completion_api",
|
||||
page_key=page_key,
|
||||
page_type=page_type,
|
||||
page_slug=page.slug,
|
||||
page_title=page.title,
|
||||
user_id=request.user.id,
|
||||
course_id=course.id,
|
||||
completion_status=completion_status,
|
||||
)
|
||||
response_data = CourseCompletionSerializer(
|
||||
CourseCompletion.objects.filter(user=request.user, course_id=course.id),
|
||||
many=True,
|
||||
).data
|
||||
|
||||
return Response(status=200, data=response_data)
|
||||
logger.debug(
|
||||
"mark_course_completion successful",
|
||||
label="completion_api",
|
||||
page_key=page_key,
|
||||
page_type=page_type,
|
||||
page_slug=page.slug,
|
||||
page_title=page.title,
|
||||
user_id=request.user.id,
|
||||
course_id=course.id,
|
||||
completion_status=completion_status,
|
||||
)
|
||||
|
||||
return Response(status=200, data=response_data)
|
||||
except PermissionDenied as e:
|
||||
raise e
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
return Response({"error": str(e)}, status=404)
|
||||
|
|
|
|||
Loading…
Reference in New Issue