2023-04-06 11:08:42 +00:00
|
|
|
# -*- coding:utf-8 -*-
|
2017-04-24 15:51:12 +00:00
|
|
|
#
|
2023-04-06 11:08:42 +00:00
|
|
|
# Copyright 2023 © AE UTBM
|
|
|
|
# ae@utbm.fr / ae.info@utbm.fr
|
|
|
|
# All contributors are listed in the CONTRIBUTORS file.
|
2017-04-24 15:51:12 +00:00
|
|
|
#
|
2023-04-06 11:08:42 +00:00
|
|
|
# This file is part of the website of the UTBM Student Association (AE UTBM),
|
|
|
|
# https://ae.utbm.fr.
|
2017-04-24 15:51:12 +00:00
|
|
|
#
|
2023-04-06 11:08:42 +00:00
|
|
|
# You can find the whole source code at https://github.com/ae-utbm/sith3
|
2017-04-24 15:51:12 +00:00
|
|
|
#
|
2023-04-06 11:08:42 +00:00
|
|
|
# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3)
|
|
|
|
# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE
|
|
|
|
# OR WITHIN THE LOCAL FILE "LICENSE"
|
2017-04-24 15:51:12 +00:00
|
|
|
#
|
2023-04-06 11:08:42 +00:00
|
|
|
# PREVIOUSLY LICENSED UNDER THE MIT LICENSE,
|
|
|
|
# SEE : https://raw.githubusercontent.com/ae-utbm/sith3/master/LICENSE.old
|
|
|
|
# OR WITHIN THE LOCAL FILE "LICENSE.old"
|
2017-04-24 15:51:12 +00:00
|
|
|
#
|
|
|
|
|
2016-12-14 17:05:19 +00:00
|
|
|
import types
|
2015-11-27 14:39:42 +00:00
|
|
|
|
2024-06-24 11:07:36 +00:00
|
|
|
from django.core.exceptions import (
|
|
|
|
ImproperlyConfigured,
|
|
|
|
PermissionDenied,
|
|
|
|
)
|
2018-10-10 00:07:13 +00:00
|
|
|
from django.http import (
|
|
|
|
HttpResponseForbidden,
|
|
|
|
HttpResponseNotFound,
|
|
|
|
HttpResponseServerError,
|
|
|
|
)
|
2024-06-24 11:07:36 +00:00
|
|
|
from django.utils.functional import cached_property
|
2015-11-27 14:39:42 +00:00
|
|
|
from django.views.generic.base import View
|
2019-04-10 11:21:34 +00:00
|
|
|
from django.views.generic.detail import SingleObjectMixin
|
2024-06-24 11:07:36 +00:00
|
|
|
from django.views.generic.edit import FormView
|
|
|
|
from sentry_sdk import last_event_id
|
2015-11-27 14:39:42 +00:00
|
|
|
|
2016-08-31 00:43:49 +00:00
|
|
|
from core.views.forms import LoginForm
|
2015-12-07 15:08:24 +00:00
|
|
|
|
2018-10-04 19:29:19 +00:00
|
|
|
|
2019-10-05 22:09:58 +00:00
|
|
|
def forbidden(request, exception):
|
2016-08-11 02:24:32 +00:00
|
|
|
try:
|
2018-10-04 19:29:19 +00:00
|
|
|
return HttpResponseForbidden(
|
|
|
|
render(
|
|
|
|
request,
|
|
|
|
"core/403.jinja",
|
|
|
|
context={
|
|
|
|
"next": request.path,
|
|
|
|
"form": LoginForm(),
|
|
|
|
"popup": request.resolver_match.kwargs["popup"] or "",
|
|
|
|
},
|
|
|
|
)
|
|
|
|
)
|
2016-08-11 02:24:32 +00:00
|
|
|
except:
|
2018-10-04 19:29:19 +00:00
|
|
|
return HttpResponseForbidden(
|
|
|
|
render(
|
|
|
|
request,
|
|
|
|
"core/403.jinja",
|
|
|
|
context={"next": request.path, "form": LoginForm()},
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2015-12-07 16:23:52 +00:00
|
|
|
|
2019-10-05 22:09:58 +00:00
|
|
|
def not_found(request, exception):
|
2023-03-02 14:11:23 +00:00
|
|
|
return HttpResponseNotFound(
|
|
|
|
render(request, "core/404.jinja", context={"exception": exception})
|
|
|
|
)
|
2015-12-07 16:23:52 +00:00
|
|
|
|
2018-10-04 19:29:19 +00:00
|
|
|
|
2018-10-10 00:07:13 +00:00
|
|
|
def internal_servor_error(request):
|
|
|
|
request.sentry_last_event_id = last_event_id
|
|
|
|
return HttpResponseServerError(render(request, "core/500.jinja"))
|
|
|
|
|
|
|
|
|
2016-02-05 15:59:42 +00:00
|
|
|
def can_edit_prop(obj, user):
|
2019-08-14 16:46:33 +00:00
|
|
|
"""
|
|
|
|
:param obj: Object to test for permission
|
|
|
|
:param user: core.models.User to test permissions against
|
|
|
|
:return: if user is authorized to edit object properties
|
|
|
|
:rtype: bool
|
|
|
|
|
|
|
|
:Example:
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
if not can_edit_prop(self.object ,request.user):
|
|
|
|
raise PermissionDenied
|
|
|
|
|
|
|
|
"""
|
2016-02-05 15:59:42 +00:00
|
|
|
if obj is None or user.is_owner(obj):
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2018-10-04 19:29:19 +00:00
|
|
|
|
2016-02-05 15:59:42 +00:00
|
|
|
def can_edit(obj, user):
|
2019-08-14 16:46:33 +00:00
|
|
|
"""
|
|
|
|
:param obj: Object to test for permission
|
|
|
|
:param user: core.models.User to test permissions against
|
|
|
|
:return: if user is authorized to edit object
|
|
|
|
:rtype: bool
|
|
|
|
|
|
|
|
:Example:
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
if not can_edit(self.object ,request.user):
|
|
|
|
raise PermissionDenied
|
|
|
|
|
|
|
|
"""
|
2016-02-05 15:59:42 +00:00
|
|
|
if obj is None or user.can_edit(obj):
|
|
|
|
return True
|
|
|
|
return can_edit_prop(obj, user)
|
|
|
|
|
2018-10-04 19:29:19 +00:00
|
|
|
|
2016-02-05 15:59:42 +00:00
|
|
|
def can_view(obj, user):
|
2019-08-14 16:46:33 +00:00
|
|
|
"""
|
|
|
|
:param obj: Object to test for permission
|
|
|
|
:param user: core.models.User to test permissions against
|
|
|
|
:return: if user is authorized to see object
|
|
|
|
:rtype: bool
|
|
|
|
|
|
|
|
:Example:
|
|
|
|
|
|
|
|
.. code-block:: python
|
|
|
|
|
|
|
|
if not can_view(self.object ,request.user):
|
|
|
|
raise PermissionDenied
|
|
|
|
|
|
|
|
"""
|
2016-02-05 15:59:42 +00:00
|
|
|
if obj is None or user.can_view(obj):
|
|
|
|
return True
|
|
|
|
return can_edit(obj, user)
|
2015-12-07 16:23:52 +00:00
|
|
|
|
2018-10-04 19:29:19 +00:00
|
|
|
|
2019-10-17 09:56:02 +00:00
|
|
|
class GenericContentPermissionMixinBuilder(View):
|
2019-10-17 09:24:51 +00:00
|
|
|
"""
|
|
|
|
Used to build permission mixins
|
|
|
|
This view protect any child view that would be showing an object that is restricted based
|
|
|
|
on two properties
|
|
|
|
|
|
|
|
:prop permission_function: function to test permission with, takes an object and an user an return a bool
|
|
|
|
:prop raised_error: permission to be raised
|
|
|
|
|
|
|
|
:raises: raised_error
|
|
|
|
"""
|
|
|
|
|
|
|
|
permission_function = lambda obj, user: False
|
|
|
|
raised_error = PermissionDenied
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_permission_function(cls, obj, user):
|
|
|
|
return cls.permission_function(obj, user)
|
|
|
|
|
|
|
|
def dispatch(self, request, *arg, **kwargs):
|
|
|
|
if hasattr(self, "get_object") and callable(self.get_object):
|
|
|
|
self.object = self.get_object()
|
|
|
|
if not self.get_permission_function(self.object, request.user):
|
|
|
|
raise self.raised_error
|
2019-10-17 09:56:02 +00:00
|
|
|
return super(GenericContentPermissionMixinBuilder, self).dispatch(
|
2019-10-17 09:24:51 +00:00
|
|
|
request, *arg, **kwargs
|
|
|
|
)
|
|
|
|
|
|
|
|
# If we get here, it's a ListView
|
|
|
|
|
|
|
|
queryset = self.get_queryset()
|
|
|
|
l_id = [o.id for o in queryset if self.get_permission_function(o, request.user)]
|
|
|
|
if not l_id and queryset.count() != 0:
|
|
|
|
raise self.raised_error
|
|
|
|
self._get_queryset = self.get_queryset
|
|
|
|
|
|
|
|
def get_qs(self2):
|
|
|
|
return self2._get_queryset().filter(id__in=l_id)
|
|
|
|
|
|
|
|
self.get_queryset = types.MethodType(get_qs, self)
|
2019-10-17 09:56:02 +00:00
|
|
|
return super(GenericContentPermissionMixinBuilder, self).dispatch(
|
|
|
|
request, *arg, **kwargs
|
|
|
|
)
|
2019-10-17 09:24:51 +00:00
|
|
|
|
|
|
|
|
2016-06-20 13:47:19 +00:00
|
|
|
class CanCreateMixin(View):
|
|
|
|
"""
|
|
|
|
This view is made to protect any child view that would create an object, and thus, that can not be protected by any
|
|
|
|
of the following mixin
|
2019-08-14 16:46:33 +00:00
|
|
|
|
|
|
|
:raises: PermissionDenied
|
2016-06-20 13:47:19 +00:00
|
|
|
"""
|
2018-10-04 19:29:19 +00:00
|
|
|
|
2016-11-05 12:37:30 +00:00
|
|
|
def dispatch(self, request, *arg, **kwargs):
|
|
|
|
res = super(CanCreateMixin, self).dispatch(request, *arg, **kwargs)
|
2019-10-05 23:34:21 +00:00
|
|
|
if not request.user.is_authenticated:
|
2016-11-05 12:37:30 +00:00
|
|
|
raise PermissionDenied
|
|
|
|
return res
|
|
|
|
|
2016-06-24 19:07:59 +00:00
|
|
|
def form_valid(self, form):
|
|
|
|
obj = form.instance
|
|
|
|
if can_edit_prop(obj, self.request.user):
|
|
|
|
return super(CanCreateMixin, self).form_valid(form)
|
2016-06-20 13:47:19 +00:00
|
|
|
raise PermissionDenied
|
|
|
|
|
2018-10-04 19:29:19 +00:00
|
|
|
|
2019-10-17 09:56:02 +00:00
|
|
|
class CanEditPropMixin(GenericContentPermissionMixinBuilder):
|
2015-11-27 14:39:42 +00:00
|
|
|
"""
|
|
|
|
This view is made to protect any child view that would be showing some properties of an object that are restricted
|
|
|
|
to only the owner group of the given object.
|
|
|
|
In other word, you can make a view with this view as parent, and it would be retricted to the users that are in the
|
|
|
|
object's owner_group
|
2019-08-14 16:46:33 +00:00
|
|
|
|
|
|
|
:raises: PermissionDenied
|
2015-11-27 14:39:42 +00:00
|
|
|
"""
|
2018-10-04 19:29:19 +00:00
|
|
|
|
2019-10-17 09:24:51 +00:00
|
|
|
permission_function = can_edit_prop
|
2019-10-16 17:28:32 +00:00
|
|
|
|
2018-10-04 19:29:19 +00:00
|
|
|
|
2019-10-17 09:56:02 +00:00
|
|
|
class CanEditMixin(GenericContentPermissionMixinBuilder):
|
2015-11-27 14:39:42 +00:00
|
|
|
"""
|
2016-08-01 17:59:22 +00:00
|
|
|
This view makes exactly the same thing as its direct parent, but checks the group on the edit_groups field of the
|
2015-11-27 14:39:42 +00:00
|
|
|
object
|
2019-08-14 16:46:33 +00:00
|
|
|
|
|
|
|
:raises: PermissionDenied
|
2015-11-27 14:39:42 +00:00
|
|
|
"""
|
2018-10-04 19:29:19 +00:00
|
|
|
|
2019-10-17 09:24:51 +00:00
|
|
|
permission_function = can_edit
|
2015-11-27 14:39:42 +00:00
|
|
|
|
2018-10-04 19:29:19 +00:00
|
|
|
|
2019-10-17 09:56:02 +00:00
|
|
|
class CanViewMixin(GenericContentPermissionMixinBuilder):
|
2015-11-27 14:39:42 +00:00
|
|
|
"""
|
2016-08-01 17:59:22 +00:00
|
|
|
This view still makes exactly the same thing as its direct parent, but checks the group on the view_groups field of
|
2015-11-27 14:39:42 +00:00
|
|
|
the object
|
2019-08-14 16:46:33 +00:00
|
|
|
|
|
|
|
:raises: PermissionDenied
|
2015-11-27 14:39:42 +00:00
|
|
|
"""
|
2018-10-04 19:29:19 +00:00
|
|
|
|
2019-10-17 09:24:51 +00:00
|
|
|
permission_function = can_view
|
2016-08-24 17:50:22 +00:00
|
|
|
|
2017-07-22 20:35:01 +00:00
|
|
|
|
2019-11-24 18:23:43 +00:00
|
|
|
class UserIsRootMixin(GenericContentPermissionMixinBuilder):
|
|
|
|
"""
|
|
|
|
This view check if the user is root
|
|
|
|
|
|
|
|
:raises: PermissionDenied
|
|
|
|
"""
|
|
|
|
|
|
|
|
permission_function = lambda obj, user: user.is_root
|
|
|
|
|
|
|
|
|
2017-07-24 10:33:34 +00:00
|
|
|
class FormerSubscriberMixin(View):
|
2017-07-22 20:35:01 +00:00
|
|
|
"""
|
|
|
|
This view check if the user was at least an old subscriber
|
2019-08-14 16:46:33 +00:00
|
|
|
|
|
|
|
:raises: PermissionDenied
|
2017-07-22 20:35:01 +00:00
|
|
|
"""
|
2018-10-04 19:29:19 +00:00
|
|
|
|
2017-07-22 20:35:01 +00:00
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
|
|
if not request.user.was_subscribed:
|
|
|
|
raise PermissionDenied
|
2017-07-24 10:33:34 +00:00
|
|
|
return super(FormerSubscriberMixin, self).dispatch(request, *args, **kwargs)
|
2017-07-22 20:35:01 +00:00
|
|
|
|
|
|
|
|
2019-07-15 10:26:04 +00:00
|
|
|
class UserIsLoggedMixin(View):
|
|
|
|
"""
|
|
|
|
This view check if the user is logged
|
2019-08-14 16:46:33 +00:00
|
|
|
|
|
|
|
:raises: PermissionDenied
|
2019-07-15 10:26:04 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
|
|
if request.user.is_anonymous:
|
|
|
|
raise PermissionDenied
|
|
|
|
return super(UserIsLoggedMixin, self).dispatch(request, *args, **kwargs)
|
|
|
|
|
|
|
|
|
2016-09-04 17:24:53 +00:00
|
|
|
class TabedViewMixin(View):
|
|
|
|
"""
|
|
|
|
This view provide the basic functions for displaying tabs in the template
|
|
|
|
"""
|
2018-10-04 19:29:19 +00:00
|
|
|
|
2016-09-04 17:24:53 +00:00
|
|
|
def get_tabs_title(self):
|
|
|
|
try:
|
|
|
|
return self.tabs_title
|
|
|
|
except:
|
|
|
|
raise ImproperlyConfigured("tabs_title is required")
|
|
|
|
|
|
|
|
def get_current_tab(self):
|
|
|
|
try:
|
|
|
|
return self.current_tab
|
|
|
|
except:
|
|
|
|
raise ImproperlyConfigured("current_tab is required")
|
|
|
|
|
|
|
|
def get_list_of_tabs(self):
|
|
|
|
try:
|
|
|
|
return self.list_of_tabs
|
|
|
|
except:
|
|
|
|
raise ImproperlyConfigured("list_of_tabs is required")
|
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
kwargs = super(TabedViewMixin, self).get_context_data(**kwargs)
|
2018-10-04 19:29:19 +00:00
|
|
|
kwargs["tabs_title"] = self.get_tabs_title()
|
|
|
|
kwargs["current_tab"] = self.get_current_tab()
|
|
|
|
kwargs["list_of_tabs"] = self.get_list_of_tabs()
|
2016-09-04 17:24:53 +00:00
|
|
|
return kwargs
|
|
|
|
|
2018-10-04 19:29:19 +00:00
|
|
|
|
2017-01-10 16:45:49 +00:00
|
|
|
class QuickNotifMixin:
|
2017-01-07 14:07:28 +00:00
|
|
|
quick_notif_list = []
|
2017-01-10 16:45:49 +00:00
|
|
|
|
|
|
|
def dispatch(self, request, *arg, **kwargs):
|
2024-06-24 09:56:38 +00:00
|
|
|
# In some cases, the class can stay instanciated, so we need to reset the list
|
|
|
|
self.quick_notif_list = []
|
2017-01-10 16:45:49 +00:00
|
|
|
return super(QuickNotifMixin, self).dispatch(request, *arg, **kwargs)
|
|
|
|
|
2017-01-07 14:07:28 +00:00
|
|
|
def get_success_url(self):
|
|
|
|
ret = super(QuickNotifMixin, self).get_success_url()
|
|
|
|
try:
|
2018-10-04 19:29:19 +00:00
|
|
|
if "?" in ret:
|
|
|
|
ret += "&" + self.quick_notif_url_arg
|
2017-01-07 14:07:28 +00:00
|
|
|
else:
|
2018-10-04 19:29:19 +00:00
|
|
|
ret += "?" + self.quick_notif_url_arg
|
|
|
|
except:
|
|
|
|
pass
|
2017-01-07 14:07:28 +00:00
|
|
|
return ret
|
|
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
|
|
|
"""Add quick notifications to context"""
|
|
|
|
kwargs = super(QuickNotifMixin, self).get_context_data(**kwargs)
|
2018-10-04 19:29:19 +00:00
|
|
|
kwargs["quick_notifs"] = []
|
2017-01-07 14:07:28 +00:00
|
|
|
for n in self.quick_notif_list:
|
2018-10-04 19:29:19 +00:00
|
|
|
kwargs["quick_notifs"].append(settings.SITH_QUICK_NOTIF[n])
|
|
|
|
for k, v in settings.SITH_QUICK_NOTIF.items():
|
2017-01-07 14:07:28 +00:00
|
|
|
for gk in self.request.GET.keys():
|
|
|
|
if k == gk:
|
2018-10-04 19:29:19 +00:00
|
|
|
kwargs["quick_notifs"].append(v)
|
2017-01-07 14:07:28 +00:00
|
|
|
return kwargs
|
|
|
|
|
|
|
|
|
2019-04-10 11:21:34 +00:00
|
|
|
class DetailFormView(SingleObjectMixin, FormView):
|
|
|
|
"""
|
2020-08-27 13:59:42 +00:00
|
|
|
Class that allow both a detail view and a form view
|
2019-04-10 11:21:34 +00:00
|
|
|
"""
|
|
|
|
|
|
|
|
def get_object(self):
|
|
|
|
"""
|
2020-08-27 13:59:42 +00:00
|
|
|
Get current group from id in url
|
2019-04-10 11:21:34 +00:00
|
|
|
"""
|
|
|
|
return self.cached_object
|
|
|
|
|
|
|
|
@cached_property
|
|
|
|
def cached_object(self):
|
|
|
|
"""
|
2020-08-27 13:59:42 +00:00
|
|
|
Optimisation on group retrieval
|
2019-04-10 11:21:34 +00:00
|
|
|
"""
|
|
|
|
return super(DetailFormView, self).get_object()
|
|
|
|
|
|
|
|
|
2016-08-10 03:48:06 +00:00
|
|
|
from .files import *
|
2015-11-26 16:40:31 +00:00
|
|
|
from .group import *
|
2024-06-24 11:07:36 +00:00
|
|
|
from .page import *
|
|
|
|
from .site import *
|
|
|
|
from .user import *
|