Browse Source

[api] Skeleton of Public API with JWT token auth

All current APIs stays the same, however a new /api/* endpoint
comes with JWT auth.

curl   -X POST   -H "Content-Type: application/json" http://localhost:9000/api/v1/create_notebook
{"detail":"Authentication credentials were not provided."}

curl   -X POST   -H "Content-Type: application/json"   -d '{"email": "romain@gethue.com", "password": "romain"}'   http://localhost:9000/api/token/
{"refresh":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTYyMTcyNDYzMSwianRpIjoiOGM0NDRjYzRhN2VhNGMxZDliMGZhNmU1YzUyMjM1MjkiLCJ1c2VyX2lkIjoxfQ.t6t7_eYrNhpGN3-Jz5MDLXM8JtGP7V9Y9lacOTInqqQ","access":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjIxNjM4NTMxLCJqdGkiOiJhZjgwN2E0ZjBmZDI0ZWMxYWQ2NTUzZjEyMjIyYzU4YyIsInVzZXJfaWQiOjF9.dQ1P3hbzSytp9-o8bWlcOcwrdwRVy95M2Eolph92QMA"}

url -X POST    -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjIxNjM5NjMxLCJqdGkiOiI0NTY3NTA4MzM5YjY0MjFmYTMzZDJjMzViZWUyMDAyMCIsInVzZXJfaWQiOjF9.qrMNrr69eo38dOsV2aYp8k6WqBeyJZkbSuavxA_o_kM"   http://localhost:9000/api/v1/create_notebook
{"status": 0, "notebook": {"name": "My Notebook", "uuid": "1e23314f-b01e-4c18-872f-dc143475f063", "description": "", "type": "notebook", "isSaved": false, "isManaged": false, "skipHistorify": false, "sessions": [], "snippets": [], "directoryUuid": null}}

Non official API:

curl -X POST    -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjIxNjM5NjMxLCJqdGkiOiI0NTY3NTA4MzM5YjY0MjFmYTMzZDJjMzViZWUyMDAyMCIsInVzZXJfaWQiOjF9.qrMNrr69eo38dOsV2aYp8k6WqBeyJZkbSuavxA_o_kM"   http://localhost:9000/notebook/execute/v1/create_notebook

[21/May/2021 16:26:46 -0700] middleware   INFO     Redirecting to login page: /notebook/execute/v1/create_notebook
[21/May/2021 16:26:46 -0700] access       INFO     127.0.0.1 -anon- - "POST /notebook/execute/v1/create_notebook HTTP/1.1" - (mem: 172mb)-- login redirection
[21/May/2021 16:26:46 -0700] access       INFO     127.0.0.1 -anon- - "POST /notebook/execute/v1/create_notebook HTTP/1.1" returned in 4ms 302 0 (mem:

Providing POST parameters
curl -X POST    -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjIxNjU2NjE2LCJqdGkiOiI4MTFkZDI4Yjg0NWI0ZjQ4OWVjNmE3MGFkNTRmMWE5MCIsInVzZXJfaWQiOjF9.bOFrFu5D7l3zY6d1c4PMplrW2qOJeqechrpR351Jeic" -d 'type=mysql'   http://localhost:9000/api/v1/create_notebook
{"status": 0, "notebook": {"name": "", "uuid": "b540b10d-10f8-44bd-b0d3-135c967e7842", "description": "", "type": "query-mysql", "isSaved": false, "isManaged": false, "skipHistorify": false, "sessions": [], "snippets": [], "directoryUuid": null}}
Romain Rigaux 4 years ago
parent
commit
5380d517c1

+ 1 - 1
desktop/core/requirements.txt

@@ -26,7 +26,7 @@ django-nose==1.4.7
 django_opentracing==1.1.0
 django_prometheus==1.0.15
 django-webpack-loader==0.5.0
-djangorestframework-simplejwt==4.6.0
+djangorestframework-simplejwt==4.4.0
 djangorestframework==3.12.2
 eventlet==0.31.0
 future==0.18.2

+ 25 - 0
desktop/core/src/desktop/api_public.py

@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+# Licensed to Cloudera, Inc. under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  Cloudera, Inc. licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from rest_framework.decorators import api_view
+
+from notebook import api as notebook_api
+
+
+@api_view(["POST"])
+def create_notebook(request):
+  return notebook_api.create_notebook(request)

+ 3 - 0
desktop/core/src/desktop/middleware.py

@@ -303,6 +303,9 @@ class LoginAndPermissionMiddleware(MiddlewareMixin):
     if request.path in ['/oidc/authenticate/', '/oidc/callback/', '/oidc/logout/', '/hue/oidc_failed/']:
       return None
 
+    if request.path.startswith('/api/'):
+      return None
+
     # Skip views not requiring login
 
     # If the view has "opted out" of login required, skip

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

@@ -21,6 +21,7 @@
 # as local_settings.py.
 
 from builtins import map, zip
+import datetime
 import gc
 import json
 import logging
@@ -211,6 +212,8 @@ INSTALLED_APPS = [
     'django_prometheus',
     'crequest',
     #'django_celery_results',
+    'rest_framework',
+    'rest_framework.authtoken',
 ]
 
 WEBPACK_LOADER = {
@@ -280,6 +283,27 @@ PYLINTRC = get_run_root('.pylintrc')
 # Custom CSRF Failure View
 CSRF_FAILURE_VIEW = 'desktop.views.csrf_failure'
 
+REST_FRAMEWORK = {
+    'DEFAULT_PERMISSION_CLASSES': [
+      # 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
+      'rest_framework.permissions.IsAuthenticated',
+      # 'rest_framework.permissions.AllowAny'
+    ],
+    'DEFAULT_AUTHENTICATION_CLASSES': (
+      'rest_framework_simplejwt.authentication.JWTAuthentication',
+    ),
+}
+
+JWT_AUTH = {
+    'JWT_VERIFY': True,
+    'JWT_VERIFY_EXPIRATION': True,
+    'JWT_LEEWAY': 0,
+    'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=86400),
+    'JWT_ALLOW_REFRESH': True,
+    'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),
+     "JWT_AUTH_HEADER_PREFIX": "Bearer",
+}
+
 ############################################################
 # Part 4: Installation of apps
 ############################################################

+ 18 - 0
desktop/core/src/desktop/urls.py

@@ -35,6 +35,11 @@ desktop.lib.metrics.file_reporter.start_file_reporter()
 
 from django.conf import settings
 from django.views.static import serve
+from rest_framework_simplejwt.views import (
+    TokenObtainPairView,
+    TokenRefreshView,
+    TokenVerifyView,
+)
 
 from notebook import views as notebook_views
 from useradmin import views as useradmin_views
@@ -44,6 +49,7 @@ from desktop import appmanager
 from desktop import views as desktop_views
 from desktop import api as desktop_api
 from desktop import api2 as desktop_api2
+from desktop import api_public
 from desktop.auth import views as desktop_auth_views
 from desktop.conf import METRICS, USE_NEW_EDITOR, ENABLE_DJANGO_DEBUG_TOOL, ANALYTICS, has_connectors, ENABLE_PROMETHEUS, SLACK
 from desktop.configuration import api as desktop_configuration_api
@@ -201,6 +207,18 @@ dynamic_patterns += [
   re_path(r'^desktop/api/users/?$', useradmin_views.get_users_by_id)
 ]
 
+dynamic_patterns += [
+  re_path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
+  re_path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
+  re_path('api/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
+]
+
+dynamic_patterns += [
+  re_path(r'^api/v1/create_notebook/?$', api_public.create_notebook, name='create_notebook'),
+  # ...
+]
+
+
 dynamic_patterns += [
   re_path(r'^desktop/api/vcs/contents/?$', desktop_lib_vcs_api.contents),
   re_path(r'^desktop/api/vcs/authorize/?$', desktop_lib_vcs_api.authorize),