diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0239f99598..d42ff7563a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -17,6 +17,10 @@ Unreleased ---------- * nothing unreleased +[4.31.0] +-------- +* feat: add new endpoint to unlink the logged in user. + [4.30.1] -------- * fix: serialize best_mode_for_course_run field in DefaultEnterpriseEnrollmentIntentionSerializer. diff --git a/consent/api/permissions.py b/consent/api/permissions.py index 57fbbdb8e0..80bf5b2a61 100644 --- a/consent/api/permissions.py +++ b/consent/api/permissions.py @@ -27,3 +27,11 @@ def has_permission(self, request, view): return True return super().has_permission(request, view) + +class IsUseremailInRequest(permissions.BasePermission): + """ + Permission that checks to see if the request user email matches the user email indicated in the request body. + """ + + def has_permission(self, request, view): + return request.user.email == get_request_value(request, 'user_email', '') diff --git a/enterprise/__init__.py b/enterprise/__init__.py index c39375715a..e1ddcb95b4 100644 --- a/enterprise/__init__.py +++ b/enterprise/__init__.py @@ -2,4 +2,4 @@ Your project description goes here. """ -__version__ = "4.30.1" +__version__ = "4.31.0" diff --git a/enterprise/api/v1/serializers.py b/enterprise/api/v1/serializers.py index 0646b3b4f0..3e3d5e309d 100644 --- a/enterprise/api/v1/serializers.py +++ b/enterprise/api/v1/serializers.py @@ -1652,6 +1652,20 @@ class EnterpriseCustomerUnlinkUsersSerializer(serializers.Serializer): default=False, ) +class EnterpriseCustomerUnlinkSelfUserSerializer(serializers.Serializer): + """ + Serializer for the ``EnterpriseCustomerViewSet`` unlink_self action. + """ + + user_email = serializers.EmailField( + allow_blank=False, + ) + + is_relinkable = serializers.BooleanField( + required=False, + default=False, + ) + class EnterpriseCatalogQuerySerializer(serializers.ModelSerializer): """ diff --git a/enterprise/api/v1/views/enterprise_customer.py b/enterprise/api/v1/views/enterprise_customer.py index cd4660dbe8..b64200cf98 100644 --- a/enterprise/api/v1/views/enterprise_customer.py +++ b/enterprise/api/v1/views/enterprise_customer.py @@ -46,6 +46,7 @@ track_enrollment, validate_email_to_link, ) +from consent.api.permissions import IsUseremailInRequest User = auth.get_user_model() @@ -460,7 +461,6 @@ def unlink_users(self, request, pk=None): # pylint: disable=unused-argument """ Unlinks users with the given emails from the enterprise. """ - serializer = serializers.EnterpriseCustomerUnlinkUsersSerializer( data=request.data ) @@ -487,3 +487,32 @@ def unlink_users(self, request, pk=None): # pylint: disable=unused-argument raise UnlinkUserFromEnterpriseError(msg) from exc return Response(status=HTTP_200_OK) + + @action(methods=['post'], detail=True, permission_classes=[permissions.IsAuthenticated, IsUseremailInRequest]) + def unlink_self(self, request, pk=None): # pylint: disable=unused-argument + """ + Unlink request user from the enterprise. + """ + serializer = serializers.EnterpriseCustomerUnlinkSelfUserSerializer( + data=request.data + ) + + serializer.is_valid(raise_exception=True) + enterprise_customer = self.get_object() + user_email = serializer.data.get('user_email') + is_relinkable = serializer.data.get('is_relinkable', True) + + try: + models.EnterpriseCustomerUser.objects.unlink_user( + enterprise_customer=enterprise_customer, user_email=user_email, is_relinkable=is_relinkable + ) + except (models.EnterpriseCustomerUser.DoesNotExist, models.PendingEnterpriseCustomerUser.DoesNotExist): + msg = "[UNLINK_SELF] User with email {} does not exist in enterprise {}.".format( + user_email, enterprise_customer + ) + LOGGER.warning(msg) + except Exception as exc: + msg = "[UNLINK_SELF] Could not unlink {} from {}".format(user_email, enterprise_customer) + raise UnlinkUserFromEnterpriseError(msg) from exc + + return Response(status=HTTP_200_OK)