예제 #1
0
    def process_request(self, request):
        """
        Reconstitute the full JWT and add a new cookie on the request object.
        """
        use_jwt_cookie_requested = request.META.get(USE_JWT_COOKIE_HEADER)
        header_payload_cookie = request.COOKIES.get(jwt_cookie_header_payload_name())
        signature_cookie = request.COOKIES.get(jwt_cookie_signature_name())

        if not use_jwt_cookie_requested:
            metric_value = 'not-requested'
        elif header_payload_cookie and signature_cookie:
            # Reconstitute JWT auth cookie if split cookies are available and jwt cookie
            # authentication was requested by the client.
            request.COOKIES[jwt_cookie_name()] = '{}{}{}'.format(
                header_payload_cookie,
                JWT_DELIMITER,
                signature_cookie,
            )
            metric_value = 'success'
        elif header_payload_cookie or signature_cookie:
            # Log unexpected case of only finding one cookie.
            if not header_payload_cookie:
                log_message, metric_value = self._get_missing_cookie_message_and_metric(
                    jwt_cookie_header_payload_name()
                )
            if not signature_cookie:
                log_message, metric_value = self._get_missing_cookie_message_and_metric(
                    jwt_cookie_signature_name()
                )
            log.warning(log_message)
        else:
            metric_value = 'missing-both'

        monitoring.set_custom_metric('request_jwt_cookie', metric_value)
예제 #2
0
파일: middleware.py 프로젝트: lxp20201/lxp
    def process_request(self, request):
        """
        Reconstitute the full JWT and add a new cookie on the request object.
        """
        use_jwt_cookie_requested = request.META.get(USE_JWT_COOKIE_HEADER)
        header_payload_cookie = request.COOKIES.get(jwt_cookie_header_payload_name())
        signature_cookie = request.COOKIES.get(jwt_cookie_signature_name())

        if not use_jwt_cookie_requested:
            metric_value = 'not-requested'
        elif header_payload_cookie and signature_cookie:
            # Reconstitute JWT auth cookie if split cookies are available and jwt cookie
            # authentication was requested by the client.
            request.COOKIES[jwt_cookie_name()] = '{}{}{}'.format(
                header_payload_cookie,
                JWT_DELIMITER,
                signature_cookie,
            )
            metric_value = 'success'
        elif header_payload_cookie or signature_cookie:
            # Log unexpected case of only finding one cookie.
            if not header_payload_cookie:
                log_message, metric_value = self._get_missing_cookie_message_and_metric(
                    jwt_cookie_header_payload_name()
                )
            if not signature_cookie:
                log_message, metric_value = self._get_missing_cookie_message_and_metric(
                    jwt_cookie_signature_name()
                )
            log.warning(log_message)
        else:
            metric_value = 'missing-both'

        monitoring.set_custom_metric('request_jwt_cookie', metric_value)
