feat: Django app to allow user retirement via API (#25800)

This adds a new django app to allow the GDPR user retirement via
Open edX's REST API. Prior to this the only way to trigger the user
retirement was either by the user themself clicking "Delete my account"
in the account setting page or via creating a User Retirement request
by admin. With these changes, the user retirement process can be
triggered using REST API.
This commit is contained in:
Pooja Kulkarni
2021-04-08 23:20:12 +05:30
committed by GitHub
parent 709b8217e1
commit 86bfcea19c
11 changed files with 270 additions and 19 deletions

View File

@@ -197,7 +197,7 @@ class TestDeactivateLogout(RetirementTestCase):
def build_post(self, password):
return {'password': password}
@mock.patch('openedx.core.djangoapps.user_api.accounts.views.retire_dot_oauth2_models')
@mock.patch('openedx.core.djangoapps.user_api.accounts.utils.retire_dot_oauth2_models')
def test_user_can_deactivate_self(self, mock_retire_dot):
"""
Verify a user calling the deactivation endpoint logs out the user, deletes all their SSO tokens,

View File

@@ -11,14 +11,19 @@ from completion.waffle import ENABLE_COMPLETION_TRACKING_SWITCH
from completion.models import BlockCompletion
from django.conf import settings
from django.utils.translation import ugettext as _
from social_django.models import UserSocialAuth
from common.djangoapps.third_party_auth.config.waffle import ENABLE_MULTIPLE_SSO_ACCOUNTS_ASSOCIATION_TO_SAML_USER
from common.djangoapps.student.models import AccountRecovery, Registration, get_retired_email_by_email
from openedx.core.djangolib.oauth2_retirement_utils import retire_dot_oauth2_models
from openedx.core.djangoapps.site_configuration.models import SiteConfiguration
from openedx.core.djangoapps.theming.helpers import get_config_value_from_site_or_settings, get_current_site
from openedx.core.djangoapps.user_api.config.waffle import ENABLE_MULTIPLE_USER_ENTERPRISES_FEATURE
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
from ..models import UserRetirementStatus
ENABLE_SECONDARY_EMAIL_FEATURE_SWITCH = 'enable_secondary_email_feature'
@@ -206,3 +211,28 @@ def is_multiple_sso_accounts_association_to_saml_user_enabled():
Boolean value representing switch status
"""
return ENABLE_MULTIPLE_SSO_ACCOUNTS_ASSOCIATION_TO_SAML_USER.is_enabled()
def create_retirement_request_and_deactivate_account(user):
"""
Adds user to retirement queue, unlinks social auth accounts, changes user passwords
and delete tokens and activation keys
"""
# Add user to retirement queue.
UserRetirementStatus.create_retirement(user)
# Unlink LMS social auth accounts
UserSocialAuth.objects.filter(user_id=user.id).delete()
# Change LMS password & email
user.email = get_retired_email_by_email(user.email)
user.set_unusable_password()
user.save()
# TODO: Unlink social accounts & change password on each IDA.
# Remove the activation keys sent by email to the user for account activation.
Registration.objects.filter(user=user).delete()
# Delete OAuth tokens associated with the user.
retire_dot_oauth2_models(user)
AccountRecovery.retire_recovery_email(user.id)

View File

@@ -34,7 +34,6 @@ from rest_framework.response import Response
from rest_framework.serializers import ValidationError
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSet
from social_django.models import UserSocialAuth
from wiki.models import ArticleRevision
from wiki.models.pluginbase import RevisionPluginRevision
@@ -48,7 +47,6 @@ from openedx.core.djangoapps.lang_pref import LANGUAGE_KEY
from openedx.core.djangoapps.profile_images.images import remove_profile_images
from openedx.core.djangoapps.user_api.accounts.image_helpers import get_profile_image_names, set_has_profile_image
from openedx.core.djangoapps.user_authn.exceptions import AuthFailedError
from openedx.core.djangolib.oauth2_retirement_utils import retire_dot_oauth2_models
from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser
from openedx.core.lib.api.parsers import MergePatchParser
from common.djangoapps.student.models import ( # lint-amnesty, pylint: disable=unused-import
@@ -81,6 +79,7 @@ from .api import get_account_settings, update_account_settings
from .permissions import CanDeactivateUser, CanReplaceUsername, CanRetireUser
from .serializers import UserRetirementPartnerReportSerializer, UserRetirementStatusSerializer
from .signals import USER_RETIRE_LMS_CRITICAL, USER_RETIRE_LMS_MISC, USER_RETIRE_MAILINGS
from .utils import create_retirement_request_and_deactivate_account
try:
from coaching.api import has_ever_consented_to_coaching
@@ -426,23 +425,8 @@ class DeactivateLogoutView(APIView):
if verify_user_password_response.status_code != status.HTTP_204_NO_CONTENT:
return verify_user_password_response
with transaction.atomic():
# Add user to retirement queue.
UserRetirementStatus.create_retirement(request.user)
# Unlink LMS social auth accounts
UserSocialAuth.objects.filter(user_id=request.user.id).delete()
# Change LMS password & email
user_email = request.user.email
request.user.email = get_retired_email_by_email(request.user.email)
request.user.save()
_set_unusable_password(request.user)
# TODO: Unlink social accounts & change password on each IDA.
# Remove the activation keys sent by email to the user for account activation.
Registration.objects.filter(user=request.user).delete()
# Delete OAuth tokens associated with the user.
retire_dot_oauth2_models(request.user)
AccountRecovery.retire_recovery_email(request.user.id)
create_retirement_request_and_deactivate_account(request.user)
try:
# Send notification email to user