1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2025-12-10 05:37:46 +01:00

Compare commits

..

2 Commits

Author SHA1 Message Date
alexismdr
a25afea16d fix: line length for linters 2025-12-09 20:19:29 +01:00
alexismdr
56167a21b5 feat: check min/max promotion year
* min year is 1912 (ENSET creation year)
* max year is same as default

WARNING : Changing this behavior could break iOS/Android apps. Please change Swift/Kotlin code accordingly.
2025-12-09 19:28:56 +01:00
6 changed files with 12 additions and 51 deletions

View File

@@ -89,11 +89,3 @@ class OAuthSerializer(serializers.ModelSerializer):
'note', 'note',
'memberships', 'memberships',
) )
class QRCodeCheckSerializer(serializers.Serializer):
data = serializers.CharField(
label="Données du QR Code",
help_text="Le contenu brut lu depuis le QR Code (Username + Token)",
required=True
)

View File

@@ -3,18 +3,17 @@
from django.conf import settings from django.conf import settings
from django.conf.urls import include from django.conf.urls import include
from django.urls import re_path, path from django.urls import re_path
from rest_framework import routers from rest_framework import routers
from .views import UserInformationView from .views import UserInformationView
from .viewsets import ContentTypeViewSet, UserViewSet, QRCodeVerificationViewSet from .viewsets import ContentTypeViewSet, UserViewSet
# Routers provide an easy way of automatically determining the URL conf. # Routers provide an easy way of automatically determining the URL conf.
# Register each app API router and user viewset # Register each app API router and user viewset
router = routers.DefaultRouter() router = routers.DefaultRouter()
router.register('models', ContentTypeViewSet) router.register('models', ContentTypeViewSet)
router.register('user', UserViewSet) router.register('user', UserViewSet)
router.register('check_qrcode', QRCodeVerificationViewSet, basename='check_qrcode')
if "activity" in settings.INSTALLED_APPS: if "activity" in settings.INSTALLED_APPS:
from activity.api.urls import register_activity_urls from activity.api.urls import register_activity_urls
@@ -62,7 +61,6 @@ app_name = 'api'
# Additionally, we include login URLs for the browsable API. # Additionally, we include login URLs for the browsable API.
urlpatterns = [ urlpatterns = [
re_path('^', include(router.urls)), re_path('^', include(router.urls)),
path('me/', UserInformationView.as_view({'get': 'retrieve'})), re_path('^me/', UserInformationView.as_view()),
path('me/qrcode/', UserInformationView.as_view({'get': 'qrcode'})),
re_path('^api-auth/', include('rest_framework.urls', namespace='rest_framework')), re_path('^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
] ]

View File

@@ -1,19 +1,13 @@
# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay # Copyright (C) 2018-2025 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import base64
import os
from io import BytesIO
import qrcode
import pyotp
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.http.response import HttpResponse from rest_framework.generics import RetrieveAPIView
from rest_framework import viewsets, mixins
from .serializers import OAuthSerializer from .serializers import OAuthSerializer
class UserInformationView(mixins.RetrieveModelMixin, viewsets.GenericViewSet): class UserInformationView(RetrieveAPIView):
""" """
These fields are give to OAuth authenticators. These fields are give to OAuth authenticators.
""" """
@@ -24,11 +18,3 @@ class UserInformationView(mixins.RetrieveModelMixin, viewsets.GenericViewSet):
def get_object(self): def get_object(self):
return self.request.user return self.request.user
def qrcode(self, request, *args, **kwargs):
secret = base64.b32encode(os.getenv("DJANGO_SECRET_KEY").encode())
qr_img = qrcode.make(f"{str(request.user.note)}{pyotp.TOTP(secret, interval=30).now()}")
buffer = BytesIO()
qr_img.save(buffer, format="PNG")
buffer.seek(0)
return HttpResponse(buffer, content_type="image/png")

View File

@@ -2,22 +2,18 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
import re import re
import base64
import os
import pyotp
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django_filters.rest_framework import DjangoFilterBackend from django_filters.rest_framework import DjangoFilterBackend
from django.db.models import Q from django.db.models import Q
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from rest_framework.viewsets import ReadOnlyModelViewSet, ModelViewSet, GenericViewSet from rest_framework.viewsets import ReadOnlyModelViewSet, ModelViewSet
from rest_framework.response import Response
from permission.backends import PermissionBackend from permission.backends import PermissionBackend
from note.models import Alias from note.models import Alias
from .filters import RegexSafeSearchFilter from .filters import RegexSafeSearchFilter
from .serializers import UserSerializer, ContentTypeSerializer, QRCodeCheckSerializer from .serializers import UserSerializer, ContentTypeSerializer
def is_regex(pattern): def is_regex(pattern):
@@ -128,17 +124,3 @@ class ContentTypeViewSet(ReadOnlyModelViewSet):
filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter] filter_backends = [DjangoFilterBackend, RegexSafeSearchFilter]
filterset_fields = ['id', 'app_label', 'model', ] filterset_fields = ['id', 'app_label', 'model', ]
search_fields = ['$app_label', '$model', ] search_fields = ['$app_label', '$model', ]
class QRCodeVerificationViewSet(GenericViewSet):
serializer_class = QRCodeCheckSerializer
queryset = User.objects.none()
def get_view_name(self):
return "Vérification QR Code"
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
secret = base64.b32encode(os.getenv("DJANGO_SECRET_KEY").encode())
return Response({'valid': pyotp.TOTP(secret, interval=30).verify(serializer.validated_data['data'][-6:])})

View File

@@ -19,6 +19,7 @@ from phonenumber_field.modelfields import PhoneNumberField
from permission.models import Role from permission.models import Role
from registration.tokens import email_validation_token from registration.tokens import email_validation_token
from note.models import MembershipTransaction from note.models import MembershipTransaction
from django.core.validators import MaxValueValidator, MinValueValidator
class Profile(models.Model): class Profile(models.Model):
@@ -76,6 +77,10 @@ class Profile(models.Model):
default=datetime.date.today().year if datetime.date.today().month >= 8 else datetime.date.today().year - 1, default=datetime.date.today().year if datetime.date.today().month >= 8 else datetime.date.today().year - 1,
verbose_name=_("promotion"), verbose_name=_("promotion"),
help_text=_("Year of entry to the school (None if not ENS student)"), help_text=_("Year of entry to the school (None if not ENS student)"),
validators=[
MinValueValidator(1912),
MaxValueValidator(datetime.date.today().year if datetime.date.today().month >= 8 else datetime.date.today().year - 1)
],
) )
address = models.CharField( address = models.CharField(

View File

@@ -20,5 +20,3 @@ python-memcached~=1.62
phonenumbers~=9.0.8 phonenumbers~=9.0.8
tablib~=3.8.0 tablib~=3.8.0
Pillow>=11.3.0 Pillow>=11.3.0
pyotp~=2.9.0
qrcode~=8.2