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

Compare commits

..

8 Commits

Author SHA1 Message Date
alexismdr
4059efa6c6 Merge branch 'aasa' into 'main'
Apple App-Site Association

See merge request bde/nk20!362
2025-12-09 20:56:51 +01:00
alexismdr
e146548658 Merge branch 'main' into 'aasa'
# Conflicts:
#   .env_example
2025-12-09 20:11:25 +01:00
alexismdr
1c891d6708 fix: import os 2025-12-09 18:51:10 +01:00
alexismdr
f3289de5d8 feat/fix: use env var instead of hardcoded 2025-12-09 18:45:07 +01:00
alexismdr
68ea640736 feat: add App Store app ID in env vars 2025-12-09 18:44:29 +01:00
alexismdr
00176c3cef fix: add aasa import in urls.py 2025-11-28 09:13:32 +01:00
alexismdr
de841c8143 feat: distribute aasa on .well-known/apple-app-site-association 2025-11-28 02:11:10 +01:00
alexismdr
43603d7359 feat: aasa view json distribution
* basic webcredentials config for password managers

See https://developer.apple.com/documentation/xcode/supporting-associated-domains for ref
2025-11-28 02:08:34 +01:00
8 changed files with 26 additions and 52 deletions

View File

@@ -28,3 +28,6 @@ OIDC_RSA_PRIVATE_KEY=CHANGE_ME
# Activity configuration # Activity configuration
TRUSTED_ACTIVITY_MAIL= TRUSTED_ACTIVITY_MAIL=
ACTIVITY_EMAIL_MANAGER= ACTIVITY_EMAIL_MANAGER=
# App Store App ID
APP_STORE_APP_ID=P5246D3AFQ.org.crans.bde.note

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

@@ -8,7 +8,7 @@ from django.views.defaults import bad_request, permission_denied, page_not_found
from member.views import CustomLoginView from member.views import CustomLoginView
from .admin import admin_site from .admin import admin_site
from .views import IndexView from .views import IndexView, apple_app_site_association
urlpatterns = [ urlpatterns = [
# Dev so redirect to something random # Dev so redirect to something random
@@ -33,6 +33,9 @@ urlpatterns = [
path('accounts/', include('django.contrib.auth.urls')), path('accounts/', include('django.contrib.auth.urls')),
path('api/', include('api.urls')), path('api/', include('api.urls')),
path('permission/', include('permission.urls')), path('permission/', include('permission.urls')),
# Apple App Site Association
path('.well-known/apple-app-site-association', apple_app_site_association),
] ]
# During development, serve static and media files # During development, serve static and media files

View File

@@ -6,6 +6,8 @@ from django.urls import reverse
from django.views.generic import RedirectView from django.views.generic import RedirectView
from note.models import Alias from note.models import Alias
from permission.backends import PermissionBackend from permission.backends import PermissionBackend
from django.http import JsonResponse
import os
class IndexView(LoginRequiredMixin, RedirectView): class IndexView(LoginRequiredMixin, RedirectView):
@@ -28,3 +30,13 @@ class IndexView(LoginRequiredMixin, RedirectView):
# Non-Kfet members will don't see the transfer page, but their profile page # Non-Kfet members will don't see the transfer page, but their profile page
return reverse("member:user_detail", args=(user.pk,)) return reverse("member:user_detail", args=(user.pk,))
def apple_app_site_association(request):
data = {
"webcredentials": {
"apps": [
os.getenv("APP_STORE_APP_ID")
]
}
}
return JsonResponse(data)

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