예제 #3
0
class TestJwtAuthCookieMiddleware(TestCase):
    def setUp(self):
        super(TestJwtAuthCookieMiddleware, self).setUp()
        self.request = RequestFactory().get('/')
        self.middleware = JwtAuthCookieMiddleware()

    @patch('edx_django_utils.monitoring.set_custom_metric')
    def test_do_not_use_jwt_cookies(self, mock_set_custom_metric):
        self.middleware.process_request(self.request)
        self.assertIsNone(self.request.COOKIES.get(jwt_cookie_name()))
        mock_set_custom_metric.assert_called_once_with('request_jwt_cookie',
                                                       'not-requested')

    @ddt.data(
        (jwt_cookie_header_payload_name(), jwt_cookie_signature_name()),
        (jwt_cookie_signature_name(), jwt_cookie_header_payload_name()),
    )
    @ddt.unpack
    @patch('edx_rest_framework_extensions.auth.jwt.middleware.log')
    @patch('edx_django_utils.monitoring.set_custom_metric')
    def test_missing_cookies(self, set_cookie_name, missing_cookie_name,
                             mock_set_custom_metric, mock_log):
        self.request.META[USE_JWT_COOKIE_HEADER] = 'true'
        self.request.COOKIES[set_cookie_name] = 'test'
        self.middleware.process_request(self.request)
        self.assertIsNone(self.request.COOKIES.get(jwt_cookie_name()))
        mock_log.warning.assert_called_once_with(
            '%s cookie is missing. JWT auth cookies will not be reconstituted.'
            % missing_cookie_name)
        mock_set_custom_metric.assert_called_once_with(
            'request_jwt_cookie', 'missing-{}'.format(missing_cookie_name))

    @patch('edx_django_utils.monitoring.set_custom_metric')
    def test_no_cookies(self, mock_set_custom_metric):
        self.request.META[USE_JWT_COOKIE_HEADER] = 'true'
        self.middleware.process_request(self.request)
        self.assertIsNone(self.request.COOKIES.get(jwt_cookie_name()))
        mock_set_custom_metric.assert_called_once_with('request_jwt_cookie',
                                                       'missing-both')

    @patch('edx_django_utils.monitoring.set_custom_metric')
    def test_success(self, mock_set_custom_metric):
        self.request.META[USE_JWT_COOKIE_HEADER] = 'true'
        self.request.COOKIES[
            jwt_cookie_header_payload_name()] = 'header.payload'
        self.request.COOKIES[jwt_cookie_signature_name()] = 'signature'
        self.middleware.process_request(self.request)
        self.assertEqual(self.request.COOKIES[jwt_cookie_name()],
                         'header.payload.signature')
        mock_set_custom_metric.assert_called_once_with('request_jwt_cookie',
                                                       'success')
    def process_view(self, request, view_func, view_args, view_kwargs):  # pylint: disable=unused-argument
        """
        Reconstitute the full JWT and add a new cookie on the request object.
        """
        assert hasattr(
            request, 'session'
        ), "The Django authentication middleware requires session middleware to be installed. Edit your MIDDLEWARE setting to insert 'django.contrib.sessions.middleware.SessionMiddleware'."  # noqa E501 line too long

        use_jwt_cookie_requested = request.META.get(USE_JWT_COOKIE_HEADER)
        header_payload_cookie = request.COOKIES.get(
            jwt_cookie_header_payload_name())
        signature_cookie = request.COOKIES.get(jwt_cookie_signature_name())

        is_set_request_user_for_jwt_cookie_enabled = get_setting(
            ENABLE_SET_REQUEST_USER_FOR_JWT_COOKIE)
        if use_jwt_cookie_requested and is_set_request_user_for_jwt_cookie_enabled:
            # DRF does not set request.user until process_response. This makes it available in process_view.
            # For more info, see https://github.com/jpadilla/django-rest-framework-jwt/issues/45#issuecomment-74996698
            request.user = SimpleLazyObject(
                lambda: _get_user_from_jwt(request, view_func))

        if not use_jwt_cookie_requested:
            metric_value = 'not-requested'
        elif header_payload_cookie and signature_cookie:
            # Reconstitute JWT auth cookie if split cookies are available and jwt cookie
            # authentication was requested by the client.
            request.COOKIES[jwt_cookie_name()] = '{}{}{}'.format(
                header_payload_cookie,
                JWT_DELIMITER,
                signature_cookie,
            )
            metric_value = 'success'
        elif header_payload_cookie or signature_cookie:
            # Log unexpected case of only finding one cookie.
            if not header_payload_cookie:
                log_message, metric_value = self._get_missing_cookie_message_and_metric(
                    jwt_cookie_header_payload_name())
            if not signature_cookie:
                log_message, metric_value = self._get_missing_cookie_message_and_metric(
                    jwt_cookie_signature_name())
            log.warning(log_message)
        else:
            metric_value = 'missing-both'
            log.warning(
                'Both JWT auth cookies missing. JWT auth cookies will not be reconstituted.'
            )

        monitoring.set_custom_metric('request_jwt_cookie', metric_value)
예제 #5
0
 def test_success(self, mock_set_custom_metric):
     self.request.META[USE_JWT_COOKIE_HEADER] = 'true'
     self.request.COOKIES[jwt_cookie_header_payload_name()] = 'header.payload'
     self.request.COOKIES[jwt_cookie_signature_name()] = 'signature'
     self.middleware.process_request(self.request)
     self.assertEqual(self.request.COOKIES[jwt_cookie_name()], 'header.payload.signature')
     mock_set_custom_metric.assert_called_once_with('request_jwt_cookie', 'success')
예제 #6
0
 def test_success(self, mock_set_custom_attribute):
     self.request.META[USE_JWT_COOKIE_HEADER] = 'true'
     self.request.COOKIES[jwt_cookie_header_payload_name()] = 'header.payload'
     self.request.COOKIES[jwt_cookie_signature_name()] = 'signature'
     self.middleware.process_view(self.request, None, None, None)
     self.assertEqual(self.request.COOKIES[jwt_cookie_name()], 'header.payload.signature')
     mock_set_custom_attribute.assert_called_once_with('request_jwt_cookie', 'success')
예제 #7
0
def _set_jwt_cookies(response, cookie_settings, jwt_header_and_payload,
                     jwt_signature):
    """
    Sets the given jwt_header_and_payload, jwt_signature, and refresh token in 3 different cookies.
    The latter 2 cookies are set as httponly.
    """
    cookie_settings['httponly'] = None
    response.set_cookie(jwt_cookies.jwt_cookie_header_payload_name(),
                        jwt_header_and_payload, **cookie_settings)

    cookie_settings['httponly'] = True
    response.set_cookie(jwt_cookies.jwt_cookie_signature_name(), jwt_signature,
                        **cookie_settings)
