Explorar o código

HUE-701 [jb] Job browser should not show the jobs from other users

Romain Rigaux %!s(int64=13) %!d(string=hai) anos
pai
achega
0fe62b5212

+ 27 - 0
apps/jobbrowser/src/jobbrowser/conf.py

@@ -0,0 +1,27 @@
+#!/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.
+"""Configuration for the job browser application"""
+
+from desktop.lib.conf import Config, coerce_bool
+
+
+SHARE_JOBS = Config(
+  key='share_jobs',
+  default=True,
+  type=coerce_bool,
+  help='Share submitted jobs information with all users. If set to false, '
+       'submitted jobs are visible only to the owner and administrators.')

+ 37 - 3
apps/jobbrowser/src/jobbrowser/views.py

@@ -26,13 +26,17 @@ from urllib import quote_plus
 from desktop.lib.paginator import Paginator
 from desktop.lib.paginator import Paginator
 from desktop.lib.django_util import render_json, MessageException, render
 from desktop.lib.django_util import render_json, MessageException, render
 from desktop.lib.django_util import copy_query_dict
 from desktop.lib.django_util import copy_query_dict
+from desktop.lib.django_util import PopupException
+
 from django.http import HttpResponseRedirect
 from django.http import HttpResponseRedirect
+from django.utils.functional import wraps
 
 
 from desktop.log.access import access_warn, access_log_level
 from desktop.log.access import access_warn, access_log_level
 from desktop.views import register_status_bar_view
 from desktop.views import register_status_bar_view
 from hadoop.api.jobtracker.ttypes import ThriftJobPriority
 from hadoop.api.jobtracker.ttypes import ThriftJobPriority
 from hadoop.api.jobtracker.ttypes import TaskTrackerNotFoundException
 from hadoop.api.jobtracker.ttypes import TaskTrackerNotFoundException
 
 
+from jobbrowser import conf
 from jobbrowser.models import Job, JobLinkage, TaskList, Tracker, Cluster
 from jobbrowser.models import Job, JobLinkage, TaskList, Tracker, Cluster
 
 
 ##################################
 ##################################
@@ -40,6 +44,23 @@ from jobbrowser.models import Job, JobLinkage, TaskList, Tracker, Cluster
 
 
 __DEFAULT_OBJ_PER_PAGINATION = 10
 __DEFAULT_OBJ_PER_PAGINATION = 10
 
 
+
+def check_job_permission(view_func):
+  """
+  Ensure that the user has access to the job.
+  Assumes that the wrapped function takes a 'jobid' param.
+  """
+  def decorate(request, *args, **kwargs):
+    jobid = kwargs['jobid']
+    job = Job.from_id(jt=request.jt, jobid=jobid)
+    if not conf.SHARE_JOBS.get() and not request.user.is_superuser \
+      and job.user != request.user.username:
+      raise PopupException("You don't have the permissions to access"
+                             " job %s" % jobid)
+    return view_func(request, *args, **kwargs)
+  return wraps(view_func)(decorate)
+
+@check_job_permission
 def single_job(request, jobid):
 def single_job(request, jobid):
   """
   """
   We get here from /jobs/jobid
   We get here from /jobs/jobid
@@ -61,6 +82,7 @@ def single_job(request, jobid):
     'recent_tasks': recent_tasks[:5]
     'recent_tasks': recent_tasks[:5]
   })
   })
 
 
+@check_job_permission
 def job_counters(request, jobid):
 def job_counters(request, jobid):
   """
   """
   We get here from /jobs/jobid/counters
   We get here from /jobs/jobid/counters
@@ -72,7 +94,11 @@ def jobs(request):
   """
   """
   We get here from /jobs?filterargs
   We get here from /jobs?filterargs
   """
   """
-  matching_jobs = sort_if_necessary(request, get_matching_jobs(request))
+  check_permission = not conf.SHARE_JOBS.get() and not request.user.is_superuser
+
+  jobs = get_matching_jobs(request, check_permission)
+
+  matching_jobs = sort_if_necessary(request, jobs)
   state = request.GET.get('state', 'all')
   state = request.GET.get('state', 'all')
   user = request.GET.get('user', '')
   user = request.GET.get('user', '')
   text = request.GET.get('text', '')
   text = request.GET.get('text', '')
@@ -122,6 +148,7 @@ def kill_job(request, jobid):
 
 
   raise Exception("Job did not appear as killed within 15 seconds")
   raise Exception("Job did not appear as killed within 15 seconds")
 
 
