|
@@ -0,0 +1,419 @@
|
|
|
|
+/**
|
|
|
|
+ * 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.mapreduce.v2.hs.webapp;
|
|
|
|
+
|
|
|
|
+import static org.junit.Assert.assertEquals;
|
|
|
|
+import static org.junit.Assert.fail;
|
|
|
|
+import static org.mockito.Mockito.mock;
|
|
|
|
+import static org.mockito.Mockito.when;
|
|
|
|
+
|
|
|
|
+import java.io.IOException;
|
|
|
|
+import java.util.Collections;
|
|
|
|
+import java.util.HashMap;
|
|
|
|
+import java.util.List;
|
|
|
|
+import java.util.Map;
|
|
|
|
+
|
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
|
+import javax.servlet.http.HttpServletResponse;
|
|
|
|
+import javax.ws.rs.WebApplicationException;
|
|
|
|
+import javax.ws.rs.core.Response.Status;
|
|
|
|
+
|
|
|
|
+import org.apache.hadoop.conf.Configuration;
|
|
|
|
+import org.apache.hadoop.fs.CommonConfigurationKeys;
|
|
|
|
+import org.apache.hadoop.fs.Path;
|
|
|
|
+import org.apache.hadoop.mapred.JobACLsManager;
|
|
|
|
+import org.apache.hadoop.mapred.JobConf;
|
|
|
|
+import org.apache.hadoop.mapred.TaskCompletionEvent;
|
|
|
|
+import org.apache.hadoop.mapreduce.Counters;
|
|
|
|
+import org.apache.hadoop.mapreduce.JobACL;
|
|
|
|
+import org.apache.hadoop.mapreduce.MRConfig;
|
|
|
|
+import org.apache.hadoop.mapreduce.v2.api.records.AMInfo;
|
|
|
|
+import org.apache.hadoop.mapreduce.v2.api.records.JobId;
|
|
|
|
+import org.apache.hadoop.mapreduce.v2.api.records.JobReport;
|
|
|
|
+import org.apache.hadoop.mapreduce.v2.api.records.JobState;
|
|
|
|
+import org.apache.hadoop.mapreduce.v2.api.records.TaskAttemptCompletionEvent;
|
|
|
|
+import org.apache.hadoop.mapreduce.v2.api.records.TaskId;
|
|
|
|
+import org.apache.hadoop.mapreduce.v2.api.records.TaskType;
|
|
|
|
+import org.apache.hadoop.mapreduce.v2.app.job.Job;
|
|
|
|
+import org.apache.hadoop.mapreduce.v2.app.job.Task;
|
|
|
|
+import org.apache.hadoop.mapreduce.v2.hs.HistoryContext;
|
|
|
|
+import org.apache.hadoop.mapreduce.v2.hs.MockHistoryContext;
|
|
|
|
+import org.apache.hadoop.security.GroupMappingServiceProvider;
|
|
|
|
+import org.apache.hadoop.security.Groups;
|
|
|
|
+import org.apache.hadoop.security.UserGroupInformation;
|
|
|
|
+import org.apache.hadoop.security.authorize.AccessControlList;
|
|
|
|
+import org.apache.hadoop.yarn.webapp.WebApp;
|
|
|
|
+import org.junit.Before;
|
|
|
|
+import org.junit.Test;
|
|
|
|
+
|
|
|
|
+public class TestHsWebServicesAcls {
|
|
|
|
+ private static String FRIENDLY_USER = "friendly";
|
|
|
|
+ private static String ENEMY_USER = "enemy";
|
|
|
|
+
|
|
|
|
+ private JobConf conf;
|
|
|
|
+ private HistoryContext ctx;
|
|
|
|
+ private String jobIdStr;
|
|
|
|
+ private String taskIdStr;
|
|
|
|
+ private String taskAttemptIdStr;
|
|
|
|
+ private HsWebServices hsWebServices;
|
|
|
|
+
|
|
|
|
+ @Before
|
|
|
|
+ public void setup() throws IOException {
|
|
|
|
+ this.conf = new JobConf();
|
|
|
|
+ this.conf.set(CommonConfigurationKeys.HADOOP_SECURITY_GROUP_MAPPING,
|
|
|
|
+ NullGroupsProvider.class.getName());
|
|
|
|
+ this.conf.setBoolean(MRConfig.MR_ACLS_ENABLED, true);
|
|
|
|
+ Groups.getUserToGroupsMappingService(conf);
|
|
|
|
+ this.ctx = buildHistoryContext(this.conf);
|
|
|
|
+ WebApp webApp = mock(HsWebApp.class);
|
|
|
|
+ when(webApp.name()).thenReturn("hsmockwebapp");
|
|
|
|
+ this.hsWebServices= new HsWebServices(ctx, conf, webApp);
|
|
|
|
+ this.hsWebServices.setResponse(mock(HttpServletResponse.class));
|
|
|
|
+
|
|
|
|
+ Job job = ctx.getAllJobs().values().iterator().next();
|
|
|
|
+ this.jobIdStr = job.getID().toString();
|
|
|
|
+ Task task = job.getTasks().values().iterator().next();
|
|
|
|
+ this.taskIdStr = task.getID().toString();
|
|
|
|
+ this.taskAttemptIdStr =
|
|
|
|
+ task.getAttempts().keySet().iterator().next().toString();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void testGetJobAcls() {
|
|
|
|
+ HttpServletRequest hsr = mock(HttpServletRequest.class);
|
|
|
|
+ when(hsr.getRemoteUser()).thenReturn(ENEMY_USER);
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ hsWebServices.getJob(hsr, jobIdStr);
|
|
|
|
+ fail("enemy can access job");
|
|
|
|
+ } catch (WebApplicationException e) {
|
|
|
|
+ assertEquals(Status.UNAUTHORIZED,
|
|
|
|
+ Status.fromStatusCode(e.getResponse().getStatus()));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ when(hsr.getRemoteUser()).thenReturn(FRIENDLY_USER);
|
|
|
|
+ hsWebServices.getJob(hsr, jobIdStr);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void testGetJobCountersAcls() {
|
|
|
|
+ HttpServletRequest hsr = mock(HttpServletRequest.class);
|
|
|
|
+ when(hsr.getRemoteUser()).thenReturn(ENEMY_USER);
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ hsWebServices.getJobCounters(hsr, jobIdStr);
|
|
|
|
+ fail("enemy can access job");
|
|
|
|
+ } catch (WebApplicationException e) {
|
|
|
|
+ assertEquals(Status.UNAUTHORIZED,
|
|
|
|
+ Status.fromStatusCode(e.getResponse().getStatus()));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ when(hsr.getRemoteUser()).thenReturn(FRIENDLY_USER);
|
|
|
|
+ hsWebServices.getJobCounters(hsr, jobIdStr);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void testGetJobConfAcls() {
|
|
|
|
+ HttpServletRequest hsr = mock(HttpServletRequest.class);
|
|
|
|
+ when(hsr.getRemoteUser()).thenReturn(ENEMY_USER);
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ hsWebServices.getJobConf(hsr, jobIdStr);
|
|
|
|
+ fail("enemy can access job");
|
|
|
|
+ } catch (WebApplicationException e) {
|
|
|
|
+ assertEquals(Status.UNAUTHORIZED,
|
|
|
|
+ Status.fromStatusCode(e.getResponse().getStatus()));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ when(hsr.getRemoteUser()).thenReturn(FRIENDLY_USER);
|
|
|
|
+ hsWebServices.getJobConf(hsr, jobIdStr);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void testGetJobTasksAcls() {
|
|
|
|
+ HttpServletRequest hsr = mock(HttpServletRequest.class);
|
|
|
|
+ when(hsr.getRemoteUser()).thenReturn(ENEMY_USER);
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ hsWebServices.getJobTasks(hsr, jobIdStr, "m");
|
|
|
|
+ fail("enemy can access job");
|
|
|
|
+ } catch (WebApplicationException e) {
|
|
|
|
+ assertEquals(Status.UNAUTHORIZED,
|
|
|
|
+ Status.fromStatusCode(e.getResponse().getStatus()));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ when(hsr.getRemoteUser()).thenReturn(FRIENDLY_USER);
|
|
|
|
+ hsWebServices.getJobTasks(hsr, jobIdStr, "m");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void testGetJobTaskAcls() {
|
|
|
|
+ HttpServletRequest hsr = mock(HttpServletRequest.class);
|
|
|
|
+ when(hsr.getRemoteUser()).thenReturn(ENEMY_USER);
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ hsWebServices.getJobTask(hsr, jobIdStr, this.taskIdStr);
|
|
|
|
+ fail("enemy can access job");
|
|
|
|
+ } catch (WebApplicationException e) {
|
|
|
|
+ assertEquals(Status.UNAUTHORIZED,
|
|
|
|
+ Status.fromStatusCode(e.getResponse().getStatus()));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ when(hsr.getRemoteUser()).thenReturn(FRIENDLY_USER);
|
|
|
|
+ hsWebServices.getJobTask(hsr, this.jobIdStr, this.taskIdStr);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void testGetSingleTaskCountersAcls() {
|
|
|
|
+ HttpServletRequest hsr = mock(HttpServletRequest.class);
|
|
|
|
+ when(hsr.getRemoteUser()).thenReturn(ENEMY_USER);
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ hsWebServices.getSingleTaskCounters(hsr, this.jobIdStr, this.taskIdStr);
|
|
|
|
+ fail("enemy can access job");
|
|
|
|
+ } catch (WebApplicationException e) {
|
|
|
|
+ assertEquals(Status.UNAUTHORIZED,
|
|
|
|
+ Status.fromStatusCode(e.getResponse().getStatus()));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ when(hsr.getRemoteUser()).thenReturn(FRIENDLY_USER);
|
|
|
|
+ hsWebServices.getSingleTaskCounters(hsr, this.jobIdStr, this.taskIdStr);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void testGetJobTaskAttemptsAcls() {
|
|
|
|
+ HttpServletRequest hsr = mock(HttpServletRequest.class);
|
|
|
|
+ when(hsr.getRemoteUser()).thenReturn(ENEMY_USER);
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ hsWebServices.getJobTaskAttempts(hsr, this.jobIdStr, this.taskIdStr);
|
|
|
|
+ fail("enemy can access job");
|
|
|
|
+ } catch (WebApplicationException e) {
|
|
|
|
+ assertEquals(Status.UNAUTHORIZED,
|
|
|
|
+ Status.fromStatusCode(e.getResponse().getStatus()));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ when(hsr.getRemoteUser()).thenReturn(FRIENDLY_USER);
|
|
|
|
+ hsWebServices.getJobTaskAttempts(hsr, this.jobIdStr, this.taskIdStr);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void testGetJobTaskAttemptIdAcls() {
|
|
|
|
+ HttpServletRequest hsr = mock(HttpServletRequest.class);
|
|
|
|
+ when(hsr.getRemoteUser()).thenReturn(ENEMY_USER);
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ hsWebServices.getJobTaskAttemptId(hsr, this.jobIdStr, this.taskIdStr,
|
|
|
|
+ this.taskAttemptIdStr);
|
|
|
|
+ fail("enemy can access job");
|
|
|
|
+ } catch (WebApplicationException e) {
|
|
|
|
+ assertEquals(Status.UNAUTHORIZED,
|
|
|
|
+ Status.fromStatusCode(e.getResponse().getStatus()));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ when(hsr.getRemoteUser()).thenReturn(FRIENDLY_USER);
|
|
|
|
+ hsWebServices.getJobTaskAttemptId(hsr, this.jobIdStr, this.taskIdStr,
|
|
|
|
+ this.taskAttemptIdStr);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Test
|
|
|
|
+ public void testGetJobTaskAttemptIdCountersAcls() {
|
|
|
|
+ HttpServletRequest hsr = mock(HttpServletRequest.class);
|
|
|
|
+ when(hsr.getRemoteUser()).thenReturn(ENEMY_USER);
|
|
|
|
+
|
|
|
|
+ try {
|
|
|
|
+ hsWebServices.getJobTaskAttemptIdCounters(hsr, this.jobIdStr,
|
|
|
|
+ this.taskIdStr, this.taskAttemptIdStr);
|
|
|
|
+ fail("enemy can access job");
|
|
|
|
+ } catch (WebApplicationException e) {
|
|
|
|
+ assertEquals(Status.UNAUTHORIZED,
|
|
|
|
+ Status.fromStatusCode(e.getResponse().getStatus()));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ when(hsr.getRemoteUser()).thenReturn(FRIENDLY_USER);
|
|
|
|
+ hsWebServices.getJobTaskAttemptIdCounters(hsr, this.jobIdStr,
|
|
|
|
+ this.taskIdStr, this.taskAttemptIdStr);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private static HistoryContext buildHistoryContext(final Configuration conf)
|
|
|
|
+ throws IOException {
|
|
|
|
+ HistoryContext ctx = new MockHistoryContext(1, 1, 1);
|
|
|
|
+ Map<JobId, Job> jobs = ctx.getAllJobs();
|
|
|
|
+ JobId jobId = jobs.keySet().iterator().next();
|
|
|
|
+ Job mockJob = new MockJobForAcls(jobs.get(jobId), conf);
|
|
|
|
+ jobs.put(jobId, mockJob);
|
|
|
|
+ return ctx;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private static class NullGroupsProvider
|
|
|
|
+ implements GroupMappingServiceProvider {
|
|
|
|
+ @Override
|
|
|
|
+ public List<String> getGroups(String user) throws IOException {
|
|
|
|
+ return Collections.emptyList();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void cacheGroupsRefresh() throws IOException {
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void cacheGroupsAdd(List<String> groups) throws IOException {
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private static class MockJobForAcls implements Job {
|
|
|
|
+ private Job mockJob;
|
|
|
|
+ private Configuration conf;
|
|
|
|
+ private Map<JobACL, AccessControlList> jobAcls;
|
|
|
|
+ private JobACLsManager aclsMgr;
|
|
|
|
+
|
|
|
|
+ public MockJobForAcls(Job mockJob, Configuration conf) {
|
|
|
|
+ this.mockJob = mockJob;
|
|
|
|
+ this.conf = conf;
|
|
|
|
+ AccessControlList viewAcl = new AccessControlList(FRIENDLY_USER);
|
|
|
|
+ this.jobAcls = new HashMap<JobACL, AccessControlList>();
|
|
|
|
+ this.jobAcls.put(JobACL.VIEW_JOB, viewAcl);
|
|
|
|
+ this.aclsMgr = new JobACLsManager(conf);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public JobId getID() {
|
|
|
|
+ return mockJob.getID();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public String getName() {
|
|
|
|
+ return mockJob.getName();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public JobState getState() {
|
|
|
|
+ return mockJob.getState();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public JobReport getReport() {
|
|
|
|
+ return mockJob.getReport();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Counters getAllCounters() {
|
|
|
|
+ return mockJob.getAllCounters();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Map<TaskId, Task> getTasks() {
|
|
|
|
+ return mockJob.getTasks();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Map<TaskId, Task> getTasks(TaskType taskType) {
|
|
|
|
+ return mockJob.getTasks(taskType);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Task getTask(TaskId taskID) {
|
|
|
|
+ return mockJob.getTask(taskID);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public List<String> getDiagnostics() {
|
|
|
|
+ return mockJob.getDiagnostics();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public int getTotalMaps() {
|
|
|
|
+ return mockJob.getTotalMaps();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public int getTotalReduces() {
|
|
|
|
+ return mockJob.getTotalReduces();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public int getCompletedMaps() {
|
|
|
|
+ return mockJob.getCompletedMaps();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public int getCompletedReduces() {
|
|
|
|
+ return mockJob.getCompletedReduces();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public float getProgress() {
|
|
|
|
+ return mockJob.getProgress();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean isUber() {
|
|
|
|
+ return mockJob.isUber();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public String getUserName() {
|
|
|
|
+ return mockJob.getUserName();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public String getQueueName() {
|
|
|
|
+ return mockJob.getQueueName();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Path getConfFile() {
|
|
|
|
+ return new Path("/some/path/to/conf");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Configuration loadConfFile() throws IOException {
|
|
|
|
+ return conf;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Map<JobACL, AccessControlList> getJobACLs() {
|
|
|
|
+ return jobAcls;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public TaskAttemptCompletionEvent[] getTaskAttemptCompletionEvents(
|
|
|
|
+ int fromEventId, int maxEvents) {
|
|
|
|
+ return mockJob.getTaskAttemptCompletionEvents(fromEventId, maxEvents);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public TaskCompletionEvent[] getMapAttemptCompletionEvents(
|
|
|
|
+ int startIndex, int maxEvents) {
|
|
|
|
+ return mockJob.getMapAttemptCompletionEvents(startIndex, maxEvents);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public List<AMInfo> getAMInfos() {
|
|
|
|
+ return mockJob.getAMInfos();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean checkAccess(UserGroupInformation callerUGI,
|
|
|
|
+ JobACL jobOperation) {
|
|
|
|
+ return aclsMgr.checkAccess(callerUGI, jobOperation,
|
|
|
|
+ this.getUserName(), jobAcls.get(jobOperation));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|