/** * 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)); } @Override public void setQueueName(String queueName) { } } }