Przeglądaj źródła

HUE-3464 [oozie] Add granular permission to enable only the dashboard

If a user is in a group than has access to Oozie but also has a permission
'disable_editor_access', it will be able to access only the dashboard.
Romain Rigaux 9 lat temu
rodzic
commit
ff33fdb83d

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

@@ -92,6 +92,14 @@ def check_document_modify_permission():
   return inner
 
 
+def check_editor_access_permission(view_func):
+
+  def decorate(request, *args, **kwargs):
+    if not request.user.is_superuser and request.user.has_hue_permission(action="disable_editor_access", app="oozie"):
+      raise PopupException(_('Missing permission to access the Oozie Editor'), error_code=401)
+    return view_func(request, *args, **kwargs)
+  return wraps(view_func)(decorate)
+
 
 ## Oozie v1 below
 

+ 1 - 0
apps/oozie/src/oozie/settings.py

@@ -24,4 +24,5 @@ IS_URL_NAMESPACED = True
 
 PERMISSION_ACTIONS = (
   ("dashboard_jobs_access", "Oozie Dashboard read-only user for all jobs"),
+  ("disable_editor_access", "Disable Oozie Editor access"),
 )

+ 30 - 0
apps/oozie/src/oozie/tests2.py

@@ -25,12 +25,14 @@ from django.db.models import Q
 
 from nose.tools import assert_true, assert_false, assert_equal, assert_not_equal
 
+from desktop.lib.test_utils import add_permission, remove_from_group
 from desktop.models import Document2
 
 from oozie.conf import ENABLE_V2
 from oozie.importlib.workflows import generate_v2_graph_nodes
 from oozie.models2 import Workflow, find_dollar_variables, find_dollar_braced_variables, Node, _create_graph_adjaceny_list, _get_hierarchy_from_adj_list
 from oozie.tests import OozieMockBase, save_temp_workflow, MockOozieApi
+from desktop.lib.django_test_util import make_logged_in_client
 
 
 LOG = logging.getLogger(__name__)
