diff --git a/dandiapi/api/views/__init__.py b/dandiapi/api/views/__init__.py index b5cb96010..d2eadef6c 100644 --- a/dandiapi/api/views/__init__.py +++ b/dandiapi/api/views/__init__.py @@ -14,7 +14,7 @@ upload_initialize_view, upload_validate_view, ) -from .users import users_me_view, users_search_view +from .users import user_email_view, users_me_view, users_search_view from .version import VersionViewSet __all__ = [ @@ -31,6 +31,7 @@ 'upload_complete_view', 'upload_validate_view', 'user_approval_view', + 'user_email_view', 'users_me_view', 'user_questionnaire_form_view', 'users_search_view', diff --git a/dandiapi/api/views/serializers.py b/dandiapi/api/views/serializers.py index c0645ae6b..ee2bc0c29 100644 --- a/dandiapi/api/views/serializers.py +++ b/dandiapi/api/views/serializers.py @@ -3,6 +3,7 @@ from typing import TYPE_CHECKING, Any from django.conf import settings +from django.contrib.auth.models import User from django.contrib.auth.validators import UnicodeUsernameValidator from django.db.models.query_utils import Q from drf_yasg.utils import swagger_serializer_method @@ -40,6 +41,17 @@ class UserDetailSerializer(serializers.Serializer): status = serializers.CharField() +class UserEmailSerializer(serializers.Serializer): + subject = serializers.CharField() + message = serializers.CharField() + username = serializers.CharField() + + def validate_username(self, value): + if not User.objects.filter(username=value).exists(): + raise serializers.ValidationError("No user found with that username.") + return value + + class DandisetSerializer(serializers.ModelSerializer): contact_person = serializers.SerializerMethodField(method_name='get_contact_person') star_count = serializers.SerializerMethodField() diff --git a/dandiapi/api/views/users.py b/dandiapi/api/views/users.py index 27362b8c1..88862d94b 100644 --- a/dandiapi/api/views/users.py +++ b/dandiapi/api/views/users.py @@ -1,21 +1,26 @@ from __future__ import annotations +import json import logging import re from typing import TYPE_CHECKING from allauth.socialaccount.models import SocialAccount from django.contrib.auth.models import User +from django.core import mail +from django.core.exceptions import PermissionDenied from django.db.models import OuterRef, Q, Subquery from drf_yasg.utils import swagger_auto_schema +from rest_framework import serializers from rest_framework.decorators import api_view, parser_classes, permission_classes from rest_framework.parsers import JSONParser from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response +from dandiapi.api.mail import build_message from dandiapi.api.models import UserMetadata from dandiapi.api.permissions import IsApproved -from dandiapi.api.views.serializers import UserDetailSerializer, UserSerializer +from dandiapi.api.views.serializers import UserDetailSerializer, UserEmailSerializer, UserSerializer if TYPE_CHECKING: from django.http.response import HttpResponseBase @@ -144,3 +149,31 @@ def users_search_view(request: Request) -> HttpResponseBase: users = [serialize_user(user) for user in qs] response_serializer = UserDetailSerializer(users, many=True) return Response(response_serializer.data) + +@swagger_auto_schema( + method='POST', + operation_summary='Send an email to the specified user (Superuser only)', + request_body=UserEmailSerializer, + responses={ + 200: UserEmailSerializer, + 403: 'Permission Denied: Only superusers can access this endpoint.' + }, +) +@parser_classes([JSONParser]) +@api_view(['POST']) +def user_email_view(request: Request) -> HttpResponseBase: + # If they are authenticated but are not a superuser, deny access + if not request.user.is_superuser: + raise PermissionDenied + + request_serializer = UserEmailSerializer(data=request.data) + request_serializer.is_valid(raise_exception=True) + message = build_message( + subject=request_serializer.validated_data["subject"], + message=request_serializer.validated_data["message"], + to=[request_serializer.validated_data["username"]], + ) + # TODO enable + # with mail.get_connection() as connection: + # connection.send_messages([message]) + return Response(request_serializer.data) diff --git a/dandiapi/urls.py b/dandiapi/urls.py index 0d94c3ebd..e453381fb 100644 --- a/dandiapi/urls.py +++ b/dandiapi/urls.py @@ -26,6 +26,7 @@ upload_initialize_view, upload_validate_view, user_approval_view, + user_email_view, user_questionnaire_form_view, users_me_view, users_search_view, @@ -75,6 +76,7 @@ name='upload-validate', ), path('api/users/me/', users_me_view), + path('api/users/mail/', user_email_view), path('api/users/search/', users_search_view), re_path( r'^api/users/questionnaire-form/$', user_questionnaire_form_view, name='user-questionnaire'