Jelajahi Sumber

[core] Add a public API endpoint to retrieve the banners

This can be used from both the login page and inside the app to retrieve any system and configured banners
Johan Åhlén 2 tahun lalu
induk
melakukan
f4a36e2c1c

+ 9 - 2
desktop/core/src/desktop/api2.py

@@ -45,7 +45,7 @@ from useradmin.models import User, Group
 
 from desktop import appmanager
 from desktop.auth.backend import is_admin
-from desktop.conf import ENABLE_CONNECTORS, ENABLE_GIST_PREVIEW, get_clusters, IS_K8S_ONLY, ENABLE_SHARING
+from desktop.conf import ENABLE_CONNECTORS, ENABLE_GIST_PREVIEW, CUSTOM, get_clusters, IS_K8S_ONLY, ENABLE_SHARING
 from desktop.lib.conf import BoundContainer, GLOBAL_CONFIG, is_anonymous
 from desktop.lib.django_util import JsonResponse, login_notrequired, render
 from desktop.lib.exceptions_renderable import PopupException
@@ -54,7 +54,7 @@ from desktop.lib.i18n import smart_str, force_unicode
 from desktop.lib.paths import get_desktop_root
 from desktop.models import Document2, Document, Directory, FilesystemException, uuid_default, \
   UserPreferences, get_user_preferences, set_user_preferences, get_cluster_config, __paginate, _get_gist_document
-from desktop.views import serve_403_error
+from desktop.views import get_banner_message, serve_403_error
 
 from hadoop.cluster import is_yarn
 
@@ -84,6 +84,13 @@ def api_error_handler(func):
 
   return decorator
 
+@api_error_handler
+def get_banners(request):
+  banners = {
+    'system': get_banner_message(request),
+    'configured': CUSTOM.BANNER_TOP_HTML.get()
+  }
+  return JsonResponse(banners)
 
 @api_error_handler
 def get_config(request):

+ 6 - 1
desktop/core/src/desktop/api_public.py

@@ -19,7 +19,8 @@ import logging
 import json
 
 from django.http import QueryDict, HttpResponse
-from rest_framework.decorators import api_view
+from rest_framework.permissions import AllowAny
+from rest_framework.decorators import api_view, authentication_classes, permission_classes
 
 from filebrowser import views as filebrowser_views, api as filebrowser_api
 from indexer import api3 as indexer_api3
@@ -52,6 +53,10 @@ def get_context_namespaces(request, interface):
   django_request = get_django_request(request)
   return desktop_api.get_context_namespaces(django_request, interface)
 
+@api_view(["GET"])
+@permission_classes([AllowAny])
+def get_banners(request):
+  return desktop_api.get_banners(request)
 
 # Editor
 

+ 33 - 6
desktop/core/src/desktop/api_public_tests.py

@@ -25,11 +25,9 @@ from nose.plugins.skip import SkipTest
 from nose.tools import assert_true, assert_false, assert_equal, assert_not_equal, assert_raises
 
 from useradmin.models import User
-
+from desktop.conf import CUSTOM
 from desktop.api_public import get_django_request
-from desktop.lib.django_test_util import make_logged_in_client
-from desktop.lib.test_utils import grant_access
-
+from desktop.lib.django_test_util import Client, make_logged_in_client
 
 if sys.version_info[0] > 2:
   from unittest.mock import patch, Mock, MagicMock
@@ -37,9 +35,38 @@ else:
   from mock import patch, Mock, MagicMock
 
 
