Prechádzať zdrojové kódy

[oozie] Check read and write permissions for workflows

Romain Rigaux 11 rokov pred
rodič
commit
beadb15a69

+ 65 - 0
apps/oozie/src/oozie/decorators.py

@@ -15,10 +15,14 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import json
 import logging
 
 from django.utils.functional import wraps
+from django.utils.translation import ugettext as _
+
 from desktop.lib.exceptions_renderable import PopupException
+from desktop.models import Document, Document2
 
 from oozie.models import Job, Node, Dataset
 
@@ -26,6 +30,67 @@ from oozie.models import Job, Node, Dataset
 LOG = logging.getLogger(__name__)
 
 
+def check_document_access_permission():
+  def inner(view_func):
+    def decorate(request, *args, **kwargs):
+      # uuid=request.GET.get('uuid')
+      
+      # workflow = json.loads(request.POST.get('workflow', '{}')) # TODO perms
+      
+      wid = {}
+      
+      try:
+        workflow_id = request.GET.get('workflow')      
+        if workflow_id:
+          if workflow_id.isdigit():
+            wid['id'] = workflow_id
+          else:
+            wid['uuid'] = workflow_id
+        elif request.GET.get('uuid'):
+          wid['uuid'] = request.GET.get('uuid')
+
+        doc2 = Document2.objects.get(type='oozie-workflow2', **wid)          
+        doc2.doc.get().can_read_or_exception(request.user)
+      except Document.DoesNotExist:
+        raise PopupException(_('Job %(id)s does not exist') % {'id': wid})
+      
+      if 'workflow' in kwargs:
+        job_type = 'workflow'
+      elif 'coordinator' in kwargs:
+        job_type = 'coordinator'
+      else:
+        job_type = 'bundle'
+
+      return view_func(request, *args, **kwargs)
+    return wraps(view_func)(decorate)
+  return inner
+
+
+def check_document_modify_permission():
+  def inner(view_func):
+    def decorate(request, *args, **kwargs):
+      wid = None            
+      
+      workflow = json.loads(request.POST.get('workflow', '{}'))
+
+      if workflow.get('id'):
+        wid = workflow.get('id')
+      
+      try:
+        doc2 = Document2.objects.get(id=workflow['id'])
+        doc2.doc.get().can_write_or_exception(request.user)
+      except Document.DoesNotExist:
+        raise PopupException(_('Job %(id)s does not exist') % {'id': wid})
+
+      return view_func(request, *args, **kwargs)
+    return wraps(view_func)(decorate)
+  return inner
+
+
+
+## Oozie v1 below
+
+
 def check_job_access_permission(exception_class=PopupException):
   """
   Decorator ensuring that the user has access to the workflow or coordinator.

+ 5 - 5
apps/oozie/src/oozie/templates/editor/workflow_editor.mako

@@ -146,13 +146,13 @@ ${ commonheader(_("Workflow Editor"), "Oozie", user, "40px") | n,unicode }
     <a title="${ _('Submit') }" rel="tooltip" data-placement="bottom" data-bind="click: showSubmitPopup, css: {'btn': true}, visible: workflow.id() != null">
       <i class="fa fa-fw fa-play"></i>
     </a>
-    <a title="${ _('Edit') }" rel="tooltip" data-placement="bottom" data-bind="click: toggleEditing, css: {'btn': true, 'btn-inverse': isEditing}">
+    <a title="${ _('Edit') }" rel="tooltip" data-placement="bottom" data-bind="click: toggleEditing, css: {'btn': true, 'btn-inverse': isEditing}, visible: canEdit">
       <i class="fa fa-fw fa-pencil"></i>
     </a>
 
     &nbsp;&nbsp;&nbsp;
 
-    <button type="button" title="${ _('Settings') }" rel="tooltip" data-placement="bottom" data-toggle="modal" data-target="#settingsModal" data-bind="css: {'btn': true}">
+    <button type="button" title="${ _('Settings') }" rel="tooltip" data-placement="bottom" data-toggle="modal" data-target="#settingsModal" data-bind="css: {'btn': true}, visible: canEdit">
       <i class="fa fa-fw fa-cog"></i>
     </button>
 
@@ -164,14 +164,14 @@ ${ commonheader(_("Workflow Editor"), "Oozie", user, "40px") | n,unicode }
 
     &nbsp;&nbsp;&nbsp;
 
-    <button type="button" title="${ _('Save') }" rel="tooltip" data-placement="bottom" data-loading-text="${ _("Saving...") }" data-bind="click: $root.save, css: {'btn': true}">
+    <button type="button" title="${ _('Save') }" rel="tooltip" data-placement="bottom" data-loading-text="${ _("Saving...") }" data-bind="click: $root.save, css: {'btn': true}, visible: canEdit">
       <i class="fa fa-fw fa-save"></i>
     </button>
 
     <a class="share-link btn" rel="tooltip" data-placement="bottom" data-bind="click: openShareModal,
         attr: {'data-original-title': '${ _("Share") } ' + name},
         css: {'isShared': isShared(), 'btn': true},
-        visible: workflow.id() != null">
+        visible: workflow.id() != null, visible: canEdit">
       <i class="fa fa-users"></i>
     </a>
 
@@ -1789,7 +1789,7 @@ ${ dashboard.import_bindings() }
 <script type="text/javascript">
   ${ utils.slaGlobal() }
 
-  var viewModel = new WorkflowEditorViewModel(${ layout_json | n,unicode }, ${ workflow_json | n,unicode }, ${ credentials_json | n,unicode }, ${ workflow_properties_json | n,unicode }, ${ subworkflows_json | n,unicode });
+  var viewModel = new WorkflowEditorViewModel(${ layout_json | n,unicode }, ${ workflow_json | n,unicode }, ${ credentials_json | n,unicode }, ${ workflow_properties_json | n,unicode }, ${ subworkflows_json | n,unicode }, ${ can_edit_job_json | n,unicode }); 
   ko.applyBindings(viewModel, $("#editor")[0]);
 
   var shareViewModel = setupSharing("#documentShareModal");

+ 18 - 13
apps/oozie/src/oozie/views/editor2.py

@@ -34,6 +34,7 @@ from liboozie.credentials import Credentials
 from liboozie.oozie_api import get_oozie
 from liboozie.submission2 import Submission
 
+from oozie.decorators import check_document_access_permission, check_document_modify_permission
 from oozie.forms import ParameterForm
 from oozie.models2 import Node, Workflow, Coordinator, Bundle, NODES, WORKFLOW_NODE_PROPERTIES, import_workflows_from_hue_3_7,\
     find_dollar_variables, find_dollar_braced_variables
@@ -43,16 +44,16 @@ LOG = logging.getLogger(__name__)
 
 
 
-def list_editor_workflows(request):
-  workflows = Document2.objects.filter(type='oozie-workflow2', owner=request.user)
+def list_editor_workflows(request):  
+  workflows = [d.content_object for d in Document.objects.get_docs(request.user, Document2, extra='workflow2')]
 
   return render('editor/list_editor_workflows.mako', request, {
       'workflows': workflows
   })
 
 
+@check_document_access_permission()
 def edit_workflow(request):
-
   workflow_id = request.GET.get('workflow')
   
   if workflow_id:
@@ -60,9 +61,9 @@ def edit_workflow(request):
     if workflow_id.isdigit():
       wid['id'] = workflow_id
     else:
-      wid['uuid'] =workflow_id
+      wid['uuid'] = workflow_id
     doc = Document2.objects.get(type='oozie-workflow2', **wid)
-    workflow = Workflow(document=doc) # Todo perms
+    workflow = Workflow(document=doc)
   else:
     doc = None
     workflow = Workflow()
@@ -85,7 +86,8 @@ def edit_workflow(request):
       'credentials_json': json.dumps(credentials.credentials.keys()),
       'workflow_properties_json': json.dumps(WORKFLOW_NODE_PROPERTIES),
       'doc1_id': doc.doc.get().id if doc else -1,
-      'subworkflows_json': json.dumps(_get_workflows(request.user)) # if has sub?
+      'subworkflows_json': json.dumps(_get_workflows(request.user)), # if has sub?
+      'can_edit_job_json': json.dumps(doc is None or doc.doc.get().is_editable(request.user))
   })
 
 
@@ -93,6 +95,7 @@ def new_workflow(request):
   return edit_workflow(request)
 
 
+@check_document_modify_permission()
 def save_workflow(request):
   response = {'status': -1}
 
@@ -128,7 +131,6 @@ def save_workflow(request):
 def new_node(request):
   response = {'status': -1}
 
-  workflow = json.loads(request.POST.get('workflow', '{}')) # TODO perms
   node = json.loads(request.POST.get('node', '{}'))
 
   properties = NODES[node['widgetType']].get_mandatory_fields()
@@ -150,14 +152,13 @@ def _get_workflows(user):
         'owner': workflow.owner.username,
         'value': workflow.uuid,
         'id': workflow.id
-      } for workflow in Document2.objects.filter(type='oozie-workflow2', owner=user)
+      } for workflow in [d.content_object for d in Document.objects.get_docs(user, Document2, extra='workflow2')]
     ]  
 
 
 def add_node(request):
   response = {'status': -1}
 
-  workflow = json.loads(request.POST.get('workflow', '{}')) # TODO perms
   node = json.loads(request.POST.get('node', '{}'))
   properties = json.loads(request.POST.get('properties', '{}'))
   copied_properties = json.loads(request.POST.get('copiedProperties', '{}'))
@@ -189,7 +190,7 @@ def action_parameters(request):
       script_path = script_path.replace('hdfs://', '')
 
       if request.fs.do_as_user(request.user, request.fs.exists, script_path):
-        data =  request.fs.do_as_user(request.user, request.fs.read, script_path, 0, 16 * 1024 ** 2)  
+        data = request.fs.do_as_user(request.user, request.fs.read, script_path, 0, 16 * 1024 ** 2)  
 
         if node_data['type'] in ('hive', 'hive2'):
           parameters = parameters.union(set(find_dollar_braced_variables(data)))
@@ -204,11 +205,12 @@ def action_parameters(request):
   return HttpResponse(json.dumps(response), mimetype="application/json")
 
 
+@check_document_access_permission()
 def workflow_parameters(request):
   response = {'status': -1}
 
   try:
-    workflow = Workflow(document=Document2.objects.get(type='oozie-workflow2', owner=request.user, uuid=request.GET.get('uuid'))) # TODO perms 
+    workflow = Workflow(document=Document2.objects.get(type='oozie-workflow2', uuid=request.GET.get('uuid'))) 
 
     response['status'] = 0
     response['parameters'] = workflow.find_all_parameters(with_lib_path=False)
@@ -222,7 +224,7 @@ def gen_xml_workflow(request):
   response = {'status': -1}
 
   try:
-    workflow_json = json.loads(request.POST.get('workflow', '{}')) # TODO perms
+    workflow_json = json.loads(request.POST.get('workflow', '{}'))
   
     workflow = Workflow(workflow=workflow_json)
   
@@ -234,8 +236,9 @@ def gen_xml_workflow(request):
   return HttpResponse(json.dumps(response), mimetype="application/json")
 
 
+@check_document_access_permission()
 def submit_workflow(request, doc_id):
-  workflow = Workflow(document=Document2.objects.get(id=doc_id)) # Todo perms
+  workflow = Workflow(document=Document2.objects.get(id=doc_id))
   ParametersFormSet = formset_factory(ParameterForm, extra=0)
 
   if request.method == 'POST':
@@ -277,7 +280,9 @@ def _submit_workflow(user, fs, jt, workflow, mapping):
   return redirect(reverse('oozie:list_oozie_workflow', kwargs={'job_id': job_id}))
 
 
+
 def list_editor_coordinators(request):
+  coordinators = [d.content_object for d in Document.objects.available_docs(Document2, request.user).filter(extra='workflow2')]
   coordinators = Document2.objects.filter(type='oozie-coordinator2', owner=request.user)
 
   return render('editor/list_editor_coordinators.mako', request, {

+ 4 - 5
apps/oozie/static/js/workflow-editor.ko.js

@@ -240,7 +240,6 @@ var Workflow = function (vm, workflow) {
       type: "POST",
       url: "/oozie/editor/workflow/new_node/",
       data: {
-        "workflow": ko.mapping.toJSON(workflow),
         "node": ko.mapping.toJSON(widget)
       },
       success: function (data) {
@@ -265,7 +264,6 @@ var Workflow = function (vm, workflow) {
 
   self.addNode = function (widget, copiedNode) {
     $.post("/oozie/editor/workflow/add_node/", {
-      "workflow": ko.mapping.toJSON(workflow),
       "node": ko.mapping.toJSON(widget),
       "properties": ko.mapping.toJSON(viewModel.addActionProperties()),
       "copiedProperties": copiedNode ? ko.mapping.toJSON(copiedNode.properties) : "{}"
@@ -447,17 +445,18 @@ var Workflow = function (vm, workflow) {
   };
 }
 
-var WorkflowEditorViewModel = function (layout_json, workflow_json, credentials_json, workflow_properties_json, subworkflows_json) {
+var WorkflowEditorViewModel = function (layout_json, workflow_json, credentials_json, workflow_properties_json, subworkflows_json, can_edit_job_json) {
   var self = this;
 
   self.isNested = ko.observable(true);
 
-  self.isEditing = ko.observable(true);
+  self.canEdit = ko.mapping.fromJS(can_edit_job_json);
+  self.isEditing = ko.observable(true && self.canEdit());
   self.isEditing.subscribe(function (newVal) {
     $(document).trigger("editingToggled");
   });
   self.toggleEditing = function () {
-    self.isEditing(!self.isEditing());
+    self.isEditing(! self.isEditing());
   };
 
   self.newAction = ko.observable();

+ 1 - 1
desktop/libs/liboozie/src/liboozie/submission2.py

@@ -216,7 +216,7 @@ class Submission(object):
 
     # Case of a shared job
     if self.user != self.job.document.owner:
-      path = REMOTE_DEPLOYMENT_DIR.get().replace('$USER', user.username).replace('$TIME', str(time.time())).replace('$JOBID', self.job.id)
+      path = REMOTE_DEPLOYMENT_DIR.get().replace('$USER', self.user.username).replace('$TIME', str(time.time())).replace('$JOBID', str(self.job.id))
       # Shared coords or bundles might not have any existing workspaces
       if self.fs.exists(self.job.deployment_dir):
         self.fs.copy_remote_dir(self.job.deployment_dir, path, owner=self.user, dir_mode=0711)