@@ -361,6 +363,34 @@ LIMIT $limit"""))
     wf_doc2.delete()
     wf_doc3.delete()
 
+  def test_editor_access_permissions(self):
+    group = 'no_editor'
+
+    try:
+      # Block editor section
+      response = self.c.get(reverse('oozie:list_editor_workflows'))
+      assert_equal(response.status_code, 200)
+      response = self.c.get(reverse('oozie:list_workflows'))
+      assert_equal(response.status_code, 200)
+
+      add_permission('test', 'no_editor', 'disable_editor_access', 'oozie')
+
+      response = self.c.get(reverse('oozie:list_editor_workflows'))
+      assert_equal(response.status_code, 401)
+      response = self.c.get(reverse('oozie:list_workflows'))
+      assert_equal(response.status_code, 200)
+
+      # Admin are not affected
+      admin = make_logged_in_client('admin', 'admin', is_superuser=True, recreate=True, groupname=group)
+
+      response = admin.get(reverse('oozie:list_editor_workflows'))
+      assert_equal(response.status_code, 200)
+      response = admin.get(reverse('oozie:list_workflows'))
+      assert_equal(response.status_code, 200)
+    finally:
+      remove_from_group("test", group)
+
+
 class TestExternalWorkflowGraph():
 
   def setUp(self):

+ 32 - 3
apps/oozie/src/oozie/views/editor2.py

@@ -36,7 +36,8 @@ 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.decorators import check_document_access_permission, check_document_modify_permission,\
+  check_editor_access_permission
 from oozie.forms import ParameterForm
 from oozie.models import Workflow as OldWorklow, Coordinator as OldCoordinator, Bundle as OldBundle, Job
 from oozie.models2 import Node, Workflow, Coordinator, Bundle, NODES, WORKFLOW_NODE_PROPERTIES, import_workflow_from_hue_3_7,\
@@ -48,7 +49,7 @@ from oozie.views.editor import edit_workflow as old_edit_workflow, edit_coordina
 LOG = logging.getLogger(__name__)
 
 
-
+@check_editor_access_permission
 def list_editor_workflows(request):
   workflows = [d.content_object.to_dict() for d in Document.objects.get_docs(request.user, Document2, extra='workflow2')]
 
@@ -61,6 +62,7 @@ def list_editor_workflows(request):
   })
 
 
+@check_editor_access_permission
 def open_old_workflow(request):
   doc_id = request.GET.get('workflow')
   workflow = Document.objects.get(id=doc_id).content_object.get_full_node()
@@ -73,6 +75,7 @@ def open_old_workflow(request):
     return old_edit_workflow(request, workflow=workflow.id)
 
 
+@check_editor_access_permission
 @check_document_access_permission()
 def edit_workflow(request):
   workflow_id = request.GET.get('workflow')
@@ -116,6 +119,7 @@ def _edit_workflow(request, doc, workflow):
   })
 
 
+@check_editor_access_permission
 def new_workflow(request):
   doc = None
   workflow = Workflow()
@@ -125,6 +129,7 @@ def new_workflow(request):
   return _edit_workflow(request, doc, workflow)
 
 
+@check_editor_access_permission
 def delete_job(request):
   if request.method != 'POST':
     raise PopupException(_('A POST request is required.'))
@@ -154,6 +159,7 @@ def delete_job(request):
   return JsonResponse(response)
 
 
+@check_editor_access_permission
 @check_document_access_permission()
 def copy_workflow(request):
   if request.method != 'POST':
@@ -192,6 +198,7 @@ def _import_workspace(fs, user, workflow):
   workflow.import_workspace(fs, source_workspace_dir, user)
 
 
+@check_editor_access_permission
 @check_document_modify_permission()
 def save_workflow(request):
   response = {'status': -1}
@@ -236,6 +243,7 @@ def save_workflow(request):
   return JsonResponse(response)
 
 
+@check_editor_access_permission
 def new_node(request):
   response = {'status': -1}
 
@@ -264,6 +272,7 @@ def _get_workflows(user):
     ]
 
 
+@check_editor_access_permission
 def add_node(request):
   response = {'status': -1}
 
@@ -284,6 +293,7 @@ def add_node(request):
   return JsonResponse(response)
 
 
+@check_editor_access_permission
 def action_parameters(request):
   response = {'status': -1}
   parameters = set()
@@ -313,6 +323,7 @@ def action_parameters(request):
   return JsonResponse(response)
 
 
+@check_editor_access_permission
 @check_document_access_permission()
 def workflow_parameters(request):
   response = {'status': -1}
@@ -328,6 +339,7 @@ def workflow_parameters(request):
   return JsonResponse(response)
 
 
+@check_editor_access_permission
 def gen_xml_workflow(request):
   response = {'status': -1}
 
@@ -344,6 +356,7 @@ def gen_xml_workflow(request):
   return JsonResponse(response)
 
 
+@check_editor_access_permission
 @check_document_access_permission()
 def submit_workflow(request, doc_id):
   workflow = Workflow(document=Document2.objects.get(id=doc_id))
@@ -351,6 +364,7 @@ def submit_workflow(request, doc_id):
   return _submit_workflow_helper(request, workflow, submit_action=reverse('oozie:editor_submit_workflow', kwargs={'doc_id': workflow.id}))
 
 
+@check_editor_access_permission
 @check_document_access_permission()
 def submit_single_action(request, doc_id, node_id):
   parent_doc = Document2.objects.get(id=doc_id)
@@ -419,7 +433,7 @@ def _submit_workflow(user, fs, jt, workflow, mapping):
   return redirect(reverse('oozie:list_oozie_workflow', kwargs={'job_id': job_id}))
 
 
-
+@check_editor_access_permission
 def list_editor_coordinators(request):
   coordinators = [d.content_object.to_dict() for d in Document.objects.get_docs(request.user, Document2, extra='coordinator2')]
 
@@ -432,6 +446,7 @@ def list_editor_coordinators(request):
   })
 
 
+@check_editor_access_permission
 @check_document_access_permission()
 def edit_coordinator(request):
   coordinator_id = request.GET.get('coordinator', request.GET.get('uuid'))
@@ -476,10 +491,12 @@ def edit_coordinator(request):
   })
 
 
+@check_editor_access_permission
 def new_coordinator(request):
   return edit_coordinator(request)
 
 
+@check_editor_access_permission
 def open_old_coordinator(request):
   doc_id = request.GET.get('coordinator')
   coordinator_id = Document.objects.get(id=doc_id).object_id
@@ -487,6 +504,7 @@ def open_old_coordinator(request):
   return old_edit_coordinator(request, coordinator=coordinator_id)
 
 
+@check_editor_access_permission
 @check_document_access_permission()
 def copy_coordinator(request):
   if request.method != 'POST':
@@ -514,6 +532,7 @@ def copy_coordinator(request):
   return JsonResponse(response)
 
 
+@check_editor_access_permission
 @check_document_modify_permission()
 def save_coordinator(request):
   response = {'status': -1}
@@ -546,6 +565,7 @@ def save_coordinator(request):
   return JsonResponse(response)
 
 
+@check_editor_access_permission
 def gen_xml_coordinator(request):
   response = {'status': -1}
 
@@ -559,6 +579,7 @@ def gen_xml_coordinator(request):
   return JsonResponse(response)
 
 
+@check_editor_access_permission
 @check_document_access_permission()
 def coordinator_parameters(request):
   response = {'status': -1}
@@ -574,6 +595,7 @@ def coordinator_parameters(request):
   return JsonResponse(response)
 
 
+@check_editor_access_permission
 @check_document_access_permission()
 def submit_coordinator(request, doc_id):
   coordinator = Coordinator(document=Document2.objects.get(id=doc_id))
@@ -622,6 +644,7 @@ def _submit_coordinator(request, coordinator, mapping):
     raise PopupException(_("Error submitting coordinator %s") % (coordinator,), detail=ex._headers.get('oozie-error-message', ex))
 
 
+@check_editor_access_permission
 def list_editor_bundles(request):
   bundles = [d.content_object.to_dict() for d in Document.objects.get_docs(request.user, Document2, extra='bundle2')]
 
@@ -634,6 +657,7 @@ def list_editor_bundles(request):
   })
 
 
+@check_editor_access_permission
 @check_document_access_permission()
 def edit_bundle(request):
   bundle_id = request.GET.get('bundle')
@@ -657,10 +681,12 @@ def edit_bundle(request):
   })
 
 
+@check_editor_access_permission
 def new_bundle(request):
   return edit_bundle(request)
 
 
+@check_editor_access_permission
 def open_old_bundle(request):
   doc_id = request.GET.get('bundle')
   bundle_id = Document.objects.get(id=doc_id).object_id
@@ -668,6 +694,7 @@ def open_old_bundle(request):
   return old_edit_bundle(request, bundle=bundle_id)
 
 
+@check_editor_access_permission
 @check_document_modify_permission()
 def save_bundle(request):
   response = {'status': -1}
@@ -700,6 +727,7 @@ def save_bundle(request):
   return JsonResponse(response)
 
 
+@check_editor_access_permission
 @check_document_access_permission()
 def copy_bundle(request):
   if request.method != 'POST':
@@ -727,6 +755,7 @@ def copy_bundle(request):
   return JsonResponse(response)
 
 
+@check_editor_access_permission
 @check_document_access_permission()
 def submit_bundle(request, doc_id):
   bundle = Bundle(document=Document2.objects.get(id=doc_id))

+ 2 - 1
apps/useradmin/src/useradmin/models.py

@@ -279,7 +279,8 @@ def update_app_permissions(**kwargs):
         if not (new_dp.app == 'useradmin' and new_dp.action == 'access') and \
            not (new_dp.app == 'metastore' and new_dp.action == 'write') and \
            not (new_dp.app == 'hbase' and new_dp.action == 'write') and \
-           not (new_dp.app == 'security' and new_dp.action == 'impersonate'):
+           not (new_dp.app == 'security' and new_dp.action == 'impersonate') and \
+           not (new_dp.app == 'oozie' and new_dp.action == 'disable_editor_access'):
           GroupPermission.objects.create(group=default_group, hue_permission=new_dp)
 
     available = HuePermission.objects.count()

+ 8 - 0
desktop/core/src/desktop/lib/test_utils.py

@@ -54,6 +54,14 @@ def add_to_group(username, groupname=None):
         user.groups.add(group)
         user.save()
 
+def remove_from_group(username, groupname):
+    user = User.objects.get(username=username)
+    group, created = Group.objects.get_or_create(name=groupname)
+
+    if not user.groups.filter(name=group.name).exists():
+        user.groups.remove(group)
+        user.save()
+
 
 def reformat_json(json_obj):
     if isinstance(json_obj, basestring):

+ 2 - 0
desktop/core/src/desktop/templates/common_header.mako

@@ -561,6 +561,7 @@ if USE_NEW_EDITOR.get():
                <li><a href="${url('oozie:list_oozie_bundles')}"><img src="${ static('oozie/art/icon_oozie_bundle_48.png') }" class="app-icon" /> ${_('Bundles')}</a></li>
              </ul>
            </li>
+           % if not user.has_hue_permission(action="disable_editor_access", app="oozie") or user.is_superuser:
            <% from oozie.conf import ENABLE_V2 %>
            % if not ENABLE_V2.get():
            <li class="dropdown-submenu">
@@ -581,6 +582,7 @@ if USE_NEW_EDITOR.get():
              </ul>
            </li>
            % endif
+           % endif
          </ul>
        </li>
        % endif