diff --git a/gregui/api/serializers/identity.py b/gregui/api/serializers/identity.py new file mode 100644 index 0000000000000000000000000000000000000000..df8d06b1a9b1bc059696953b1ef5869bfd22b06e --- /dev/null +++ b/gregui/api/serializers/identity.py @@ -0,0 +1,52 @@ +from django.utils import timezone +from rest_framework import serializers + +from greg.models import Identity +from gregui.models import GregUserProfile + + +class IdentitySerializer(serializers.ModelSerializer): + """Serializer for the Identity model with validation of various fields""" + + class Meta: + model = Identity + fields = "__all__" + read_only_fields = [ + "id", + "person", + "type", + "source", + "value", + "verified", + "verified_by", + ] + + def _get_sponsor(self): + """ + Fetch the sponsor doing the request + + Since the ViewSet using this Serializer uses the IsSponsor permission, we know + that the user is connect to a GregUserProfile with a sponsor object. + """ + user = None + request = self.context.get("request") + if request and hasattr(request, "user"): + user = request.user + return GregUserProfile.objects.get(user=user).sponsor + + def validate(self, attrs): + """ + Set values automatically when updating. + + - All updates are manual from the ui endpoints. + - The one verifying must be the logged in user which we know is a sponsor since + the user passed the IsSponsor permission in the view using this serializer. + - No point leaving setting the verified_at time to anyone other than the + server itself. + + Note: Get requests do not use this method, making it safe. + """ + attrs["verified"] = Identity.Verified.MANUAL + attrs["verified_by"] = self._get_sponsor() + attrs["verified_at"] = timezone.now() + return attrs diff --git a/gregui/api/urls.py b/gregui/api/urls.py index 6cb9764a4785481399ff927cc176716ea5ef527b..aaa40487cd74bcf78be456e59afb32578bab0abd 100644 --- a/gregui/api/urls.py +++ b/gregui/api/urls.py @@ -1,5 +1,6 @@ from django.urls import re_path, path from rest_framework.routers import DefaultRouter +from gregui.api.views.identity import IdentityViewSet from gregui.api.views.invitation import ( CheckInvitationView, @@ -14,6 +15,7 @@ from gregui.api.views.unit import UnitsViewSet router = DefaultRouter(trailing_slash=False) router.register(r"role", RoleInfoViewSet, basename="role") +router.register(r"identity", IdentityViewSet, basename="identity") urlpatterns = router.urls urlpatterns += [ diff --git a/gregui/api/views/identity.py b/gregui/api/views/identity.py new file mode 100644 index 0000000000000000000000000000000000000000..18c88586611af8ad85e7a291b9bb115960d6a9ea --- /dev/null +++ b/gregui/api/views/identity.py @@ -0,0 +1,31 @@ +from rest_framework.authentication import SessionAuthentication, BasicAuthentication +from rest_framework.permissions import IsAuthenticated +from rest_framework.viewsets import GenericViewSet +from rest_framework import mixins +from rest_framework.exceptions import MethodNotAllowed +from greg.models import Identity +from greg.permissions import IsSponsor +from gregui.api.serializers.identity import IdentitySerializer +from gregui.models import GregUserProfile + + +class IdentityViewSet( + mixins.RetrieveModelMixin, + mixins.UpdateModelMixin, + GenericViewSet, +): + """ + Fetch or update identity info for any guest as long as you are a sponsor. + + This is required for when a host(sponsor) needs to verify the identity of a guest + so that they are considered "active". + + Limited to GET and PATCH so that we can only update or get a single identity at a + time. + """ + + queryset = Identity.objects.all() + authentication_classes = [SessionAuthentication, BasicAuthentication] + permission_classes = [IsAuthenticated, IsSponsor] + serializer_class = IdentitySerializer + http_method_names = ["get", "patch"]