mirror of
https://github.com/ae-utbm/sith.git
synced 2025-07-09 19:40:19 +00:00
replace drf by django-ninja
This commit is contained in:
29
core/api.py
Normal file
29
core/api.py
Normal file
@ -0,0 +1,29 @@
|
||||
from django.conf import settings
|
||||
from django.http import HttpResponse
|
||||
from ninja_extra import ControllerBase, api_controller, route
|
||||
from ninja_extra.exceptions import PermissionDenied
|
||||
|
||||
from club.models import Mailing
|
||||
from core.schemas import MarkdownSchema
|
||||
from core.templatetags.renderer import markdown
|
||||
|
||||
|
||||
@api_controller("/markdown")
|
||||
class MarkdownController(ControllerBase):
|
||||
@route.post("", url_name="markdown")
|
||||
def render_markdown(self, body: MarkdownSchema):
|
||||
"""Convert the markdown text into html."""
|
||||
return HttpResponse(markdown(body.text), content_type="text/html")
|
||||
|
||||
|
||||
@api_controller("/mailings")
|
||||
class MailingListController(ControllerBase):
|
||||
@route.get("", response=str)
|
||||
def fetch_mailing_lists(self, key: str):
|
||||
if key != settings.SITH_MAILING_FETCH_KEY:
|
||||
raise PermissionDenied
|
||||
mailings = Mailing.objects.filter(
|
||||
is_moderated=True, club__is_active=True
|
||||
).prefetch_related("subscriptions")
|
||||
data = "\n".join(m.fetch_format() for m in mailings)
|
||||
return data
|
96
core/api_permissions.py
Normal file
96
core/api_permissions.py
Normal file
@ -0,0 +1,96 @@
|
||||
"""Permission classes to be used within ninja-extra controllers.
|
||||
|
||||
Some permissions are global (like `IsInGroup` or `IsRoot`),
|
||||
and some others are per-object (like `CanView` or `CanEdit`).
|
||||
|
||||
Examples:
|
||||
```python
|
||||
# restrict all the routes of this controller
|
||||
# to subscribed users
|
||||
@api_controller("/foo", permissions=[IsSubscriber])
|
||||
class FooController(ControllerBase):
|
||||
@route.get("/bar")
|
||||
def bar_get(self):
|
||||
# This route inherits the permissions of the controller
|
||||
# ...
|
||||
|
||||
@route.bar("/bar/{bar_id}", permissions=[CanView])
|
||||
def bar_get_one(self, bar_id: int):
|
||||
# per-object permission resolution happens
|
||||
# when calling either the `get_object_or_exception`
|
||||
# or `get_object_or_none` method.
|
||||
bar = self.get_object_or_exception(Counter, pk=bar_id)
|
||||
|
||||
# you can also call the `check_object_permission` manually
|
||||
other_bar = Counter.objects.first()
|
||||
self.check_object_permissions(other_bar)
|
||||
|
||||
# ...
|
||||
|
||||
# This route is restricted to counter admins and root users
|
||||
@route.delete(
|
||||
"/bar/{bar_id}",
|
||||
permissions=[IsRoot | IsInGroup(settings.SITH_GROUP_COUNTER_ADMIN_ID)
|
||||
]
|
||||
def bar_delete(self, bar_id: int):
|
||||
# ...
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from django.http import HttpRequest
|
||||
from ninja_extra import ControllerBase
|
||||
from ninja_extra.permissions import BasePermission
|
||||
|
||||
|
||||
class IsInGroup(BasePermission):
|
||||
def __init__(self, group_pk: int):
|
||||
self._group_pk = group_pk
|
||||
|
||||
def has_permission(self, request: HttpRequest, controller: ControllerBase) -> bool:
|
||||
return request.user.is_in_group(pk=self._group_pk)
|
||||
|
||||
|
||||
class IsRoot(BasePermission):
|
||||
def has_permission(self, request: HttpRequest, controller: ControllerBase) -> bool:
|
||||
return request.user.is_root
|
||||
|
||||
|
||||
class IsSubscriber(BasePermission):
|
||||
def has_permission(self, request: HttpRequest, controller: ControllerBase) -> bool:
|
||||
return request.user.is_subscribed
|
||||
|
||||
|
||||
class IsOldSubscriber(BasePermission):
|
||||
def has_permission(self, request: HttpRequest, controller: ControllerBase) -> bool:
|
||||
return request.user.was_subscribed
|
||||
|
||||
|
||||
class CanView(BasePermission):
|
||||
def has_permission(self, request: HttpRequest, controller: ControllerBase) -> bool:
|
||||
return True
|
||||
|
||||
def has_object_permission(
|
||||
self, request: HttpRequest, controller: ControllerBase, obj: Any
|
||||
) -> bool:
|
||||
return request.user.can_view(obj)
|
||||
|
||||
|
||||
class CanEdit(BasePermission):
|
||||
def has_permission(self, request: HttpRequest, controller: ControllerBase) -> bool:
|
||||
return True
|
||||
|
||||
def has_object_permission(
|
||||
self, request: HttpRequest, controller: ControllerBase, obj: Any
|
||||
) -> bool:
|
||||
return request.user.can_edit(obj)
|
||||
|
||||
|
||||
class IsOwner(BasePermission):
|
||||
def has_permission(self, request: HttpRequest, controller: ControllerBase) -> bool:
|
||||
return True
|
||||
|
||||
def has_object_permission(
|
||||
self, request: HttpRequest, controller: ControllerBase, obj: Any
|
||||
) -> bool:
|
||||
return request.user.is_owner(obj)
|
15
core/schemas.py
Normal file
15
core/schemas.py
Normal file
@ -0,0 +1,15 @@
|
||||
from ninja import ModelSchema, Schema
|
||||
|
||||
from core.models import User
|
||||
|
||||
|
||||
class SimpleUserSchema(ModelSchema):
|
||||
"""A schema with the minimum amount of information to represent a user."""
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ["id", "nick_name", "first_name", "last_name"]
|
||||
|
||||
|
||||
class MarkdownSchema(Schema):
|
||||
text: str
|
@ -84,10 +84,10 @@
|
||||
}
|
||||
function download_pictures() {
|
||||
$("#download_all_pictures").prop("disabled", true);
|
||||
var xhr = new XMLHttpRequest();
|
||||
const xhr = new XMLHttpRequest();
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "{{ url('api:all_pictures_of_user', user=object.id) }}",
|
||||
url: "{{ url('api:pictures') }}?users_identified={{ object.id }}",
|
||||
tryCount: 0,
|
||||
xhr: function(){
|
||||
return xhr;
|
||||
|
@ -65,7 +65,7 @@ class TestUserRegistration:
|
||||
{"password2": "not the same as password1"},
|
||||
"Les deux mots de passe ne correspondent pas.",
|
||||
),
|
||||
({"email": "not-an-email"}, "Saisissez une adresse e-mail valide."),
|
||||
({"email": "not-an-email"}, "Saisissez une adresse de courriel valide."),
|
||||
({"first_name": ""}, "Ce champ est obligatoire."),
|
||||
({"last_name": ""}, "Ce champ est obligatoire."),
|
||||
({"captcha_1": "WRONG_CAPTCHA"}, "CAPTCHA invalide"),
|
||||
@ -310,7 +310,6 @@ http://git.an
|
||||
)
|
||||
response = self.client.get(reverse("core:page", kwargs={"page_name": "guy"}))
|
||||
assert response.status_code == 200
|
||||
print(response.content.decode())
|
||||
expected = """
|
||||
<p>Guy <em>bibou</em></p>
|
||||
<p><a href="http://git.an">http://git.an</a></p>
|
||||
|
@ -25,6 +25,7 @@
|
||||
import types
|
||||
from typing import Any
|
||||
|
||||
from django.contrib.auth.mixins import AccessMixin
|
||||
from django.core.exceptions import (
|
||||
ImproperlyConfigured,
|
||||
PermissionDenied,
|
||||
@ -234,7 +235,7 @@ class UserIsRootMixin(GenericContentPermissionMixinBuilder):
|
||||
permission_function = lambda obj, user: user.is_root
|
||||
|
||||
|
||||
class FormerSubscriberMixin(View):
|
||||
class FormerSubscriberMixin(AccessMixin):
|
||||
"""Check if the user was at least an old subscriber.
|
||||
|
||||
Raises:
|
||||
@ -247,16 +248,10 @@ class FormerSubscriberMixin(View):
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
class UserIsLoggedMixin(View):
|
||||
"""Check if the user is logged.
|
||||
|
||||
Raises:
|
||||
PermissionDenied:
|
||||
"""
|
||||
|
||||
class SubscriberMixin(AccessMixin):
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if request.user.is_anonymous:
|
||||
raise PermissionDenied
|
||||
if not request.user.is_subscribed:
|
||||
return self.handle_no_permission()
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
|
@ -104,7 +104,7 @@ class MarkdownInput(Textarea):
|
||||
"fullscreen": _("Toggle fullscreen"),
|
||||
"guide": _("Markdown guide"),
|
||||
}
|
||||
context["markdown_api_url"] = reverse("api:api_markdown")
|
||||
context["markdown_api_url"] = reverse("api:markdown")
|
||||
return context
|
||||
|
||||
|
||||
|
@ -29,6 +29,7 @@ from smtplib import SMTPException
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import login, views
|
||||
from django.contrib.auth.forms import PasswordChangeForm
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.core.exceptions import PermissionDenied, ValidationError
|
||||
from django.forms import CheckboxSelectMultiple
|
||||
from django.forms.models import modelform_factory
|
||||
@ -50,7 +51,6 @@ from django.views.generic.dates import MonthMixin, YearMixin
|
||||
from django.views.generic.edit import FormView, UpdateView
|
||||
from honeypot.decorators import check_honeypot
|
||||
|
||||
from api.views.sas import all_pictures_of_user
|
||||
from core.models import Gift, Preferences, SithFile, User
|
||||
from core.views import (
|
||||
CanEditMixin,
|
||||
@ -58,7 +58,6 @@ from core.views import (
|
||||
CanViewMixin,
|
||||
QuickNotifMixin,
|
||||
TabedViewMixin,
|
||||
UserIsLoggedMixin,
|
||||
)
|
||||
from core.views.forms import (
|
||||
GiftForm,
|
||||
@ -68,6 +67,7 @@ from core.views.forms import (
|
||||
UserProfileForm,
|
||||
)
|
||||
from counter.forms import StudentCardForm
|
||||
from sas.models import Picture
|
||||
from subscription.models import Subscription
|
||||
from trombi.views import UserTrombiForm
|
||||
|
||||
@ -313,7 +313,11 @@ class UserPicturesView(UserTabsMixin, CanViewMixin, DetailView):
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
kwargs["albums"] = []
|
||||
kwargs["pictures"] = {}
|
||||
picture_qs = all_pictures_of_user(self.object)
|
||||
picture_qs = (
|
||||
Picture.objects.filter(people__user_id=self.object.id)
|
||||
.order_by("parent__date", "id")
|
||||
.all()
|
||||
)
|
||||
last_album = None
|
||||
for picture in picture_qs:
|
||||
album = picture.parent
|
||||
@ -720,7 +724,7 @@ class UserUpdateGroupView(UserTabsMixin, CanEditPropMixin, UpdateView):
|
||||
current_tab = "groups"
|
||||
|
||||
|
||||
class UserToolsView(QuickNotifMixin, UserTabsMixin, UserIsLoggedMixin, TemplateView):
|
||||
class UserToolsView(LoginRequiredMixin, QuickNotifMixin, UserTabsMixin, TemplateView):
|
||||
"""Displays the logged user's tools."""
|
||||
|
||||
template_name = "core/user_tools.jinja"
|
||||
|
Reference in New Issue
Block a user