|
@@ -19,6 +19,7 @@
|
|
|
package org.apache.hadoop.yarn.server.router.webapp;
|
|
|
|
|
|
import java.io.IOException;
|
|
|
+import java.security.Principal;
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.List;
|
|
|
import java.util.HashMap;
|
|
@@ -27,9 +28,11 @@ import java.util.Set;
|
|
|
import java.util.HashSet;
|
|
|
import java.util.Collections;
|
|
|
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
import javax.ws.rs.core.Response;
|
|
|
import javax.ws.rs.core.Response.Status;
|
|
|
|
|
|
+import org.apache.hadoop.security.authorize.AuthorizationException;
|
|
|
import org.apache.hadoop.test.LambdaTestUtils;
|
|
|
import org.apache.hadoop.conf.Configuration;
|
|
|
import org.apache.hadoop.http.HttpConfig;
|
|
@@ -43,8 +46,10 @@ import org.apache.hadoop.yarn.api.records.NodeLabel;
|
|
|
import org.apache.hadoop.yarn.api.records.ApplicationTimeoutType;
|
|
|
import org.apache.hadoop.yarn.api.records.YarnApplicationState;
|
|
|
import org.apache.hadoop.yarn.api.records.ContainerId;
|
|
|
+import org.apache.hadoop.yarn.api.records.QueueACL;
|
|
|
import org.apache.hadoop.yarn.conf.YarnConfiguration;
|
|
|
import org.apache.hadoop.yarn.exceptions.YarnException;
|
|
|
+import org.apache.hadoop.yarn.exceptions.YarnRuntimeException;
|
|
|
import org.apache.hadoop.yarn.server.federation.policies.manager.UniformBroadcastPolicyManager;
|
|
|
import org.apache.hadoop.yarn.server.federation.store.impl.MemoryFederationStateStore;
|
|
|
import org.apache.hadoop.yarn.server.federation.store.records.SubClusterId;
|
|
@@ -81,6 +86,7 @@ import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppPriority;
|
|
|
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppQueue;
|
|
|
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationListInfo;
|
|
|
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationInfo;
|
|
|
+import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.RMQueueAclInfo;
|
|
|
import org.apache.hadoop.yarn.server.resourcemanager.webapp.NodeIDsInfo;
|
|
|
import org.apache.hadoop.yarn.server.router.webapp.cache.RouterAppInfoCacheKey;
|
|
|
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ApplicationStatisticsInfo;
|
|
@@ -90,6 +96,7 @@ import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationReque
|
|
|
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationRequestInfo;
|
|
|
import org.apache.hadoop.yarn.server.webapp.dao.ContainerInfo;
|
|
|
import org.apache.hadoop.yarn.server.webapp.dao.ContainersInfo;
|
|
|
+import org.apache.hadoop.yarn.server.router.webapp.dao.FederationRMQueueAclInfo;
|
|
|
import org.apache.hadoop.yarn.util.LRUCacheHashMap;
|
|
|
import org.apache.hadoop.yarn.util.MonotonicClock;
|
|
|
import org.apache.hadoop.yarn.util.Times;
|
|
@@ -100,6 +107,8 @@ import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
|
import static org.apache.hadoop.yarn.server.router.webapp.MockDefaultRequestInterceptorREST.QUEUE_DEDICATED_FULL;
|
|
|
+import static org.mockito.Mockito.mock;
|
|
|
+import static org.mockito.Mockito.when;
|
|
|
|
|
|
/**
|
|
|
* Extends the {@code BaseRouterClientRMTest} and overrides methods in order to
|
|
@@ -1170,4 +1179,82 @@ public class TestFederationInterceptorREST extends BaseRouterWebServicesTest {
|
|
|
WebAppUtils.getHttpSchemePrefix(this.getConf()) + webAppAddress;
|
|
|
Assert.assertEquals(expectedHttpsWebAddress, webAppAddressWithScheme2);
|
|
|
}
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void testCheckUserAccessToQueue() throws Exception {
|
|
|
+
|
|
|
+ // Case 1: Only queue admin user can access other user's information
|
|
|
+ HttpServletRequest mockHsr = mockHttpServletRequestByUserName("non-admin");
|
|
|
+ String errorMsg1 = "User=non-admin doesn't haven access to queue=queue " +
|
|
|
+ "so it cannot check ACLs for other users.";
|
|
|
+ LambdaTestUtils.intercept(YarnRuntimeException.class, errorMsg1,
|
|
|
+ () -> interceptor.checkUserAccessToQueue("queue", "jack",
|
|
|
+ QueueACL.SUBMIT_APPLICATIONS.name(), mockHsr));
|
|
|
+
|
|
|
+ // Case 2: request an unknown ACL causes BAD_REQUEST
|
|
|
+ HttpServletRequest mockHsr1 = mockHttpServletRequestByUserName("admin");
|
|
|
+ String errorMsg2 = "Specified queueAclType=XYZ_ACL is not a valid type, " +
|
|
|
+ "valid queue acl types={SUBMIT_APPLICATIONS/ADMINISTER_QUEUE}";
|
|
|
+ LambdaTestUtils.intercept(YarnRuntimeException.class, errorMsg2,
|
|
|
+ () -> interceptor.checkUserAccessToQueue("queue", "jack", "XYZ_ACL", mockHsr1));
|
|
|
+
|
|
|
+ // We design a test, admin user has ADMINISTER_QUEUE, SUBMIT_APPLICATIONS permissions,
|
|
|
+ // yarn user has SUBMIT_APPLICATIONS permissions, other users have no permissions
|
|
|
+
|
|
|
+ // Case 3: get FORBIDDEN for rejected ACL
|
|
|
+ checkUserAccessToQueueFailed("queue", "jack", QueueACL.SUBMIT_APPLICATIONS, "admin");
|
|
|
+ checkUserAccessToQueueFailed("queue", "jack", QueueACL.ADMINISTER_QUEUE, "admin");
|
|
|
+
|
|
|
+ // Case 4: get OK for listed ACLs
|
|
|
+ checkUserAccessToQueueSuccess("queue", "admin", QueueACL.ADMINISTER_QUEUE, "admin");
|
|
|
+ checkUserAccessToQueueSuccess("queue", "admin", QueueACL.SUBMIT_APPLICATIONS, "admin");
|
|
|
+
|
|
|
+ // Case 5: get OK only for SUBMIT_APP acl for "yarn" user
|
|
|
+ checkUserAccessToQueueFailed("queue", "yarn", QueueACL.ADMINISTER_QUEUE, "admin");
|
|
|
+ checkUserAccessToQueueSuccess("queue", "yarn", QueueACL.SUBMIT_APPLICATIONS, "admin");
|
|
|
+ }
|
|
|
+
|
|
|
+ private void checkUserAccessToQueueSuccess(String queue, String userName,
|
|
|
+ QueueACL queueACL, String mockUser) throws AuthorizationException {
|
|
|
+ HttpServletRequest mockHsr = mockHttpServletRequestByUserName(mockUser);
|
|
|
+ RMQueueAclInfo aclInfo =
|
|
|
+ interceptor.checkUserAccessToQueue(queue, userName, queueACL.name(), mockHsr);
|
|
|
+ Assert.assertNotNull(aclInfo);
|
|
|
+ Assert.assertTrue(aclInfo instanceof FederationRMQueueAclInfo);
|
|
|
+ FederationRMQueueAclInfo fedAclInfo = FederationRMQueueAclInfo.class.cast(aclInfo);
|
|
|
+ List<RMQueueAclInfo> aclInfos = fedAclInfo.getList();
|
|
|
+ Assert.assertNotNull(aclInfos);
|
|
|
+ Assert.assertEquals(4, aclInfos.size());
|
|
|
+ for (RMQueueAclInfo rMQueueAclInfo : aclInfos) {
|
|
|
+ Assert.assertTrue(rMQueueAclInfo.isAllowed());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void checkUserAccessToQueueFailed(String queue, String userName,
|
|
|
+ QueueACL queueACL, String mockUser) throws AuthorizationException {
|
|
|
+ HttpServletRequest mockHsr = mockHttpServletRequestByUserName(mockUser);
|
|
|
+ RMQueueAclInfo aclInfo =
|
|
|
+ interceptor.checkUserAccessToQueue(queue, userName, queueACL.name(), mockHsr);
|
|
|
+ Assert.assertNotNull(aclInfo);
|
|
|
+ Assert.assertTrue(aclInfo instanceof FederationRMQueueAclInfo);
|
|
|
+ FederationRMQueueAclInfo fedAclInfo = FederationRMQueueAclInfo.class.cast(aclInfo);
|
|
|
+ List<RMQueueAclInfo> aclInfos = fedAclInfo.getList();
|
|
|
+ Assert.assertNotNull(aclInfos);
|
|
|
+ Assert.assertEquals(4, aclInfos.size());
|
|
|
+ for (RMQueueAclInfo rMQueueAclInfo : aclInfos) {
|
|
|
+ Assert.assertFalse(rMQueueAclInfo.isAllowed());
|
|
|
+ String expectDiagnostics = "User=" + userName +
|
|
|
+ " doesn't have access to queue=queue with acl-type=" + queueACL.name();
|
|
|
+ Assert.assertEquals(expectDiagnostics, rMQueueAclInfo.getDiagnostics());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private HttpServletRequest mockHttpServletRequestByUserName(String username) {
|
|
|
+ HttpServletRequest mockHsr = mock(HttpServletRequest.class);
|
|
|
+ when(mockHsr.getRemoteUser()).thenReturn(username);
|
|
|
+ Principal principal = mock(Principal.class);
|
|
|
+ when(principal.getName()).thenReturn(username);
|
|
|
+ when(mockHsr.getUserPrincipal()).thenReturn(principal);
|
|
|
+ return mockHsr;
|
|
|
+ }
|
|
|
}
|