def test_get_potentially_retired_user_username_match(retirement_user):
    """
    Check that we can pass in an un-retired username and get the
    user-to-be-retired back.
    """
    hashed_username = get_retired_username_by_username(retirement_user.username)
    assert get_potentially_retired_user_by_username_and_hash(retirement_user.username, hashed_username) == retirement_user
Exemple #2
0
    def test_can_retire_user_from_credit_request(self):
        test_parameters = {'hi': 'there'}
        CreditRequest.objects.create(
            username=self.user.username,
            course=self.credit_course,
            provider=self.provider,
            parameters=test_parameters,
        )

        credit_request_before_retire = CreditRequest.objects.filter(
            username=self.user.username
        )[0]

        self.assertEqual(credit_request_before_retire.parameters, test_parameters)

        user_was_retired = CreditRequest.retire_user(
            original_username=self.user.username,
            retired_username=get_retired_username_by_username(self.user.username)
        )
        credit_request_before_retire.refresh_from_db()
        credit_requests_after_retire = CreditRequest.objects.filter(
            username=self.user.username
        )

        self.assertTrue(user_was_retired)
        self.assertEqual(credit_request_before_retire.parameters, {})
        self.assertFalse(credit_requests_after_retire.exists())
def test_get_retired_username(retirement_user):
    """
    Basic testing of getting retired usernames. The hasher is opaque
    to us, we just care that it's succeeding and using our format.
    """
    hashed_username = get_retired_username_by_username(retirement_user.username)
    check_username_against_fmt(hashed_username)
def test_get_retired_username(retirement_user):
    """
    Basic testing of getting retired usernames. The hasher is opaque
    to us, we just care that it's succeeding and using our format.
    """
    hashed_username = get_retired_username_by_username(retirement_user.username)
    check_username_against_fmt(hashed_username)
def test_get_potentially_retired_user_username_match(retirement_user):
    """
    Check that we can pass in an un-retired username and get the
    user-to-be-retired back.
    """
    hashed_username = get_retired_username_by_username(retirement_user.username)
    assert get_potentially_retired_user_by_username_and_hash(retirement_user.username, hashed_username) == retirement_user
Exemple #6
0
    def create_retirement(cls, user):
        """
        Creates a UserRetirementStatus for the given user, in the correct initial state. Will
        fail if the user already has a UserRetirementStatus row or if states are not yet populated.
        """
        try:
            pending = RetirementState.objects.all().order_by('state_execution_order')[0]
        except IndexError:
            raise RetirementStateError('Default state does not exist! Populate retirement states to retire users.')

        if cls.objects.filter(user=user).exists():
            raise RetirementStateError('User {} already has a retirement status row!'.format(user))

        retired_username = get_retired_username_by_username(user.username)
        retired_email = get_retired_email_by_email(user.email)

        UserRetirementRequest.create_retirement_request(user)

        return cls.objects.create(
            user=user,
            original_username=user.username,
            original_email=user.email,
            original_name=user.profile.name,
            retired_username=retired_username,
            retired_email=retired_email,
            current_state=pending,
            last_state=pending,
            responses='Created in state {} by create_retirement'.format(pending)
        )
Exemple #7
0
    def test_can_retire_user_from_credit_request(self):
        test_parameters = {'hi': 'there'}
        CreditRequest.objects.create(
            username=self.user.username,
            course=self.credit_course,
            provider=self.provider,
            parameters=test_parameters,
        )

        credit_request_before_retire = CreditRequest.objects.filter(
            username=self.user.username)[0]

        self.assertEqual(credit_request_before_retire.parameters,
                         test_parameters)

        user_was_retired = CreditRequest.retire_user(
            original_username=self.user.username,
            retired_username=get_retired_username_by_username(
                self.user.username))
        credit_request_before_retire.refresh_from_db()
        credit_requests_after_retire = CreditRequest.objects.filter(
            username=self.user.username)

        self.assertTrue(user_was_retired)
        self.assertEqual(credit_request_before_retire.parameters, {})
        self.assertFalse(credit_requests_after_retire.exists())
Exemple #8
0
 def setUp(self):
     super(CreditRequirementStatusTests, self).setUp()
     self.course_key = CourseKey.from_string("edX/DemoX/Demo_Course")
     self.old_username = "******"
     self.retired_username = get_retired_username_by_username(
         self.old_username)
     self.credit_course = add_credit_course(self.course_key)
Exemple #9
0
    def post(self, request):
        """
        POST /api/user/v1/accounts/retire/

        {
            'username': '******'
        }

        Retires the user with the given username.  This includes
        retiring this username, the associates email address, and
        any other PII associated with this user.
        """
        username = request.data['username']
        if is_username_retired(username):
            return Response(status=status.HTTP_404_NOT_FOUND)

        try:
            retirement_status = UserRetirementStatus.get_retirement_for_retirement_action(username)
            user = retirement_status.user
            retired_username = retirement_status.retired_username or get_retired_username_by_username(username)
            retired_email = retirement_status.retired_email or get_retired_email_by_email(user.email)
            original_email = retirement_status.original_email

            # Retire core user/profile information
            self.clear_pii_from_userprofile(user)
            self.delete_users_profile_images(user)
            self.delete_users_country_cache(user)

            # Retire data from Enterprise models
            self.retire_users_data_sharing_consent(username, retired_username)
            self.retire_sapsf_data_transmission(user)
            self.retire_user_from_pending_enterprise_customer_user(user, retired_email)
            self.retire_entitlement_support_detail(user)

            # Retire misc. models that may contain PII of this user
            SoftwareSecurePhotoVerification.retire_user(user.id)
            PendingEmailChange.delete_by_user_value(user, field='user')
            UserOrgTag.delete_by_user_value(user, field='user')

            # Retire any objects linked to the user via their original email
            CourseEnrollmentAllowed.delete_by_user_value(original_email, field='email')
            UnregisteredLearnerCohortAssignments.delete_by_user_value(original_email, field='email')

            # TODO: Password Reset links - https://openedx.atlassian.net/browse/PLAT-2104
            # TODO: Delete OAuth2 records - https://openedx.atlassian.net/browse/EDUCATOR-2703

            user.first_name = ''
            user.last_name = ''
            user.is_active = False
            user.username = retired_username
            user.save()
        except UserRetirementStatus.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)
        except RetirementStateError as exc:
            return Response(text_type(exc), status=status.HTTP_400_BAD_REQUEST)
        except Exception as exc:  # pylint: disable=broad-except
            return Response(text_type(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        return Response(status=status.HTTP_204_NO_CONTENT)
Exemple #10
0
    def post(self, request):
        """
        POST /api/user/v1/accounts/retire/

        {
            'username': '******'
        }

        Retires the user with the given username.  This includes
        retiring this username, the associates email address, and
        any other PII associated with this user.
        """
        username = request.data['username']
        if is_username_retired(username):
            return Response(status=status.HTTP_404_NOT_FOUND)

        try:
            retirement_status = UserRetirementStatus.get_retirement_for_retirement_action(username)
            user = retirement_status.user
            retired_username = retirement_status.retired_username or get_retired_username_by_username(username)
            retired_email = retirement_status.retired_email or get_retired_email_by_email(user.email)
            original_email = retirement_status.original_email

            # Retire core user/profile information
            self.clear_pii_from_userprofile(user)
            self.delete_users_profile_images(user)
            self.delete_users_country_cache(user)

            # Retire data from Enterprise models
            self.retire_users_data_sharing_consent(username, retired_username)
            self.retire_sapsf_data_transmission(user)
            self.retire_user_from_pending_enterprise_customer_user(user, retired_email)
            self.retire_entitlement_support_detail(user)

            # Retire misc. models that may contain PII of this user
            SoftwareSecurePhotoVerification.retire_user(user.id)
            PendingEmailChange.delete_by_user_value(user, field='user')
            UserOrgTag.delete_by_user_value(user, field='user')

            # Retire any objects linked to the user via their original email
            CourseEnrollmentAllowed.delete_by_user_value(original_email, field='email')
            UnregisteredLearnerCohortAssignments.delete_by_user_value(original_email, field='email')

            # TODO: Password Reset links - https://openedx.atlassian.net/browse/PLAT-2104
            # TODO: Delete OAuth2 records - https://openedx.atlassian.net/browse/EDUCATOR-2703

            user.first_name = ''
            user.last_name = ''
            user.is_active = False
            user.username = retired_username
            user.save()
        except UserRetirementStatus.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)
        except RetirementStateError as exc:
            return Response(text_type(exc), status=status.HTTP_400_BAD_REQUEST)
        except Exception as exc:  # pylint: disable=broad-except
            return Response(text_type(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        return Response(status=status.HTTP_204_NO_CONTENT)
def test_get_potentially_retired_user_username_match():
    """
    Check that we can pass in an un-retired username and get the
    user-to-be-retired back.
    """
    user = UserFactory()
    hashed_username = get_retired_username_by_username(user.username)
    assert get_potentially_retired_user_by_username_and_hash(user.username, hashed_username) == user
def test_get_retired_username_status_exists(retirement_user, retirement_status):  # pylint: disable=redefined-outer-name
    """
    Checks that a retired username is gotten from a UserRetirementStatus
    object when one already exists for a user.
    """
    hashed_username = get_retired_username_by_username(retirement_user.username)
    check_username_against_fmt(hashed_username)
    assert retirement_status.retired_username == hashed_username
def test_get_retired_username_status_exists(retirement_user, retirement_status):  # pylint: disable=redefined-outer-name
    """
    Checks that a retired username is gotten from a UserRetirementStatus
    object when one already exists for a user.
    """
    hashed_username = get_retired_username_by_username(retirement_user.username)
    check_username_against_fmt(hashed_username)
    assert retirement_status.retired_username == hashed_username
Exemple #14
0
    def post(self, request):
        """
        POST /api/user/v1/accounts/retire/

        {
            'username': '******'
        }

        Retires the user with the given username.  This includes
        retiring this username, the associated email address, and
        any other PII associated with this user.
        """
        username = request.data['username']

        try:
            retirement_status = UserRetirementStatus.get_retirement_for_retirement_action(username)
            user = retirement_status.user
            retired_username = retirement_status.retired_username or get_retired_username_by_username(username)
            retired_email = retirement_status.retired_email or get_retired_email_by_email(user.email)
            original_email = retirement_status.original_email

            # Retire core user/profile information
            self.clear_pii_from_userprofile(user)
            self.delete_users_profile_images(user)
            self.delete_users_country_cache(user)

            # Retire data from Enterprise models
            self.retire_users_data_sharing_consent(username, retired_username)
            self.retire_sapsf_data_transmission(user)
            self.retire_degreed_data_transmission(user)
            self.retire_user_from_pending_enterprise_customer_user(user, retired_email)
            self.retire_entitlement_support_detail(user)

            # Retire misc. models that may contain PII of this user
            PendingEmailChange.delete_by_user_value(user, field='user')
            UserOrgTag.delete_by_user_value(user, field='user')

            # Retire any objects linked to the user via their original email
            CourseEnrollmentAllowed.delete_by_user_value(original_email, field='email')
            UnregisteredLearnerCohortAssignments.delete_by_user_value(original_email, field='email')

            # This signal allows code in higher points of LMS to retire the user as necessary
            USER_RETIRE_LMS_CRITICAL.send(sender=self.__class__, user=user)

            user.first_name = ''
            user.last_name = ''
            user.is_active = False
            user.username = retired_username
            user.save()
        except UserRetirementStatus.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)
        except RetirementStateError as exc:
            return Response(text_type(exc), status=status.HTTP_400_BAD_REQUEST)
        except Exception as exc:  # pylint: disable=broad-except
            return Response(text_type(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        return Response(status=status.HTTP_204_NO_CONTENT)
def test_get_potentially_retired_user_does_not_exist():
    """
    Check that the call to get a user with a non-existent
    username and hashed username bubbles up User.DoesNotExist
    """
    fake_username = "******"
    hashed_username = get_retired_username_by_username(fake_username)

    with pytest.raises(User.DoesNotExist):
        get_potentially_retired_user_by_username_and_hash(fake_username, hashed_username)
def test_get_potentially_retired_user_does_not_exist():
    """
    Check that the call to get a user with a non-existent
    username and hashed username bubbles up User.DoesNotExist
    """
    fake_username = "******"
    hashed_username = get_retired_username_by_username(fake_username)

    with pytest.raises(User.DoesNotExist):
        get_potentially_retired_user_by_username_and_hash(fake_username, hashed_username)
def test_is_username_retired_is_retired(retirement_user):
    """
    Check functionality of is_username_retired when username is retired
    """
    original_username = retirement_user.username
    retired_username = get_retired_username_by_username(retirement_user.username)

    # Fake username retirement.
    retirement_user.username = retired_username
    retirement_user.save()

    assert is_username_retired(original_username)
def test_is_username_retired_is_retired(retirement_user):
    """
    Check functionality of is_username_retired when username is retired
    """
    original_username = retirement_user.username
    retired_username = get_retired_username_by_username(retirement_user.username)

    # Fake username retirement.
    retirement_user.username = retired_username
    retirement_user.save()

    assert is_username_retired(original_username)
Exemple #19
0
def _fake_logged_out_user(user, retire_username=False):
    """
    Simulate the initial logout retirement endpoint.
    """
    # By default, do not change the username to the retired hash version because
    # that is not what happens upon actual retirement requests immediately after
    # logout.
    if retire_username:
        user.username = get_retired_username_by_username(user.username)
    user.email = get_retired_email_by_email(user.email)
    user.set_unusable_password()
    user.save()
def _fake_logged_out_user(user, retire_username=False):
    """
    Simulate the initial logout retirement endpoint.
    """
    # By default, do not change the username to the retired hash version because
    # that is not what happens upon actual retirement requests immediately after
    # logout.
    if retire_username:
        user.username = get_retired_username_by_username(user.username)
    user.email = get_retired_email_by_email(user.email)
    user.set_unusable_password()
    user.save()
Exemple #21
0
def test_is_username_retired_is_retired():
    """
    Check functionality of is_username_retired when username is retired
    """
    user = UserFactory()
    original_username = user.username
    retired_username = get_retired_username_by_username(user.username)

    # Fake username retirement.
    user.username = retired_username
    user.save()

    assert is_username_retired(original_username)
def test_is_username_retired_is_retired():
    """
    Check functionality of is_username_retired when username is retired
    """
    user = UserFactory()
    original_username = user.username
    retired_username = get_retired_username_by_username(user.username)

    # Fake username retirement.
    user.username = retired_username
    user.save()

    assert is_username_retired(original_username)
Exemple #23
0
    def post(self, request):
        """
        POST /api/user/v1/accounts/retire/

        {
            'username': '******'
        }

        Retires the user with the given username.  This includes
        retiring this username, the associates email address, and
        any other PII associated with this user.
        """
        username = request.data['username']
        if is_username_retired(username):
            return Response(status=status.HTTP_404_NOT_FOUND)

        try:
            retirement_status = UserRetirementStatus.get_retirement_for_retirement_action(
                username)
            user = retirement_status.user
            retired_username = retirement_status.retired_username or get_retired_username_by_username(
                username)
            retired_email = retirement_status.retired_email or get_retired_email_by_email(
                user.email)

            self.clear_pii_from_userprofile(user)
            self.delete_users_profile_images(user)
            self.delete_users_country_cache(user)
            self.retire_users_data_sharing_consent(username, retired_username)
            self.retire_sapsf_data_transmission(user)
            self.retire_user_from_pending_enterprise_customer_user(
                user, retired_email)
            self.retire_entitlement_support_detail(user)
            # TODO: Password Reset links - https://openedx.atlassian.net/browse/PLAT-2104
            # TODO: Delete OAuth2 records - https://openedx.atlassian.net/browse/EDUCATOR-2703
            user.first_name = ''
            user.last_name = ''
            user.is_active = False
            user.username = retired_username
            user.save()
        except UserRetirementStatus.DoesNotExist:
            return Response(status=status.HTTP_404_NOT_FOUND)
        except RetirementStateError as exc:
            return Response(text_type(exc), status=status.HTTP_400_BAD_REQUEST)
        except Exception as exc:  # pylint: disable=broad-except
            return Response(text_type(exc),
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        return Response(status=status.HTTP_204_NO_CONTENT)
def test_get_potentially_retired_user_hashed_match(retirement_user):
    """
    Check that we can pass in a hashed username and get the
    user-to-be-retired back.
    """
    orig_username = retirement_user.username
    hashed_username = get_retired_username_by_username(orig_username)

    # Fake username retirement.
    retirement_user.username = hashed_username
    retirement_user.save()

    # Check to find the user by original username should fail,
    # 2nd check by hashed username should succeed.
    assert get_potentially_retired_user_by_username_and_hash(orig_username, hashed_username) == retirement_user
    def test_retired_username(self):
        """
        Ensure that a retired username cannot be registered again.
        """
        user = UserFactory()
        orig_username = user.username

        # Fake retirement of the username.
        user.username = get_retired_username_by_username(orig_username)
        user.save()

        # Attempt to create another account with the same username that's been retired.
        self.url_params['username'] = orig_username
        response = self.client.post(self.url, self.url_params)
        self._validate_exiting_username_response(orig_username, response, self.INVALID_ERR_MSG[0], self.INVALID_ERR_MSG[1])
Exemple #26
0
    def test_retired_username(self):
        """
        Ensure that a retired username cannot be registered again.
        """
        user = UserFactory()
        orig_username = user.username

        # Fake retirement of the username.
        user.username = get_retired_username_by_username(orig_username)
        user.save()

        # Attempt to create another account with the same username that's been retired.
        self.url_params['username'] = orig_username
        response = self.client.post(self.url, self.url_params)
        self._validate_exiting_username_response(orig_username, response)
def test_get_potentially_retired_user_hashed_match(retirement_user):
    """
    Check that we can pass in a hashed username and get the
    user-to-be-retired back.
    """
    orig_username = retirement_user.username
    hashed_username = get_retired_username_by_username(orig_username)

    # Fake username retirement.
    retirement_user.username = hashed_username
    retirement_user.save()

    # Check to find the user by original username should fail,
    # 2nd check by hashed username should succeed.
    assert get_potentially_retired_user_by_username_and_hash(orig_username, hashed_username) == retirement_user
Exemple #28
0
def _assert_retirementstatus_is_user(retirement, user):
    """
    Helper function to compare a newly created UserRetirementStatus object to expected values for
    the given user.
    """
    pending = RetirementState.objects.all().order_by('state_execution_order')[0]
    retired_username = get_retired_username_by_username(user.username)
    retired_email = get_retired_email_by_email(user.email)

    assert retirement.user == user
    assert retirement.original_username == user.username
    assert retirement.original_email == user.email
    assert retirement.original_name == user.profile.name
    assert retirement.retired_username == retired_username
    assert retirement.retired_email == retired_email
    assert retirement.current_state == pending
    assert retirement.last_state == pending
    assert pending.state_name in retirement.responses
Exemple #29
0
def _assert_retirementstatus_is_user(retirement, user):
    """
    Helper function to compare a newly created UserRetirementStatus object to expected values for
    the given user.
    """
    pending = RetirementState.objects.all().order_by(
        'state_execution_order')[0]
    retired_username = get_retired_username_by_username(user.username)
    retired_email = get_retired_email_by_email(user.email)

    assert retirement.user == user
    assert retirement.original_username == user.username
    assert retirement.original_email == user.email
    assert retirement.original_name == user.profile.name
    assert retirement.retired_username == retired_username
    assert retirement.retired_email == retired_email
    assert retirement.current_state == pending
    assert retirement.last_state == pending
    assert pending.state_name in retirement.responses
Exemple #30
0
    def retire_user(cls, username_to_retire):
        """
        Retire a user by anonymizing

        Args:
            username_to_retire(str): Username of the user
        """
        requirement_statuses = cls.objects.filter(username=username_to_retire)
        retirement_username = get_retired_username_by_username(
            username_to_retire)
        if requirement_statuses.exists():
            requirement_statuses.update(username=retirement_username,
                                        reason={})
            return True
        else:
            log.info(
                u'Can not retire requirement statuses for user "%s" because the user could not be found',
                username_to_retire)
            return False
Exemple #31
0
    def retire_user(cls, username_to_retire):
        """
        Retire a user by anonymizing

        Args:
            username_to_retire(str): Username of the user
        """
        requirement_statuses = cls.objects.filter(username=username_to_retire)
        retirement_username = get_retired_username_by_username(username_to_retire)
        if requirement_statuses.exists():
            requirement_statuses.update(
                username=retirement_username,
                reason={}
            )
            return True
        else:
            log.info(
                u'Can not retire requirement statuses for user "%s" because the user could not be found',
                username_to_retire
            )
            return False
    def _create_retirement(self, state, create_datetime=None):
        """
        Helper method to create a RetirementStatus with useful defaults
        """
        if create_datetime is None:
            create_datetime = datetime.datetime.now(pytz.UTC) - datetime.timedelta(days=8)

        user = UserFactory()
        return UserRetirementStatus.objects.create(
            user=user,
            original_username=user.username,
            original_email=user.email,
            original_name=user.profile.name,
            retired_username=get_retired_username_by_username(user.username),
            retired_email=get_retired_email_by_email(user.email),
            current_state=state,
            last_state=state,
            responses="",
            created=create_datetime,
            modified=create_datetime
        )
Exemple #33
0
    def test_cannot_retire_nonexistent_user(self):
        test_parameters = {'hi': 'there'}
        CreditRequest.objects.create(
            username=self.user.username,
            course=self.credit_course,
            provider=self.provider,
            parameters=test_parameters,
        )
        another_user = UserFactory.create()

        credit_request_before_retire = CreditRequest.objects.filter(
            username=self.user.username)[0]

        was_retired = CreditRequest.retire_user(
            original_username=another_user.username,
            retired_username=get_retired_username_by_username(
                another_user.username))
        credit_request_before_retire.refresh_from_db()

        self.assertFalse(was_retired)
        self.assertEqual(credit_request_before_retire.parameters,
                         test_parameters)
Exemple #34
0
    def test_cannot_retire_nonexistent_user(self):
        test_parameters = {'hi': 'there'}
        CreditRequest.objects.create(
            username=self.user.username,
            course=self.credit_course,
            provider=self.provider,
            parameters=test_parameters,
        )
        another_user = UserFactory.create()

        credit_request_before_retire = CreditRequest.objects.filter(
            username=self.user.username
        )[0]

        was_retired = CreditRequest.retire_user(
            original_username=another_user.username,
            retired_username=get_retired_username_by_username(another_user.username)
        )
        credit_request_before_retire.refresh_from_db()

        self.assertFalse(was_retired)
        self.assertEqual(credit_request_before_retire.parameters, test_parameters)
def fake_retirement(user):
    """
    Makes an attempt to put user for the given user into a "COMPLETED"
    retirement state by faking important parts of retirement.

    Use to test idempotency for retirement API calls. Since there are many
    configurable retirement steps this is only a "best guess" and may need
    additional changes added to more accurately reflect post-retirement state.
    """
    # Deactivate / logout and hash username & email
    UserSocialAuth.objects.filter(user_id=user.id).delete()
    user.first_name = ''
    user.last_name = ''
    user.is_active = False
    user.username = get_retired_username_by_username(user.username)
    user.email = get_retired_email_by_email(user.email)
    user.set_unusable_password()
    user.save()

    # Clear profile
    AccountRetirementView.clear_pii_from_userprofile(user)

    # Unenroll from all courses
    api.unenroll_user_from_all_courses(user.username)
Exemple #36
0
 def setUp(self):
     super(CreditRequirementStatusTests, self).setUp()
     self.course_key = CourseKey.from_string("edX/DemoX/Demo_Course")
     self.old_username = "******"
     self.retired_username = get_retired_username_by_username(self.old_username)
     self.credit_course = add_credit_course(self.course_key)
Exemple #37
0
def _fake_logged_out_user(user):
    # Simulate the initial logout retirement endpoint.
    user.username = get_retired_username_by_username(user.username)
    user.email = get_retired_email_by_email(user.email)
    user.set_unusable_password()
    user.save()
Exemple #38
0
 def build_post(self, user):
     retired_username = get_retired_username_by_username(user.username)
     return {'retired_username': retired_username}