Commit 18282626 by zubair-arbi

enable jwt auth with feature flag

parent 8f7c158f
......@@ -2147,6 +2147,14 @@ if FEATURES.get('CLASS_DASHBOARD'):
ENABLE_CREDIT_ELIGIBILITY = True
FEATURES['ENABLE_CREDIT_ELIGIBILITY'] = ENABLE_CREDIT_ELIGIBILITY
################ Enable JWT auth ####################
# When this feature flag is set to False, API endpoints using
# JSONWebTokenAuthentication will reject requests using JWT to authenticate,
# even if those tokens are valid. Set this to True only if you need those
# endpoints, and have configured settings 'JWT_AUTH' to override its default
# values with secure values.
FEATURES['ENABLE_JWT_AUTH'] = False
######################## CAS authentication ###########################
if FEATURES.get('AUTH_USE_CAS'):
......
......@@ -7,7 +7,9 @@ doesn't expose settings to enforce this.
"""
import logging
from django.conf import settings
import jwt
from rest_framework import exceptions
from rest_framework_jwt.settings import api_settings
......@@ -19,6 +21,10 @@ def decode(token):
Ensure InvalidTokenErrors are logged for diagnostic purposes, before
failing authentication.
"""
if not settings.FEATURES.get('ENABLE_JWT_AUTH', False):
msg = 'JWT auth not supported.'
log.error(msg)
raise exceptions.AuthenticationFailed(msg)
options = {
'verify_exp': api_settings.JWT_VERIFY_EXPIRATION,
......
"""
Mixins for JWT auth tests.
"""
from time import time
from django.conf import settings
import jwt
JWT_AUTH = 'JWT_AUTH'
class JwtMixin(object):
""" Mixin with JWT-related helper functions. """
JWT_SECRET_KEY = getattr(settings, JWT_AUTH)['JWT_SECRET_KEY'] if hasattr(settings, JWT_AUTH) else ''
JWT_ISSUER = getattr(settings, JWT_AUTH)['JWT_ISSUER'] if hasattr(settings, JWT_AUTH) else ''
JWT_AUDIENCE = getattr(settings, JWT_AUTH)['JWT_AUDIENCE'] if hasattr(settings, JWT_AUTH) else ''
def generate_token(self, payload, secret=None):
""" Generate a JWT token with the provided payload."""
secret = secret or self.JWT_SECRET_KEY
token = jwt.encode(payload, secret)
return token
def generate_id_token(self, user, ttl=1, **overrides):
""" Generate a JWT id_token that looks like the ones currently
returned by the edx oidc provider.
"""
payload = self.default_payload(user=user, ttl=ttl)
payload.update(overrides)
return self.generate_token(payload)
def default_payload(self, user, ttl=1):
""" Generate a bare payload, in case tests need to manipulate
it directly before encoding.
"""
now = int(time())
return {
"iss": self.JWT_ISSUER,
"aud": self.JWT_AUDIENCE,
"nonce": "dummy-nonce",
"exp": now + ttl,
"iat": now,
"username": user.username,
"email": user.email,
}
......@@ -10,25 +10,31 @@ import itertools
import json
import ddt
from django.conf import settings
from django.conf.urls import patterns, url, include
from django.contrib.auth.models import User
from django.http import HttpResponse
from django.test import TestCase
from django.utils import unittest
from django.utils.http import urlencode
from mock import patch
from rest_framework import exceptions
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from rest_framework_oauth import permissions
from rest_framework_oauth.compat import oauth2_provider, oauth2_provider_scope
from rest_framework.test import APIRequestFactory, APIClient
from rest_framework.views import APIView
from rest_framework_jwt.settings import api_settings
from provider import scope, constants
from openedx.core.lib.api import authentication
from openedx.core.lib.api.tests.mixins import JwtMixin
from provider import constants, scope
from student.tests.factories import UserFactory
factory = APIRequestFactory() # pylint: disable=invalid-name
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER # pylint: disable=invalid-name
class MockView(APIView): # pylint: disable=missing-docstring
......@@ -284,3 +290,31 @@ class OAuth2Tests(TestCase):
self.assertEqual(response.status_code, scope_statuses.read_status)
response = self.post_with_bearer_token('/oauth2-with-scope-test/', token=self.access_token.token)
self.assertEqual(response.status_code, scope_statuses.write_status)
@ddt.ddt
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
class TestJWTAuthToggle(JwtMixin, TestCase):
""" Test JWT authentication toggling with feature flag 'ENABLE_JWT_AUTH'."""
USERNAME = 'test-username'
def setUp(self):
self.user = UserFactory.create(username=self.USERNAME)
self.jwt_token = self.generate_id_token(user=self.user)
super(TestJWTAuthToggle, self).setUp()
@patch.dict('django.conf.settings.FEATURES', {'ENABLE_JWT_AUTH': True})
def test_enabled_jwt_auth(self):
""" Ensure that the JWT auth works fine when its feature flag
'ENABLE_JWT_AUTH' is set.
"""
jwt_decode_handler(self.jwt_token)
@patch.dict('django.conf.settings.FEATURES', {'ENABLE_JWT_AUTH': False})
def test_disabled_jwt_auth(self):
""" Ensure that the JWT auth raises exception when its feature flag
'ENABLE_JWT_AUTH' is not set.
"""
with self.assertRaises(exceptions.AuthenticationFailed):
jwt_decode_handler(self.jwt_token)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment