Commit 6f07c0ea authored by Alexandre Lanceart's avatar Alexandre Lanceart Committed by Florent Chehab

feat(site): CGU/RGPD requierements & ban user

* Added the necessary behavior to require the validation of CGU and RGPD.
* Added matching tests for this
* Added the possibility to ban users
* Also fixed the API Viewsets so that they can be registered like any model viewset

Fixes #121
Fixes #122
parent 9caa1a59
Pipeline #40889 passed with stages
in 3 minutes and 58 seconds
# Generated by Django 2.1.7 on 2019-05-31 18:07
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("backend_app", "0008_auto_20190525_1522")]
operations = [
migrations.AlterField(
model_name="coursefeedback",
name="adequation",
field=models.IntegerField(
default=0,
validators=[
django.core.validators.MinValueValidator(-5),
django.core.validators.MaxValueValidator(5),
],
),
),
migrations.AlterField(
model_name="coursefeedback",
name="language_following_ease",
field=models.IntegerField(
default=0,
validators=[
django.core.validators.MinValueValidator(-5),
django.core.validators.MaxValueValidator(5),
],
),
),
migrations.AlterField(
model_name="coursefeedback",
name="working_dose",
field=models.IntegerField(
default=0,
validators=[
django.core.validators.MinValueValidator(-5),
django.core.validators.MaxValueValidator(5),
],
),
),
migrations.AlterField(
model_name="exchangefeedback",
name="academical_level_appreciation",
field=models.IntegerField(
validators=[
django.core.validators.MinValueValidator(-5),
django.core.validators.MaxValueValidator(5),
]
),
),
]
......@@ -65,6 +65,17 @@ class WithUserTestCase(TestCase):
username=cls.authenticated_user_2.username, password=password
)
for user in [
cls.staff_user,
cls.moderator_user,
cls.dri_user,
cls.authenticated_user,
cls.authenticated_user_2,
]:
user.has_validated_cgu = True
user.has_validated_rgpd = True
user.save()
cls.unauthenticated_client = APIClient()
cls.setUpMoreTestData()
......
......@@ -13,13 +13,8 @@ urlpatterns = [url(r"^api-doc/", include_docs_urls(title="REX-DRI API"))]
# router will hold all api related endpoints
router = routers.DefaultRouter()
for v in ALL_API_VIEWSETS:
for v in ALL_API_VIEWSETS + ALL_API_VIEW_VIEWSETS:
router.register(v.end_point_route, v, str(v))
# Add all the endpoints for the base api
urlpatterns.append(url(r"^api/", include(router.urls)))
# We need to register some extra viewset
for v in ALL_API_VIEW_VIEWSETS:
urlpatterns.append(url(r"^api/{}/".format(v.end_point_route), v.as_view()))
......@@ -2,7 +2,7 @@ import logging
from django.conf import settings
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSet
from backend_app.checks import check_viewsets
from backend_app.models.abstract.essentialModule import EssentialModuleViewSet
......@@ -37,10 +37,10 @@ from backend_app.models.universitySemestersDates import UniversitySemestersDates
from backend_app.models.universityTaggedItem import UniversityTaggedItemViewSet
from backend_app.models.userData import UserDataViewSet
from backend_app.models.version import VersionViewSet
from backend_app.permissions.app_permissions import ReadOnly
from backend_app.permissions.app_permissions import ReadOnly, IsStaff
from backend_app.serializers import CourseFeedbackSerializer
from backend_app.settings.defaults import OBJ_MODERATION_PERMISSIONS
from base_app.models import UserViewset
from base_app.models import UserViewset, User
class CourseFeedbackViewSet(EssentialModuleViewSet):
......@@ -87,7 +87,7 @@ if settings.TESTING:
ALL_API_VIEWSETS += [ForTestingModerationViewSet, ForTestingVersioningViewSet]
class AppModerationStatusViewSet(APIView):
class AppModerationStatusViewSet(ViewSet):
"""
Viewset to know what is the app moderation status
"""
......@@ -97,7 +97,7 @@ class AppModerationStatusViewSet(APIView):
permission_classes = (ReadOnly,)
end_point_route = "serverModerationStatus"
def get(self, request):
def list(self, request):
return Response(
{
"activated": settings.MODERATION_ACTIVATED,
......@@ -106,7 +106,7 @@ class AppModerationStatusViewSet(APIView):
)
class LogFrontendErrorsViewSet(APIView):
class LogFrontendErrorsViewSet(ViewSet):
"""
Viewset to handle the logging of errors coming from the frontend.
"""
......@@ -114,7 +114,7 @@ class LogFrontendErrorsViewSet(APIView):
permission_classes = tuple()
end_point_route = "frontendErrors"
def post(self, request):
def create(self, request):
logger = logging.getLogger("frontend")
data = request.data
if "componentStack" in data.keys():
......@@ -124,7 +124,49 @@ class LogFrontendErrorsViewSet(APIView):
return Response(status=201)
ALL_API_VIEW_VIEWSETS = [AppModerationStatusViewSet, LogFrontendErrorsViewSet]
class BannedUserViewSet(ViewSet):
"""
Viewset to be able to ban and un-ban users from the site
"""
end_point_route = "banned_users"
permission_classes = (IsStaff,)
def list(self, request, **kwargs):
return Response(
[
dict(user_id=user.pk, user_login=user.username)
for user in User.objects.filter(is_banned=True)
]
)
def update(self, request, pk=None):
if pk is None:
return Response(status=403)
user = User.objects.get(pk=pk)
if user.is_staff:
# Prevent ban of admin users
return Response(status=403)
user.is_banned = True
user.save()
return Response(status=200)
def delete(self, request, pk=None):
if pk is None:
return Response(status=403)
user = User.objects.get(pk=pk)
user.is_banned = False
user.save()
return Response(status=200)
ALL_API_VIEW_VIEWSETS = [
AppModerationStatusViewSet,
LogFrontendErrorsViewSet,
BannedUserViewSet,
]
ALL_VIEWSETS = ALL_API_VIEWSETS + ALL_API_VIEW_VIEWSETS
......
from django import forms
from base_app.models import User
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ("has_validated_rgpd", "has_validated_cgu")
......@@ -42,8 +42,20 @@ class RexDriRequestMiddleware(MiddlewareMixin):
work, ensure your TEMPLATE_CONTEXT_PROCESSORS setting includes\
'django.core.context_processors.auth'."
path = request.path_info.lstrip("/")
full_path = request.get_full_path()
# If the user is not authenticated redirect him/her to the login page
if not request.user.is_authenticated:
path = request.path_info.lstrip("/")
if not any(m.match(path) for m in EXEMPT_URLS):
return HttpResponseRedirect(settings.LOGIN_URL + "?next=/" + path)
else:
# User is authenticated
# We check if he / she has validated the CGU and the full RGPD
if not (request.user.has_validated_cgu and request.user.has_validated_rgpd):
if not path.startswith("cgu"):
return HttpResponseRedirect("/cgu/?next={}".format(full_path))
# Handling of banned users
elif request.user.is_banned and full_path != "/banned_note/":
return HttpResponseRedirect("/banned_note/")
# Generated by Django 2.1.7 on 2019-05-31 18:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("base_app", "0001_squashed_0002_auto_20190402_2159")]
operations = [
migrations.AddField(
model_name="user",
name="has_validated_cgu",
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name="user",
name="has_validated_rgpd",
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name="user",
name="is_banned",
field=models.BooleanField(default=False),
),
]
......@@ -40,6 +40,9 @@ class User(AbstractUser):
pseudo = models.CharField(
blank=False, null=False, max_length=12, default="Anonymous42"
)
has_validated_cgu = models.BooleanField(default=False, null=False)
has_validated_rgpd = models.BooleanField(default=False, null=False)
is_banned = models.BooleanField(default=False, null=False)
def save(self, *args, **kwargs):
"""
......
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vous êtes banni du site</title>
</head>
<body>
Vous êtes banni du site, veuillez contacter le SIMDE.
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<br>
<br>
<form method="post">
{% csrf_token %}
Valider le CGU
{{ form.has_validated_cgu }}
<br>
<br>
Valider la charte RGPD
{{ form.has_validated_rgpd }}
<br>
<br>
<input type="submit" value="Valider">
</form>
</body>
</html>
\ No newline at end of file
from django.test import TestCase
from rest_framework.test import APIClient
from base_app.models import User
class HasAccessTestCase(TestCase):
"""
Test to check that validation of the CGU and RGPD is Required
"""
@classmethod
def setUpTestData(cls):
password = "123456"
cls.has_validated_user = User.objects.create_user(
username="yolo", email="osef@master.fr", password=password
)
cls.has_validated_user.has_validated_cgu = True
cls.has_validated_user.has_validated_rgpd = True
cls.has_validated_user.save()
cls.has_not_validated_user = User.objects.create_user(
username="yolo2", email="osef@master.fr", password=password
)
cls.has_not_validated_user.has_validated_cgu = False
cls.has_not_validated_user.has_validated_rgpd = False
cls.has_not_validated_user.save()
cls.has_validated_client = APIClient()
cls.has_validated_client.login(
username=cls.has_validated_user.username, password=password
)
cls.has_not_validated_client = APIClient()
cls.has_not_validated_client.login(
username=cls.has_not_validated_user.username, password=password
)
def test_accepted_has_access(self):
response = self.has_validated_client.get("/api/")
self.assertEqual(response.status_code, 200)
def test_not_accepted_refused(self):
response = self.has_not_validated_client.get("/api/")
self.assertEqual(response.status_code, 302) # We have a redirect
self.assertEqual(response.url, "/cgu/?next=/api/")
......@@ -26,6 +26,8 @@ urlpatterns += [
name="cas_ng_proxy_callback",
),
url(r"^app/.*", views.index),
url(r"^cgu/.*", views.cgu),
url(r"^banned_note/", views.banned),
url(r"^$", RedirectView.as_view(url="./app/"), name="go to real home"),
url(r"", include("backend_app.urls")),
url(r"^media/(?P<path>.*)", media_files_view, name="media"),
......
from django.http import HttpResponse
import logging
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseNotFound
from django.shortcuts import render
from webpack_loader.utils import get_files
from backend_app.utils import clean_route
from backend_app.viewsets import ALL_VIEWSETS
from base_app.forms import UserForm
from base_app.models import User
logger = logging.getLogger("django")
def index(request):
......@@ -28,6 +34,32 @@ def index(request):
)
def cgu(request):
if request.method == "POST":
user = User.objects.get(pk=request.user.pk)
form = UserForm(request.POST, instance=user)
if form.is_valid():
user = form.save(commit=True)
user.save()
else:
user = User.objects.get(pk=request.user.pk)
form = UserForm(instance=user)
if "next" in request.GET and user.has_validated_cgu and user.has_validated_rgpd:
# if the user has just validated everything, we redirect him to the location he requested
return HttpResponseRedirect(request.GET["next"])
else:
return render(request, "cgu.html", dict(user=user, form=form))
def banned(request):
user = request.user
if user.is_banned:
return render(request, "banned.html", dict(user=user))
else:
return HttpResponseNotFound()
def media_files_view(request, path):
"""
Media files are served by nginx only if the user is connected.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment