Bläddra i källkod

[security] List existing HDFS ACLs

Romain Rigaux 11 år sedan
förälder
incheckning
b24b35d

+ 26 - 0
apps/security/src/security/api.py

@@ -0,0 +1,26 @@
+#!/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.
+
+import json
+
+from django.http import HttpResponse
+
+
+def get_acls(request):  
+  path = request.GET.get('path')
+  acls = request.fs.get_acl_status(path)
+  return HttpResponse(json.dumps(acls['AclStatus']), mimetype="application/json")

+ 58 - 29
apps/security/src/security/templates/hdfs.mako

@@ -31,47 +31,76 @@ ${ layout.menubar(section='hdfs') }
     <div class="span2">
       <div class="sidebar-nav">
         <ul class="nav nav-list">
-          <li class="nav-header">${ _('Properties') }</li>
-          <li class="active"><a href="#properties"><i class="fa fa-eye"></i> ${ _('View') }</a></li>
-          <li><a href="#listDataset"><i class="fa fa-cubes"></i> ${ _('ACLs') }</a></li>
+          <li class="nav-header">${ _('ACLs') }</li>
+          <li class="active"><a href="#edits"><i class="fa fa-pencil"></i> ${ _('Edit') }</a></li>
+          <li><a href="#view"><i class="fa fa-eye"></i> ${ _('View') }</a></li>
         </ul>
       </div>
     </div>
     <div class="span10">
-      <div class="card card-small">
-        <h1 class="card-heading simple">${ _('View') }</h1>
-        <div class="card-body">
-          Assist: 
-          ${ assist }        
-        <div>
-        <div class="card-body">     
-          Hadoop Groups: 
-          ${ hadoop_groups }
-        <div>
-        <div class="form-actions" id="bottom-nav">
-          <a id="backBtn" class="btn disabled">${ _('Back') }</a>
-          <a id="nextBtn" class="btn btn-primary disable-feedback">${ _('Next') }</a>
-        </div>
-
-        </div>
-
-        <div id="listDataset" class="section hide">
-          <div class="alert alert-info"><h3>${ _('Existing datasets') }</h3></div>
-        </div>
-
-        <div id="listHistory" class="section hide">
-          <div class="alert alert-info"><h3>${ _('History') }</h3></div>          
+      <div id="edit" class="section card card-small">
+        <h1 class="card-heading simple">${ _('Edit ACLs') }</h1>        
+        <div class="card-body">          
+          <div>
+            <input type="text" class="input-xxlarge" value="/tmp"/>
+            <button type="submit" class="btn btn-inverse" style="margin-left:10px">
+              ## open in FB
+              <i class="fa fa-external-link"></i>                
+            </button>
+          </div>
+          <div>
+            <div class="span8">
+              <div data-bind="foreach: viewModel.assist.files">
+                <div data-bind="text: $data"></div>
+              </div>
+            </div>
+            <div class="span4">
+              <span data-bind="text: viewModel.assist.owner"></span>
+              <span data-bind="text: viewModel.assist.group"></span>
+              <div data-bind="foreach: viewModel.assist.acls">
+                ## xeditable for edition
+                <div data-bind="text: $data"></div><i class="fa fa-minus"></i>
+              </div>
+              <i class="fa fa-plus"></i>
+              <div data-bind="visible: viewModel.assist.changed">
+                <button type="button" rel="tooltip" data-placement="bottom" data-bind="click: save, css: {'btn': true}" data-original-title="${ _('Cancel') }" class="btn">
+                  <i class="fa fa-undo"></i>
+                </button>
+                <button type="button" rel="tooltip" data-placement="bottom" data-loading-text="${ _('Saving...') }" data-bind="click: save, css: {'btn': true}" data-original-title="${ _('Save') }" class="btn">
+                  <i class="fa fa-save"></i>
+                </button>
+              <div>              
+            </div>
+          </div>
         </div>
+      </div>
 
-      </form>
+      <div id="listDataset" class="section card card-small hide">
+        <div class="alert alert-info"><h3>${ _('Existing datasets') }</h3></div>
+      </div>
 
-    </div>
+      <div id="listHistory" class="section  card card-small hide">
+        <div class="alert alert-info"><h3>${ _('History') }</h3></div>          
+      </div>
     </div>
 
   </div>
-
 </div>
 
 
+<script src="/static/ext/js/knockout-min.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/ext/js/knockout.mapping-2.3.2.js" type="text/javascript" charset="utf-8"></script>
+
+<script src="/security/static/js/hdfs.ko.js" type="text/javascript" charset="utf-8"></script>
+
+
+<script type="text/javascript" charset="utf-8">
+  var viewModel;
+  
+  $(document).ready(function () {
+    viewModel = new HdfsViewModel(${ assist | n,unicode });
+    ko.applyBindings(viewModel);
+  });
+</script>
 
 ${ commonfooter(messages) | n,unicode }

+ 5 - 0
apps/security/src/security/urls.py

@@ -23,3 +23,8 @@ urlpatterns = patterns('security.views',
   url(r'^hive$', 'hive', name='hive'),
   url(r'^hdfs$', 'hdfs', name='hdfs'),
 )