+class TestCoreApi():
+  def setUp(self):
+    self.unauthorized_client = Client()
+
+  def test_banners(self):
+    with patch('desktop.api2.get_banner_message') as get_banner_message:
+      try:
+        configured_banner = '<div>Some config banner</div>'
+        system_banner = '<div>Some system banner</div>'
+        done = CUSTOM.BANNER_TOP_HTML.set_for_testing(configured_banner)
+        get_banner_message.return_value = system_banner
+
+        response = self.unauthorized_client.get(reverse('api:core_banners'))
+
+        get_banner_message.assert_called()
+        json_resp = json.loads(response.content)
+        assert_equal(json_resp['configured'], configured_banner)
+        assert_equal(json_resp['system'], system_banner)
+      finally:
+        done()
+
+
 class TestEditorApi():
   TEST_INTERPRETER = {
-    'name': 'MySql', 'displayName': 'MySql', 'type': '1', 'interface': 'sqlalchemy', 'options': {'url': 'mysql://hue:hue@localhost:3306/hue', 'has_ssh': False, 'ssh_server_host': '127.0.0.1'}, 'dialect': 'mysql', 'dialect_properties': {'is_sql': True, 'sql_identifier_quote': '`', 'sql_identifier_comment_single': '--', 'has_catalog': True, 'has_database': True, 'has_table': True, 'has_live_queries': False, 'has_optimizer_risks': True, 'has_optimizer_values': True, 'has_auto_limit': False, 'has_reference_language': False, 'has_reference_functions': False, 'has_use_statement': False}, 'category': 'editor', 'is_sql': True, 'is_catalog': False
+    'name': 'MySql', 'displayName': 'MySql', 'type': '1', 'interface': 'sqlalchemy',
+    'options': {'url': 'mysql://hue:hue@localhost:3306/hue', 'has_ssh': False, 'ssh_server_host': '127.0.0.1'},
+    'dialect': 'mysql',
+    'dialect_properties': {'is_sql': True, 'sql_identifier_quote': '`', 'sql_identifier_comment_single': '--',
+                           'has_catalog': True, 'has_database': True, 'has_table': True, 'has_live_queries': False,
+                           'has_optimizer_risks': True, 'has_optimizer_values': True, 'has_auto_limit': False,
+                           'has_reference_language': False, 'has_reference_functions': False,
+                           'has_use_statement': False}, 'category': 'editor', 'is_sql': True, 'is_catalog': False
   }
 
   def setUp(self):
@@ -58,7 +85,7 @@ class TestEditorApi():
         execute.return_value = HttpResponse()
         _get_interpreter_from_dialect.return_value = TestEditorApi.TEST_INTERPRETER
 
-        self.client.post(reverse('api:editor_execute', args=['hive']), {'statement':'SHOW TABLES'})
+        self.client.post(reverse('api:editor_execute', args=['hive']), {'statement': 'SHOW TABLES'})
 
       execute.assert_called()
       if not execute.call_args.args[1]:

+ 1 - 0
desktop/core/src/desktop/api_public_urls.py

@@ -36,6 +36,7 @@ urlpatterns = [
 # Compatibility with "old" private API.
 # e.g. https://demo.gethue.com/notebook/api/execute/hive
 urlpatterns += [
+  re_path(r'^banners/?$', api_public.get_banners, name='core_banners'),
   re_path(r'^get_config/?$', api_public.get_config),
   re_path(r'^get_namespaces/(?P<interface>[\w\-]+)/?$', api_public.get_context_namespaces),  # To remove
 

+ 4 - 9
desktop/core/src/desktop/views.py

@@ -554,24 +554,19 @@ def get_banner_message(request):
   forwarded_host = request.get_host()
 
   if hasattr(request, 'environ'):
-    message = None
     path_info = request.environ.get("PATH_INFO")
     if path_info.find("/hue") < 0 and path_info.find("accounts/login") < 0 and not request.path.startswith('/api/'):
       url = request.build_absolute_uri("/hue")
-      link = '<a href="%s" style="color: #FFF; font-weight: bold">%s</a>' % (url, url)
-      message = _('You are accessing an older version of Hue, please switch to the latest version: %s.') % link
+      link = '<a href="%s">%s</a>' % (url, url)
+      banner_message = _('You are accessing an older version of Hue, please switch to the latest version: %s.') % link
       LOG.warning('User %s is using Hue 3 UI' % request.user.username)
 
     if HUE_LOAD_BALANCER.get() and HUE_LOAD_BALANCER.get() != [''] and \
       (not forwarded_host or not any(forwarded_host in lb for lb in HUE_LOAD_BALANCER.get())):
-      message = _('You are accessing a non-optimized Hue, please switch to one of the available addresses: %s') % \
-        (", ".join(['<a href="%s" style="color: #FFF; font-weight: bold">%s</a>' % (host, host) for host in HUE_LOAD_BALANCER.get()]))
+      banner_message = _('You are accessing a non-optimized Hue, please switch to one of the available addresses: %s') % \
+        (", ".join(['<a href="%s">%s</a>' % (host, host) for host in HUE_LOAD_BALANCER.get()]))
       LOG.warning('User %s is bypassing the load balancer' % request.user.username)
 
-    if message:
-      banner_message = '<div style="padding: 4px; text-align: center; background-color: #003F6C; height: 24px; color: #DBE8F1">%s</div>' \
-          % message
-
   return banner_message
 
 def commonshare():