예제 #8
0
def _set_jwt_cookies(response, cookie_settings, jwt_header_and_payload, jwt_signature):
    """
    Sets the given jwt_header_and_payload, jwt_signature, and refresh token in 3 different cookies.
    The latter 2 cookies are set as httponly.
    """
    cookie_settings['httponly'] = None
    response.set_cookie(
        jwt_cookies.jwt_cookie_header_payload_name(),
        jwt_header_and_payload,
        **cookie_settings
    )

    cookie_settings['httponly'] = True
    response.set_cookie(
        jwt_cookies.jwt_cookie_signature_name(),
        jwt_signature,
        **cookie_settings
    )
예제 #9
0
from oauth2_provider.models import Application
from openedx.core.djangoapps.oauth_dispatch.adapters import DOTAdapter
from openedx.core.djangoapps.oauth_dispatch.api import create_dot_access_token
from openedx.core.djangoapps.oauth_dispatch.jwt import create_jwt_from_token
from openedx.core.djangoapps.user_api.accounts.utils import retrieve_last_sitewide_block_completed
from openedx.core.djangoapps.user_authn.exceptions import AuthFailedError
from student.models import CourseEnrollment

log = logging.getLogger(__name__)

CREATE_LOGON_COOKIE = Signal(providing_args=['user', 'response'])

JWT_COOKIE_NAMES = (
    # Header and payload sections of a JSON Web Token containing user
    # information and used as an access token.
    jwt_cookies.jwt_cookie_header_payload_name(),

    # Signature section of a JSON Web Token.
    jwt_cookies.jwt_cookie_signature_name(),
)

# TODO (ARCH-245): Remove the following deprecated cookies.
DEPRECATED_LOGGED_IN_COOKIE_NAMES = (
    # Set to 'true' if the user is logged in.
    settings.EDXMKTG_LOGGED_IN_COOKIE_NAME,

    # JSON-encoded dictionary with user information.
    settings.EDXMKTG_USER_INFO_COOKIE_NAME,
)

ALL_LOGGED_IN_COOKIE_NAMES = JWT_COOKIE_NAMES + DEPRECATED_LOGGED_IN_COOKIE_NAMES
예제 #10
0
from openedx.core.djangoapps.oauth_dispatch.jwt import create_jwt_from_token
from openedx.core.djangoapps.user_api.accounts.utils import retrieve_last_sitewide_block_completed
from openedx.core.djangoapps.user_authn.exceptions import AuthFailedError
from student.models import CourseEnrollment


log = logging.getLogger(__name__)


CREATE_LOGON_COOKIE = Signal(providing_args=['user', 'response'])


JWT_COOKIE_NAMES = (
    # Header and payload sections of a JSON Web Token containing user
    # information and used as an access token.
    jwt_cookies.jwt_cookie_header_payload_name(),

    # Signature section of a JSON Web Token.
    jwt_cookies.jwt_cookie_signature_name(),

    # Refresh token, which can be used to get a new JSON Web Token.
    jwt_cookies.jwt_refresh_cookie_name(),
)

