diff --git a/jdav_web/jdav_web/settings/components/base.py b/jdav_web/jdav_web/settings/components/base.py index c8c4aa32..0e9d55cf 100644 --- a/jdav_web/jdav_web/settings/components/base.py +++ b/jdav_web/jdav_web/settings/components/base.py @@ -56,6 +56,8 @@ "rules", "jet", "oauth2_provider", + "rest_framework", + "rest_framework.authtoken", "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", @@ -207,3 +209,14 @@ ADMINS = get_var("section", "admins", default=[]) LOGIN_URL = "/de/kompass/login/" + +# Django REST Framework +REST_FRAMEWORK = { + "DEFAULT_PERMISSION_CLASSES": [ + "rest_framework.permissions.IsAuthenticated", + ], + "DEFAULT_AUTHENTICATION_CLASSES": [ + "rest_framework.authentication.SessionAuthentication", + "rest_framework.authentication.TokenAuthentication", + ], +} diff --git a/jdav_web/jdav_web/urls.py b/jdav_web/jdav_web/urls.py index 03435099..8ae69e53 100644 --- a/jdav_web/jdav_web/urls.py +++ b/jdav_web/jdav_web/urls.py @@ -23,6 +23,7 @@ from django.views.generic.base import RedirectView from oauth2_provider import urls as oauth2_urls +from members.api import email_forward_lookup from .views import media_access admin.site.index_title = _("Startpage") @@ -47,6 +48,7 @@ urlpatterns += [ re_path(r"^markdownx/", include("markdownx.urls")), + path("api/email-forward/", email_forward_lookup, name="email-forward-lookup"), ] handler404 = "startpage.views.handler404" diff --git a/jdav_web/members/api.py b/jdav_web/members/api.py new file mode 100644 index 00000000..f4b94396 --- /dev/null +++ b/jdav_web/members/api.py @@ -0,0 +1,88 @@ +from django.contrib.auth.models import User +from rest_framework import serializers, status +from rest_framework.authentication import TokenAuthentication +from rest_framework.decorators import api_view, authentication_classes, permission_classes +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response + +from mailer.models import EmailAddress +from members.models import Member + + +class EmailForwardSerializer(serializers.Serializer): + """Serializer for email forwarding request""" + email = serializers.EmailField(required=True) + + +class EmailForwardResponseSerializer(serializers.Serializer): + """Serializer for email forwarding response""" + forward_to = serializers.ListField(child=serializers.EmailField()) + + +@api_view(['POST']) +@authentication_classes([TokenAuthentication]) +@permission_classes([IsAuthenticated]) +def email_forward_lookup(request): + """ + API endpoint to lookup email forwarding addresses. + + Given an email address (username@domain.com), returns a list of email addresses + that the incoming mail should be forwarded to based on two rules: + + 1. If username matches a logindata user's username, forward to the associated member's email + 2. If username matches an EmailAddress, forward to all members and members in groups + specified in the to_members and to_groups fields + + Request body: + { + "email": "username@domain.com" + } + + Response: + { + "forward_to": ["email1@example.com", "email2@example.com"] + } + """ + serializer = EmailForwardSerializer(data=request.data) + if not serializer.is_valid(): + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + email = serializer.validated_data['email'] + # Extract username from email (part before @) + username = email.split('@')[0] + + forward_addresses = set() + + # Rule 1: Check if username matches a User + try: + user = User.objects.get(username=username) + # Get the associated member + if hasattr(user, 'member'): + member = user.member + if member.email: + forward_addresses.add(member.email) + except User.DoesNotExist: + pass + + # Rule 2: Check if username matches an EmailAddress + try: + email_address = EmailAddress.objects.get(name=username) + # Get all members from to_members + for member in email_address.to_members.all(): + if member.email: + forward_addresses.add(member.email) + + # Get all members from groups in to_groups + for group in email_address.to_groups.all(): + for member in group.member_set.all(): + if member.email: + forward_addresses.add(member.email) + except EmailAddress.DoesNotExist: + pass + + # Convert set to sorted list for consistent output + response_data = { + 'forward_to': sorted(list(forward_addresses)) + } + + return Response(response_data, status=status.HTTP_200_OK) diff --git a/requirements.txt b/requirements.txt index abfb4de2..c57a0b94 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,6 +21,7 @@ cryptography==44.0.2 Deprecated==1.2.13 Django==4.2.20 django-appconf==1.0.5 +djangorestframework==3.15.2 django-celery-beat==2.5.0 django-celery-email==3.0.0 django-jet-reboot==1.3.10