Merge pull request #18556 from edx/arch/jwt-config
Authn: remove unneeded oauth_dispatch views
This commit is contained in:
@@ -646,104 +646,3 @@ class TestRevokeTokenView(AccessTokenLoginMixin, _DispatchingViewTestCase): # p
|
||||
Tests invalidation/revoke of user access token for django-oauth-toolkit
|
||||
"""
|
||||
self.verify_revoke_token(self.access_token)
|
||||
|
||||
|
||||
@unittest.skipUnless(OAUTH_PROVIDER_ENABLED, 'OAuth2 not enabled')
|
||||
class JwksViewTests(TestCase):
|
||||
def test_serialize_rsa_key(self):
|
||||
key = """\
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCkK6N/mhkEYrgx
|
||||
p8xEZj37N1FEj1gObWv7zVygMLKxKvCSFOQUjA/Z2ZLqVi8m5DnCJ+5BrdYW/UqH
|
||||
02vZdEnWb04vf8mmYzJOL9i7APu0h/rm1pvVI5JFiSjE4pG669m5dAb2dZtesYOd
|
||||
yfC5bF97KbBZoisCEAtRLn6cNrt1q6PxWeCxZq4ysQD8xZKETOxHnfAYqVyIRkDW
|
||||
v8B9DnldLjYa8GhuGHL1J5ncHoseJoATLCnAWYo+yy6gdI2Fs9rj0tbeBcnoKwUZ
|
||||
ENwEUp3En+Xw7zjtDuSDWW9ySkuwrK7nXrs0r1CPVf87dLBUEvdzHHUelDr6rdIY
|
||||
tnieCjCHAgMBAAECggEBAJvTiAdQPzq4cVlAilTKLz7KTOsknFJlbj+9t5OdZZ9g
|
||||
wKQIDE2sfEcti5O+Zlcl/eTaff39gN6lYR73gMEQ7h0J3U6cnsy+DzvDkpY94qyC
|
||||
/ZYqUhPHBcnW3Mm0vNqNj0XGae15yBXjrKgSy9lUknSXJ3qMwQHeNL/DwA2KrfiL
|
||||
g0iVjk32dvSSHWcBh0M+Qy1WyZU0cf9VWzx+Q1YLj9eUCHteStVubB610XV3JUZt
|
||||
UTWiUCffpo2okHsTBuKPVXK/5BL+BpGplcxRSlnSbMaI611kN3iKlO8KGISXHBz7
|
||||
nOPdkfZC9poEXt5SshtINuGGCCc8hDxpg1otYqCLaYECgYEA1MSCPs3pBkEagchV
|
||||
g0rxYmDUC8QkeIOBuZFjhkdoUgZ6rFntyRZd1NbCUi3YBbV1YC12ZGohqWUWom1S
|
||||
AtNbQ2ZTbqEnDKWbNvLBRwkdp/9cKBce85lCCD6+U2o2Ha8C0+hKeLBn8un1y0zY
|
||||
1AQTqLAz9ItNr0aDPb89cs5voWcCgYEAxYdC8vR3t8iYMUnK6LWYDrKSt7YiorvF
|
||||
qXIMANcXQrnO0ptC0B56qrUCgKHNrtPi5bGpNBJ0oKMfbmGfwX+ca8sCUlLvq/O8
|
||||
S2WZwSJuaHH4lEBi8ErtY++8F4B4l3ENCT84Hyy5jiMpbpkHEnh/1GNcvvmyI8ud
|
||||
3jzovCNZ4+ECgYEA0r+Oz0zAOzyzV8gqw7Cw5iRJBRqUkXaZQUj8jt4eO9lFG4C8
|
||||
IolwCclrk2Drb8Qsbka51X62twZ1ZA/qwve9l0Y88ADaIBHNa6EKxyUFZglvrBoy
|
||||
w1GT8XzMou06iy52G5YkZeU+IYOSvnvw7hjXrChUXi65lRrAFqJd6GEIe5MCgYA/
|
||||
0LxDa9HFsWvh+JoyZoCytuSJr7Eu7AUnAi54kwTzzL3R8tE6Fa7BuesODbg6tD/I
|
||||
v4YPyaqePzUnXyjSxdyOQq8EU8EUx5Dctv1elTYgTjnmA4szYLGjKM+WtC3Bl4eD
|
||||
pkYGZFeqYRfAoHXVdNKvlk5fcKIpyF2/b+Qs7CrdYQKBgQCc/t+JxC9OpI+LhQtB
|
||||
tEtwvklxuaBtoEEKJ76P9vrK1semHQ34M1XyNmvPCXUyKEI38MWtgCCXcdmg5syO
|
||||
PBXdDINx+wKlW7LPgaiRL0Mi9G2aBpdFNI99CWVgCr88xqgSE24KsOxViMwmi0XB
|
||||
Ld/IRK0DgpGP5EJRwpKsDYe/UQ==
|
||||
-----END PRIVATE KEY-----"""
|
||||
|
||||
# pylint: disable=line-too-long
|
||||
expected = {
|
||||
'kty': 'RSA',
|
||||
'use': 'sig',
|
||||
'alg': 'RS512',
|
||||
'n': 'pCujf5oZBGK4MafMRGY9-zdRRI9YDm1r-81coDCysSrwkhTkFIwP2dmS6lYvJuQ5wifuQa3WFv1Kh9Nr2XRJ1m9OL3_JpmMyTi_YuwD7tIf65tab1SOSRYkoxOKRuuvZuXQG9nWbXrGDncnwuWxfeymwWaIrAhALUS5-nDa7dauj8VngsWauMrEA_MWShEzsR53wGKlciEZA1r_AfQ55XS42GvBobhhy9SeZ3B6LHiaAEywpwFmKPssuoHSNhbPa49LW3gXJ6CsFGRDcBFKdxJ_l8O847Q7kg1lvckpLsKyu5167NK9Qj1X_O3SwVBL3cxx1HpQ6-q3SGLZ4ngowhw',
|
||||
'e': 'AQAB',
|
||||
'kid': '6e80b9d2e5075ae8bb5d1dd762ebc62e'
|
||||
}
|
||||
self.assertEqual(views.JwksView.serialize_rsa_key(key), expected)
|
||||
|
||||
def test_get(self):
|
||||
JWT_PRIVATE_SIGNING_KEY = RSA.generate(2048).exportKey('PEM')
|
||||
JWT_EXPIRED_PRIVATE_SIGNING_KEYS = [RSA.generate(2048).exportKey('PEM'), RSA.generate(2048).exportKey('PEM')]
|
||||
secret_keys = [JWT_PRIVATE_SIGNING_KEY] + JWT_EXPIRED_PRIVATE_SIGNING_KEYS
|
||||
|
||||
with override_settings(JWT_PRIVATE_SIGNING_KEY=JWT_PRIVATE_SIGNING_KEY,
|
||||
JWT_EXPIRED_PRIVATE_SIGNING_KEYS=JWT_EXPIRED_PRIVATE_SIGNING_KEYS):
|
||||
response = self.client.get(reverse('jwks'))
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
actual = json.loads(response.content)
|
||||
expected = {
|
||||
'keys': [views.JwksView.serialize_rsa_key(key) for key in secret_keys],
|
||||
}
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
@override_settings(JWT_PRIVATE_SIGNING_KEY=None, JWT_EXPIRED_PRIVATE_SIGNING_KEYS=[])
|
||||
def test_get_without_keys(self):
|
||||
""" The view should return an empty list if no keys are configured. """
|
||||
response = self.client.get(reverse('jwks'))
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
actual = json.loads(response.content)
|
||||
self.assertEqual(actual, {'keys': []})
|
||||
|
||||
|
||||
@unittest.skipUnless(OAUTH_PROVIDER_ENABLED, 'OAuth2 not enabled')
|
||||
class ProviderInfoViewTests(TestCase):
|
||||
DOMAIN = 'testserver.fake'
|
||||
|
||||
def build_url(self, path):
|
||||
return 'http://{domain}{path}'.format(domain=self.DOMAIN, path=path)
|
||||
|
||||
def test_get(self):
|
||||
issuer = 'test-issuer'
|
||||
self.client = self.client_class(SERVER_NAME=self.DOMAIN)
|
||||
|
||||
expected = {
|
||||
'issuer': issuer,
|
||||
'authorization_endpoint': self.build_url(reverse('authorize')),
|
||||
'token_endpoint': self.build_url(reverse('access_token')),
|
||||
'end_session_endpoint': self.build_url(reverse('logout')),
|
||||
'token_endpoint_auth_methods_supported': ['client_secret_post'],
|
||||
'access_token_signing_alg_values_supported': ['RS512', 'HS256'],
|
||||
'scopes_supported': ['openid', 'profile', 'email'],
|
||||
'claims_supported': ['sub', 'iss', 'name', 'given_name', 'family_name', 'email'],
|
||||
'jwks_uri': self.build_url(reverse('jwks')),
|
||||
}
|
||||
|
||||
with override_settings(JWT_AUTH={'JWT_ISSUER': issuer}):
|
||||
response = self.client.get(reverse('openid-config'))
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
actual = json.loads(response.content)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
@@ -12,8 +12,6 @@ urlpatterns = [
|
||||
url(r'^authorize/?$', csrf_exempt(views.AuthorizationView.as_view()), name='authorize'),
|
||||
url(r'^access_token/?$', csrf_exempt(views.AccessTokenView.as_view()), name='access_token'),
|
||||
url(r'^revoke_token/?$', csrf_exempt(views.RevokeTokenView.as_view()), name='revoke_token'),
|
||||
url(r'^\.well-known/openid-configuration/?$', views.ProviderInfoView.as_view(), name='openid-config'),
|
||||
url(r'^jwks\.json$', views.JwksView.as_view(), name='jwks')
|
||||
]
|
||||
|
||||
if settings.FEATURES.get('ENABLE_THIRD_PARTY_AUTH'):
|
||||
|
||||
@@ -185,45 +185,3 @@ class RevokeTokenView(_DispatchingView):
|
||||
Dispatch to the RevokeTokenView of django-oauth-toolkit
|
||||
"""
|
||||
dot_view = dot_views.RevokeTokenView
|
||||
|
||||
|
||||
class ProviderInfoView(View):
|
||||
def get(self, request, *args, **kwargs):
|
||||
data = {
|
||||
'issuer': settings.JWT_AUTH['JWT_ISSUER'],
|
||||
'authorization_endpoint': request.build_absolute_uri(reverse('authorize')),
|
||||
'token_endpoint': request.build_absolute_uri(reverse('access_token')),
|
||||
'end_session_endpoint': request.build_absolute_uri(reverse('logout')),
|
||||
'token_endpoint_auth_methods_supported': ['client_secret_post'],
|
||||
# NOTE (CCB): This is not part of the OpenID Connect standard. It is added here since we
|
||||
# use JWS for our access tokens.
|
||||
'access_token_signing_alg_values_supported': ['RS512', 'HS256'],
|
||||
'scopes_supported': ['openid', 'profile', 'email'],
|
||||
'claims_supported': ['sub', 'iss', 'name', 'given_name', 'family_name', 'email'],
|
||||
'jwks_uri': request.build_absolute_uri(reverse('jwks')),
|
||||
}
|
||||
response = JsonResponse(data)
|
||||
return response
|
||||
|
||||
|
||||
class JwksView(View):
|
||||
@staticmethod
|
||||
def serialize_rsa_key(key):
|
||||
kid = hashlib.md5(key.encode('utf-8')).hexdigest()
|
||||
key = RSAKey(kid=kid, key=RSA.importKey(key), use='sig', alg='RS512')
|
||||
return key.serialize(private=False)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
secret_keys = []
|
||||
|
||||
if settings.JWT_PRIVATE_SIGNING_KEY:
|
||||
secret_keys.append(settings.JWT_PRIVATE_SIGNING_KEY)
|
||||
|
||||
# NOTE: We provide the expired keys in case there are unexpired access tokens
|
||||
# that need to have their signatures verified.
|
||||
if settings.JWT_EXPIRED_PRIVATE_SIGNING_KEYS:
|
||||
secret_keys += settings.JWT_EXPIRED_PRIVATE_SIGNING_KEYS
|
||||
|
||||
return JsonResponse({
|
||||
'keys': [self.serialize_rsa_key(key) for key in secret_keys if key],
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user