Эх сурвалжийг харах

[jwt] Refactor custom JWT authentication config (#2438)

- Much cleaner config flags and values to enable it.
- Add flexibility for users to use their own auth and not hardcode it.
- Separate section [[[jwt]]] under [[auth]].
- Update unit tests.
Harsh Gupta 4 жил өмнө
parent
commit
fb5541163b

+ 11 - 5
desktop/conf.dist/hue.ini

@@ -358,13 +358,9 @@
     # Multiple Authentication backend combinations are supported by specifying a comma-separated list in order of priority.
     ## backend=desktop.auth.backend.AllowFirstUserDjangoBackend
 
-    # Custom Authentication backends for the REST API.
-    # Multiple Authentication backends are supported by specifying a comma-separated list in order of priority.
+    # Multiple Authentication backends for REST APIs are supported by specifying a comma-separated list in order of priority.
     ## api_auth=rest_framework_simplejwt.authentication.JWTAuthentication,rest_framework.authentication.SessionAuthentication
 
-    # Verify custom JWT.
-    ## verify_custom_jwt=true
-
     # Class which defines extra accessor methods for User objects.
     ## user_aug=desktop.auth.backend.DefaultUserAugmentor
 
@@ -433,6 +429,16 @@
     # If behind_reverse_proxy is True, it will look for the IP address from this header. Default: HTTP_X_FORWARDED_FOR
     ## reverse_proxy_header=HTTP_X_FORWARDED_FOR
 
+    [[[jwt]]]
+      # Adds custom JWT Authentication backend for REST APIs in top priority.
+      ## is_enabled=false
+
+      # Endpoint to fetch the public key from verification server.
+      ## key_server_url=https://ext_authz:8000
+
+      # Verify custom JWT signature.
+      ## verify=true
+
   # Configuration options for connecting to LDAP and Active Directory
   # -------------------------------------------------------------------
   [[ldap]]

+ 11 - 5
desktop/conf/pseudo-distributed.ini.tmpl

@@ -362,13 +362,9 @@
     # Multiple Authentication backend combinations are supported by specifying a comma-separated list in order of priority.
     ## backend=desktop.auth.backend.AllowFirstUserDjangoBackend
 
-    # Custom Authentication backends for the REST API.
-    # Multiple Authentication backends are supported by specifying a comma-separated list in order of priority.
+    # Multiple Authentication backends for REST APIs are supported by specifying a comma-separated list in order of priority.
     ## api_auth=rest_framework_simplejwt.authentication.JWTAuthentication,rest_framework.authentication.SessionAuthentication
 
-    # Verify custom JWT.
-    ## verify_custom_jwt=true
-
     # Class which defines extra accessor methods for User objects.
     ## user_aug=desktop.auth.backend.DefaultUserAugmentor
 
@@ -437,6 +433,16 @@
     # If behind_reverse_proxy is True, it will look for the IP address from this header. Default: HTTP_X_FORWARDED_FOR
     ## reverse_proxy_header=HTTP_X_FORWARDED_FOR
 
+    [[[jwt]]]
+      # Adds custom JWT Authentication backend for REST APIs in top priority.
+      ## is_enabled=false
+
+      # Endpoint to fetch the public key from verification server.
+      ## key_server_url=https://ext_authz:8000
+
+      # Verify custom JWT signature.
+      ## verify=true
+
   # Configuration options for connecting to LDAP and Active Directory
   # -------------------------------------------------------------------
   [[ldap]]

+ 1 - 1
desktop/core/src/desktop/auth/api_authentications.py

@@ -54,7 +54,7 @@ class JwtAuthentication(authentication.BaseAuthentication):
         access_token,
         'secret',
         algorithms=["RS256"],
-        verify=AUTH.VERIFY_CUSTOM_JWT.get()
+        verify=AUTH.JWT.VERIFY.get()
       )
     except jwt.DecodeError:
       raise exceptions.AuthenticationFailed('JwtAuthentication: Invalid token')

+ 7 - 3
desktop/core/src/desktop/auth/api_authentications_tests.py

@@ -18,6 +18,7 @@
 import sys
 
 from nose.tools import assert_equal, assert_true, assert_false, assert_raises
