浏览代码

HUE-8983 [fb] Handle s3 with ListAllMyBuckets denied

When you don't have ListAllMyBuckets permission a message is now
displayed and the breadcrumbs path is focused.
Jean-Francois Desjeans Gauthier 6 年之前
父节点
当前提交
03916b59be

文件差异内容过多而无法显示
+ 0 - 0
apps/filebrowser/src/filebrowser/static/filebrowser/css/listdir_components.css


+ 0 - 4
apps/filebrowser/src/filebrowser/static/filebrowser/less/listdir_components.less

@@ -246,9 +246,5 @@
   margin-bottom: 3px;
   margin-bottom: 3px;
   padding-left: 14px;
   padding-left: 14px;
   border-radius: 2px;
   border-radius: 2px;
-  border: 1px solid @cui-gray-050;
-  -webkit-box-shadow: none;
-  -moz-box-shadow: none;
-  box-shadow: none;
   font-weight: bold;
   font-weight: bold;
 }
 }

+ 2 - 0
apps/filebrowser/src/filebrowser/templates/listdir.mako

@@ -197,6 +197,8 @@ ${ fb_components.menubar() }
       <div class="alert alert-warn" data-bind="visible: ! isCurrentDirSentryManaged() && selectedSentryFiles().length > 0">
       <div class="alert alert-warn" data-bind="visible: ! isCurrentDirSentryManaged() && selectedSentryFiles().length > 0">
         ${ _('The permissions of some of the selected files are managed by the Sentry Namenode plugin.') }
         ${ _('The permissions of some of the selected files are managed by the Sentry Namenode plugin.') }
       </div>
       </div>
+      <div class="alert alert-warn" data-bind="visible: errorMessage(), text: errorMessage">
+      </div>
 
 
       % if breadcrumbs:
       % if breadcrumbs:
         ${fb_components.breadcrumbs(path, breadcrumbs, True)}
         ${fb_components.breadcrumbs(path, breadcrumbs, True)}

+ 18 - 7
apps/filebrowser/src/filebrowser/templates/listdir_components.mako

@@ -938,6 +938,7 @@ from filebrowser.conf import ENABLE_EXTRACT_UPLOADED_ARCHIVE
       self.skipTrash = ko.observable(false);
       self.skipTrash = ko.observable(false);
       self.enableFilterAfterSearch = true;
       self.enableFilterAfterSearch = true;
       self.isCurrentDirSentryManaged = ko.observable(false);
       self.isCurrentDirSentryManaged = ko.observable(false);
+      self.errorMessage = ko.observable("");
       self.pendingUploads = ko.observable(0);
       self.pendingUploads = ko.observable(0);
       self.pendingUploads.subscribe(function (val) {
       self.pendingUploads.subscribe(function (val) {
         if (val > 0) {
         if (val > 0) {
@@ -1188,24 +1189,34 @@ from filebrowser.conf import ENABLE_EXTRACT_UPLOADED_ARCHIVE
             return false;
             return false;
           }
           }
 
 
-          self.updateFileList(data.files, data.page, data.breadcrumbs, data.current_dir_path, data.is_sentry_managed);
+          self.updateFileList(data.files, data.page, data.breadcrumbs, data.current_dir_path, data.is_sentry_managed, data.s3_listing_not_allowed);
 
 
           if (clearAssistCache) {
           if (clearAssistCache) {
             huePubSub.publish('assist.'+self.fs()+'.refresh');
             huePubSub.publish('assist.'+self.fs()+'.refresh');
           }
           }
-
-          if ($("#hueBreadcrumbText").is(":visible")) {
-            $(".hue-breadcrumbs").show();
-            $("#hueBreadcrumbText").hide();
-            $("#editBreadcrumb").show();
+          if (data.s3_listing_not_allowed) {
+            if (!$("#hueBreadcrumbText").is(":visible")) {
+              $(".hue-breadcrumbs").hide();
+              $("#hueBreadcrumbText").show();
+              $("#editBreadcrumb").hide();
+            }
+            $("#hueBreadcrumbText").focus();
+          } else {
+            if ($("#hueBreadcrumbText").is(":visible")) {
+              $(".hue-breadcrumbs").show();
+              $("#hueBreadcrumbText").hide();
+              $("#editBreadcrumb").show();
+            }
           }
           }
+
         });
         });
       };
       };
 
 