+@check_job_permission
 def tasks(request, jobid):
 def tasks(request, jobid):
   """
   """
   We get here from /jobs/jobid/tasks?filterargs, with the options being:
   We get here from /jobs/jobid/tasks?filterargs, with the options being:
@@ -174,6 +201,7 @@ def tasks(request, jobid):
   })
   })
 
 
 
 
+@check_job_permission
 def single_task(request, jobid, taskid):
 def single_task(request, jobid, taskid):
   """
   """
   We get here from /jobs/jobid/tasks/taskid
   We get here from /jobs/jobid/tasks/taskid
@@ -186,6 +214,7 @@ def single_task(request, jobid, taskid):
     'joblnk': job_link
     'joblnk': job_link
   })
   })
 
 
+@check_job_permission
 def single_task_attempt(request, jobid, taskid, attemptid):
 def single_task_attempt(request, jobid, taskid, attemptid):
   """
   """
   We get here from /jobs/jobid/tasks/taskid/attempts/attemptid
   We get here from /jobs/jobid/tasks/taskid/attempts/attemptid
@@ -216,6 +245,7 @@ def single_task_attempt(request, jobid, taskid, attemptid):
       "logs": logs
       "logs": logs
     })
     })
 
 
+@check_job_permission
 def task_attempt_counters(request, jobid, taskid, attemptid):
 def task_attempt_counters(request, jobid, taskid, attemptid):
   """
   """
   We get here from /jobs/jobid/tasks/taskid/attempts/attemptid/counters
   We get here from /jobs/jobid/tasks/taskid/attempts/attemptid/counters
@@ -264,6 +294,7 @@ def queues(request):
   """
   """
   return render("queues.html", request, { "queuelist" : request.jt.queues()})
   return render("queues.html", request, { "queuelist" : request.jt.queues()})
 
 
+@check_job_permission
 def set_job_priority(request, jobid):
 def set_job_priority(request, jobid):
   """
   """
   We get here from /jobs/jobid/setpriority?priority=PRIORITY
   We get here from /jobs/jobid/setpriority?priority=PRIORITY
@@ -424,7 +455,7 @@ def get_single_job(request, jobid):
   return Job.from_id(jt=request.jt, jobid=jobid)
   return Job.from_id(jt=request.jt, jobid=jobid)
 
 
 
 
-def get_matching_jobs(request, **kwargs):
+def get_matching_jobs(request, check_permission=False, **kwargs):
   """
   """
   Returns an array of jobs where the returned
   Returns an array of jobs where the returned
   jobs are matched by the provided filter arguments.
   jobs are matched by the provided filter arguments.
@@ -433,6 +464,8 @@ def get_matching_jobs(request, **kwargs):
   in the request object.
   in the request object.
 
 
   Filter arguments may be jobid, pools, user, tasks, text and state.
   Filter arguments may be jobid, pools, user, tasks, text and state.
+
+  Filter by user ownership if check_permission is set to true.
   """
   """
   jobfunc = {"completed" : request.jt.completed_jobs,
   jobfunc = {"completed" : request.jt.completed_jobs,
              # Succeeded and completed are synonyms here.
              # Succeeded and completed are synonyms here.
@@ -449,7 +482,8 @@ def get_matching_jobs(request, **kwargs):
   joblist = jobfunc[selection]().jobs
   joblist = jobfunc[selection]().jobs
 
 
   return [Job.from_thriftjob(request.jt, j)
   return [Job.from_thriftjob(request.jt, j)
-          for j in _filter_jobs_by_req(joblist, request, **kwargs) ]
+          for j in _filter_jobs_by_req(joblist, request, **kwargs)
+          if not check_permission or request.user.is_superuser or j.profile.user == request.user]
 
 
 
 
 def get_job_count_by_state(request, username):
 def get_job_count_by_state(request, username):

+ 10 - 0
desktop/conf/pseudo-distributed.ini.tmpl

@@ -293,6 +293,16 @@
   ## security_enabled=false
   ## security_enabled=false
 
 
 
 
+###########################################################################
+# Settings to configure Job Browser.
+###########################################################################
+
+[jobbrowser]
+  # Share submitted jobs information with all users. If set to false,
+  # submitted jobs are visible only to the owner and administrators.
+  ## share_jobs=true
+
+
 ###########################################################################
 ###########################################################################
 # Settings to configure the Shell application
 # Settings to configure the Shell application
 ###########################################################################
 ###########################################################################