diff --git a/lms/djangoapps/commerce/signals.py b/lms/djangoapps/commerce/signals.py index 0d54ab4e7d..b135325dca 100644 --- a/lms/djangoapps/commerce/signals.py +++ b/lms/djangoapps/commerce/signals.py @@ -5,6 +5,7 @@ import logging from urlparse import urljoin from django.conf import settings +from django.contrib.auth.models import AnonymousUser from django.core.mail import EmailMultiAlternatives from django.dispatch import receiver from django.utils.translation import ugettext as _ @@ -32,6 +33,13 @@ def handle_unenroll_done(sender, course_enrollment=None, skip_refund=False, **kw if course_enrollment and course_enrollment.refundable(): try: request_user = get_request_user() or course_enrollment.user + if isinstance(request_user, AnonymousUser): + # Assume the request was initiated via server-to-server + # api call (presumably Otto). In this case we cannot + # construct a client to call Otto back anyway, because + # the client does not work anonymously, and furthermore, + # there's certainly no need to inform Otto about this request. + return refund_seat(course_enrollment, request_user) except: # pylint: disable=bare-except # don't assume the signal was fired with `send_robust`. diff --git a/lms/djangoapps/commerce/tests/test_signals.py b/lms/djangoapps/commerce/tests/test_signals.py index 85eb7cb591..014d572a57 100644 --- a/lms/djangoapps/commerce/tests/test_signals.py +++ b/lms/djangoapps/commerce/tests/test_signals.py @@ -1,6 +1,7 @@ """ Tests for signal handling in commerce djangoapp. """ +from django.contrib.auth.models import AnonymousUser from django.test import TestCase from django.test.utils import override_settings @@ -109,6 +110,12 @@ class TestRefundSignal(TestCase): self.assertTrue(mock_refund_seat.called) self.assertEqual(mock_refund_seat.call_args[0], (self.course_enrollment, self.requester)) + # HTTP user is another server (AnonymousUser): do not try to initiate a refund at all. + mock_get_request_user.return_value = AnonymousUser() + mock_refund_seat.reset_mock() + self.send_signal() + self.assertFalse(mock_refund_seat.called) + @mock.patch('commerce.signals.log.warning') def test_not_authorized_warning(self, mock_log_warning): """