+
+
+urlpatterns += patterns('security.api',
+  url(r'^api/get_acls$', 'get_acls', name='get_acls'),
+)

+ 3 - 9
apps/security/src/security/views.py

@@ -17,7 +17,6 @@
 
 from beeswax.api import autocomplete
 from desktop.lib.django_util import render
-from filebrowser.views import view
 
 from libsentry.api import get_api
 
@@ -28,23 +27,18 @@ def get_hadoop_groups(): # Mock for now, maybe pull from LDAP
   
 def hive(request):
   assist = autocomplete(request, database=None, table=None)
-  hadoop_groups = get_hadoop_groups()
   roles = get_api(request.user).list_sentry_roles_by_group()
   
   return render("hive.mako", request, {      
       'assist': assist,
-      'hadoop_groups': hadoop_groups,
+      'hadoop_groups': get_hadoop_groups(),
       'roles': roles
   })
 
 
 def hdfs(request):
-  request.ajax = True # Hack for now
-  hadoop_groups = get_hadoop_groups()
-  assist = view(request, '/')    
-  del request.ajax
   
   return render("hdfs.mako", request, {      
-      'assist': assist,
-      'hadoop_groups': hadoop_groups,
+      'assist': {},
+      'hadoop_groups': get_hadoop_groups(),
   })

+ 75 - 0
apps/security/static/js/hdfs.ko.js

@@ -0,0 +1,75 @@
+// 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.
+
+
+var Assist = function (vm, assist) {
+  var self = this;
+
+  self.path = ko.observable('/tmp');
+  self.files = ko.observableArray();
+
+  self.acls = ko.observableArray();
+  self.owner = ko.observable('');
+  self.group = ko.observable('');
+  
+  self.changed = ko.observable(true); // mocking to true
+  
+  self.fetchPath = function () {
+    $.getJSON('/filebrowser/view' + self.path() + "?pagesize=15&format=json", function (data) { // Will create a cleaner API by calling directly directly webhdfs#LISTDIR?
+      self.files.removeAll();
+      $.each(data.files, function(index, item) {
+    	self.files.push(item.path); 
+      });
+      self.getAcls();
+    }).fail(function (xhr, textStatus, errorThrown) {
+      $(document).trigger("error", xhr.responseText);
+    });
+  };  
+  
+  self.getAcls = function () {
+    $.getJSON('/security/api/get_acls', {
+    	'path': self.path()
+      }, function (data) {
+        self.acls.removeAll();
+        $.each(data.entries, function(index, item) {
+    	  self.acls.push(item); 
+        });
+        self.acls.push("group::r-x"); // mocking
+        self.acls.push("group:execs:r--"); // mocking
+        self.owner(data.owner);
+        self.group(data.group);
+    }).fail(function (xhr, textStatus, errorThrown) {
+      $(document).trigger("error", xhr.responseText);
+    });
+  };   
+}
+
+
+var HdfsViewModel = function (context_json) {
+  var self = this;
+
+  // Models
+  self.assist = new Assist(self, context_json.assist);
+
+  self.assist.fetchPath();
+
+
+  function logGA(page) {
+    if (typeof trackOnGA == 'function') {
+      trackOnGA('security/hdfs' + page);
+    }
+  }
+};

+ 7 - 7
desktop/libs/hadoop/src/hadoop/fs/webhdfs.py

@@ -480,7 +480,7 @@ class WebHdfs(Hdfs):
     params = self._getparams()
     params['op'] = 'MODIFYACLENTRIES'
     params['aclspec'] = aclspec
-    self._invoke_with_redirect('PUT', path, params)
+    return self._root.put(path, params)
 
 
   def remove_acl_entries(self, path, aclspec):
@@ -488,36 +488,36 @@ class WebHdfs(Hdfs):
       params = self._getparams()
       params['op'] = 'REMOVEACLENTRIES'
       params['aclspec'] = aclspec
-      self._invoke_with_redirect('PUT', path, params)
+      return self._root.put(path, params)
 
 
   def remove_default_acl(self, path):
       path = Hdfs.normpath(path)
       params = self._getparams()
       params['op'] = 'REMOVEDEFAULTACL'
-      self._invoke_with_redirect('PUT', path, params)
+      return self._root.put(path, params)
 
 
   def remove_acl(self, path):
       path = Hdfs.normpath(path)
       params = self._getparams()
       params['op'] = 'REMOVEACL'
-      self._invoke_with_redirect('PUT', path, params)
+      return self._root.put(path, params)
 
 
   def set_acl(self, path, aclspec):
       path = Hdfs.normpath(path)
       params = self._getparams()
-      params['op'] = 'REMOVEACL'
+      params['op'] = 'SETACL'
       params['aclspec'] = aclspec
-      self._invoke_with_redirect('PUT', path, params)
+      return self._root.put(path, params)
 
 
   def get_acl_status(self, path):
       path = Hdfs.normpath(path)
       params = self._getparams()
       params['op'] = 'GETACLSTATUS'
-      self._invoke_with_redirect('PUT', path, params)
+      return self._root.get(path, params)
 
 
   def copyfile(self, src, dst, skip_header=False):