Merge pull request #11599 from edx/zub/feature-flag-jwt-auth
enable jwt auth with feature flag
This commit is contained in:
@@ -2143,6 +2143,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,
|
||||
|
||||
48
openedx/core/lib/api/tests/mixins.py
Normal file
48
openedx/core/lib/api/tests/mixins.py
Normal file
@@ -0,0 +1,48 @@
|
||||
"""
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user