-      self.updateFileList = function (files, page, breadcrumbs, currentDirPath, isSentryManaged) {
+      self.updateFileList = function (files, page, breadcrumbs, currentDirPath, isSentryManaged, s3_listing_not_allowed) {
         $(".tooltip").hide();
         $(".tooltip").hide();
 
 
         self.isCurrentDirSentryManaged(isSentryManaged);
         self.isCurrentDirSentryManaged(isSentryManaged);
+        self.errorMessage(s3_listing_not_allowed);
 
 
         self.page(new Page(page));
         self.page(new Page(page));
         self.files(ko.utils.arrayMap(files, function (file) {
         self.files(ko.utils.arrayMap(files, function (file) {

+ 11 - 5
apps/filebrowser/src/filebrowser/views.py

@@ -49,7 +49,7 @@ from django.utils.http import http_date
 from django.utils.html import escape
 from django.utils.html import escape
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 
 
-from aws.s3.s3fs import S3FileSystemException
+from aws.s3.s3fs import S3FileSystemException, S3ListAllBucketsException
 from avro import datafile, io
 from avro import datafile, io
 from desktop import appmanager
 from desktop import appmanager
 from desktop.lib import i18n
 from desktop.lib import i18n
@@ -461,11 +461,16 @@ def listdir_paged(request, path):
     else:
     else:
       home_dir_path = None
       home_dir_path = None
     breadcrumbs = parse_breadcrumbs(path)
     breadcrumbs = parse_breadcrumbs(path)
+    s3_listing_not_allowed = ''
 
 
-    if do_as:
-      all_stats = request.fs.do_as_user(do_as, request.fs.listdir_stats, path)
-    else:
-      all_stats = request.fs.listdir_stats(path)
+    try:
+      if do_as:
+        all_stats = request.fs.do_as_user(do_as, request.fs.listdir_stats, path)
+      else:
+        all_stats = request.fs.listdir_stats(path)
+    except S3ListAllBucketsException as e:
+      s3_listing_not_allowed = e.message
+      all_stats = []
 
 
 
 
     # Filter first
     # Filter first
@@ -546,6 +551,7 @@ def listdir_paged(request, path):
         'show_download_button': SHOW_DOWNLOAD_BUTTON.get(),
         'show_download_button': SHOW_DOWNLOAD_BUTTON.get(),
         'show_upload_button': SHOW_UPLOAD_BUTTON.get(),
         'show_upload_button': SHOW_UPLOAD_BUTTON.get(),
         'is_embeddable': request.GET.get('is_embeddable', False),
         'is_embeddable': request.GET.get('is_embeddable', False),
+        's3_listing_not_allowed': s3_listing_not_allowed
     }
     }
     return render('listdir.mako', request, data)
     return render('listdir.mako', request, data)
 
 

+ 13 - 0
desktop/core/src/desktop/js/jquery/plugins/jquery.filechooser.js

@@ -574,6 +574,19 @@ Plugin.prototype.navigateTo = function(path) {
         }
         }
       }, 100);
       }, 100);
 
 
+      if (data.s3_listing_not_allowed) {
+        $("<div class='clearfix'>").appendTo($(_parent.element).find('.filechooser-tree'));
+        const _errorMsg = $('<div>')
+          .addClass('alert')
+          .addClass('alert-warn')
+          .text(data.s3_listing_not_allowed);
+        _errorMsg.appendTo($(_parent.element).find('.filechooser-tree'));
+
+        $scrollingBreadcrumbs.hide();
+        $hdfsAutocomplete.show();
+        $hdfsAutocomplete.focus();
+      }
+
       $(data.files).each((cnt, file) => {
       $(data.files).each((cnt, file) => {
         let _addFile = file.name !== '.';
         let _addFile = file.name !== '.';
         if (_parent.options.filterExtensions != '' && file.type == 'file') {
         if (_parent.options.filterExtensions != '' && file.type == 'file') {

+ 2 - 0
desktop/core/src/desktop/js/ko/components/assist/assistStorageEntry.js

@@ -154,6 +154,8 @@ class AssistStorageEntry {
         );
         );
         self.loaded = true;
         self.loaded = true;
         self.loading(false);
         self.loading(false);
+        self.hasErrors(!!data.s3_listing_not_allowed); // Special case where we want errors inline instead of the default popover. We don't want errorCallback handling
+        self.errorText(data.s3_listing_not_allowed);
         if (callback) {
         if (callback) {
           callback();
           callback();
         }
         }

+ 4 - 3
desktop/core/src/desktop/js/ko/components/assist/ko.assistStoragePanel.js

@@ -164,9 +164,10 @@ const TEMPLATE = `
       <!-- /ko -->
       <!-- /ko -->
     </div>
     </div>
     <!-- ko hueSpinner: { spin: loading, center: true, size: 'large' } --><!-- /ko -->
     <!-- ko hueSpinner: { spin: loading, center: true, size: 'large' } --><!-- /ko -->
-    <div class="assist-errors" data-bind="visible: ! loading() && hasErrors()">
-      <span>${I18n('Error loading contents.')}</span>
-    </div>
+    <span class="assist-errors" data-bind="visible: ! loading() && hasErrors(), text: errorText() || '${I18n(
+      'Error loading contents.'
+    )}'">
+    </span>
   </div>
   </div>
   <!-- /ko -->
   <!-- /ko -->
   <!-- /ko -->
   <!-- /ko -->

文件差异内容过多而无法显示
+ 0 - 0
desktop/core/src/desktop/static/desktop/css/hue.css


文件差异内容过多而无法显示
+ 0 - 0
desktop/core/src/desktop/static/desktop/css/hue3-extra.css


+ 0 - 1
desktop/core/src/desktop/static/desktop/js/document/hueFileEntry.js

@@ -206,7 +206,6 @@ var HueFileEntry = (function () {
     self.loaded = ko.observable(false);
     self.loaded = ko.observable(false);
     self.loading = ko.observable(false);
     self.loading = ko.observable(false);
     self.hasErrors = ko.observable(false);
     self.hasErrors = ko.observable(false);
-
     self.uploading = ko.observable(false);
     self.uploading = ko.observable(false);
     self.uploadComplete = ko.observable(false);
     self.uploadComplete = ko.observable(false);
     self.uploadFailed = ko.observable(false);
     self.uploadFailed = ko.observable(false);

+ 1 - 0
desktop/core/src/desktop/static/desktop/less/hue-assist.less

@@ -283,6 +283,7 @@
 .assist-errors {
 .assist-errors {
   padding: 4px 5px;
   padding: 4px 5px;
   font-style: italic;
   font-style: italic;
+  white-space: normal;
 }
 }
 
 
 .assist-tables > li {
 .assist-tables > li {

+ 9 - 4
desktop/libs/aws/src/aws/s3/s3fs.py

@@ -46,10 +46,12 @@ LOG = logging.getLogger(__name__)
 
 
 
 
 class S3FileSystemException(IOError):
 class S3FileSystemException(IOError):
-
   def __init__(self, *args, **kwargs):
   def __init__(self, *args, **kwargs):
     super(S3FileSystemException, self).__init__(*args, **kwargs)
     super(S3FileSystemException, self).__init__(*args, **kwargs)
 
 
+class S3ListAllBucketsException(S3FileSystemException):
+  def __init__(self, *args, **kwargs):
+    super(S3FileSystemException, self).__init__(*args, **kwargs)
 
 
 def auth_error_handler(view_fn):
 def auth_error_handler(view_fn):
   def decorator(*args, **kwargs):
   def decorator(*args, **kwargs):
@@ -203,8 +205,8 @@ class S3FileSystem(object):
 
 
   @staticmethod
   @staticmethod
   def isroot(path):
   def isroot(path):
-    parsed = urlparse(path) 
-    return parsed.path == '/' or parsed.path == ''
+    parsed = urlparse(path)
+    return (parsed.path == '/' or parsed.path == '') and parsed.netloc == ''
 
 
   @staticmethod
   @staticmethod
   def join(*comp_list):
   def join(*comp_list):
@@ -280,7 +282,10 @@ class S3FileSystem(object):
       except S3FileSystemException as e:
       except S3FileSystemException as e:
         raise e
         raise e
       except S3ResponseError as e:
       except S3ResponseError as e:
-        raise S3FileSystemException(_('Failed to retrieve buckets: %s') % e.reason)
+        if 'Forbidden' in str(e) or (hasattr(e, 'status') and e.status == 403):
+          raise S3ListAllBucketsException(_('You do not have permissions to list all buckets. Please specify a bucket name you have access to.'))
+        else:
+          raise S3FileSystemException(_('Failed to retrieve buckets: %s') % e.reason)
       except Exception as e:
       except Exception as e:
         raise S3FileSystemException(_('Failed to retrieve buckets: %s') % e)
         raise S3FileSystemException(_('Failed to retrieve buckets: %s') % e)
 
 

部分文件因为文件数量过多而无法显示