You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by om...@apache.org on 2011/03/04 04:55:53 UTC

svn commit: r1077247 [1/2] - in /hadoop/common/branches/branch-0.20-security-patches/src: mapred/org/apache/hadoop/mapred/ test/org/apache/hadoop/mapred/ webapps/job/

Author: omalley
Date: Fri Mar  4 03:55:52 2011
New Revision: 1077247

URL: http://svn.apache.org/viewvc?rev=1077247&view=rev
Log:
commit c1a7b67a52c2613600e6bc23a4d369142c4a673e
Author: Vinod Kumar <vi...@yahoo-inc.com>
Date:   Sat Feb 27 15:44:10 2010 +0530

    MAPREDUCE-1455 from https://issues.apache.org/jira/secure/attachment/12437322/1455.20S.2.patch

Added:
    hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobTrackerJobACLsManager.java
    hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskTrackerJobACLsManager.java
    hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/TestWebUIAuthorization.java
    hadoop/common/branches/branch-0.20-security-patches/src/webapps/job/job_authorization_error.jsp
Modified:
    hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JSPUtil.java
    hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobACLsManager.java
    hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobClient.java
    hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobConf.java
    hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobInProgress.java
    hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobTracker.java
    hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskGraphServlet.java
    hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskInProgress.java
    hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskLogServlet.java
    hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskRunner.java
    hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskTracker.java
    hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/ClusterMapReduceTestCase.java
    hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/TestJobACLs.java
    hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/TestNodeRefresh.java
    hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/TestTaskTrackerLocalization.java
    hadoop/common/branches/branch-0.20-security-patches/src/webapps/job/jobblacklistedtrackers.jsp
    hadoop/common/branches/branch-0.20-security-patches/src/webapps/job/jobconf.jsp
    hadoop/common/branches/branch-0.20-security-patches/src/webapps/job/jobdetails.jsp
    hadoop/common/branches/branch-0.20-security-patches/src/webapps/job/jobfailures.jsp
    hadoop/common/branches/branch-0.20-security-patches/src/webapps/job/jobqueue_details.jsp
    hadoop/common/branches/branch-0.20-security-patches/src/webapps/job/jobtasks.jsp
    hadoop/common/branches/branch-0.20-security-patches/src/webapps/job/jobtracker.jsp
    hadoop/common/branches/branch-0.20-security-patches/src/webapps/job/taskdetails.jsp
    hadoop/common/branches/branch-0.20-security-patches/src/webapps/job/taskstats.jsp

Modified: hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JSPUtil.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JSPUtil.java?rev=1077247&r1=1077246&r2=1077247&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JSPUtil.java (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JSPUtil.java Fri Mar  4 03:55:52 2011
@@ -20,12 +20,15 @@ package org.apache.hadoop.mapred;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
+import java.security.PrivilegedExceptionAction;
 import java.util.Collection;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
@@ -36,11 +39,14 @@ import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.http.HtmlQuoting;
 import org.apache.hadoop.mapred.JobHistory.JobInfo;
 import org.apache.hadoop.mapred.JobTracker.RetireJobInfo;
+import org.apache.hadoop.mapreduce.JobACL;
+import org.apache.hadoop.security.AccessControlException;
+import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.util.ServletUtil;
 import org.apache.hadoop.util.StringUtils;
 
 class JSPUtil {
-  private static final String PRIVATE_ACTIONS_KEY = "webinterface.private.actions";
+  static final String PRIVATE_ACTIONS_KEY = "webinterface.private.actions";
   
   public static final Configuration conf = new Configuration();
 
@@ -52,6 +58,94 @@ class JSPUtil {
     conf.getInt("mapred.job.tracker.jobhistory.lru.cache.size", 5);
 
   private static final Log LOG = LogFactory.getLog(JSPUtil.class);
+
+  /**
+   * Wraps the {@link JobInProgress} object and contains boolean for
+   * 'job view access' allowed or not.
+   * This class is only for usage by JSPs and Servlets.
+   */
+  static class JobWithViewAccessCheck {
+    private JobInProgress job = null;
+    
+    // true if user is authorized to view this job
+    private boolean isViewAllowed = true;
+
+    JobWithViewAccessCheck(JobInProgress job) {
+      this.job = job;
+    }
+
+    JobInProgress getJob() {
+      return job;
+    }
+
+    boolean isViewJobAllowed() {
+      return isViewAllowed;
+    }
+
+    void setViewAccess(boolean isViewAllowed) {
+      this.isViewAllowed = isViewAllowed;
+    }
+  }
+
+  /**
+   * Validates if current user can view the job.
+   * If user is not authorized to view the job, this method will modify the
+   * response and forwards to an error page and returns Job with
+   * viewJobAccess flag set to false.
+   * @return JobWithViewAccessCheck object(contains JobInProgress object and
+   *         viewJobAccess flag). Callers of this method will check the flag
+   *         and decide if view should be allowed or not. Job will be null if
+   *         the job with given jobid doesnot exist at the JobTracker.
+   */
+  public static JobWithViewAccessCheck checkAccessAndGetJob(JobTracker jt,
+      JobID jobid, HttpServletRequest request, HttpServletResponse response)
+      throws ServletException, IOException {
+    final JobInProgress job = jt.getJob(jobid);
+    JobWithViewAccessCheck myJob = new JobWithViewAccessCheck(job);
+
+    String user = request.getRemoteUser();
+    if (user != null && job != null && jt.isJobLevelAuthorizationEnabled()) {
+      final UserGroupInformation ugi =
+        UserGroupInformation.createRemoteUser(user);
+      try {
+        ugi.doAs(new PrivilegedExceptionAction<Void>() {
+          public Void run() throws IOException, ServletException {
+
+            // checks job view permission
+            job.checkAccess(ugi, JobACL.VIEW_JOB);
+            return null;
+          }
+        });
+      } catch (AccessControlException e) {
+        String errMsg = "User " + ugi.getShortUserName() +
+            " failed to view " + jobid + "!<br><br>" + e.getMessage() +
+            "<hr><a href=\"jobtracker.jsp\">Go back to JobTracker</a><br>";
+        JSPUtil.setErrorAndForward(errMsg, request, response);
+        myJob.setViewAccess(false);
+      } catch (InterruptedException e) {
+        String errMsg = " Interrupted while trying to access " + jobid +
+        "<hr><a href=\"jobtracker.jsp\">Go back to JobTracker</a><br>";
+        JSPUtil.setErrorAndForward(errMsg, request, response);
+        myJob.setViewAccess(false);
+      }
+    }
+    return myJob;
+  }
+
+  /**
+   * Sets error code SC_UNAUTHORIZED in response and forwards to
+   * error page which contains error message and a back link.
+   */
+  public static void setErrorAndForward(String errMsg,
+      HttpServletRequest request, HttpServletResponse response)
+      throws ServletException, IOException {
+    request.setAttribute("error.msg", errMsg);
+    RequestDispatcher dispatcher = request.getRequestDispatcher(
+        "/job_authorization_error.jsp");
+    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+    dispatcher.forward(request, response);
+  }
+
   /**
    * Method used to process the request from the job page based on the 
    * request which it has received. For example like changing priority.
@@ -60,33 +154,99 @@ class JSPUtil {
    * @param response HTTP response object.
    * @param tracker {@link JobTracker} instance
    * @throws IOException
+   * @throws InterruptedException 
+   * @throws ServletException 
    */
   public static void processButtons(HttpServletRequest request,
-      HttpServletResponse response, JobTracker tracker) throws IOException {
+      HttpServletResponse response, final JobTracker tracker)
+      throws IOException, InterruptedException, ServletException {
 
-    if (conf.getBoolean(PRIVATE_ACTIONS_KEY, false)
+	String user = request.getRemoteUser();
+    if (privateActionsAllowed(tracker.conf)
         && request.getParameter("killJobs") != null) {
-      String[] jobs = request.getParameterValues("jobCheckBox");
-      if (jobs != null) {
-        for (String job : jobs) {
-          tracker.killJob(JobID.forName(job));
+        String[] jobs = request.getParameterValues("jobCheckBox");
+        if (jobs != null) {
+          boolean notAuthorized = false;
+          String errMsg = "User " + user
+              + " failed to kill the following job(s)!<br><br>";
+          for (String job : jobs) {
+            final JobID jobId = JobID.forName(job);
+            if (user != null) {
+              UserGroupInformation ugi =
+                UserGroupInformation.createRemoteUser(user);
+              try {
+                ugi.doAs(new PrivilegedExceptionAction<Void>() {
+                  public Void run() throws IOException{
+
+                    tracker.killJob(jobId);// checks job modify permission
+                    return null;
+                  }
+                });
+              } catch(AccessControlException e) {
+                errMsg = errMsg.concat("<br>" + e.getMessage());
+                notAuthorized = true;
+                // We don't return right away so that we can try killing other
+                // jobs that are requested to be killed.
+                continue;
+              }
+            }
+            else {// no authorization needed
+              tracker.killJob(jobId);
+            }
+          }
+          if (notAuthorized) {// user is not authorized to kill some/all of jobs
+            errMsg = errMsg.concat(
+              "<br><hr><a href=\"jobtracker.jsp\">Go back to JobTracker</a><br>");
+            setErrorAndForward(errMsg, request, response);
+            return;
+          }
         }
       }
-    }
 
-    if (conf.getBoolean(PRIVATE_ACTIONS_KEY, false) && 
+    if (privateActionsAllowed(tracker.conf) && 
           request.getParameter("changeJobPriority") != null) {
-      String[] jobs = request.getParameterValues("jobCheckBox");
-
-      if (jobs != null) {
-        JobPriority jobPri = JobPriority.valueOf(request
-            .getParameter("setJobPriority"));
-
-        for (String job : jobs) {
-          tracker.setJobPriority(JobID.forName(job), jobPri);
+        String[] jobs = request.getParameterValues("jobCheckBox");
+        if (jobs != null) {
+          final JobPriority jobPri = JobPriority.valueOf(request
+              .getParameter("setJobPriority"));
+          boolean notAuthorized = false;
+          String errMsg = "User " + user
+              + " failed to set priority for the following job(s)!<br><br>";
+
+          for (String job : jobs) {
+            final JobID jobId = JobID.forName(job);
+            if (user != null) {
+              UserGroupInformation ugi = UserGroupInformation.
+                  createRemoteUser(user);
+              try {
+                ugi.doAs(new PrivilegedExceptionAction<Void>() {
+                  public Void run() throws IOException{
+
+                    // checks job modify permission
+                    tracker.setJobPriority(jobId, jobPri);
+                    return null;
+                  }
+                });
+              } catch(AccessControlException e) {
+                errMsg = errMsg.concat("<br>" + e.getMessage());
+                notAuthorized = true;
+                // We don't return right away so that we can try operating on
+                // other jobs.
+                continue;
+              }
+            }
+            else {// no authorization needed
+              tracker.setJobPriority(jobId, jobPri);
+            }
+          }
+          if (notAuthorized) {// user is not authorized to kill some/all of jobs
+            errMsg = errMsg.concat(
+              "<br><hr><a href=\"jobtracker.jsp\">Go back to JobTracker</a><br>");
+            setErrorAndForward(errMsg, request, response);
+            return;
+          }
         }
       }
-    }
   }
 
   /**
@@ -100,11 +260,10 @@ class JSPUtil {
    * @throws IOException
    */
   public static String generateJobTable(String label, Collection<JobInProgress> jobs
-      , int refresh, int rowId) throws IOException {
+      , int refresh, int rowId, JobConf conf) throws IOException {
 
     boolean isModifiable = label.equals("Running") 
-                                && conf.getBoolean(
-                                      PRIVATE_ACTIONS_KEY, false);
+                                && privateActionsAllowed(conf);
     StringBuffer sb = new StringBuffer();
     
     sb.append("<table border=\"1\" cellpadding=\"5\" cellspacing=\"0\">\n");
@@ -302,4 +461,8 @@ class JSPUtil {
       return jobInfo;
     }
   }
+
+  static boolean privateActionsAllowed(JobConf conf) {
+    return conf.getBoolean(PRIVATE_ACTIONS_KEY, false);
+  }
 }

Modified: hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobACLsManager.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobACLsManager.java?rev=1077247&r1=1077246&r2=1077247&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobACLsManager.java (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobACLsManager.java Fri Mar  4 03:55:52 2011
@@ -20,101 +20,128 @@ package org.apache.hadoop.mapred;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.hadoop.mapreduce.JobACL;
 import org.apache.hadoop.security.AccessControlException;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.hadoop.security.authorize.AccessControlList;
 
-public class JobACLsManager {
+public abstract class JobACLsManager {
 
-  private JobTracker jobTracker = null;
+	  static final Log LOG = LogFactory.getLog(JobACLsManager.class);
 
-  public JobACLsManager(JobTracker tracker) {
-    jobTracker = tracker;
-  }
-
-  /**
-   * Construct the jobACLs from the configuration so that they can be kept in
-   * the memory. If authorization is disabled on the JT, nothing is constructed
-   * and an empty map is returned.
-   * 
-   * @return JobACl to AccessControlList map.
-   */
-  Map<JobACL, AccessControlList> constructJobACLs(JobConf conf) {
-    
-    Map<JobACL, AccessControlList> acls =
-      new HashMap<JobACL, AccessControlList>();
-
-    // Don't construct anything if authorization is disabled.
-    if (!jobTracker.isJobLevelAuthorizationEnabled()) {
-      return acls;
-    }
-
-    for (JobACL aclName : JobACL.values()) {
-      String aclConfigName = aclName.getAclName();
-      String aclConfigured = conf.get(aclConfigName);
-      if (aclConfigured == null) {
-        // If ACLs are not configured at all, we grant no access to anyone. So
-        // jobOwner and superuser/supergroup _only_ can do 'stuff'
-        aclConfigured = "";
-      }
-      acls.put(aclName, new AccessControlList(aclConfigured));
-    }
-    return acls;
-  }
-
-  /**
-   * If authorization is enabled on the JobTracker, checks whether the user (in
-   * the callerUGI) is authorized to perform the operation specify by
-   * 'jobOperation' on the job.
-   * <ul>
-   * <li>The owner of the job can do any operation on the job</li>
-   * <li>The superuser/supergroup of the JobTracker is always permitted to do
-   * operations on any job.</li>
-   * <li>For all other users/groups job-acls are checked</li>
-   * </ul>
-   * 
-   * @param jobStatus
-   * @param callerUGI
-   * @param jobOperation
-   */
-  void checkAccess(JobStatus jobStatus, UserGroupInformation callerUGI,
-      JobACL jobOperation) throws AccessControlException {
-
-    if (!jobTracker.isJobLevelAuthorizationEnabled()) {
-      return;
-    }
-
-    JobID jobId = jobStatus.getJobID();
-
-    // Check for superusers/supergroups
-    if (jobTracker.isSuperUserOrSuperGroup(callerUGI)) {
-      JobInProgress.LOG.info("superuser/supergroup "
-          + callerUGI.getShortUserName() + " trying to perform "
-          + jobOperation.toString() + " on " + jobId);
-      return;
-    }
-
-    // Job-owner is always part of all the ACLs
-    if (callerUGI.getShortUserName().equals(jobStatus.getUsername())) {
-      JobInProgress.LOG.info("Jobowner " + callerUGI.getShortUserName()
-          + " trying to perform " + jobOperation.toString() + " on "
-          + jobId);
-      return;
-    }
-
-    AccessControlList acl = jobStatus.getJobACLs().get(jobOperation);
-    if (acl.isUserAllowed(callerUGI)) {
-      JobInProgress.LOG.info("Normal user " + callerUGI.getShortUserName()
-          + " trying to perform " + jobOperation.toString() + " on "
-          + jobId);
-      return;
-    }
-
-    throw new AccessControlException(callerUGI
-        + " not authorized for performing the operation "
-        + jobOperation.toString() + " on " + jobId + ". "
-        + jobOperation.toString() + " configured for this job : "
-        + acl.toString());
-  }
-}
+	  protected abstract boolean isJobLevelAuthorizationEnabled();
+
+	  protected abstract boolean isSuperUserOrSuperGroup(
+	      UserGroupInformation callerUGI);
+
+	  /**
+	   * Construct the jobACLs from the configuration so that they can be kept in
+	   * the memory. If authorization is disabled on the JT, nothing is constructed
+	   * and an empty map is returned.
+	   * 
+	   * @return JobACL to AccessControlList map.
+	   */
+	  Map<JobACL, AccessControlList> constructJobACLs(JobConf conf) {
+	    
+	    Map<JobACL, AccessControlList> acls =
+	      new HashMap<JobACL, AccessControlList>();
+
+	    // Don't construct anything if authorization is disabled.
+	    if (!isJobLevelAuthorizationEnabled()) {
+	      return acls;
+	    }
+
+	    for (JobACL aclName : JobACL.values()) {
+	      String aclConfigName = aclName.getAclName();
+	      String aclConfigured = conf.get(aclConfigName);
+	      if (aclConfigured == null) {
+	        // If ACLs are not configured at all, we grant no access to anyone. So
+	        // jobOwner and superuser/supergroup _only_ can do 'stuff'
+	        aclConfigured = "";
+	      }
+	      acls.put(aclName, new AccessControlList(aclConfigured));
+	    }
+	    return acls;
+	  }
+
+	  /**
+	   * If authorization is enabled, checks whether the user (in the callerUGI) is
+	   * authorized to perform the operation specified by 'jobOperation' on the job.
+	   * <ul>
+	   * <li>The owner of the job can do any operation on the job</li>
+	   * <li>The superuser/supergroup is always permitted to do operations on any
+	   * job.</li>
+	   * <li>For all other users/groups job-acls are checked</li>
+	   * </ul>
+	   * 
+	   * @param jobStatus
+	   * @param callerUGI
+	   * @param jobOperation
+	   */
+	  void checkAccess(JobStatus jobStatus, UserGroupInformation callerUGI,
+	      JobACL jobOperation) throws AccessControlException {
+
+	    JobID jobId = jobStatus.getJobID();
+	    String jobOwner = jobStatus.getUsername();
+	    AccessControlList acl = jobStatus.getJobACLs().get(jobOperation);
+	    checkAccess(jobId, callerUGI, jobOperation, jobOwner, acl);
+	  }
+
+	  /**
+	   * If authorization is enabled, checks whether the user (in the callerUGI) is
+	   * authorized to perform the operation specified by 'jobOperation' on the job.
+	   * <ul>
+	   * <li>The owner of the job can do any operation on the job</li>
+	   * <li>The superuser/supergroup is always permitted to do operations on any
+	   * job.</li>
+	   * <li>For all other users/groups job-acls are checked</li>
+	   * </ul>
+	   * @param jobId
+	   * @param callerUGI
+	   * @param jobOperation
+	   * @param jobOwner
+	   * @param jobACL
+	   * @throws AccessControlException
+	   */
+	  void checkAccess(JobID jobId, UserGroupInformation callerUGI,
+	      JobACL jobOperation, String jobOwner, AccessControlList jobACL)
+	      throws AccessControlException {
+
+	    if (!isJobLevelAuthorizationEnabled()) {
+	      return;
+	    }
+
+	    // Check for superusers/supergroups
+	    if (isSuperUserOrSuperGroup(callerUGI)) {
+	      LOG.info("superuser/supergroupMember "
+	          + callerUGI.getShortUserName() + " trying to perform "
+	          + jobOperation.toString() + " on " + jobId);
+	      return;
+	    }
+
+	    // Job-owner is always part of all the ACLs
+	    if (callerUGI.getShortUserName().equals(jobOwner)) {
+	      LOG.info("Jobowner " + callerUGI.getShortUserName()
+	          + " trying to perform " + jobOperation.toString() + " on "
+	          + jobId);
+	      return;
+	    }
+
+	    
+	    if (jobACL.isUserAllowed(callerUGI)) {
+	      LOG.info("Normal user " + callerUGI.getShortUserName()
+	          + " trying to perform " + jobOperation.toString() + " on "
+	          + jobId);
+	      return;
+	    }
+
+	    throw new AccessControlException(callerUGI
+	        + " is not authorized for performing the operation "
+	        + jobOperation.toString() + " on " + jobId + ". "
+	        + jobOperation.toString()
+	        + " Access control list configured for this job : "
+	        + jobACL.toString());
+	  }
+	}

Modified: hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobClient.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobClient.java?rev=1077247&r1=1077246&r2=1077247&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobClient.java (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobClient.java Fri Mar  4 03:55:52 2011
@@ -1228,7 +1228,7 @@ public class JobClient extends Configure
   }
 
   static String getTaskLogURL(TaskAttemptID taskId, String baseUrl) {
-    return (baseUrl + "/tasklog?plaintext=true&taskid=" + taskId); 
+    return (baseUrl + "/tasklog?plaintext=true&attemptid=" + taskId); 
   }
   
   private static void displayTaskLogs(TaskAttemptID taskId, String baseUrl)

Modified: hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobConf.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobConf.java?rev=1077247&r1=1077246&r2=1077247&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobConf.java (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobConf.java Fri Mar  4 03:55:52 2011
@@ -163,6 +163,10 @@ public class JobConf extends Configurati
   static final String MAPRED_JOB_REDUCE_MEMORY_MB_PROPERTY =
       "mapred.job.reduce.memory.mb";
 
+  public static final String JOB_LEVEL_AUTHORIZATION_ENABLING_FLAG = 
+	    "mapreduce.cluster.job-authorization-enabled";
+  static final String MR_SUPERGROUP = "mapred.permissions.supergroup";
+
   /**
    * Configuration key to set the java command line options for the child
    * map and reduce tasks.

Modified: hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobInProgress.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobInProgress.java?rev=1077247&r1=1077246&r2=1077247&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobInProgress.java (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobInProgress.java Fri Mar  4 03:55:52 2011
@@ -1016,7 +1016,7 @@ public class JobInProgress {
           host = ttStatus.getHost();
         }
         httpTaskLogLocation = "http://" + host + ":" + ttStatus.getHttpPort(); 
-           //+ "/tasklog?plaintext=true&taskid=" + status.getTaskID();
+           //+ "/tasklog?plaintext=true&attemptid=" + status.getTaskID();
       }
 
       TaskCompletionEvent taskEvent = null;

Modified: hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobTracker.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobTracker.java?rev=1077247&r1=1077246&r2=1077247&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobTracker.java (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobTracker.java Fri Mar  4 03:55:52 2011
@@ -1930,7 +1930,7 @@ public class JobTracker implements MRCon
                                                 "expireLaunchingTasks");
 
   CompletedJobStatusStore completedJobStatusStore = null;
-  private JobACLsManager jobACLsManager;
+  private JobTrackerJobACLsManager jobACLsManager;
   Thread completedJobsStoreThread = null;
   RecoveryManager recoveryManager;
 
@@ -1969,7 +1969,7 @@ public class JobTracker implements MRCon
   static final String SUBDIR = "jobTracker";
   FileSystem fs = null;
   Path systemDir = null;
-  private JobConf conf;
+  JobConf conf;
   private final UserGroupInformation mrOwner;
   private final String supergroup;
 
@@ -2018,7 +2018,8 @@ public class JobTracker implements MRCon
       mrOwner = UserGroupInformation.getCurrentUser();
     }
   
-    supergroup = conf.get("mapred.permissions.supergroup", "supergroup");
+    supergroup = conf.get(JobConf.MR_SUPERGROUP,
+                          "supergroup");
     LOG.info("Starting jobtracker with owner as " + mrOwner.getShortUserName() 
              + " and supergroup as " + supergroup);
     long secretKeyInterval = 
@@ -2269,7 +2270,7 @@ public class JobTracker implements MRCon
         NetworkTopology.DEFAULT_HOST_LEVEL);
 
     // Initialize the jobACLSManager
-    jobACLsManager = new JobACLsManager(this);
+    jobACLsManager = new JobTrackerJobACLsManager(this);
     //initializes the job status store
     completedJobStatusStore = new CompletedJobStatusStore(jobACLsManager, conf);
   }
@@ -3750,7 +3751,7 @@ public class JobTracker implements MRCon
    * @return
    */
   boolean isJobLevelAuthorizationEnabled() {
-    return conf.getBoolean(JOB_LEVEL_AUTHORIZATION_ENABLING_FLAG, false);
+    return conf.getBoolean(JobConf.JOB_LEVEL_AUTHORIZATION_ENABLING_FLAG, false);
   }
 
   /**
@@ -4196,9 +4197,6 @@ public class JobTracker implements MRCon
   static final String MAPRED_CLUSTER_MAX_REDUCE_MEMORY_MB_PROPERTY =
       "mapred.cluster.max.reduce.memory.mb";
 
-  public static final String JOB_LEVEL_AUTHORIZATION_ENABLING_FLAG =
-      "mapreduce.cluster.job-authorization-enabled";
-
   /* 
    * Returns a list of TaskCompletionEvent for the given job, 
    * starting from fromEventId.
@@ -4561,13 +4559,14 @@ public class JobTracker implements MRCon
    * Is the calling user a super user? Or part of the supergroup?
    * @return true, if it is a super user
    */
-  boolean isSuperUserOrSuperGroup(UserGroupInformation callerUGI) {
-    if (mrOwner.getShortUserName().equals(callerUGI.getShortUserName())) {
+  static boolean isSuperUserOrSuperGroup(UserGroupInformation callerUGI,
+      UserGroupInformation superUser, String superGroup) {
+    if (superUser.getShortUserName().equals(callerUGI.getShortUserName())) {
       return true;
     }
     String[] groups = callerUGI.getGroupNames();
     for(int i=0; i < groups.length; ++i) {
-      if (groups[i].equals(supergroup)) {
+      if (groups[i].equals(superGroup)) {
         return true;
       }
     }
@@ -4580,7 +4579,8 @@ public class JobTracker implements MRCon
    */
   public synchronized void refreshNodes() throws IOException {
     // check access
-    if (!isSuperUserOrSuperGroup(UserGroupInformation.getCurrentUser())) {
+    if (!isSuperUserOrSuperGroup(UserGroupInformation.getCurrentUser(), mrOwner,
+                                 supergroup)) {
       String user = UserGroupInformation.getCurrentUser().getShortUserName();
       throw new AccessControlException(user + 
                                        " is not authorized to refresh nodes.");
@@ -4590,6 +4590,10 @@ public class JobTracker implements MRCon
     refreshHosts();
   }
   
+  UserGroupInformation getMROwner() {
+    return mrOwner;
+  }
+
   String getSuperGroup() {
     return supergroup;
   }

Added: hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobTrackerJobACLsManager.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobTrackerJobACLsManager.java?rev=1077247&view=auto
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobTrackerJobACLsManager.java (added)
+++ hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/JobTrackerJobACLsManager.java Fri Mar  4 03:55:52 2011
@@ -0,0 +1,49 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.
+ */
+package org.apache.hadoop.mapred;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.security.UserGroupInformation;
+
+/**
+ * Manages the job ACLs and the operations on them at JobTracker.
+ *
+ */
+public class JobTrackerJobACLsManager extends JobACLsManager {
+
+  static final Log LOG = LogFactory.getLog(JobTrackerJobACLsManager.class);
+
+  private JobTracker jobTracker = null;
+
+  public JobTrackerJobACLsManager(JobTracker tracker) {
+    jobTracker = tracker;
+  }
+
+  @Override
+  protected boolean isJobLevelAuthorizationEnabled() {
+    return jobTracker.isJobLevelAuthorizationEnabled();
+  }
+
+  @Override
+  protected boolean isSuperUserOrSuperGroup(UserGroupInformation callerUGI) {
+    return JobTracker.isSuperUserOrSuperGroup(callerUGI,
+        jobTracker.getMROwner(), jobTracker.getSuperGroup());
+  }
+
+}

Modified: hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskGraphServlet.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskGraphServlet.java?rev=1077247&r1=1077246&r2=1077247&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskGraphServlet.java (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskGraphServlet.java Fri Mar  4 03:55:52 2011
@@ -19,12 +19,14 @@ package org.apache.hadoop.mapred;
 
 import java.io.IOException;
 import java.io.PrintWriter;
-
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.hadoop.mapred.JSPUtil.JobWithViewAccessCheck;
+import org.apache.hadoop.security.UserGroupInformation;
+
 /** The servlet that outputs svg graphics for map / reduce task
  *  statuses
  */
@@ -52,13 +54,20 @@ public class TaskGraphServlet extends Ht
 
     response.setContentType("image/svg+xml");
 
-    JobTracker tracker = 
+    final JobTracker tracker = 
       (JobTracker) getServletContext().getAttribute("job.tracker");
     
     String jobIdStr = request.getParameter("jobid");
     if(jobIdStr == null)
       return;
-    JobID jobId = JobID.forName(jobIdStr);
+    final JobID jobId = JobID.forName(jobIdStr);
+
+    // verify if user has view access for this job
+    JobWithViewAccessCheck myJob = JSPUtil.checkAccessAndGetJob(
+        tracker, jobId, request, response);
+    if (!myJob.isViewJobAllowed()) {
+      return;// user is not authorized to view this job
+    }
 
     final boolean isMap = "map".equalsIgnoreCase(request.getParameter("type"));
     final TaskReport[] reports = isMap? tracker.getMapTaskReports(jobId) 

Modified: hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskInProgress.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskInProgress.java?rev=1077247&r1=1077246&r2=1077247&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskInProgress.java (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskInProgress.java Fri Mar  4 03:55:52 2011
@@ -21,7 +21,6 @@ package org.apache.hadoop.mapred;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Comparator;
 import java.util.Iterator;
 import java.util.List;

Modified: hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskLogServlet.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskLogServlet.java?rev=1077247&r1=1077246&r2=1077247&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskLogServlet.java (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskLogServlet.java Fri Mar  4 03:55:52 2011
@@ -22,11 +22,19 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 
+import javax.servlet.ServletContext;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.mapreduce.JobACL;
+import org.apache.hadoop.mapreduce.JobContext;
+import org.apache.hadoop.security.AccessControlException;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.security.authorize.AccessControlList;
 import org.apache.hadoop.http.HtmlQuoting;
 import org.apache.hadoop.util.StringUtils;
 
@@ -51,7 +59,7 @@ public class TaskLogServlet extends Http
   public static String getTaskLogUrl(String taskTrackerHostName,
       String httpPort, String taskAttemptID) {
     return ("http://" + taskTrackerHostName + ":" + httpPort
-        + "/tasklog?taskid=" + taskAttemptID);
+        + "/tasklog?attemptid=" + taskAttemptID);
   }
 
   private void printTaskLog(HttpServletResponse response,
@@ -103,6 +111,42 @@ public class TaskLogServlet extends Http
   }
 
   /**
+   * Validates if the given user has job view permissions for this job.
+   * conf contains jobOwner and job-view-ACLs.
+   * We allow jobOwner, superUser(i.e. mrOwner) and members of superGroup and
+   * users and groups specified in configuration using
+   * mapreduce.job.acl-view-job to view job.
+   */
+  private void checkAccessForTaskLogs(JobConf conf, String user, JobID jobId,
+      TaskTracker tracker) throws AccessControlException {
+
+    if (!tracker.isJobLevelAuthorizationEnabled()) {
+      return;
+    }
+
+    // buiild job view acl by reading from conf
+    AccessControlList jobViewACL = tracker.getJobACLsManager().
+        constructJobACLs(conf).get(JobACL.VIEW_JOB);
+
+    String jobOwner = conf.get("user.name");
+    UserGroupInformation callerUGI = UserGroupInformation.createRemoteUser(user);
+
+    tracker.getJobACLsManager().checkAccess(jobId, callerUGI, JobACL.VIEW_JOB,
+        jobOwner, jobViewACL);
+  }
+
+  /**
+   * Builds a Configuration object by reading the xml file.
+   * This doesn't load the default resources.
+   */
+  static Configuration getConfFromJobACLsFile(String attemptIdStr) {
+    Configuration conf = new Configuration(false);
+    conf.addResource(new Path(TaskLog.getBaseDir(attemptIdStr).toString(),
+        TaskRunner.jobACLsFile));
+    return conf;
+  }
+
+  /**
    * Get the logs via http.
    */
   @Override
@@ -115,13 +159,35 @@ public class TaskLogServlet extends Http
     TaskLog.LogName filter = null;
     boolean isCleanup = false;
 
-    String taskIdStr = request.getParameter("taskid");
-    if (taskIdStr == null) {
+    String attemptIdStr = request.getParameter("attemptid");
+    if (attemptIdStr == null) {
       response.sendError(HttpServletResponse.SC_BAD_REQUEST, 
-                         "Argument taskid is required");
+                         "Argument attemptid is required");
       return;
     }
-    TaskAttemptID taskId = TaskAttemptID.forName(taskIdStr);
+
+    TaskAttemptID attemptId = TaskAttemptID.forName(attemptIdStr);
+
+    // get user name who is accessing
+    String user = request.getRemoteUser();
+    if (user != null) {
+      // get jobACLConf from ACLs file
+      JobConf jobACLConf = new JobConf(getConfFromJobACLsFile(attemptIdStr));
+      ServletContext context = getServletContext();
+      TaskTracker taskTracker = (TaskTracker) context.getAttribute(
+          "task.tracker");
+      JobID jobId = attemptId.getJobID();
+
+      try {
+        checkAccessForTaskLogs(jobACLConf, user, jobId, taskTracker);
+      } catch (AccessControlException e) {
+        String errMsg = "User " + user + " failed to view tasklogs of job " +
+            jobId + "!\n\n" + e.getMessage();
+        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, errMsg);
+        return;
+      }
+    }
+
     String logFilter = request.getParameter("filter");
     if (logFilter != null) {
       try {
@@ -157,27 +223,27 @@ public class TaskLogServlet extends Http
     OutputStream out = response.getOutputStream();
     if( !plainText ) {
       out.write(("<html>\n" +
-                 "<title>Task Logs: '" + taskId + "'</title>\n" +
+                 "<title>Task Logs: '" + attemptId + "'</title>\n" +
                  "<body>\n" +
-                 "<h1>Task Logs: '" +  taskId +  "'</h1><br>\n").getBytes()); 
+                 "<h1>Task Logs: '" +  attemptId +  "'</h1><br>\n").getBytes()); 
 
       if (filter == null) {
-        printTaskLog(response, out, taskId, start, end, plainText, 
+        printTaskLog(response, out, attemptId, start, end, plainText, 
                      TaskLog.LogName.STDOUT, isCleanup);
-        printTaskLog(response, out, taskId, start, end, plainText, 
+        printTaskLog(response, out, attemptId, start, end, plainText, 
                      TaskLog.LogName.STDERR, isCleanup);
-        printTaskLog(response, out, taskId, start, end, plainText, 
+        printTaskLog(response, out, attemptId, start, end, plainText, 
                      TaskLog.LogName.SYSLOG, isCleanup);
-        if (haveTaskLog(taskId, TaskLog.LogName.DEBUGOUT)) {
-          printTaskLog(response, out, taskId, start, end, plainText, 
+        if (haveTaskLog(attemptId, TaskLog.LogName.DEBUGOUT)) {
+          printTaskLog(response, out, attemptId, start, end, plainText, 
                        TaskLog.LogName.DEBUGOUT, isCleanup);
         }
-        if (haveTaskLog(taskId, TaskLog.LogName.PROFILE)) {
-          printTaskLog(response, out, taskId, start, end, plainText, 
+        if (haveTaskLog(attemptId, TaskLog.LogName.PROFILE)) {
+          printTaskLog(response, out, attemptId, start, end, plainText, 
                        TaskLog.LogName.PROFILE, isCleanup);
         }
       } else {
-        printTaskLog(response, out, taskId, start, end, plainText, filter,
+        printTaskLog(response, out, attemptId, start, end, plainText, filter,
                      isCleanup);
       }
       
@@ -187,7 +253,7 @@ public class TaskLogServlet extends Http
       response.sendError(HttpServletResponse.SC_BAD_REQUEST,
           "You must supply a value for `filter' (STDOUT, STDERR, or SYSLOG) if you set plainText = true");
     } else {
-      printTaskLog(response, out, taskId, start, end, plainText, filter, 
+      printTaskLog(response, out, attemptId, start, end, plainText, filter, 
                    isCleanup);
     } 
   }

Modified: hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskRunner.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskRunner.java?rev=1077247&r1=1077246&r2=1077247&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskRunner.java (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskRunner.java Fri Mar  4 03:55:52 2011
@@ -19,6 +19,7 @@ package org.apache.hadoop.mapred;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.PrintStream;
@@ -33,6 +34,7 @@ import java.util.Vector;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.filecache.DistributedCache;
 import org.apache.hadoop.filecache.TaskDistributedCacheManager;
 import org.apache.hadoop.filecache.TrackerDistributedCacheManager;
@@ -78,6 +80,8 @@ abstract class TaskRunner extends Thread
    */
   protected MapOutputFile mapOutputFile;
 
+  static String jobACLsFile = "job-acl.xml";
+
   public TaskRunner(TaskTracker.TaskInProgress tip, TaskTracker tracker, 
       JobConf conf) {
     this.tip = tip;
@@ -264,8 +268,9 @@ abstract class TaskRunner extends Thread
    * 
    * @param taskid
    * @return an array of files. The first file is stdout, the second is stderr.
+   * @throws IOException 
    */
-  static File[] prepareLogFiles(TaskAttemptID taskid) {
+  File[] prepareLogFiles(TaskAttemptID taskid) throws IOException {
     File[] logFiles = new File[2];
     logFiles[0] = TaskLog.getTaskLogFile(taskid, TaskLog.LogName.STDOUT);
     logFiles[1] = TaskLog.getTaskLogFile(taskid, TaskLog.LogName.STDERR);
@@ -277,9 +282,34 @@ abstract class TaskRunner extends Thread
       Localizer.PermissionsHandler.setPermissions(logDir,
           Localizer.PermissionsHandler.sevenZeroZero);
     }
+    // write job acls into a file to know the access for task logs
+    writeJobACLs(logDir);
     return logFiles;
   }
 
+  // Writes job-view-acls and user name into an xml file
+  private void writeJobACLs(File logDir) throws IOException {
+    File aclFile = new File(logDir, TaskRunner.jobACLsFile);
+    Configuration aclConf = new Configuration(false);
+
+    // set the job view acls in aclConf
+    String jobViewACLs = conf.get(JobContext.JOB_ACL_VIEW_JOB);
+    if (jobViewACLs != null) {
+      aclConf.set(JobContext.JOB_ACL_VIEW_JOB, jobViewACLs);
+    }
+    // set jobOwner as mapreduce.job.user.name in aclConf
+    String jobOwner = conf.getUser();
+    aclConf.set("user.name", jobOwner);
+    FileOutputStream out = new FileOutputStream(aclFile);
+    try {
+      aclConf.writeXml(out);
+    } finally {
+      out.close();
+    }
+    Localizer.PermissionsHandler.setPermissions(aclFile,
+        Localizer.PermissionsHandler.sevenZeroZero);
+  }
+
   /**
    * Write the child's configuration to the disk and set it in configuration so
    * that the child can pick it up from there.

Modified: hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskTracker.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskTracker.java?rev=1077247&r1=1077246&r2=1077247&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskTracker.java (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskTracker.java Fri Mar  4 03:55:52 2011
@@ -247,6 +247,10 @@ public class TaskTracker 
   private int maxMapSlots;
   private int maxReduceSlots;
   private int failures;
+
+  // MROwner's ugi
+  private UserGroupInformation mrOwner;
+  private String supergroup;
   
   // Performance-related config knob to send an out-of-band heartbeat
   // on task completion
@@ -275,6 +279,9 @@ public class TaskTracker 
   static final String MAPRED_TASKTRACKER_MEMORY_CALCULATOR_PLUGIN_PROPERTY =
       "mapred.tasktracker.memory_calculator_plugin";
 
+  // Manages job acls of jobs in TaskTracker
+  private TaskTrackerJobACLsManager jobACLsManager;
+
   /**
    * the minimum interval between jobtracker polls
    */
@@ -578,18 +585,22 @@ public class TaskTracker 
   synchronized void initialize() throws IOException, InterruptedException {
     this.fConf = new JobConf(originalConf);
     String keytabFilename = fConf.get(TT_KEYTAB_FILE);
-    UserGroupInformation ttUgi;
     UserGroupInformation.setConfiguration(fConf);
     if (keytabFilename != null) {
       String desiredUser = fConf.get(TT_USER_NAME,
                                     System.getProperty("user.name"));
       UserGroupInformation.loginUserFromKeytab(desiredUser,
                                                keytabFilename);
-      ttUgi = UserGroupInformation.getLoginUser();
+      mrOwner = UserGroupInformation.getLoginUser();
 
     } else {
-      ttUgi = UserGroupInformation.getCurrentUser();
+      mrOwner = UserGroupInformation.getCurrentUser();
     }
+    supergroup = fConf.get(JobConf.MR_SUPERGROUP,
+                           "supergroup");
+    LOG.info("Starting tasktracker with owner as " + mrOwner.getShortUserName()
+             + " and supergroup as " + supergroup);
+
     localFs = FileSystem.getLocal(fConf);
     if (fConf.get("slave.host.name") != null) {
       this.localHostname = fConf.get("slave.host.name");
@@ -685,7 +696,7 @@ public class TaskTracker 
         this.fConf, taskController);
 
     this.jobClient = (InterTrackerProtocol) 
-    ttUgi.doAs(new PrivilegedExceptionAction<Object>() {
+    mrOwner.doAs(new PrivilegedExceptionAction<Object>() {
       public Object run() throws IOException {
         return RPC.waitForProxy(InterTrackerProtocol.class,
             InterTrackerProtocol.versionID,
@@ -727,6 +738,22 @@ public class TaskTracker 
       fConf.getBoolean(TT_OUTOFBAND_HEARBEAT, false);
   }
 
+  UserGroupInformation getMROwner() {
+    return mrOwner;
+  }
+
+  String getSuperGroup() {
+    return supergroup;
+  }
+  
+  /**
+   * Is job level authorization enabled on the TT ?
+   */
+  boolean isJobLevelAuthorizationEnabled() {
+    return fConf.getBoolean(
+        JobConf.JOB_LEVEL_AUTHORIZATION_ENABLING_FLAG, false);
+  }
+
   public static Class<? extends TaskTrackerInstrumentation> getInstrumentationClass(
     Configuration conf) {
     return conf.getClass("mapred.tasktracker.instrumentation",
@@ -1229,10 +1256,13 @@ public class TaskTracker 
     server.setAttribute("localDirAllocator", localDirAllocator);
     server.setAttribute("shuffleServerMetrics", shuffleServerMetrics);
     server.addInternalServlet("mapOutput", "/mapOutput", MapOutputServlet.class);
-    server.addInternalServlet("taskLog", "/tasklog", TaskLogServlet.class);
+    server.addServlet("taskLog", "/tasklog", TaskLogServlet.class);
     server.start();
     this.httpPort = server.getPort();
     checkJettyPort(httpPort);
+    
+    // Initialize the jobACLSManager
+    jobACLsManager = new TaskTrackerJobACLsManager(this);
     initialize();
   }
 
@@ -3793,4 +3823,7 @@ public class TaskTracker 
       return localJobTokenFileStr;
     }
 
+    TaskTrackerJobACLsManager getJobACLsManager() {
+      return jobACLsManager;
+    }
 }

Added: hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskTrackerJobACLsManager.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskTrackerJobACLsManager.java?rev=1077247&view=auto
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskTrackerJobACLsManager.java (added)
+++ hadoop/common/branches/branch-0.20-security-patches/src/mapred/org/apache/hadoop/mapred/TaskTrackerJobACLsManager.java Fri Mar  4 03:55:52 2011
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.
+ */
+package org.apache.hadoop.mapred;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.security.UserGroupInformation;
+
+/**
+ * Manages the job ACLs and the operations on them at TaskTracker.
+ *
+ */
+public class TaskTrackerJobACLsManager extends JobACLsManager {
+
+  static final Log LOG = LogFactory.getLog(TaskTrackerJobACLsManager.class);
+
+  private TaskTracker taskTracker = null;
+
+  public TaskTrackerJobACLsManager(TaskTracker tracker) {
+    taskTracker = tracker;
+  }
+
+  @Override
+  protected boolean isJobLevelAuthorizationEnabled() {
+    return taskTracker.isJobLevelAuthorizationEnabled();
+  }
+
+  @Override
+  protected boolean isSuperUserOrSuperGroup(UserGroupInformation callerUGI) {
+    return JobTracker.isSuperUserOrSuperGroup(callerUGI,
+        taskTracker.getMROwner(), taskTracker.getSuperGroup());
+  }
+}

Modified: hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/ClusterMapReduceTestCase.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/ClusterMapReduceTestCase.java?rev=1077247&r1=1077246&r2=1077247&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/ClusterMapReduceTestCase.java (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/ClusterMapReduceTestCase.java Fri Mar  4 03:55:52 2011
@@ -82,7 +82,8 @@ public abstract class ClusterMapReduceTe
 
       ConfigurableMiniMRCluster.setConfiguration(props);
       //noinspection deprecation
-      mrCluster = new ConfigurableMiniMRCluster(2, getFileSystem().getName(), 1);
+      mrCluster = new ConfigurableMiniMRCluster(2, getFileSystem().getName(),
+                                                1, conf);
     }
   }
 
@@ -94,8 +95,9 @@ public abstract class ClusterMapReduceTe
     }
 
     public ConfigurableMiniMRCluster(int numTaskTrackers, String namenode,
-                                     int numDir) throws Exception {
-      super(numTaskTrackers, namenode, numDir);
+                                     int numDir, JobConf conf)
+        throws Exception {
+      super(0,0, numTaskTrackers, namenode, numDir, null, null, null, conf);
     }
 
     public JobConf createJobConf() {

Modified: hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/TestJobACLs.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/TestJobACLs.java?rev=1077247&r1=1077246&r2=1077247&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/TestJobACLs.java (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/TestJobACLs.java Fri Mar  4 03:55:52 2011
@@ -71,7 +71,7 @@ public class TestJobACLs {
     JobConf conf = new JobConf();
 
     // Enable job-level authorization
-    conf.setBoolean(JobTracker.JOB_LEVEL_AUTHORIZATION_ENABLING_FLAG, true);
+    conf.setBoolean(JobConf.JOB_LEVEL_AUTHORIZATION_ENABLING_FLAG, true);
 
     // Enable CompletedJobStore
     FileSystem fs = FileSystem.getLocal(conf);

Modified: hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/TestNodeRefresh.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/TestNodeRefresh.java?rev=1077247&r1=1077246&r2=1077247&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/TestNodeRefresh.java (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/TestNodeRefresh.java Fri Mar  4 03:55:52 2011
@@ -210,7 +210,7 @@ public class TestNodeRefresh extends Tes
     // start a cluster with 1 host and specified superuser and supergroup
     Configuration conf = new Configuration();
     // set the supergroup
-    conf.set("mapred.permissions.supergroup", "abc");
+    conf.set("mapreduce.cluster.permissions.supergroup", "abc");
     startCluster(2, 1, 0, UserGroupInformation.createRemoteUser("user1"), conf);
     
     conf = mr.createJobConf(new JobConf(conf));

Modified: hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/TestTaskTrackerLocalization.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/TestTaskTrackerLocalization.java?rev=1077247&r1=1077246&r2=1077247&view=diff
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/TestTaskTrackerLocalization.java (original)
+++ hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/TestTaskTrackerLocalization.java Fri Mar  4 03:55:52 2011
@@ -28,6 +28,7 @@ import junit.framework.TestCase;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.FileUtil;
 import org.apache.hadoop.fs.LocalDirAllocator;
@@ -457,6 +458,11 @@ public class TestTaskTrackerLocalization
     localizedJobConf = tracker.localizeJobFiles(task, 
         new TaskTracker.RunningJob(task.getJobID()));
 
+    // Set job view ACLs in conf sothat validation of contents of jobACLsFile
+    // can be done against this value. Have both users and groups
+    String jobViewACLs = "user1,user2, group1,group2";
+    localizedJobConf.set(JobContext.JOB_ACL_VIEW_JOB, jobViewACLs);
+
     // Now initialize the job via task-controller so as to set
     // ownership/permissions of jars, job-work-dir
     JobInitializationContext jobContext = new JobInitializationContext();
@@ -496,7 +502,7 @@ public class TestTaskTrackerLocalization
     runner.setupChildTaskConfiguration(lDirAlloc);
     TaskRunner.createChildTmpDir(new File(attemptWorkDir.toUri().getPath()),
         localizedJobConf);
-    attemptLogFiles = TaskRunner.prepareLogFiles(task.getTaskID());
+    attemptLogFiles = runner.prepareLogFiles(task.getTaskID());
 
     // Make sure the task-conf file is created
     Path localTaskFile =
@@ -566,6 +572,24 @@ public class TestTaskTrackerLocalization
         + expectedStderr.toString() + " Observed : "
         + attemptLogFiles[1].toString(), expectedStderr.toString().equals(
         attemptLogFiles[1].toString()));
+
+    // Make sure that the job ACLs file exists in the task log dir
+    File jobACLsFile = new File(logDir, TaskRunner.jobACLsFile);
+    assertTrue("JobACLsFile is missing in the task log dir " + logDir,
+        jobACLsFile.exists());
+
+    // With default task controller, the job-acls file is owned by TT and
+    // permissions are 700
+    checkFilePermissions(jobACLsFile.getAbsolutePath(), "-rwx------",
+        taskTrackerUGI.getShortUserName(), taskTrackerUGI.getGroupNames()[0]);
+
+    // Validate the contents of jobACLsFile(both user name and job-view-acls)
+    Configuration jobACLsConf =
+        TaskLogServlet.getConfFromJobACLsFile(task.getTaskID().toString());
+    assertTrue(jobACLsConf.get("user.name").equals(
+        localizedJobConf.getUser()));
+    assertTrue(jobACLsConf.get(JobContext.JOB_ACL_VIEW_JOB).
+        equals(localizedJobConf.get(JobContext.JOB_ACL_VIEW_JOB)));
   }
 
   /**
@@ -711,7 +735,7 @@ public class TestTaskTrackerLocalization
     runner.setupChildTaskConfiguration(lDirAlloc);
     TaskRunner.createChildTmpDir(new File(workDir.toUri().getPath()),
         localizedJobConf);
-    TaskRunner.prepareLogFiles(task.getTaskID());
+    runner.prepareLogFiles(task.getTaskID());
     Path localTaskFile =
         lDirAlloc.getLocalPathToRead(TaskTracker.getTaskConfFile(task
             .getUser(), task.getJobID().toString(), task.getTaskID()

Added: hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/TestWebUIAuthorization.java
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/TestWebUIAuthorization.java?rev=1077247&view=auto
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/TestWebUIAuthorization.java (added)
+++ hadoop/common/branches/branch-0.20-security-patches/src/test/org/apache/hadoop/mapred/TestWebUIAuthorization.java Fri Mar  4 03:55:52 2011
@@ -0,0 +1,628 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.
+ */
+package org.apache.hadoop.mapred;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URL;
+import java.net.HttpURLConnection;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.hadoop.fs.CommonConfigurationKeys;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.http.TestHttpServer.DummyFilterInitializer;
+import org.apache.hadoop.mapreduce.JobContext;
+import org.apache.hadoop.mapreduce.Job;
+import org.apache.hadoop.examples.SleepJob;
+import org.apache.hadoop.security.Groups;
+import org.apache.hadoop.security.ShellBasedUnixGroupsMapping;
+import org.apache.hadoop.security.UserGroupInformation;
+import org.junit.Test;
+
+import java.security.PrivilegedExceptionAction;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+public class TestWebUIAuthorization extends ClusterMapReduceTestCase {
+
+  private static final Log LOG = LogFactory.getLog(
+      TestWebUIAuthorization.class);
+
+  // user1 submits the jobs
+  private static final String jobSubmitter = "user1";
+  // mrOwner starts the cluster
+  private static String mrOwner = null;
+  // member of supergroup
+  private static final String superGroupMember = "user2";
+  // "colleague1" is there in job-view-acls config
+  private static final String viewColleague = "colleague1";
+  // "colleague2" is there in job-modify-acls config
+  private static final String modifyColleague = "colleague2";
+  // "colleague3" is there in both job-view-acls and job-modify-acls
+  private static final String viewAndModifyColleague = "colleague3";
+  // "evilJohn" is not having view/modify access on the jobs
+  private static final String unauthorizedUser = "evilJohn";
+
+  protected void setUp() throws Exception {
+    // do not do anything
+  };
+
+  /** access a url, ignoring some IOException such as the page does not exist */
+  static int getHttpStatusCode(String urlstring, String userName,
+      String method) throws IOException {
+    LOG.info("Accessing " + urlstring + " as user " + userName);
+    URL url = new URL(urlstring + "&user.name=" + userName);
+    HttpURLConnection connection = (HttpURLConnection)url.openConnection();
+    connection.setRequestMethod(method);
+    if (method.equals("POST")) {
+      String encodedData = "action=kill&user.name=" + userName;      
+      connection.setRequestProperty("Content-Type",
+                                    "application/x-www-form-urlencoded");
+      connection.setRequestProperty("Content-Length",
+                                    Integer.toString(encodedData.length()));
+      connection.setDoOutput(true);
+
+      OutputStream os = connection.getOutputStream();
+      os.write(encodedData.getBytes());
+    }
+    connection.connect();
+
+    return connection.getResponseCode();
+  }
+
+  public static class MyGroupsProvider extends ShellBasedUnixGroupsMapping {
+    static Map<String, List<String>> mapping = new HashMap<String, List<String>>();
+
+    @Override
+    public List<String> getGroups(String user) throws IOException {
+      return mapping.get(user);
+    }
+  }
+
+  /**
+   * Validates the given jsp/servlet against different user names who
+   * can(or cannot) view the job.
+   * (1) jobSubmitter can view the job
+   * (2) superGroupMember can view the job
+   * (3) user mentioned in job-view-acls should be able to view the job
+   * (4) user mentioned in job-modify-acls but not in job-view-acls
+   *     cannot view the job
+   * (5) other unauthorized users cannot view the job
+   */
+  private void validateViewJob(String url, String method) throws IOException {
+    assertEquals(HttpURLConnection.HTTP_OK,
+        getHttpStatusCode(url, jobSubmitter, method));
+    assertEquals(HttpURLConnection.HTTP_OK,
+        getHttpStatusCode(url, superGroupMember, method));
+    assertEquals(HttpURLConnection.HTTP_OK,
+        getHttpStatusCode(url, mrOwner, method));
+    assertEquals(HttpURLConnection.HTTP_OK,
+        getHttpStatusCode(url, viewColleague, method));
+    assertEquals(HttpURLConnection.HTTP_OK,
+        getHttpStatusCode(url, viewAndModifyColleague, method));
+    assertEquals(HttpURLConnection.HTTP_UNAUTHORIZED,
+        getHttpStatusCode(url, modifyColleague, method));
+    assertEquals(HttpURLConnection.HTTP_UNAUTHORIZED,
+        getHttpStatusCode(url, unauthorizedUser, method));
+  }
+
+  /**
+   * Validates the given jsp/servlet against different user names who
+   * can(or cannot) modify the job.
+   * (1) jobSubmitter and superGroupMember can modify the job. But we are not
+   *     validating this in this method. Let the caller explicitly validate
+   *     this, if needed.
+   * (2) user mentioned in job-view-acls but not in job-modify-acls cannot
+   *     modify the job
+   * (3) user mentioned in job-modify-acls (irrespective of job-view-acls)
+   *     can modify the job
+   * (4) other unauthorized users cannot modify the job
+   */
+  private void validateModifyJob(String url, String method) throws IOException {
+    assertEquals(HttpURLConnection.HTTP_UNAUTHORIZED,
+        getHttpStatusCode(url, viewColleague, method));
+    assertEquals(HttpURLConnection.HTTP_UNAUTHORIZED,
+        getHttpStatusCode(url, unauthorizedUser, method));
+    assertEquals(HttpURLConnection.HTTP_OK,
+        getHttpStatusCode(url, modifyColleague, method));
+  }
+
+  // starts a sleep job with 1 map task that runs for a long time
+  private RunningJob startSleepJobAsUser(String user, final JobConf conf)
+      throws Exception {
+	final SleepJob sleepJob = new SleepJob();
+    sleepJob.setConf(conf);
+    UserGroupInformation jobSubmitterUGI = 
+        UserGroupInformation.createRemoteUser(user);
+    RunningJob job =
+        jobSubmitterUGI.doAs(new PrivilegedExceptionAction<RunningJob>() {
+        public RunningJob run() throws Exception {
+          JobClient jobClient = new JobClient(conf);
+          SleepJob sleepJob = new SleepJob();
+          sleepJob.setConf(conf);
+          JobConf jobConf =
+              sleepJob.setupJobConf(1, 0, 900000, 1, 1000, 1000);
+          RunningJob runningJob = jobClient.submitJob(jobConf);
+          return runningJob;
+        }
+      });
+    return job;
+  }
+
+  // Waits till the map task gets started and gets its tipId from map reports
+  // and returns the tipId
+  private TaskID getTIPId(MiniMRCluster cluster,
+      org.apache.hadoop.mapreduce.JobID jobid) throws Exception {
+    JobClient client = new JobClient(cluster.createJobConf());
+    JobID jobId = (JobID) jobid;
+    TaskReport[] mapReports = null;
+
+    TaskID tipId = null;
+    do { // make sure that the map task is running
+      Thread.sleep(200);
+      mapReports = client.getMapTaskReports(jobId);
+    } while (mapReports.length == 0);
+
+    for (TaskReport r : mapReports) {
+      tipId = r.getTaskID();
+      break;// because we have only one map
+    }
+    return tipId;
+  }
+
+  /**
+   * Make sure that the given user can do killJob using jobdetails.jsp url
+   * @param cluster
+   * @param conf
+   * @param jtURL
+   * @param jobTrackerJSP
+   * @param user
+   * @throws Exception
+   */
+  private void confirmJobDetailsJSPKillJobAsUser(MiniMRCluster cluster,
+      JobConf conf, String jtURL, String jobTrackerJSP, String user)
+      throws Exception {
+    RunningJob job = startSleepJobAsUser(jobSubmitter, conf);
+    org.apache.hadoop.mapreduce.JobID jobid = job.getID();
+    getTIPId(cluster, jobid);// wait till the map task is started
+    // jobDetailsJSP killJob url
+    String url = jtURL + "/jobdetails.jsp?" +
+        "action=kill&jobid="+ jobid.toString();
+    try {
+      assertEquals(HttpURLConnection.HTTP_OK,
+          getHttpStatusCode(url, user, "POST"));
+    } finally {
+      if (!job.isComplete()) {
+        LOG.info("Killing job " + jobid + " from finally block");
+        assertEquals(HttpURLConnection.HTTP_OK,
+            getHttpStatusCode(jobTrackerJSP + "&killJobs=true&jobCheckBox=" +
+            jobid.toString(), jobSubmitter, "GET"));
+      }
+    }
+  }
+
+  /**
+   * Starts a sleep job and tries to kill the job using jobdetails.jsp as
+   * (1) viewColleague (2) unauthorizedUser (3) modifyColleague
+   * (4) viewAndModifyColleague (5) mrOwner (6) superGroupMember and
+   * (7) jobSubmitter
+   *
+   * Validates the given jsp/servlet against different user names who
+   * can(or cannot) do both view and modify on the job.
+   * (1) jobSubmitter, mrOwner and superGroupMember can do both view and modify
+   *     on the job. But we are not validating this in this method. Let the
+   *     caller explicitly validate this, if needed.
+   * (2) user mentioned in job-view-acls but not in job-modify-acls cannot
+   *     do this
+   * (3) user mentioned in job-modify-acls but not in job-view-acls cannot
+   *     do this
+   * (4) other unauthorized users cannot do this
+   *
+   * @throws Exception
+   */
+  private void validateJobDetailsJSPKillJob(MiniMRCluster cluster,
+      JobConf clusterConf, String jtURL) throws Exception {
+
+    JobConf conf = new JobConf(cluster.createJobConf());
+    conf.set(JobContext.JOB_ACL_VIEW_JOB, viewColleague + " group3");
+
+    // Let us add group1 and group3 to modify-job-acl. So modifyColleague and
+    // viewAndModifyColleague will be able to modify the job
+    conf.set(JobContext.JOB_ACL_MODIFY_JOB, " group1,group3");
+
+    String jobTrackerJSP =  jtURL + "/jobtracker.jsp?a=b";
+    RunningJob job = startSleepJobAsUser(jobSubmitter, conf);
+    org.apache.hadoop.mapreduce.JobID jobid = job.getID();
+    getTIPId(cluster, jobid);// wait till the map task is started
+    // jobDetailsJSPKillJobAction url
+    String url = jtURL + "/jobdetails.jsp?" +
+        "action=kill&jobid="+ jobid.toString();
+    try {
+      assertEquals(HttpURLConnection.HTTP_UNAUTHORIZED,
+          getHttpStatusCode(url, viewColleague, "POST"));
+      assertEquals(HttpURLConnection.HTTP_UNAUTHORIZED,
+          getHttpStatusCode(url, unauthorizedUser, "POST"));
+      assertEquals(HttpURLConnection.HTTP_UNAUTHORIZED,
+          getHttpStatusCode(url, modifyColleague, "POST"));
+      assertEquals(HttpURLConnection.HTTP_OK,
+          getHttpStatusCode(url, viewAndModifyColleague, "POST"));
+    } finally {
+      if (!job.isComplete()) {
+        LOG.info("Killing job " + jobid + " from finally block");
+        assertEquals(HttpURLConnection.HTTP_OK,
+            getHttpStatusCode(jobTrackerJSP + "&killJobs=true&jobCheckBox=" +
+            jobid.toString(), jobSubmitter, "GET"));
+      }
+    }
+
+    // check if jobSubmitter, mrOwner and superGroupMember can do killJob
+    // using jobdetails.jsp url
+    confirmJobDetailsJSPKillJobAsUser(cluster, conf, jtURL, jobTrackerJSP,
+                                       jobSubmitter);
+    confirmJobDetailsJSPKillJobAsUser(cluster, conf, jtURL, jobTrackerJSP,
+                                       mrOwner);
+    confirmJobDetailsJSPKillJobAsUser(cluster, conf, jtURL, jobTrackerJSP,
+                                       superGroupMember);
+  }
+
+  /**
+   * Make sure that the given user can do killJob using jobtracker.jsp url
+   * @param cluster
+   * @param conf
+   * @param jtURL
+   * @param user
+   * @throws Exception
+   */
+  private void confirmJobTrackerJSPKillJobAsUser(MiniMRCluster cluster,
+      JobConf conf, String jtURL, String user)
+      throws Exception {
+    String jobTrackerJSP =  jtURL + "/jobtracker.jsp?a=b";
+    RunningJob job = startSleepJobAsUser(jobSubmitter, conf);
+    org.apache.hadoop.mapreduce.JobID jobid = job.getID();
+    getTIPId(cluster, jobid);// wait till the map task is started
+    // jobTrackerJSP killJob url
+    String url = jobTrackerJSP +
+        "&killJobs=true&jobCheckBox=" + jobid.toString();
+    try {
+      assertEquals(HttpURLConnection.HTTP_OK,
+          getHttpStatusCode(url, user, "POST"));
+    } finally {
+      if (!job.isComplete()) {
+        LOG.info("Killing job " + jobid + " from finally block");
+        assertEquals(HttpURLConnection.HTTP_OK,
+            getHttpStatusCode(jobTrackerJSP + "&killJobs=true&jobCheckBox=" +
+            jobid.toString(), jobSubmitter, "GET"));
+      }
+    }
+  }
+
+  /**
+   * Waits for a while for the job to become completed
+   * @param job
+   * @throws IOException
+   */
+  void waitForKillJobToFinish(RunningJob job) throws IOException {
+    for (int i = 0;!job.isComplete() && i < 20;i++) {
+      try {
+        Thread.sleep(1000);
+      } catch (InterruptedException e){
+        LOG.warn("Interrupted while waiting for killJob() to finish for "
+                 + job.getID());
+      }
+    }
+  }
+
+  /**
+   * Make sure that multiple jobs get killed using jobtracker.jsp url when
+   * user has modify access on only some of those jobs.
+   * @param cluster
+   * @param conf
+   * @param jtURL
+   * @param user
+   * @throws Exception
+   */
+  private void validateKillMultipleJobs(MiniMRCluster cluster,
+      JobConf conf, String jtURL) throws Exception {
+    String jobTrackerJSP =  jtURL + "/jobtracker.jsp?a=b";
+    // jobTrackerJSP killJob url
+    String url = jobTrackerJSP + "&killJobs=true";
+    // view-job-acl doesn't matter for killJob from jobtracker jsp page
+    conf.set(JobContext.JOB_ACL_VIEW_JOB, "");
+    
+    // Let us start jobs as 4 different users(none of these 4 users is
+    // mrOwner and none of these users is a member of superGroup). So only
+    // based on the config JobContext.JOB_ACL_MODIFY_JOB being set here,
+    // killJob on each of the jobs will be succeeded.
+
+    // start 1st job.
+    // Out of these 4 users, only jobSubmitter can do killJob on 1st job
+    conf.set(JobContext.JOB_ACL_MODIFY_JOB, "");
+    RunningJob job1 = startSleepJobAsUser(jobSubmitter, conf);
+    org.apache.hadoop.mapreduce.JobID jobid = job1.getID();
+    getTIPId(cluster, jobid);// wait till the map task is started
+    url = url.concat("&jobCheckBox=" + jobid.toString());
+    // start 2nd job.
+    // Out of these 4 users, only viewColleague can do killJob on 2nd job
+    RunningJob job2 = startSleepJobAsUser(viewColleague, conf);
+    jobid = job2.getID();
+    getTIPId(cluster, jobid);// wait till the map task is started
+    url = url.concat("&jobCheckBox=" + jobid.toString());
+    // start 3rd job.
+    // Out of these 4 users, only modifyColleague can do killJob on 3rd job
+    RunningJob job3 = startSleepJobAsUser(modifyColleague, conf);
+    jobid = job3.getID();
+    getTIPId(cluster, jobid);// wait till the map task is started
+    url = url.concat("&jobCheckBox=" + jobid.toString());
+    // start 4rd job.
+    // Out of these 4 users, viewColleague and viewAndModifyColleague
+    // can do killJob on 4th job
+    conf.set(JobContext.JOB_ACL_MODIFY_JOB, viewColleague);
+    RunningJob job4 = startSleepJobAsUser(viewAndModifyColleague, conf);
+    jobid = job4.getID();
+    getTIPId(cluster, jobid);// wait till the map task is started
+    url = url.concat("&jobCheckBox=" + jobid.toString());
+
+    try {
+      // Try killing all the 4 jobs as user viewColleague who can kill only
+      // 2nd and 4th jobs. Check if 1st and 3rd jobs are not killed and
+      // 2nd and 4th jobs get killed
+      assertEquals(HttpURLConnection.HTTP_UNAUTHORIZED,
+          getHttpStatusCode(url, viewColleague, "POST"));
+      
+      waitForKillJobToFinish(job2);
+      assertTrue("killJob failed for a job for which user has "
+          + "job-modify permission", job2.isComplete());
+      waitForKillJobToFinish(job4);
+      assertTrue("killJob failed for a job for which user has "
+          + "job-modify permission", job4.isComplete());
+      assertFalse("killJob succeeded for a job for which user doesnot "
+          + " have job-modify permission", job1.isComplete());
+      assertFalse("killJob succeeded for a job for which user doesnot "
+          + " have job-modify permission", job3.isComplete());
+    } finally {
+      // Kill all 4 jobs as user mrOwner(even though some of them
+      // were already killed)
+      assertEquals(HttpURLConnection.HTTP_OK,
+          getHttpStatusCode(url, mrOwner, "GET"));
+    }
+  }
+
+  /**
+   * Run a job and validate if JSPs/Servlets are going through authentication
+   * and authorization.
+   * @throws Exception 
+   */
+  @Test
+  public void testWebUIAuthorization() throws Exception {
+    JobConf conf = new JobConf();
+    conf.set(CommonConfigurationKeys.HADOOP_SECURITY_GROUP_MAPPING,
+        MyGroupsProvider.class.getName());
+    Groups.getUserToGroupsMappingService(conf);
+    Properties props = new Properties();
+    props.setProperty("hadoop.http.filter.initializers",
+        DummyFilterInitializer.class.getName());
+    props.setProperty(
+       JobConf.JOB_LEVEL_AUTHORIZATION_ENABLING_FLAG, String.valueOf(true));
+    props.setProperty("dfs.permissions", "false");
+    
+    props.setProperty(JSPUtil.PRIVATE_ACTIONS_KEY, "true");
+    props.setProperty("mapreduce.job.committer.setup.cleanup.needed", "false");
+    props.setProperty(JobConf.MR_SUPERGROUP, "superGroup");
+
+    MyGroupsProvider.mapping.put(jobSubmitter, Arrays.asList("group1"));
+    MyGroupsProvider.mapping.put(viewColleague, Arrays.asList("group2"));
+    MyGroupsProvider.mapping.put(modifyColleague, Arrays.asList("group1"));
+    MyGroupsProvider.mapping.put(unauthorizedUser, Arrays.asList("evilSociety"));
+    MyGroupsProvider.mapping.put(superGroupMember, Arrays.asList("superGroup"));
+    MyGroupsProvider.mapping.put(viewAndModifyColleague, Arrays.asList("group3"));
+
+    mrOwner = UserGroupInformation.getCurrentUser().getShortUserName();
+    MyGroupsProvider.mapping.put(mrOwner, Arrays.asList(
+        new String[] { "group4", "group5" }));
+
+    startCluster(true, props);
+    MiniMRCluster cluster = getMRCluster();
+    int infoPort = cluster.getJobTrackerRunner().getJobTrackerInfoPort();
+
+    JobConf clusterConf = cluster.createJobConf();
+    conf = new JobConf(clusterConf);
+    conf.set(JobContext.JOB_ACL_VIEW_JOB, viewColleague + " group3");
+
+    // Let us add group1 and group3 to modify-job-acl. So modifyColleague and
+    // viewAndModifyColleague will be able to modify the job
+    conf.set(JobContext.JOB_ACL_MODIFY_JOB, " group1,group3");
+
+    RunningJob job = startSleepJobAsUser(jobSubmitter, conf);
+
+    org.apache.hadoop.mapreduce.JobID jobid = job.getID();
+
+    String jtURL = "http://localhost:" + infoPort;
+
+    String jobTrackerJSP =  jtURL + "/jobtracker.jsp?a=b";
+    try {
+      // Currently, authorization is not done for jobtracker page. So allow
+      // everyone to view it.
+      validateJobTrackerJSPAccess(jtURL);
+      validateJobDetailsJSPAccess(jobid, jtURL);
+      validateTaskGraphServletAccess(jobid, jtURL);
+      validateJobTasksJSPAccess(jobid, jtURL);
+      validateJobConfJSPAccess(jobid, jtURL);
+      validateJobFailuresJSPAccess(jobid, jtURL);
+      valiateJobBlacklistedTrackerJSPAccess(jobid, jtURL);
+      validateJobTrackerJSPSetPriorityAction(jobid, jtURL);
+
+      // Wait for the tip to start so as to test task related JSP
+      TaskID tipId = getTIPId(cluster, jobid);
+      validateTaskStatsJSPAccess(jobid, jtURL, tipId);
+      validateTaskDetailsJSPAccess(jobid, jtURL, tipId);
+      validateJobTrackerJSPKillJobAction(jobid, jtURL);
+    } finally {
+      if (!job.isComplete()) { // kill the job(as jobSubmitter) if needed
+        LOG.info("Killing job " + jobid + " from finally block");
+        assertEquals(HttpURLConnection.HTTP_OK,
+            getHttpStatusCode(jobTrackerJSP + "&killJobs=true&jobCheckBox=" +
+            jobid.toString(), jobSubmitter, "GET"));
+      }
+    }
+
+    // validate killJob of jobdetails.jsp
+    validateJobDetailsJSPKillJob(cluster, clusterConf, jtURL);
+
+    // validate killJob of jobtracker.jsp as users viewAndModifyColleague,
+    // jobSubmitter, mrOwner and superGroupMember
+    confirmJobTrackerJSPKillJobAsUser(cluster, conf, jtURL,
+        viewAndModifyColleague);
+    confirmJobTrackerJSPKillJobAsUser(cluster, conf, jtURL, jobSubmitter);
+    confirmJobTrackerJSPKillJobAsUser(cluster, conf, jtURL, mrOwner);
+    confirmJobTrackerJSPKillJobAsUser(cluster, conf, jtURL,
+        superGroupMember);
+
+    // validate killing of multiple jobs using jobtracker jsp and check
+    // if all the jobs which can be killed by user are actually the ones that
+    // got killed
+    validateKillMultipleJobs(cluster, conf, jtURL);
+  }
+
+  // validate killJob of jobtracker.jsp
+  private void validateJobTrackerJSPKillJobAction(
+      org.apache.hadoop.mapreduce.JobID jobid, String jtURL)
+      throws IOException {
+    String jobTrackerJSP =  jtURL + "/jobtracker.jsp?a=b";
+    String jobTrackerJSPKillJobAction = jobTrackerJSP +
+        "&killJobs=true&jobCheckBox="+ jobid.toString();
+    validateModifyJob(jobTrackerJSPKillJobAction, "GET");
+  }
+
+  // validate viewing of job of taskdetails.jsp
+  private void validateTaskDetailsJSPAccess(
+      org.apache.hadoop.mapreduce.JobID jobid, String jtURL, TaskID tipId)
+      throws IOException {
+    String taskDetailsJSP =  jtURL + "/taskdetails.jsp?jobid=" +
+        jobid.toString() + "&tipid=" + tipId;
+    validateViewJob(taskDetailsJSP, "GET");
+  }
+
+  // validate taskstats.jsp
+  private void validateTaskStatsJSPAccess(
+      org.apache.hadoop.mapreduce.JobID jobid, String jtURL, TaskID tipId)
+      throws IOException {
+    String taskStatsJSP =  jtURL + "/taskstats.jsp?jobid=" +
+        jobid.toString() + "&tipid=" + tipId;
+    validateViewJob(taskStatsJSP, "GET");
+  }
+
+  // validate setJobPriority
+  private void validateJobTrackerJSPSetPriorityAction(
+      org.apache.hadoop.mapreduce.JobID jobid, String jtURL)
+      throws IOException {
+    String jobTrackerJSP =  jtURL + "/jobtracker.jsp?a=b";
+    String jobTrackerJSPSetJobPriorityAction = jobTrackerJSP +
+        "&changeJobPriority=true&setJobPriority="+"HIGH"+"&jobCheckBox=" +
+        jobid.toString();
+    validateModifyJob(jobTrackerJSPSetJobPriorityAction, "GET");
+    // jobSubmitter, mrOwner and superGroupMember are not validated for
+    // job-modify permission in validateModifyJob(). So let us do it
+    // explicitly here
+    assertEquals(HttpURLConnection.HTTP_OK, getHttpStatusCode(
+        jobTrackerJSPSetJobPriorityAction, jobSubmitter, "GET"));
+    assertEquals(HttpURLConnection.HTTP_OK, getHttpStatusCode(
+        jobTrackerJSPSetJobPriorityAction, superGroupMember, "GET"));
+    assertEquals(HttpURLConnection.HTTP_OK, getHttpStatusCode(
+        jobTrackerJSPSetJobPriorityAction, mrOwner, "GET"));
+  }
+
+  // validate access of jobblacklistedtrackers.jsp
+  private void valiateJobBlacklistedTrackerJSPAccess(
+      org.apache.hadoop.mapreduce.JobID jobid, String jtURL)
+      throws IOException {
+    String jobBlacklistedTrackersJSP =  jtURL +
+        "/jobblacklistedtrackers.jsp?jobid="+jobid.toString();
+    validateViewJob(jobBlacklistedTrackersJSP, "GET");
+  }
+
+  // validate access of jobfailures.jsp
+  private void validateJobFailuresJSPAccess(
+      org.apache.hadoop.mapreduce.JobID jobid, String jtURL)
+      throws IOException {
+    String jobFailuresJSP =  jtURL + "/jobfailures.jsp?jobid="+jobid.toString();
+    validateViewJob(jobFailuresJSP, "GET");
+  }
+
+  // validate access of jobconf.jsp
+  private void validateJobConfJSPAccess(
+      org.apache.hadoop.mapreduce.JobID jobid, String jtURL)
+      throws IOException {
+    String jobConfJSP =  jtURL + "/jobconf.jsp?jobid="+jobid.toString();
+    validateViewJob(jobConfJSP, "GET");
+  }
+
+  // validate access of jobtasks.jsp
+  private void validateJobTasksJSPAccess(
+      org.apache.hadoop.mapreduce.JobID jobid, String jtURL)
+      throws IOException {
+    String jobTasksJSP =  jtURL + "/jobtasks.jsp?jobid="
+        + jobid.toString() + "&type=map&pagenum=1&state=running";
+    validateViewJob(jobTasksJSP, "GET");
+  }
+
+  // validate access of TaskGraphServlet
+  private void validateTaskGraphServletAccess(
+      org.apache.hadoop.mapreduce.JobID jobid, String jtURL)
+      throws IOException {
+    String taskGraphServlet = jtURL + "/taskgraph?type=map&jobid="
+        + jobid.toString();
+    validateViewJob(taskGraphServlet, "GET");
+    taskGraphServlet = jtURL + "/taskgraph?type=reduce&jobid="
+        + jobid.toString();
+    validateViewJob(taskGraphServlet, "GET");
+  }
+
+  // validate access of jobdetails.jsp
+  private void validateJobDetailsJSPAccess(
+      org.apache.hadoop.mapreduce.JobID jobid, String jtURL)
+      throws IOException {
+    String jobDetailsJSP =  jtURL + "/jobdetails.jsp?jobid="
+        + jobid.toString();
+    validateViewJob(jobDetailsJSP, "GET");
+  }
+
+  // validate access of jobtracker.jsp
+  private void validateJobTrackerJSPAccess(String jtURL)
+      throws IOException {
+    String jobTrackerJSP =  jtURL + "/jobtracker.jsp?a=b";
+    assertEquals(HttpURLConnection.HTTP_OK,
+        getHttpStatusCode(jobTrackerJSP, jobSubmitter, "GET"));
+    assertEquals(HttpURLConnection.HTTP_OK,
+        getHttpStatusCode(jobTrackerJSP, viewColleague, "GET"));
+    assertEquals(HttpURLConnection.HTTP_OK,
+        getHttpStatusCode(jobTrackerJSP, unauthorizedUser, "GET"));
+    assertEquals(HttpURLConnection.HTTP_OK,
+        getHttpStatusCode(jobTrackerJSP, modifyColleague, "GET"));
+    assertEquals(HttpURLConnection.HTTP_OK,
+        getHttpStatusCode(jobTrackerJSP, viewAndModifyColleague, "GET"));
+    assertEquals(HttpURLConnection.HTTP_OK,
+        getHttpStatusCode(jobTrackerJSP, mrOwner, "GET"));
+    assertEquals(HttpURLConnection.HTTP_OK,
+        getHttpStatusCode(jobTrackerJSP, superGroupMember, "GET"));
+  }
+}

Added: hadoop/common/branches/branch-0.20-security-patches/src/webapps/job/job_authorization_error.jsp
URL: http://svn.apache.org/viewvc/hadoop/common/branches/branch-0.20-security-patches/src/webapps/job/job_authorization_error.jsp?rev=1077247&view=auto
==============================================================================
--- hadoop/common/branches/branch-0.20-security-patches/src/webapps/job/job_authorization_error.jsp (added)
+++ hadoop/common/branches/branch-0.20-security-patches/src/webapps/job/job_authorization_error.jsp Fri Mar  4 03:55:52 2011
@@ -0,0 +1,52 @@
+<%
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file 
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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.
+ */
+%>
+<%@ page
+  contentType="text/html; charset=UTF-8"
+  import="javax.servlet.*"
+  import="javax.servlet.http.*"
+  import="java.io.*"
+  import="java.net.URL"
+  import="org.apache.hadoop.util.*"
+%>
+<%!	private static final long serialVersionUID = 1L;
+%>
+
+<html>
+<head>
+<title>Error: User cannot access this Job</title>
+</head>
+<body>
+<h2>Error: User cannot do this operation on this Job</h2><br>
+
+<%
+  String errorMsg = (String) request.getAttribute("error.msg");
+%>
+
+<font size="5"> 
+<%
+  out.println(errorMsg);
+%>
+</font>
+
+<hr>
+
+<%
+out.println(ServletUtil.htmlFooter());
+%>