Commit 0406509a authored by Hypsug0's avatar Hypsug0
Browse files

improve auth mixins

parent 5144b978
......@@ -338,3 +338,12 @@ INTERNAL_IPS = "127.0.0.1"
IMAGE_FILE_TYPES = ["png", "jpg", "jpeg"]
DOCUMENT_FILE_TYPES = ["doc", "docx", "odt", "pdf", "xls", "csv", "xlsx", "ods"]
# Read-only access for all non-sensitive datas
try:
SEE_ALL_NON_SENSITIVE_DATA = config.SEE_ALL_NON_SENSITIVE_DATA
except AttributeError:
SEE_ALL_NON_SENSITIVE_DATA = False
print("SEE_ALL_NON_SENSITIVE_DATA", SEE_ALL_NON_SENSITIVE_DATA)
......@@ -7,4 +7,5 @@ def site_info(request):
return {
"SITE_NAME": settings.SITE_NAME,
"VERSION": version(),
"SEE_ALL_NON_SENSITIVE_DATA": settings.SEE_ALL_NON_SENSITIVE_DATA,
}
......@@ -7,9 +7,15 @@ import logging
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Prefetch
from django.db.models import Q
from django.shortcuts import redirect
from rest_framework_gis.pagination import GeoJsonPagination
from dbchiro.settings.base import SEE_ALL_NON_SENSITIVE_DATA
from geodata.models import Municipality
from sights.models import Place, Sighting
# Get an instance of a logger
logger = logging.getLogger(__name__)
......@@ -149,7 +155,7 @@ class PlaceDetailViewAuthMixin:
request, *args, **kwargs
)
else:
return redirect("core:update_unauth")
return redirect("core:view_unauth")
class PlaceDetailEditAuthMixin:
......@@ -212,7 +218,7 @@ class TreeGiteViewAuthMixin:
request, *args, **kwargs
)
else:
return redirect("core:update_unauth")
return redirect("core:view_unauth")
class TreeGiteEditAuthMixin:
......@@ -272,12 +278,17 @@ class SessionViewAuthMixin:
or self.obj.created_by == loggeduser
or self.obj.main_observer == loggeduser
or loggeduser in self.obj.other_observer.all()
or (
SEE_ALL_NON_SENSITIVE_DATA
and not self.obj.is_confidential
and not self.obj.place.is_hidden
)
):
return super(SessionViewAuthMixin, self).dispatch(
request, *args, **kwargs
)
else:
return redirect("core:update_unauth")
return redirect("core:view_unauth")
class SessionEditAuthMixin:
......@@ -342,12 +353,20 @@ class SightingViewAuthMixin:
or self.obj.created_by == loggeduser
or self.obj.session.main_observer == loggeduser
or loggeduser in self.obj.session.other_observer.all()
or (
SEE_ALL_NON_SENSITIVE_DATA
and not self.obj.session.is_confidential
and (
not self.obj.session.place.is_hidden
or loggeduser in self.obj.session.place.authorized_user.all()
)
)
):
return super(SightingViewAuthMixin, self).dispatch(
request, *args, **kwargs
)
else:
return redirect("core:update_unauth")
return redirect("core:view_unauth")
class SightingEditAuthMixin:
......@@ -412,6 +431,11 @@ class CountDetailViewAuthMixin:
or self.obj.created_by == loggeduser
or self.obj.sighting.session.main_observer == loggeduser
or loggeduser in self.obj.sighting.session.other_observer.all()
or (
SEE_ALL_NON_SENSITIVE_DATA
and not self.obj.sighting.session.is_confidential
and not self.obj.sighting.session.place.is_hidden
)
):
return super(CountDetailViewAuthMixin, self).dispatch(
request, *args, **kwargs
......@@ -454,32 +478,315 @@ class CountDetailEditAuthMixin:
return redirect("core:update_unauth")
class SightingListAuthMixin(LoginRequiredMixin):
"""
Classe mixin de vérification que l'utilisateur possède les droits de voir les données (view_all_data, edit_all_data ou is_resp):
Soit il est créateur de la donnée,
Soit il dispose de l'authorisation de modifier toutes les données ( :get_user_model():access_all_data is True)
"""
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return redirect("%s?next=%s" % (settings.LOGIN_URL, request.path))
else:
loggeduser = get_user_model().objects.get(id=request.user.id)
if (
loggeduser.edit_all_data
or loggeduser.access_all_data
or loggeduser.is_resp
):
logger.debug("SightingListAuthMixin : access granted")
return super(SightingListAuthMixin, self).dispatch(
request, *args, **kwargs
)
else:
return redirect("core:view_unauth")
class LargeGeoJsonPageNumberPagination(GeoJsonPagination):
page_size = 100
page_size_query_param = "page_size"
max_page_size = 1000
class PlaceFilteringMixin:
"""Mixin used for Place lists filtering"""
def get_queryset(self, *args, **kwargs):
qs = Place.objects
municipality = self.request.query_params.get("municipality", None)
name = self.request.query_params.get("name", None)
type = self.request.query_params.get("type", None)
alt_min = self.request.query_params.get("alt_min", None)
alt_max = self.request.query_params.get("alt_max", None)
gite = self.request.query_params.get("gite", None)
creator = self.request.query_params.get("creator", None)
metaplace = self.request.query_params.get("metaplace", None)
convention = self.request.query_params.get("convention", None)
managed = self.request.query_params.get("managed", None)
qs = Place.objects.order_by("-timestamp_create")
if municipality is not None:
qs = qs.filter(
Q(municipality__name__icontains=municipality)
| Q(municipality__code__icontains=municipality)
)
print("unaccent", qs)
if name is not None:
qs = qs.filter(name__icontains=name)
if type is not None:
qs = qs.filter(
Q(type__code__icontains=type)
| Q(type__category__icontains=type)
| Q(type__descr__icontains=type)
)
if alt_min is not None:
qs = qs.filter(altitude__gte=alt_min)
if alt_max is not None:
qs = qs.filter(altitude__lte=alt_max)
if creator is not None:
qs = qs.filter(creator__username__icontains=creator)
if gite is not None:
qs = qs.filter(is_gite=True)
if metaplace is not None:
qs = qs.filter(
Q(metaplace__name__icontains=metaplace)
| Q(metaplace__type__code__icontains=metaplace)
| Q(metaplace__type__category__icontains=metaplace)
| Q(metaplace__type__descr__icontains=metaplace)
)
if convention is not None:
qs = qs.filter(convention=True)
if managed is not None:
qs = qs.filter(is_managed=True)
if creator is not None:
qs = qs.filter(
Q(created_by__first_name__icontains=creator)
| Q(created_by__last_name__icontains=creator)
| Q(created_by__username__icontains=creator)
)
qs = qs.select_related("created_by", "type").prefetch_related(
Prefetch("municipality", queryset=Municipality.objects.only("name", "code"))
)
return qs
class PlaceListPermissionsMixin:
def get_queryset(self, *args, **kwargs):
qs = super(PlaceListPermissionsMixin, self).get_queryset()
logged_user = self.request.user
user = get_user_model().objects.get(id=logged_user.id)
if user.access_all_data or user.edit_all_data or user.is_superuser:
qs = qs
# Right access
else:
qs = (
qs.filter(
Q(created_by=user)
| Q(territory__in=user.resp_territory.all())
| Q(
~Q(session__place__territory__in=user.resp_territory.all())
& (
Q(session__place__is_hidden=False)
| Q(session__place__authorized_user=user)
)
)
)
.distinct()
.order_by("-timestamp_update")
)
print(qs.query)
return qs
class PlaceFilteredListWithPermissions(
PlaceListPermissionsMixin, PlaceFilteringMixin,
):
pass
class SightingFilteringMixin:
"""Mixin used for Sighting lists filtering"""
def get_queryset(self, *args, **kwargs):
qs = Sighting.objects
municipality = self.request.query_params.get("municipality", None)
name = self.request.query_params.get("name", None)
specie = self.request.query_params.get("specie", None)
count_min = self.request.query_params.get("count_min", None)
count_max = self.request.query_params.get("count_max", None)
place_type = self.request.query_params.get("place_type", None)
contact_type = self.request.query_params.get("contact_type", None)
alt_min = self.request.query_params.get("alt_min", None)
alt_max = self.request.query_params.get("alt_max", None)
gite = self.request.query_params.get("gite", None)
creator = self.request.query_params.get("creator", None)
metaplace = self.request.query_params.get("metaplace", None)
date_min = self.request.query_params.get("date_min", None)
date_max = self.request.query_params.get("date_max", None)
period = self.request.query_params.get("period", None)
sex = self.request.query_params.get("sex", None)
sexual_status = self.request.query_params.get("sexual_state", None)
age = self.request.query_params.get("age", None)
breed_colo = self.request.query_params.get("breed_colo", None)
if municipality is not None:
qs = qs.filter(
Q(session__place__municipality__name__icontains=municipality)
| Q(session__place__municipality__code__icontains=municipality)
)
if name is not None:
qs = qs.filter(session__place__name__icontains=name)
if place_type is not None:
qs = qs.filter(
Q(session__place__type__code__icontains=place_type)
| Q(session__place__type__category__icontains=place_type)
| Q(session__place__type__descr__icontains=place_type)
)
if contact_type is not None:
qs = qs.filter(
Q(session__contact__code__icontains=contact_type)
| Q(session__contact__descr__icontains=contact_type)
)
if alt_min is not None:
qs = qs.filter(session__place__altitude__gte=alt_min)
if alt_max is not None:
qs = qs.filter(session__place__altitude__lte=alt_max)
if creator is not None:
qs = qs.filter(created_by__username__icontains=creator)
if gite is not None:
qs = qs.filter(session__place__is_gite=True)
if metaplace is not None:
qs = qs.filter(
Q(session__place__metaplace__name__icontains=metaplace)
| Q(session__place__metaplace__type__code__icontains=metaplace)
| Q(session__place__metaplace__type__category__icontains=metaplace)
| Q(session__place__metaplace__type__descr__icontains=metaplace)
)
if specie is not None:
qs = qs.filter(
Q(codesp__sci_name__icontains=specie)
| Q(codesp__codesp__icontains=specie)
| Q(codesp__common_name_fr__icontains=specie)
)
if count_min is not None:
qs = qs.filter(total_count__gte=count_min)
if count_max is not None:
qs = qs.filter(total_count__lte=count_max)
if sex is not None:
qs = qs.filter(
Q(countdetail_sighting__sex__code__icontains=sex)
| Q(countdetail_sighting__sex__descr__icontains=sex)
)
if age is not None:
qs = qs.filter(
Q(countdetail_sighting__age__code__icontains=age)
| Q(countdetail_sighting__age__descr__icontains=age)
)
if sexual_status is not None:
qs = qs.filter(
Q(countdetail_sighting__mammelle__code__icontains=sexual_status)
| Q(countdetail_sighting__mammelle__descr__icontains=sexual_status)
| Q(countdetail_sighting__gestation__code__icontains=sexual_status)
| Q(countdetail_sighting__gestation__descr__icontains=sexual_status)
| Q(countdetail_sighting__testicule__code__icontains=sexual_status)
| Q(countdetail_sighting__testicule__descr__icontains=sexual_status)
| Q(countdetail_sighting__etat_sexuel__icontains=sexual_status)
| Q(countdetail_sighting__etat_sexuel__icontains=sexual_status)
)
if date_min is not None:
qs = qs.filter(
Q(session__date_start__gte=date_min)
| Q(session__date_end__gte=date_min)
)
if date_max is not None:
qs = qs.filter(
Q(session__date_start__lte=date_max)
| Q(session__date_end__lte=date_max)
)
if period is not None:
qs = qs.filter(period__icontains=period)
if breed_colo is not None:
qs = qs.filter(breed_colo=True)
return qs
class SightingListPermissionsMixin(object):
"""Mixin used for Sighting lists permissions"""
def get_queryset(self, *args, **kwargs):
qs = super(SightingListPermissionsMixin, self).get_queryset()
logged_user = self.request.user
user = get_user_model().objects.get(id=logged_user.id)
if user.access_all_data or user.edit_all_data or user.is_superuser:
qs = qs.distinct().order_by("-timestamp_update")
else:
if SEE_ALL_NON_SENSITIVE_DATA:
if user.is_resp:
qs = (
qs.filter(
Q(
Q(created_by=user)
| Q(observer=user)
| Q(session__main_observer=user)
| Q(session__other_observer=user)
)
| Q(session__place__territory__in=user.resp_territory.all())
| Q(
~Q(
session__place__territory__in=user.resp_territory.all()
)
& (
(Q(session__is_confidential=False))
& (
Q(session__place__is_hidden=False)
| Q(session__place__authorized_user=user)
)
)
)
)
.distinct()
.order_by("-timestamp_update")
)
else:
qs = (
qs.filter(
(
Q(created_by=user)
| Q(observer=user)
| Q(session__main_observer=user)
| Q(session__other_observer=user)
)
| (
(Q(session__is_confidential=False))
& (
Q(session__place__is_hidden=False)
| Q(session__place__authorized_user=user)
)
)
)
.distinct()
.order_by("-timestamp_update")
)
else:
if user.is_resp:
qs = (
qs.filter(
Q(
Q(created_by=user)
| Q(observer=user)
| Q(session__main_observer=user)
| Q(session__other_observer=user)
)
| Q(session__place__territory__in=user.resp_territory.all())
)
.distinct()
.order_by("-timestamp_update")
)
else:
qs = (
qs.filter(
Q(created_by=user)
| Q(observer=user)
| Q(
session__other_observer__username__contains=user.username
)
)
.distinct()
.order_by("-timestamp_update")
)
return qs
class SightingFilteredListWithPermissions(
SightingListPermissionsMixin, SightingFilteringMixin,
):
pass
# import the logging library
import logging
from django.contrib.auth import get_user_model
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Q
from rest_framework import generics
from sights.mixins import LargeGeoJsonPageNumberPagination
from sights.models import Sighting
from sights.mixins import (
LargeGeoJsonPageNumberPagination,
SightingFilteredListWithPermissions,
)
from sights.observation.serializers import SightingSerializer
# Get an instance of a logger
logger = logging.getLogger(__name__)
class GeoJSONSighting(LoginRequiredMixin, generics.ListAPIView):
class GeoJSONSighting(
LoginRequiredMixin, SightingFilteredListWithPermissions, generics.ListAPIView,
):
serializer_class = SightingSerializer
pagination_class = LargeGeoJsonPageNumberPagination
def get_queryset(self):
logged_user = self.request.user
user = get_user_model().objects.get(id=logged_user.id)
municipality = self.request.query_params.get("municipality", None)
name = self.request.query_params.get("name", None)
specie = self.request.query_params.get("specie", None)
count_min = self.request.query_params.get("count_min", None)
count_max = self.request.query_params.get("count_max", None)
place_type = self.request.query_params.get("place_type", None)
contact_type = self.request.query_params.get("contact_type", None)
alt_min = self.request.query_params.get("alt_min", None)
alt_max = self.request.query_params.get("alt_max", None)
gite = self.request.query_params.get("gite", None)
creator = self.request.query_params.get("creator", None)
metaplace = self.request.query_params.get("metaplace", None)
date_min = self.request.query_params.get("date_min", None)
date_max = self.request.query_params.get("date_max", None)
period = self.request.query_params.get("period", None)
sex = self.request.query_params.get("sex", None)
sexual_status = self.request.query_params.get("sexual_state", None)
age = self.request.query_params.get("age", None)
breed_colo = self.request.query_params.get("breed_colo", None)
def get_queryset(self, *args, **kwargs):
qs = super(GeoJSONSighting, self).get_queryset()
qs = Sighting.objects.order_by("-timestamp_create")
if municipality is not None:
qs = qs.filter(
Q(session__place__municipality__name__icontains=municipality)
| Q(session__place__municipality__code__icontains=municipality)
)
if name is not None:
qs = qs.filter(session__place__name__icontains=name)
if place_type is not None:
qs = qs.filter(
Q(session__place__type__code__icontains=place_type)
| Q(session__place__type__category__icontains=place_type)
| Q(session__place__type__descr__icontains=place_type)
)
if contact_type is not None:
qs = qs.filter(
Q(session__contact__code__icontains=contact_type)
| Q(session__contact__descr__icontains=contact_type)
)
if alt_min is not None:
qs = qs.filter(session__place__altitude__gte=alt_min)
if alt_max is not None:
qs = qs.filter(session__place__altitude__lte=alt_max)
if creator is not None:
qs = qs.filter(created_by__username__icontains=creator)
if gite is not None:
qs = qs.filter(session__place__is_gite=True)
if metaplace is not None:
qs = qs.filter(
Q(session__place__metaplace__name__icontains=metaplace)
| Q(session__place__metaplace__type__code__icontains=metaplace)
| Q(session__place__metaplace__type__category__icontains=metaplace)
| Q(session__place__metaplace__type__descr__icontains=metaplace)
)
if specie is not None:
qs = qs.filter(
Q(codesp__sci_name__icontains=specie)
| Q(codesp__codesp__icontains=specie)
| Q(codesp__common_name_fr__icontains=specie)
)
if count_min is not None:
qs = qs.filter(total_count__gte=count_min)
if count_max is not None:
qs = qs.filter(total_count__lte=count_max)
if sex is not None:
qs = qs.filter(
Q(countdetail_sighting__sex__code__icontains=sex)
| Q(countdetail_sighting__sex__descr__icontains=sex)
)
if age is not None:
qs = qs.filter(
Q(countdetail_sighting__age__code__icontains=age)
| Q(countdetail_sighting__age__descr__icontains=age)
)
if sexual_status is not None:
qs = qs.filter(
Q(countdetail_sighting__mammelle__code__icontains=sexual_status)
| Q(countdetail_sighting__mammelle__descr__icontains=sexual_status)
| Q(countdetail_sighting__gestation__code__icontains=sexual_status)
| Q(countdetail_sighting__gestation__descr__icontains=sexual_status)
| Q(countdetail_sighting__testicule__code__icontains=sexual_status)
| Q(countdetail_sighting__testicule__descr__icontains=sexual_status)
| Q(countdetail_sighting__etat_sexuel__icontains=sexual_status)
| Q(countdetail_sighting__etat_sexuel__icontains=sexual_status)
)
if date_min is not None:
qs = qs.filter(
Q(session__date_start__gte=date_min)
| Q(session__date_end__gte=date_min)
)
if date_max is not None:
qs = qs.filter(
Q(session__date_start__lte=date_max)
| Q(session__date_end__lte=date_max)
)
if period is not None:
qs = qs.filter(period__icontains=period)
if breed_colo is not None:
qs = qs.filter(breed_colo=True)
if user.access_all_data or user.edit_all_data:
qs = qs
elif user.is_resp:
qs = (
qs.filter(
Q(created_by=user)
| Q(observer=user)
| Q(session__other_observer__username__contains=user.username)
| Q(session__place__territory__in=user.resp_territory.all())
)
.distinct()
.order_by("-timestamp_update")
)
print(qs.query)
qs = qs.select_related(
"created_by",
"codesp",
......@@ -145,4 +31,5 @@ class GeoJSONSighting(LoginRequiredMixin, generics.ListAPIView):
"session__place",
"session__place__municipality",
)
return qs.all()
return qs.distinct().order_by("-timestamp_update")
......@@ -7,10 +7,8 @@ from sights.observation.views import (