浏览代码

HUE-5275 [metadata] Perform basic Sentry SELECT filtering on tables

For iterating on the UX and Sentry lib.
Romain Rigaux 9 年之前
父节点
当前提交
04c3cd56bb

+ 3 - 0
desktop/conf.dist/hue.ini

@@ -1541,3 +1541,6 @@
 
     # Execute this script to produce the auth password. This will be used when `auth_password` is not set.
     ## auth_password_script=
+
+    # Perform Sentry privilege filtering.
+    ## apply_sentry_permissions=False

+ 3 - 0
desktop/conf/pseudo-distributed.ini.tmpl

@@ -1548,3 +1548,6 @@
 
     # Execute this script to produce the auth password. This will be used when `auth_password` is not set.
     ## auth_password_script=
+
+    # Perform Sentry privilege filtering.
+    ## apply_sentry_permissions=False

+ 7 - 1
desktop/libs/metadata/src/metadata/conf.py

@@ -157,6 +157,12 @@ NAVIGATOR = ConfigSection(
       key='conf_dir',
       help=_t('Navigator configuration directory, where navigator.client.properties is located.'),
       dynamic_default=default_navigator_config_dir
-    )
+    ),
+    APPLY_SENTRY_PERMISSIONS = Config(
+      key="apply_sentry_permissions",
+      help=_t("Perform Sentry privilege filtering."),
+      default=True,
+      type=coerce_bool
+    ),
   )
 )

+ 11 - 11
desktop/libs/metadata/src/metadata/navigator_api.py

@@ -74,7 +74,7 @@ def search_entities(request):
   """
   For displaying results.
   """
-  api = NavigatorApi()
+  api = NavigatorApi(request.user)
 
   query_s = json.loads(request.POST.get('query_s', ''))
   query_s = smart_str(query_s)
@@ -106,7 +106,7 @@ def search_entities_interactive(request):
   """
   For search autocomplete.
   """
-  api = NavigatorApi()
+  api = NavigatorApi(request.user)
 
   query_s = json.loads(request.POST.get('query_s', ''))
   prefix = request.POST.get('prefix')
@@ -279,7 +279,7 @@ def _highlight_tags(record, term):
 
 @error_handler
 def list_tags(request):
-  api = NavigatorApi()
+  api = NavigatorApi(request.user)
 
   prefix = request.POST.get('prefix')
   offset = request.POST.get('offset', 0)
@@ -299,7 +299,7 @@ def list_tags(request):
 def find_entity(request):
   response = {'status': -1}
 
-  api = NavigatorApi()
+  api = NavigatorApi(request.user)
   entity_type = request.GET.get('type', '')
   database = request.GET.get('database', '')
   table = request.GET.get('table', '')
@@ -346,7 +346,7 @@ def find_entity(request):
 def suggest(request):
   response = {'status': -1}
 
-  api = NavigatorApi()
+  api = NavigatorApi(request.user)
   prefix = request.POST.get('prefix')
 
   suggest = api.suggest(prefix)
@@ -361,7 +361,7 @@ def suggest(request):
 def get_entity(request):
   response = {'status': -1}
 
-  api = NavigatorApi()
+  api = NavigatorApi(request.user)
   entity_id = request.REQUEST.get('id')
 
   if not entity_id:
@@ -380,7 +380,7 @@ def get_entity(request):
 def add_tags(request):
   response = {'status': -1}
 
-  api = NavigatorApi()
+  api = NavigatorApi(request.user)
   entity_id = json.loads(request.POST.get('id', ''))
   tags = json.loads(request.POST.get('tags', []))
 
@@ -398,7 +398,7 @@ def add_tags(request):
 def delete_tags(request):
   response = {'status': -1}
 
-  api = NavigatorApi()
+  api = NavigatorApi(request.user)
   entity_id = json.loads(request.POST.get('id', ''))
   tags = json.loads(request.POST.get('tags', []))
 
@@ -416,7 +416,7 @@ def delete_tags(request):
 def update_properties(request):
   response = {'status': -1}
 
-  api = NavigatorApi()
+  api = NavigatorApi(request.user)
   entity_id = json.loads(request.POST.get('id', ''))
   properties = json.loads(request.POST.get('properties', {}))
 
@@ -434,7 +434,7 @@ def update_properties(request):
 def delete_properties(request):
   response = {'status': -1}
 
