hdfs.mako 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. ## Licensed to Cloudera, Inc. under one
  2. ## or more contributor license agreements. See the NOTICE file
  3. ## distributed with this work for additional information
  4. ## regarding copyright ownership. Cloudera, Inc. licenses this file
  5. ## to you under the Apache License, Version 2.0 (the
  6. ## "License"); you may not use this file except in compliance
  7. ## with the License. You may obtain a copy of the License at
  8. ##
  9. ## http://www.apache.org/licenses/LICENSE-2.0
  10. ##
  11. ## Unless required by applicable law or agreed to in writing, software
  12. ## distributed under the License is distributed on an "AS IS" BASIS,
  13. ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. ## See the License for the specific language governing permissions and
  15. ## limitations under the License.
  16. <%!
  17. from desktop.views import commonheader, commonfooter, _ko
  18. from django.utils.translation import ugettext as _
  19. %>
  20. <%namespace name="layout" file="layout.mako" />
  21. <%namespace name="tree" file="common_tree.mako" />
  22. %if not is_embeddable:
  23. ${ commonheader(_('Hadoop Security'), "security", user, request) | n,unicode }
  24. %endif
  25. ${ layout.menubar(section='hdfs', is_embeddable=is_embeddable) }
  26. <span id="securityHdfsComponents" class="security-components">
  27. <script type="text/html" id="aclDisplay">
  28. <div data-bind="visible: status() != 'deleted'">
  29. <span data-bind="text: $root.printAcl($data)"></span>
  30. </div>
  31. </script>
  32. <script type="text/html" id="aclEdit">
  33. <div data-bind="visible: status() != 'deleted'" class="acl-block">
  34. <a class="pointer pull-right" style="margin-right: 4px" data-bind="click: $root.assist.removeAcl">
  35. <i class="fa fa-times"></i>
  36. </a>
  37. <label class="radio inline-block">
  38. <input type="radio" value="group" data-bind="checked: type, attr: { name: 'aclType' + $index() + (isDefault() ? 'isDefault' : 'notDefault') }"/> ${ _('group') }
  39. </label>
  40. <label class="radio inline-block">
  41. <input type="radio" value="user" data-bind="checked: type, attr: { name: 'aclType' + $index() + (isDefault() ? 'isDefault' : 'notDefault') }"/> ${ _('user') }
  42. </label>
  43. <label class="radio inline-block">
  44. <input type="radio" value="mask" data-bind="checked: type, attr: { name: 'aclType' + $index() + (isDefault() ? 'isDefault' : 'notDefault') }"/> ${ _('mask') }
  45. </label>
  46. <label class="radio inline-block">
  47. <input type="radio" value="other" data-bind="checked: type, attr: { name: 'aclType' + $index() + (isDefault() ? 'isDefault' : 'notDefault') }"/> ${ _('other') }
  48. </label>
  49. <div style="margin-left: 6px">
  50. <div data-bind="visible: type() == 'user'">
  51. <select class="user-list-acl" data-bind="options: $root.selectableHadoopUsers, select2: { dropdownAutoWidth: true, placeholder: '${ _ko("Select a user") }', update: name, type: 'user', vm: $root}" style="width: 200px"></select>
  52. </div>
  53. <div data-bind="visible: type() == 'group'">
  54. <select class="group-list-acl" data-bind="options: $root.selectableHadoopGroups, select2: { dropdownAutoWidth: true, placeholder: '${ _ko("Select a group") }', update: name, type: 'group', vm: $root}" style="width: 200px"></select>
  55. </div>
  56. <input type="text" data-bind="value: name, valueUpdate: 'afterkeydown', visible: type() == 'mask' || type() == 'other'" placeholder="${ _('name ...') }" style="width: 180px; margin-bottom: 0px; height: 26px; min-height: 26px"/>
  57. </div>
  58. <br/>
  59. <label class="checkbox inline-block">
  60. <input type="checkbox" data-bind="checked: r"/>
  61. ${ _('Read') } <span class="muted">(r)</span>
  62. </label>
  63. <label class="checkbox inline-block">
  64. <input type="checkbox" data-bind="checked: w"/>
  65. ${ _('Write') } <span class="muted">(w)</span>
  66. </label>
  67. <label class="checkbox inline-block">
  68. <input type="checkbox" data-bind="checked: x"/>
  69. ${ _('Execute') } <span class="muted">(x)</span>
  70. </label>
  71. </div>
  72. </script>
  73. <div class="container-fluid">
  74. <div class="row-fluid">
  75. <div class="span12">
  76. <div class="card card-small">
  77. <h1 class="card-heading simple">
  78. ${ _('File ACLs') }
  79. </h1>
  80. <div class="card-body">
  81. <div class="row-fluid" data-bind="visible: $root.doAs() != '${ user.username }' && ! $root.assist.isDiffMode()">
  82. <div class="span12">
  83. <div class="alert"><i class="fa fa-warning"></i> ${ _('You are currently impersonating the user') } <strong data-bind="text: $root.doAs"></strong></div>
  84. </div>
  85. </div>
  86. <div class="row-fluid">
  87. <div class="span8">
  88. <div class="path-container">
  89. <div class="input-append span12">
  90. <input id="path" class="path" type="text" data-bind="value: $root.assist.path" autocomplete="off" />
  91. <a data-bind="hueLink: '/filebrowser/view=' + $root.assist.path(), attr: {target: IS_HUE_4 ? 'self' : 'blank'}" title="${ _('Open in File Browser') }" class="btn btn-inverse">
  92. <i class="fa fa-external-link"></i>
  93. </a>
  94. </div>
  95. <div class="clearfix"></div>
  96. <div class="tree-toolbar">
  97. <div class="pull-right">
  98. % if has_impersonation_perm:
  99. <div class="dropdown inline-block" style="margin-right: 6px">
  100. <a class="dropdown-toggle" data-toggle="dropdown" href="#"><i class="fa fa-eye-slash" data-bind="visible: $root.assist.isDiffMode"></i><i class="fa fa-eye" data-bind="visible: ! $root.assist.isDiffMode()"></i> <span data-bind="visible: $root.assist.isDiffMode">${ _('Show non accessible files for') }</span><span data-bind="visible: ! $root.assist.isDiffMode()">${ _('Impersonate the user') }</span></a>
  101. <ul class="dropdown-menu">
  102. <li data-bind="visible: ! $root.assist.isDiffMode(), click: function() { $root.assist.isDiffMode(true); }"><a tabindex="-1" href="#">${ _('Show non accessible files') }</a></li>
  103. <li data-bind="visible: $root.assist.isDiffMode(), click: function() { $root.assist.isDiffMode(false); }"><a tabindex="-1" href="#">${ _('Impersonate the user') }</a></li>
  104. </ul>
  105. </div>
  106. <select class="user-list" data-bind="options: $root.selectableHadoopUsers, select2: { dropdownAutoWidth: true, placeholder: '${ _ko("Select a user") }', update: $root.doAs, type: 'user', vm: $root}" style="width: 120px"></select>
  107. % endif
  108. </div>
  109. <div>
  110. <a class="pointer" data-bind="click: $root.assist.collapseOthers" rel="tooltip" data-placement="right" title="${_('Close other nodes')}">
  111. <i class="fa fa-compress"></i>
  112. </a>
  113. &nbsp;
  114. <a class="pointer" data-bind="click: $root.assist.refreshTree" rel="tooltip" data-placement="right" title="${_('Refresh the tree')}">
  115. <i class="fa fa-refresh"></i>
  116. </a>
  117. &nbsp;
  118. <a class="pointer" data-bind="visible: $root.assist.checkedItems().length > 0, click: function(){ $('#bulkActionsModal').modal('show'); }" rel="tooltip" data-placement="right" title="${ _('Add, replace or remove ACLs for the checked paths') }">
  119. <i class="fa fa-copy"></i>
  120. </a>
  121. &nbsp;
  122. <i class="fa fa-spinner fa-spin" data-bind="visible: $root.assist.isLoadingTree()"></i>
  123. </div>
  124. </div>
  125. </div>
  126. ${ tree.render(id='expandableTree', data='$root.assist.treeData', afterRender='$root.assist.afterRender', component='hdfs') }
  127. </div>
  128. <div class="span4">
  129. <div class="acl-panel" data-bind="visible: ! $root.assist.isLoadingAcls()">
  130. <ul class="nav nav-tabs">
  131. <li data-bind="css: {'active': ! $root.assist.showAclsAsText()}"><a class="pointer" data-bind="click: function() { $root.assist.showAclsAsText(false); }"><i class="fa fa-pencil"></i> ${ _('Edit') }</a></li>
  132. <li data-bind="css: {'active': $root.assist.showAclsAsText()}"><a class="pointer" data-bind="click: function() { $root.assist.showAclsAsText(true); }"><i class="fa fa-header"></i> ${ _('View as text') }</a></li>
  133. </ul>
  134. <div class="acl-panel-content">
  135. <span class="fake-pre" data-bind="visible: $root.assist.showAclsAsText">
  136. # file: <a class="force-word-break" data-bind="hueLink: '/filebrowser/view=' + $root.assist.path(), text: $root.assist.path()"></a><br/>
  137. # owner: <span data-bind="text: $root.assist.owner"></span><br/>
  138. # group: <span data-bind="text: $root.assist.group"></span><br/>
  139. <div data-bind="foreach: $root.assist.regularAcls">
  140. <div data-bind="template: {name: 'aclDisplay'}"></div>
  141. </div>
  142. <div data-bind="foreach: $root.assist.defaultAcls">
  143. <div data-bind="template: {name: 'aclDisplay'}"></div>
  144. </div>
  145. </span>
  146. <span data-bind="visible: ! $root.assist.showAclsAsText()">
  147. <h4>${ _('Path') }</h4>
  148. <a class="force-word-break" data-bind="hueLink: '/filebrowser/view=' + $root.assist.path(), text: $root.assist.path(), attr: {target: IS_HUE_4 ? 'self' : 'blank'}" title="${ _('Open in File Browser') }" rel="tooltip"></a>
  149. <h4>${ _('User/Group') }</h4>
  150. <i class="fa fa-user" style="color: #999999" title="${_('User')}"></i> <span title="${_('User')}" data-bind="text: $root.assist.owner"></span>&nbsp;
  151. <i class="fa fa-users" style="color: #999999" title="${_('Group')}"></i> <span title="${_('Group')}" data-bind="text: $root.assist.group"></span>
  152. <h4>${ _('ACLs') }</h4>
  153. <div data-bind="foreach: $root.assist.regularAcls">
  154. <div data-bind="template: {name: 'aclEdit'}"></div>
  155. </div>
  156. <div class="acl-block acl-actions">
  157. <span class="pointer" data-bind="click: $root.assist.addAcl"><i class="fa fa-plus"></i></span>
  158. <span class="pointer" data-bind="visible: $root.assist.changedRegularAcls().length, click: $root.assist.getAcls"> &nbsp; <i class="fa fa-undo"></i></span>
  159. <span class="pointer" data-bind="visible: $root.assist.changedRegularAcls().length, click: $root.assist.updateAcls"> &nbsp; <i class="fa fa-save"></i></span>
  160. </div>
  161. <div data-bind="visible: $root.assist.pathType() == 'dir'">
  162. <h4>${ _('Default ACLs') }</h4>
  163. <div data-bind="foreach: $root.assist.defaultAcls">
  164. <div data-bind="template: {name: 'aclEdit'}"></div>
  165. </div>
  166. <div class="acl-block acl-actions">
  167. <span class="pointer" data-bind="click: $root.assist.addDefaultAcl"><i class="fa fa-plus"></i></span>
  168. <span class="pointer" data-bind="visible: $root.assist.defaultAcls().length, click: $root.assist.getAcls"> &nbsp; <i class="fa fa-undo"></i></span>
  169. <span class="pointer" data-bind="visible: $root.assist.defaultAcls().length, click: $root.assist.updateAcls"> &nbsp; <i class="fa fa-save"></i></span>
  170. </div>
  171. </div>
  172. </span>
  173. </div>
  174. </div>
  175. <div class="loading-popover center" data-bind="visible: $root.assist.isLoadingAcls()"><i class="fa fa-spinner fa-spin fa-5x muted"></i></div>
  176. </div>
  177. </div>
  178. </div>
  179. </div>
  180. </div>
  181. </div>
  182. </div>
  183. <div id="bulkActionsModal" class="modal hide fade in" role="dialog">
  184. <div class="modal-header">
  185. <button type="button" class="close" data-dismiss="modal" aria-label="${ _('Close') }"><span aria-hidden="true">&times;</span></button>
  186. <h2 class="modal-title">${_('Select one operation')}</h2>
  187. </div>
  188. <div class="modal-body" style="overflow-x: hidden">
  189. <div class="row-fluid">
  190. <div class="span8">
  191. <div class="row-fluid">
  192. <div class="span4 center">
  193. <div class="big-btn" data-bind="css: {'selected': $root.assist.bulkAction() == 'add'}, click: function(){$root.assist.bulkAction('add')}">
  194. <i class="fa fa-plus"></i><br/><br/>
  195. <span>${ _('Add current ACLs to selection') }</span>
  196. </div>
  197. </div>
  198. <div class="span4 center">
  199. <div class="big-btn" data-bind="css: {'selected': $root.assist.bulkAction() == 'sync'}, click: function(){$root.assist.bulkAction('sync')}">
  200. <i class="fa fa-random"></i><br/><br/>
  201. <span>${ _('Replace selection with current ACLs') }</span>
  202. </div>
  203. </div>
  204. <div class="span4 center">
  205. <div class="big-btn" data-bind="css: {'selected': $root.assist.bulkAction() == 'delete'}, click: function(){$root.assist.bulkAction('delete')}">
  206. <i class="fa fa-eraser"></i><br/><br/>
  207. <span>${ _('Remove all ACLs of selection') }</span>
  208. </div>
  209. </div>
  210. </div>
  211. </div>
  212. <div class="span4">
  213. <h4>${ _('to apply to the selection') }</h4>
  214. <ul class="unstyled modal-panel" data-bind="foreach: $root.assist.checkedItems">
  215. <li><a class="force-word-break" data-bind="hueLink: '/filebrowser/view=' + path, text: path, attr: {target: IS_HUE_4 ? 'self' : 'blank'}" title="${ _('Open in File Browser') }" rel="tooltip"></a></li>
  216. </ul>
  217. </div>
  218. </div>
  219. <div class="row-fluid" data-bind="visible: $root.assist.bulkAction() != '' && $root.assist.bulkAction() != 'delete'">
  220. <div class="span12">
  221. <h4>${ _('ACLs to apply') }</h4>
  222. <span class="fake-pre modal-panel">
  223. # file: <a class="force-word-break" data-bind="hueLink: '/filebrowser/view=' + $root.assist.path(), text: $root.assist.path()"></a><br/>
  224. # owner: <span data-bind="text: $root.assist.owner"></span><br/>
  225. # group: <span data-bind="text: $root.assist.group"></span><br/>
  226. <div data-bind="foreach: $root.assist.regularAcls">
  227. <div data-bind="template: {name: 'aclDisplay'}"></div>
  228. </div>
  229. <div data-bind="foreach: $root.assist.defaultAcls">
  230. <div data-bind="template: {name: 'aclDisplay'}"></div>
  231. </div>
  232. </span>
  233. </div>
  234. </div>
  235. </div>
  236. <div class="modal-footer">
  237. <label class="checkbox pull-left"><input type="checkbox" data-bind="checked: $root.assist.recursive"> ${ _('Apply recursively to all subfolders and files') }</label>
  238. <button class="btn" data-dismiss="modal" aria-hidden="true">${ _('Cancel') }</button>
  239. <button class="btn" data-bind="enable: $root.assist.bulkAction(), css: {'btn-primary': $root.assist.bulkAction() != 'delete', 'btn-danger': $root.assist.bulkAction() == 'delete'}, click: $root.assist.bulkPerfomAction">${ _('Confirm') }</button>
  240. </div>
  241. </div>
  242. <%def name="treeIcons()">
  243. 'fa-folder-open-o': isDir() && (nodes().length > 0 || isLoaded()) && !aclBit(),
  244. 'fa-folder-open': isDir() && (nodes().length > 0 || isLoaded()) && aclBit(),
  245. 'fa-folder-o': isDir() && nodes().length == 0 && !aclBit(),
  246. 'fa-folder': isDir() && nodes().length == 0 && aclBit(),
  247. 'fa-file-o': !isDir() && !aclBit(),
  248. 'fa-file': !isDir() && aclBit(),
  249. 'striked': striked()
  250. </%def>
  251. <%def name="aclBitPullRight()">
  252. <div class="pull-right rwx" data-bind="style: { color: aclBit() ? '#0B7FAD': '#999999'}">
  253. <span data-bind="text: rwx"></span>
  254. </div>
  255. <div class="pull-right">
  256. <i class="fa fa-shield" data-bind="visible: aclBit()" style="color: #0B7FAD" title="${ _('Has some ACLs') }"></i>
  257. </div>
  258. </%def>
  259. ${ tree.import_templates(itemClick='$root.assist.setPath', iconClick='$root.assist.togglePath', itemSelected='$root.assist.path() == path()', iconModifier=treeIcons, styleModifier='aclBit', styleModifierPullRight=aclBitPullRight, anchorProperty='path', showMore='$root.assist.loadMore', strikedProperty='striked', itemChecked='isChecked', component='hdfs') }
  260. <script src="${ static('security/js/hdfs.ko.js') }" type="text/javascript" charset="utf-8"></script>
  261. <script type="text/javascript">
  262. (function () {
  263. var viewModel = new HdfsViewModel(${ initial | n,unicode });
  264. ko.cleanNode($('#securityHdfsComponents')[0]);
  265. ko.applyBindings(viewModel, $('#securityHdfsComponents')[0]);
  266. $(document).ready(function () {
  267. $(document).on("loadedUsers", function(){
  268. $(".user-list").select2("val", viewModel.doAs());
  269. });
  270. var _initialPath = "/";
  271. if (window.location.hash != "") {
  272. _initialPath = window.location.hash.substr(1).replace(/(<([^>]+)>)/ig, "");
  273. }
  274. viewModel.init(_initialPath);
  275. $("#path").jHueHdfsAutocomplete({
  276. home: viewModel.assist.path(),
  277. skipKeydownEvents: true,
  278. onPathChange: function (path) {
  279. viewModel.assist.path(path);
  280. },
  281. onEnter: function (el) {
  282. viewModel.assist.path(el.val());
  283. },
  284. smartTooltip: "${_('Did you know? You can use the tab key or CTRL + Space to autocomplete file and folder names')}"
  285. });
  286. function resizeComponents () {
  287. $("#path").width($(".tree-toolbar").width() - 64);
  288. $("#expandableTree").height($(window).height() - 260);
  289. $(".acl-panel-content").height($(window).height() - 260);
  290. }
  291. resizeComponents();
  292. $(document).on("renderedTree", function() {
  293. var _path = viewModel.assist.path();
  294. if (_path[_path.length-1] == "/"){
  295. _path = _path.substr(0, _path.length - 1);
  296. }
  297. window.setTimeout(function(){
  298. if ($("a.anchor[href^='"+_path+"']").length > 0){
  299. $("#expandableTree").animate({
  300. scrollTop: ($("a.anchor[href^='"+_path+"']:first").position().top + $("#expandableTree").scrollTop() - $("#expandableTree").position().top - 4)+"px"
  301. }, 200);
  302. }
  303. }, 200)
  304. });
  305. $(document).on("updatedAcls", function() {
  306. $(document).trigger("info", "${ _('The selected ACLs have been successfully updated.') }");
  307. });
  308. $(document).on("addedBulkAcls", function() {
  309. $(document).trigger("info", "${ _('The current ACLs have been successfully added to the checked paths.') }");
  310. $("#bulkActionsModal").modal("hide");
  311. });
  312. $(document).on("deletedBulkAcls", function() {
  313. $(document).trigger("info", "${ _('All the ACLs have been successfully removed from the checked paths.') }");
  314. $("#bulkActionsModal").modal("hide");
  315. });
  316. $(document).on("syncdBulkAcls", function() {
  317. $(document).trigger("info", "${ _('All the ACLs for the checked items have been replaced with the current selection.') }");
  318. $("#bulkActionsModal").modal("hide");
  319. });
  320. var _resizeTimeout = -1;
  321. $(window).resize(function(){
  322. window.clearTimeout(_resizeTimeout);
  323. _resizeTimeout = window.setTimeout(resizeComponents, 100);
  324. });
  325. window.onhashchange = function() {
  326. if (window.location.pathname.indexOf('/security/hdfs') > -1) {
  327. viewModel.assist.path(window.location.hash.substr(1));
  328. }
  329. };
  330. $("#bulkActionsModal").modal({
  331. show: false
  332. });
  333. huePubSub.subscribe('app.gained.focus', function (app) {
  334. if (app === 'security_hdfs') {
  335. window.setTimeout(function () {
  336. window.location.hash = viewModel.lastHash;
  337. }, 0);
  338. }
  339. }, 'security_hdfs');
  340. });
  341. })();
  342. </script>
  343. </span>
  344. %if not is_embeddable:
  345. ${ commonfooter(request, messages) | n,unicode }
  346. %endif