YARN-8091. Revisit checkUserAccessToQueue RM REST API. (wangda)

Change-Id: I5fab3fe229c34e967487b7327c7b3c8ddf7cb795
This commit is contained in:
Wangda Tan 2018-04-02 15:22:05 -07:00 committed by Arpit Agarwal
parent 7031a853f4
commit 994c7d66e0
9 changed files with 127 additions and 52 deletions

View File

@ -53,6 +53,7 @@ import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeLabelsInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeToLabelsEntryList;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeToLabelsInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodesInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.RMQueueAclInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationDeleteRequestInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationSubmissionRequestInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationUpdateRequestInfo;
@ -673,7 +674,7 @@ public interface RMWebServiceProtocol {
* @throws AuthorizationException if the user is not authorized to invoke this
* method.
*/
Response checkUserAccessToQueue(String queue, String username,
RMQueueAclInfo checkUserAccessToQueue(String queue, String username,
String queueAclType, HttpServletRequest hsr)
throws AuthorizationException;
}

View File

@ -173,6 +173,7 @@ import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeToLabelsEntr
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeToLabelsEntryList;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeToLabelsInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodesInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.RMQueueAclInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationDefinitionInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationDeleteRequestInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationDeleteResponseInfo;
@ -2523,7 +2524,7 @@ public class RMWebServices extends WebServices implements RMWebServiceProtocol {
@Path(RMWSConsts.CHECK_USER_ACCESS_TO_QUEUE)
@Produces({ MediaType.APPLICATION_JSON + "; " + JettyUtils.UTF_8,
MediaType.APPLICATION_XML + "; " + JettyUtils.UTF_8 })
public Response checkUserAccessToQueue(
public RMQueueAclInfo checkUserAccessToQueue(
@PathParam(RMWSConsts.QUEUE) String queue,
@QueryParam(RMWSConsts.USER) String username,
@QueryParam(RMWSConsts.QUEUE_ACL_TYPE)
@ -2531,42 +2532,39 @@ public class RMWebServices extends WebServices implements RMWebServiceProtocol {
@Context HttpServletRequest hsr) throws AuthorizationException {
init();
// Check if the specified queue acl is valid.
QueueACL queueACL;
try {
queueACL = QueueACL.valueOf(queueAclType);
} catch (IllegalArgumentException e) {
return Response.status(Status.BAD_REQUEST).entity(
"Specified queueAclType=" + queueAclType
+ " is not a valid type, valid queue acl types={"
+ "SUBMIT_APPLICATIONS/ADMINISTER_QUEUE}").build();
}
// For the user who invokes this REST call, he/she should have admin access
// to the queue. Otherwise we will reject the call.
UserGroupInformation callerUGI = getCallerUserGroupInformation(hsr, true);
if (callerUGI != null && !this.rm.getResourceScheduler().checkAccess(
callerUGI, QueueACL.ADMINISTER_QUEUE, queue)) {
return Response.status(Status.FORBIDDEN).entity(
throw new ForbiddenException(
"User=" + callerUGI.getUserName() + " doesn't haven access to queue="
+ queue + " so it cannot check ACLs for other users.")
.build();
+ queue + " so it cannot check ACLs for other users.");
}
// Create UGI for the to-be-checked user.
UserGroupInformation user = UserGroupInformation.createRemoteUser(username);
if (user == null) {
return Response.status(Status.FORBIDDEN).entity(
"Failed to retrieve UserGroupInformation for user=" + username)
.build();
throw new ForbiddenException(
"Failed to retrieve UserGroupInformation for user=" + username);
}
// Check if the specified queue acl is valid.
QueueACL queueACL;
try {
queueACL = QueueACL.valueOf(queueAclType);
} catch (IllegalArgumentException e) {
throw new BadRequestException("Specified queueAclType=" + queueAclType
+ " is not a valid type, valid queue acl types={"
+ "SUBMIT_APPLICATIONS/ADMINISTER_QUEUE}");
}
if (!this.rm.getResourceScheduler().checkAccess(user, queueACL, queue)) {
return Response.status(Status.FORBIDDEN).entity(
return new RMQueueAclInfo(false, user.getUserName(),
"User=" + username + " doesn't have access to queue=" + queue
+ " with acl-type=" + queueAclType).build();
+ " with acl-type=" + queueAclType);
}
return Response.status(Status.OK).build();
return new RMQueueAclInfo(true, user.getUserName(), "");
}
}

View File

@ -0,0 +1,65 @@
/**
* 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.yarn.server.resourcemanager.webapp.dao;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class RMQueueAclInfo {
protected boolean allowed;
protected String user;
protected String diagnostics;
public RMQueueAclInfo() {
}
public RMQueueAclInfo(boolean allowed, String user, String diagnostics) {
this.allowed = allowed;
this.user = user;
this.diagnostics = diagnostics;
}
public boolean isAllowed() {
return allowed;
}
public void setAllowed(boolean allowed) {
this.allowed = allowed;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getDiagnostics() {
return diagnostics;
}
public void setDiagnostics(String diagnostics) {
this.diagnostics = diagnostics;
}
}

View File

@ -68,6 +68,7 @@ import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppsInfo;
import org.apache.hadoop.yarn.server.security.ApplicationACLsManager;
import org.apache.hadoop.yarn.util.AdHocLogDumper;
import org.apache.hadoop.yarn.util.YarnVersionInfo;
import org.apache.hadoop.yarn.webapp.BadRequestException;
import org.apache.hadoop.yarn.webapp.ForbiddenException;
import org.apache.hadoop.yarn.webapp.GenericExceptionHandler;
import org.apache.hadoop.yarn.webapp.GuiceServletConfig;
@ -798,42 +799,47 @@ public class TestRMWebServices extends JerseyTestBase {
RMWebServices webSvc =
new RMWebServices(mockRM, conf, mock(HttpServletResponse.class));
boolean caughtException = false;
// Case 1: Only queue admin user can access other user's information
HttpServletRequest mockHsr = mockHttpServletRequestByUserName("non-admin");
Assert.assertEquals(webSvc.checkUserAccessToQueue("queue", "jack",
QueueACL.SUBMIT_APPLICATIONS.name(), mockHsr).getStatus(),
Response.SC_FORBIDDEN);
try {
webSvc.checkUserAccessToQueue("queue", "jack",
QueueACL.SUBMIT_APPLICATIONS.name(), mockHsr);
} catch (ForbiddenException e) {
caughtException = true;
}
Assert.assertTrue(caughtException);
// Case 2: request an unknown ACL causes BAD_REQUEST
mockHsr = mockHttpServletRequestByUserName("admin");
Assert.assertEquals(webSvc.checkUserAccessToQueue("queue", "jack",
"XYZ_ACL", mockHsr).getStatus(), Response.SC_BAD_REQUEST);
caughtException = false;
try {
webSvc.checkUserAccessToQueue("queue", "jack", "XYZ_ACL", mockHsr);
} catch (BadRequestException e) {
caughtException = true;
}
Assert.assertTrue(caughtException);
// Case 3: get FORBIDDEN for rejected ACL
mockHsr = mockHttpServletRequestByUserName("admin");
Assert.assertEquals(webSvc.checkUserAccessToQueue("queue", "jack",
QueueACL.SUBMIT_APPLICATIONS.name(), mockHsr).getStatus(),
Response.SC_FORBIDDEN);
Assert.assertEquals(webSvc.checkUserAccessToQueue("queue", "jack",
QueueACL.ADMINISTER_QUEUE.name(), mockHsr).getStatus(),
Response.SC_FORBIDDEN);
Assert.assertFalse(webSvc.checkUserAccessToQueue("queue", "jack",
QueueACL.SUBMIT_APPLICATIONS.name(), mockHsr).isAllowed());
Assert.assertFalse(webSvc.checkUserAccessToQueue("queue", "jack",
QueueACL.ADMINISTER_QUEUE.name(), mockHsr).isAllowed());
// Case 4: get OK for listed ACLs
mockHsr = mockHttpServletRequestByUserName("admin");
Assert.assertEquals(webSvc.checkUserAccessToQueue("queue", "admin",
QueueACL.SUBMIT_APPLICATIONS.name(), mockHsr).getStatus(),
Response.SC_OK);
Assert.assertEquals(webSvc.checkUserAccessToQueue("queue", "admin",
QueueACL.ADMINISTER_QUEUE.name(), mockHsr).getStatus(),
Response.SC_OK);
Assert.assertTrue(webSvc.checkUserAccessToQueue("queue", "admin",
QueueACL.SUBMIT_APPLICATIONS.name(), mockHsr).isAllowed());
Assert.assertTrue(webSvc.checkUserAccessToQueue("queue", "admin",
QueueACL.ADMINISTER_QUEUE.name(), mockHsr).isAllowed());
// Case 5: get OK only for SUBMIT_APP acl for "yarn" user
mockHsr = mockHttpServletRequestByUserName("admin");
Assert.assertEquals(webSvc.checkUserAccessToQueue("queue", "yarn",
QueueACL.SUBMIT_APPLICATIONS.name(), mockHsr).getStatus(),
Response.SC_OK);
Assert.assertEquals(webSvc.checkUserAccessToQueue("queue", "yarn",
QueueACL.ADMINISTER_QUEUE.name(), mockHsr).getStatus(),
Response.SC_FORBIDDEN);
Assert.assertTrue(webSvc.checkUserAccessToQueue("queue", "yarn",
QueueACL.SUBMIT_APPLICATIONS.name(), mockHsr).isAllowed());
Assert.assertFalse(webSvc.checkUserAccessToQueue("queue", "yarn",
QueueACL.ADMINISTER_QUEUE.name(), mockHsr).isAllowed());
}
}

View File

@ -53,6 +53,7 @@ import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeLabelsInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeToLabelsEntryList;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeToLabelsInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodesInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.RMQueueAclInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationDeleteRequestInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationSubmissionRequestInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationUpdateRequestInfo;
@ -471,10 +472,10 @@ public class DefaultRequestInterceptorREST
}
@Override
public Response checkUserAccessToQueue(String queue, String username,
public RMQueueAclInfo checkUserAccessToQueue(String queue, String username,
String queueAclType, HttpServletRequest hsr) {
return RouterWebServiceUtil.genericForward(webAppAddress, hsr,
Response.class, HTTPMethods.GET,
RMQueueAclInfo.class, HTTPMethods.GET,
RMWSConsts.RM_WEB_SERVICE_PATH + RMWSConsts.QUEUES + "/" + queue
+ "/access", null, null);
}

View File

@ -79,6 +79,7 @@ import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeLabelsInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeToLabelsEntryList;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeToLabelsInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodesInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.RMQueueAclInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationDeleteRequestInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationSubmissionRequestInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationUpdateRequestInfo;
@ -1184,7 +1185,7 @@ public class FederationInterceptorREST extends AbstractRESTRequestInterceptor {
}
@Override
public Response checkUserAccessToQueue(String queue, String username,
public RMQueueAclInfo checkUserAccessToQueue(String queue, String username,
String queueAclType, HttpServletRequest hsr) {
throw new NotImplementedException();
}

View File

@ -75,6 +75,7 @@ import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeLabelsInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeToLabelsEntryList;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeToLabelsInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodesInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.RMQueueAclInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationDeleteRequestInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationSubmissionRequestInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationUpdateRequestInfo;
@ -837,7 +838,7 @@ public class RouterWebServices implements RMWebServiceProtocol {
@Produces({ MediaType.APPLICATION_JSON + "; " + JettyUtils.UTF_8,
MediaType.APPLICATION_XML + "; " + JettyUtils.UTF_8 })
@Override
public Response checkUserAccessToQueue(
public RMQueueAclInfo checkUserAccessToQueue(
@PathParam(RMWSConsts.QUEUE) String queue,
@QueryParam(RMWSConsts.USER) String username,
@QueryParam(RMWSConsts.QUEUE_ACL_TYPE)

View File

@ -50,6 +50,7 @@ import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeLabelsInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeToLabelsEntryList;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeToLabelsInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodesInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.RMQueueAclInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationDeleteRequestInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationSubmissionRequestInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationUpdateRequestInfo;
@ -319,9 +320,9 @@ public class MockRESTRequestInterceptor extends AbstractRESTRequestInterceptor {
}
@Override
public Response checkUserAccessToQueue(String queue, String username,
public RMQueueAclInfo checkUserAccessToQueue(String queue, String username,
String queueAclType, HttpServletRequest hsr) {
return Response.status(Status.OK).build();
return new RMQueueAclInfo(true, username, "");
}
@Override

View File

@ -48,6 +48,7 @@ import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeLabelsInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeToLabelsEntryList;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodeToLabelsInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NodesInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.RMQueueAclInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationDeleteRequestInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationSubmissionRequestInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ReservationUpdateRequestInfo;
@ -69,7 +70,7 @@ public class PassThroughRESTRequestInterceptor
}
@Override
public Response checkUserAccessToQueue(String queue, String username,
public RMQueueAclInfo checkUserAccessToQueue(String queue, String username,
String queueAclType, HttpServletRequest hsr)
throws AuthorizationException {
return getNextInterceptor().checkUserAccessToQueue(queue, username,