-  api = NavigatorApi()
+  api = NavigatorApi(request.user)
   entity_id = json.loads(request.POST.get('id', ''))
   keys = json.loads(request.POST.get('keys', []))
 
@@ -451,7 +451,7 @@ def delete_properties(request):
 def get_lineage(request):
   response = {'status': -1, 'inputs': [], 'source_query': '', 'target_queries': [], 'targets': []}
 
-  api = NavigatorApi()
+  api = NavigatorApi(request.user)
   entity_id = request.REQUEST.get('id')
 
   if not entity_id:

+ 22 - 4
desktop/libs/metadata/src/metadata/navigator_client.py

@@ -24,6 +24,7 @@ from desktop.lib.rest import resource
 from desktop.lib.rest.http_client import HttpClient, RestException
 
 from hadoop.conf import HDFS_CLUSTERS
+from libsentry.privilege_checker import PrivilegeChecker
 
 from metadata.conf import NAVIGATOR
 
@@ -51,11 +52,12 @@ class NavigatorApi(object):
   http://cloudera.github.io/navigator/apidocs/v3/index.html
   """
 
-  def __init__(self, api_url=None, user=None, password=None):
-    self._api_url = '%s/%s' % ((api_url or NAVIGATOR.API_URL.get()).strip('/'), VERSION)
-    self._username = user or NAVIGATOR.AUTH_USERNAME.get()
-    self._password = password or NAVIGATOR.AUTH_PASSWORD.get()
+  def __init__(self, user=None):
+    self._api_url = '%s/%s' % (NAVIGATOR.API_URL.get().strip('/'), VERSION)
+    self._username = NAVIGATOR.AUTH_USERNAME.get()
+    self._password = NAVIGATOR.AUTH_PASSWORD.get()
 
+    self.user = user
     self._client = HttpClient(self._api_url, logger=LOG)
     self._client.set_basic_auth(self._username, self._password)
     self._root = resource.Resource(self._client, urlencode=False) # For search_entities_interactive
@@ -127,6 +129,8 @@ class NavigatorApi(object):
       LOG.info(params)
       response = self._root.get('entities', headers=self.__headers, params=params)
 
+      self._secure_results(response)
+
       return response
     except RestException, e:
       msg = 'Failed to search for entities with search query: %s' % query_s
@@ -188,6 +192,8 @@ class NavigatorApi(object):
       LOG.info(data)
       response = self._root.post('interactive/entities?limit=%(limit)s&offset=%(offset)s' % pagination, data=data, contenttype=_JSON_CONTENT_TYPE, clear_cookies=True)
 
+      self._secure_results(response)
+
       return response
     except RestException:
       msg = 'Failed to search for entities with search query %s' % json.dumps(body)
@@ -195,6 +201,18 @@ class NavigatorApi(object):
       raise NavigatorApiException(msg)
 
 
+  def _secure_results(self, response):
+    if NAVIGATOR.APPLY_SENTRY_PERMISSIONS.get():
+      checker = PrivilegeChecker(user=self.user)
+      action = 'SELECT'
+      for result in response['results']:
+        if result['type'] == 'TABLE':
+          result.update({u'column': None, u'table': result['originalName'], u'db': result['parentPath'].strip('/'), u'server': u'server1'})
+        else:
+          result.update({u'column': None, u'table': None, u'db': None, u'server': None})
+      response['results'] = checker.filter_objects(response['results'], action)
+
+
   def suggest(self, prefix=None):
     try:
       return self._root.get('interactive/suggestions?query=%s' % (prefix or '*'))

+ 1 - 1
desktop/libs/metadata/src/metadata/navigator_client_tests.py

@@ -53,7 +53,7 @@ class TestNavigatorclient:
       raise SkipTest
 
   def test_search_entities(self):
-    api = NavigatorApi()
+    api = NavigatorApi(self.user)
     api._root = MockedRoot()
 
     assert_equal(

+ 1 - 1
desktop/libs/metadata/src/metadata/navigator_tests.py

@@ -50,7 +50,7 @@ class TestNavigator(object):
     if not is_live_cluster() or not has_navigator(cls.user):
       raise SkipTest
 
-    cls.api = NavigatorApi()
+    cls.api = NavigatorApi(cls.user)
 
 
   @classmethod