+from nose.plugins.skip import SkipTest
 
 from desktop.auth.backend import rewrite_user
 from desktop.auth.api_authentications import JwtAuthentication
@@ -47,7 +48,6 @@ class TestJwtAuthentication():
       }
     )
 
-
   def test_authenticate_existing_user(self):
     with patch('desktop.auth.api_authentications.jwt.decode') as jwt_decode:
 
@@ -104,10 +104,14 @@ class TestJwtAuthentication():
   def test_check_token_verification_flag(self):
 
     # When verification flag is True for old sample token
-    assert_raises(exceptions.AuthenticationFailed, JwtAuthentication().authenticate, self.request)
+    reset = AUTH.JWT.VERIFY.set_for_testing(True)
+    try:
+      assert_raises(exceptions.AuthenticationFailed, JwtAuthentication().authenticate, self.request)
+    finally:
+      reset()
 
     # When verification flag is False
-    reset = AUTH.VERIFY_CUSTOM_JWT.set_for_testing(False)
+    reset = AUTH.JWT.VERIFY.set_for_testing(False)
     try:
       user, token = JwtAuthentication().authenticate(request=self.request)
 

+ 32 - 16
desktop/core/src/desktop/conf.py

@@ -1078,22 +1078,6 @@ AUTH = ConfigSection(
                         "desktop.auth.backend.AllowAllBackend (allows everyone), " +
                         "desktop.auth.backend.AllowFirstUserDjangoBackend (relies on Django and user manager, after the first login). " +
                         "Multiple Authentication backends are supported by specifying a comma-separated list in order of priority.")),
-    API_AUTH=Config(
-        "api_auth",
-        default=[
-          'rest_framework_simplejwt.authentication.JWTAuthentication',
-          'rest_framework.authentication.SessionAuthentication'],
-        type=coerce_csv,
-        help=_(
-          "Custom Authentication backends for the REST API." +
-          "Multiple Authentication backends are supported by specifying a comma-separated list in order of priority.")
-    ),
-    VERIFY_CUSTOM_JWT=Config(
-        key="verify_custom_jwt",
-        default=True,
-        type=coerce_bool,
-        help=_("Verify custom JWT.")
-    ),
     USER_AUGMENTOR=Config("user_augmentor",
                    default="desktop.auth.backend.DefaultUserAugmentor",
                    help=_("Class which defines extra accessor methods for User objects.")),
@@ -1205,6 +1189,38 @@ AUTH = ConfigSection(
       type=coerce_bool,
       default=False,
     ),
+    API_AUTH=Config(
+        "api_auth",
+        default=[
+          'rest_framework_simplejwt.authentication.JWTAuthentication',
+          'rest_framework.authentication.SessionAuthentication'],
+        type=coerce_csv,
+        help=_("Multiple Authentication backends are supported by specifying a comma-separated list in order of priority.")
+    ),
+    JWT=ConfigSection(
+      key="jwt",
+      help=_("Configuration for Custom JWT Authentication."),
+      members=dict(
+        IS_ENABLED=Config(
+            key='is_enabled',
+            help=_('Adds custom JWT Authentication backend for REST APIs in top priority.'),
+            type=coerce_bool,
+            default=False,
+        ),
+        KEY_SERVER_URL=Config(
+            key="key_server_url",
+            default=None,
+            type=str,
+            help=_("Endpoint to fetch the public key from verification server.")
+        ),
+        VERIFY=Config(
+            key="verify",
+            default=True,
+            type=coerce_bool,
+            help=_("Verify custom JWT signature.")
+        ),
+      )
+    ),
 ))
 
 

+ 2 - 0
desktop/core/src/desktop/settings.py

@@ -336,6 +336,8 @@ REST_FRAMEWORK = {
     ],
     'DEFAULT_AUTHENTICATION_CLASSES': desktop.conf.AUTH.API_AUTH.get()
 }
+if desktop.conf.AUTH.JWT.IS_ENABLED.get() and 'desktop.auth.api_authentications.JwtAuthentication' not in REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES']:
+  REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'].insert(0, 'desktop.auth.api_authentications.JwtAuthentication')
 
 SIMPLE_JWT = {
   'ACCESS_TOKEN_LIFETIME': datetime.timedelta(days=1),