# TODO (ARCH-245): Remove the following deprecated cookies.
DEPRECATED_LOGGED_IN_COOKIE_NAMES = (
    # Set to 'true' if the user is logged in.
    settings.EDXMKTG_LOGGED_IN_COOKIE_NAME,

    # JSON-encoded dictionary with user information.
    settings.EDXMKTG_USER_INFO_COOKIE_NAME,
def _get_test_cookie(is_cookie_valid=True):
    header_payload_value = 'header.payload' if is_cookie_valid else 'header.payload.invalid'
    return SimpleCookie({
        jwt_cookie_header_payload_name(): header_payload_value,
        jwt_cookie_signature_name(): 'signature',
    })
class TestJwtAuthCookieMiddleware(TestCase):
    def setUp(self):
        super(TestJwtAuthCookieMiddleware, self).setUp()
        self.request = RequestFactory().get('/')
        self.request.session = 'mock session'
        self.middleware = JwtAuthCookieMiddleware()

    @patch('edx_django_utils.monitoring.set_custom_metric')
    def test_do_not_use_jwt_cookies(self, mock_set_custom_metric):
        self.middleware.process_view(self.request, None, None, None)
        self.assertIsNone(self.request.COOKIES.get(jwt_cookie_name()))
        mock_set_custom_metric.assert_called_once_with('request_jwt_cookie',
                                                       'not-requested')

    @ddt.data(
        (jwt_cookie_header_payload_name(), jwt_cookie_signature_name()),
        (jwt_cookie_signature_name(), jwt_cookie_header_payload_name()),
    )
    @ddt.unpack
    @patch('edx_rest_framework_extensions.auth.jwt.middleware.log')
    @patch('edx_django_utils.monitoring.set_custom_metric')
    def test_missing_cookies(self, set_cookie_name, missing_cookie_name,
                             mock_set_custom_metric, mock_log):
        self.request.META[USE_JWT_COOKIE_HEADER] = 'true'
        self.request.COOKIES[set_cookie_name] = 'test'
        self.middleware.process_view(self.request, None, None, None)
        self.assertIsNone(self.request.COOKIES.get(jwt_cookie_name()))
        mock_log.warning.assert_called_once_with(
            '%s cookie is missing. JWT auth cookies will not be reconstituted.'
            % missing_cookie_name)
        mock_set_custom_metric.assert_called_once_with(
            'request_jwt_cookie', 'missing-{}'.format(missing_cookie_name))

    @patch('edx_django_utils.monitoring.set_custom_metric')
    def test_no_cookies(self, mock_set_custom_metric):
        self.request.META[USE_JWT_COOKIE_HEADER] = 'true'
        self.middleware.process_view(self.request, None, None, None)
        self.assertIsNone(self.request.COOKIES.get(jwt_cookie_name()))
        mock_set_custom_metric.assert_called_once_with('request_jwt_cookie',
                                                       'missing-both')

    @patch('edx_django_utils.monitoring.set_custom_metric')
    def test_success(self, mock_set_custom_metric):
        self.request.META[USE_JWT_COOKIE_HEADER] = 'true'
        self.request.COOKIES[
            jwt_cookie_header_payload_name()] = 'header.payload'
        self.request.COOKIES[jwt_cookie_signature_name()] = 'signature'
        self.middleware.process_view(self.request, None, None, None)
        self.assertEqual(self.request.COOKIES[jwt_cookie_name()],
                         'header.payload.signature')
        mock_set_custom_metric.assert_called_once_with('request_jwt_cookie',
                                                       'success')

    _LOG_WARN_AUTHENTICATION_FAILED = 0
    _LOG_WARN_MISSING_JWT_AUTHENTICATION_CLASS = 1

    @patch('edx_rest_framework_extensions.auth.jwt.middleware.log')
    @ddt.data(
        ('/nopermissionsrequired/', True, True, True, None),
        ('/nopermissionsrequired/', True, False, False, None),
        ('/nopermissionsrequired/', False, False, True,
         _LOG_WARN_AUTHENTICATION_FAILED),
        ('/nopermissionsrequired/', False, False, False, None),
        ('/unauthenticated/', True, False, True,
         _LOG_WARN_MISSING_JWT_AUTHENTICATION_CLASS),
        ('/unauthenticated/', True, False, False, None),
    )
    @ddt.unpack
    def test_set_request_user_with_use_jwt_cookie(
        self,
        url,
        is_cookie_valid,
        is_request_user_set,
        is_toggle_enabled,
        log_warning,
        mock_log,
    ):
        header = {USE_JWT_COOKIE_HEADER: 'true'}
        self.client.cookies = _get_test_cookie(is_cookie_valid=is_cookie_valid)
        check_user_middleware_assertion_class = (
            'CheckRequestUserForJwtAuthMiddleware' if is_request_user_set else
            'CheckRequestUserAnonymousForJwtAuthMiddleware')
        with override_settings(
                ROOT_URLCONF=
                'edx_rest_framework_extensions.auth.jwt.tests.test_middleware',
                MIDDLEWARE=
            (
                'django.contrib.sessions.middleware.SessionMiddleware',
                'edx_rest_framework_extensions.auth.jwt.middleware.JwtAuthCookieMiddleware',
                'django.contrib.auth.middleware.AuthenticationMiddleware',
                'edx_rest_framework_extensions.auth.jwt.tests.test_middleware.{}'
                .format(check_user_middleware_assertion_class),
            ),
                EDX_DRF_EXTENSIONS={
                    ENABLE_SET_REQUEST_USER_FOR_JWT_COOKIE: is_toggle_enabled,
                }):
            response = self.client.get(url, **header)
            self.assertEqual(200, response.status_code)

            if log_warning == self._LOG_WARN_AUTHENTICATION_FAILED:
                mock_log.warning.assert_called_once_with(
                    'Jwt Authentication failed and request.user could not be set.'
                )
            elif log_warning == self._LOG_WARN_MISSING_JWT_AUTHENTICATION_CLASS:
                mock_log.warning.assert_called_once_with(
                    'Jwt Authentication expected, but view %s is not using a JwtAuthentication class.',
                    ANY)
            else:
                mock_log.warn.assert_not_called()