YARN-8455. Add basic ACL check for all ATS v2 REST APIs. Contributed by Rohith Sharma K S.
(cherry picked from commit 469b29c081
)
This commit is contained in:
parent
122cb81b65
commit
e7f2c9886a
|
@ -0,0 +1,93 @@
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.timelineservice.reader;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for decoding FROM_ID
|
||||||
|
*/
|
||||||
|
enum TimelineFromIdConverter {
|
||||||
|
|
||||||
|
APPLICATION_FROMID {
|
||||||
|
@Override TimelineReaderContext decodeUID(String fromId) throws Exception {
|
||||||
|
if (fromId == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> appTupleList = TimelineReaderUtils.split(fromId);
|
||||||
|
if (appTupleList == null || appTupleList.size() != 5) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Invalid row key for application table.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TimelineReaderContext(appTupleList.get(0), appTupleList.get(1),
|
||||||
|
appTupleList.get(2), Long.parseLong(appTupleList.get(3)),
|
||||||
|
appTupleList.get(4), null, null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
SUB_APPLICATION_ENTITY_FROMID {
|
||||||
|
@Override TimelineReaderContext decodeUID(String fromId) throws Exception {
|
||||||
|
if (fromId == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<String> split = TimelineReaderUtils.split(fromId);
|
||||||
|
if (split == null || split.size() != 6) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Invalid row key for sub app table.");
|
||||||
|
}
|
||||||
|
|
||||||
|
String subAppUserId = split.get(0);
|
||||||
|
String clusterId = split.get(1);
|
||||||
|
String entityType = split.get(2);
|
||||||
|
Long entityIdPrefix = Long.valueOf(split.get(3));
|
||||||
|
String entityId = split.get(4);
|
||||||
|
String userId = split.get(5);
|
||||||
|
return new TimelineReaderContext(clusterId, userId, null, null, null,
|
||||||
|
entityType, entityIdPrefix, entityId, subAppUserId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
GENERIC_ENTITY_FROMID {
|
||||||
|
@Override TimelineReaderContext decodeUID(String fromId) throws Exception {
|
||||||
|
if (fromId == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<String> split = TimelineReaderUtils.split(fromId);
|
||||||
|
if (split == null || split.size() != 8) {
|
||||||
|
throw new IllegalArgumentException("Invalid row key for entity table.");
|
||||||
|
}
|
||||||
|
Long flowRunId = Long.valueOf(split.get(3));
|
||||||
|
Long entityIdPrefix = Long.valueOf(split.get(6));
|
||||||
|
return new TimelineReaderContext(split.get(0), split.get(1), split.get(2),
|
||||||
|
flowRunId, split.get(4), split.get(5), entityIdPrefix, split.get(7));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes FROM_ID depending on FROM_ID implementation.
|
||||||
|
*
|
||||||
|
* @param fromId FROM_ID to be decoded.
|
||||||
|
* @return a {@link TimelineReaderContext} object if FROM_ID passed can be
|
||||||
|
* decoded, null otherwise.
|
||||||
|
* @throws Exception if any problem occurs while decoding.
|
||||||
|
*/
|
||||||
|
abstract TimelineReaderContext decodeUID(String fromId) throws Exception;
|
||||||
|
}
|
|
@ -55,6 +55,7 @@ import org.apache.hadoop.yarn.conf.YarnConfiguration;
|
||||||
import org.apache.hadoop.yarn.server.timelineservice.storage.TimelineReader.Field;
|
import org.apache.hadoop.yarn.server.timelineservice.storage.TimelineReader.Field;
|
||||||
import org.apache.hadoop.yarn.util.timeline.TimelineUtils;
|
import org.apache.hadoop.yarn.util.timeline.TimelineUtils;
|
||||||
import org.apache.hadoop.yarn.webapp.BadRequestException;
|
import org.apache.hadoop.yarn.webapp.BadRequestException;
|
||||||
|
import org.apache.hadoop.yarn.webapp.ForbiddenException;
|
||||||
import org.apache.hadoop.yarn.webapp.NotFoundException;
|
import org.apache.hadoop.yarn.webapp.NotFoundException;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
@ -188,6 +189,8 @@ public class TimelineReaderWebServices {
|
||||||
"Filter Parsing failed." : e.getMessage());
|
"Filter Parsing failed." : e.getMessage());
|
||||||
} else if (e instanceof BadRequestException) {
|
} else if (e instanceof BadRequestException) {
|
||||||
throw (BadRequestException)e;
|
throw (BadRequestException)e;
|
||||||
|
} else if (e instanceof ForbiddenException) {
|
||||||
|
throw (ForbiddenException) e;
|
||||||
} else {
|
} else {
|
||||||
LOG.error("Error while processing REST request", e);
|
LOG.error("Error while processing REST request", e);
|
||||||
throw new WebApplicationException(e,
|
throw new WebApplicationException(e,
|
||||||
|
@ -339,6 +342,7 @@ public class TimelineReaderWebServices {
|
||||||
TimelineReaderWebServicesUtils.createTimelineDataToRetrieve(
|
TimelineReaderWebServicesUtils.createTimelineDataToRetrieve(
|
||||||
confsToRetrieve, metricsToRetrieve, fields, metricsLimit,
|
confsToRetrieve, metricsToRetrieve, fields, metricsLimit,
|
||||||
metricsTimeStart, metricsTimeEnd));
|
metricsTimeStart, metricsTimeEnd));
|
||||||
|
checkAccessForGenericEntities(entities, callerUGI, entityType);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
handleException(e, url, startTime,
|
handleException(e, url, startTime,
|
||||||
"createdTime start/end or limit or flowrunid");
|
"createdTime start/end or limit or flowrunid");
|
||||||
|
@ -607,13 +611,15 @@ public class TimelineReaderWebServices {
|
||||||
.createTimelineReaderContext(clusterId, userId, flowName, flowRunId,
|
.createTimelineReaderContext(clusterId, userId, flowName, flowRunId,
|
||||||
appId, entityType, null, null);
|
appId, entityType, null, null);
|
||||||
entities = timelineReaderManager.getEntities(context,
|
entities = timelineReaderManager.getEntities(context,
|
||||||
TimelineReaderWebServicesUtils.createTimelineEntityFilters(
|
TimelineReaderWebServicesUtils
|
||||||
limit, createdTimeStart, createdTimeEnd, relatesTo, isRelatedTo,
|
.createTimelineEntityFilters(limit, createdTimeStart,
|
||||||
infofilters, conffilters, metricfilters, eventfilters,
|
createdTimeEnd, relatesTo, isRelatedTo, infofilters,
|
||||||
fromId),
|
conffilters, metricfilters, eventfilters, fromId),
|
||||||
TimelineReaderWebServicesUtils.createTimelineDataToRetrieve(
|
TimelineReaderWebServicesUtils
|
||||||
confsToRetrieve, metricsToRetrieve, fields, metricsLimit,
|
.createTimelineDataToRetrieve(confsToRetrieve, metricsToRetrieve,
|
||||||
metricsTimeStart, metricsTimeEnd));
|
fields, metricsLimit, metricsTimeStart, metricsTimeEnd));
|
||||||
|
|
||||||
|
checkAccessForGenericEntities(entities, callerUGI, entityType);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
handleException(e, url, startTime,
|
handleException(e, url, startTime,
|
||||||
"createdTime start/end or limit or flowrunid");
|
"createdTime start/end or limit or flowrunid");
|
||||||
|
@ -704,6 +710,7 @@ public class TimelineReaderWebServices {
|
||||||
TimelineReaderWebServicesUtils.createTimelineDataToRetrieve(
|
TimelineReaderWebServicesUtils.createTimelineDataToRetrieve(
|
||||||
confsToRetrieve, metricsToRetrieve, fields, metricsLimit,
|
confsToRetrieve, metricsToRetrieve, fields, metricsLimit,
|
||||||
metricsTimeStart, metricsTimeEnd));
|
metricsTimeStart, metricsTimeEnd));
|
||||||
|
checkAccessForGenericEntity(entity, callerUGI);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
handleException(e, url, startTime, "flowrunid");
|
handleException(e, url, startTime, "flowrunid");
|
||||||
}
|
}
|
||||||
|
@ -893,6 +900,7 @@ public class TimelineReaderWebServices {
|
||||||
TimelineReaderWebServicesUtils.createTimelineDataToRetrieve(
|
TimelineReaderWebServicesUtils.createTimelineDataToRetrieve(
|
||||||
confsToRetrieve, metricsToRetrieve, fields, metricsLimit,
|
confsToRetrieve, metricsToRetrieve, fields, metricsLimit,
|
||||||
metricsTimeStart, metricsTimeEnd));
|
metricsTimeStart, metricsTimeEnd));
|
||||||
|
checkAccessForGenericEntity(entity, callerUGI);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
handleException(e, url, startTime, "flowrunid");
|
handleException(e, url, startTime, "flowrunid");
|
||||||
}
|
}
|
||||||
|
@ -956,6 +964,8 @@ public class TimelineReaderWebServices {
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
throw new BadRequestException("Incorrect UID " + uId);
|
throw new BadRequestException("Incorrect UID " + uId);
|
||||||
}
|
}
|
||||||
|
// TODO to be removed or modified once ACL story is played
|
||||||
|
checkAccess(timelineReaderManager, callerUGI, context.getUserId());
|
||||||
context.setEntityType(TimelineEntityType.YARN_FLOW_RUN.toString());
|
context.setEntityType(TimelineEntityType.YARN_FLOW_RUN.toString());
|
||||||
entity = timelineReaderManager.getEntity(context,
|
entity = timelineReaderManager.getEntity(context,
|
||||||
TimelineReaderWebServicesUtils.createTimelineDataToRetrieve(
|
TimelineReaderWebServicesUtils.createTimelineDataToRetrieve(
|
||||||
|
@ -1063,12 +1073,16 @@ public class TimelineReaderWebServices {
|
||||||
TimelineReaderManager timelineReaderManager = getTimelineReaderManager();
|
TimelineReaderManager timelineReaderManager = getTimelineReaderManager();
|
||||||
TimelineEntity entity = null;
|
TimelineEntity entity = null;
|
||||||
try {
|
try {
|
||||||
entity = timelineReaderManager.getEntity(
|
TimelineReaderContext context = TimelineReaderWebServicesUtils
|
||||||
TimelineReaderWebServicesUtils.createTimelineReaderContext(
|
.createTimelineReaderContext(clusterId, userId, flowName, flowRunId,
|
||||||
clusterId, userId, flowName, flowRunId, null,
|
null, TimelineEntityType.YARN_FLOW_RUN.toString(), null, null);
|
||||||
TimelineEntityType.YARN_FLOW_RUN.toString(), null, null),
|
// TODO to be removed or modified once ACL story is played
|
||||||
TimelineReaderWebServicesUtils.createTimelineDataToRetrieve(
|
checkAccess(timelineReaderManager, callerUGI, context.getUserId());
|
||||||
null, metricsToRetrieve, null, null, null, null));
|
|
||||||
|
entity = timelineReaderManager.getEntity(context,
|
||||||
|
TimelineReaderWebServicesUtils
|
||||||
|
.createTimelineDataToRetrieve(null, metricsToRetrieve, null, null,
|
||||||
|
null, null));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
handleException(e, url, startTime, "flowrunid");
|
handleException(e, url, startTime, "flowrunid");
|
||||||
}
|
}
|
||||||
|
@ -1156,6 +1170,8 @@ public class TimelineReaderWebServices {
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
throw new BadRequestException("Incorrect UID " + uId);
|
throw new BadRequestException("Incorrect UID " + uId);
|
||||||
}
|
}
|
||||||
|
// TODO to be removed or modified once ACL story is played
|
||||||
|
checkAccess(timelineReaderManager, callerUGI, context.getUserId());
|
||||||
context.setEntityType(TimelineEntityType.YARN_FLOW_RUN.toString());
|
context.setEntityType(TimelineEntityType.YARN_FLOW_RUN.toString());
|
||||||
entities = timelineReaderManager.getEntities(context,
|
entities = timelineReaderManager.getEntities(context,
|
||||||
TimelineReaderWebServicesUtils.createTimelineEntityFilters(
|
TimelineReaderWebServicesUtils.createTimelineEntityFilters(
|
||||||
|
@ -1304,15 +1320,21 @@ public class TimelineReaderWebServices {
|
||||||
TimelineReaderManager timelineReaderManager = getTimelineReaderManager();
|
TimelineReaderManager timelineReaderManager = getTimelineReaderManager();
|
||||||
Set<TimelineEntity> entities = null;
|
Set<TimelineEntity> entities = null;
|
||||||
try {
|
try {
|
||||||
entities = timelineReaderManager.getEntities(
|
TimelineReaderContext timelineReaderContext = TimelineReaderWebServicesUtils
|
||||||
TimelineReaderWebServicesUtils.createTimelineReaderContext(
|
.createTimelineReaderContext(clusterId, userId, flowName, null,
|
||||||
clusterId, userId, flowName, null, null,
|
null, TimelineEntityType.YARN_FLOW_RUN.toString(), null,
|
||||||
TimelineEntityType.YARN_FLOW_RUN.toString(), null, null),
|
null);
|
||||||
TimelineReaderWebServicesUtils.createTimelineEntityFilters(
|
// TODO to be removed or modified once ACL story is played
|
||||||
limit, createdTimeStart, createdTimeEnd, null, null, null,
|
checkAccess(timelineReaderManager, callerUGI,
|
||||||
null, null, null, fromId),
|
timelineReaderContext.getUserId());
|
||||||
TimelineReaderWebServicesUtils.createTimelineDataToRetrieve(
|
|
||||||
null, metricsToRetrieve, fields, null, null, null));
|
entities = timelineReaderManager.getEntities(timelineReaderContext,
|
||||||
|
TimelineReaderWebServicesUtils
|
||||||
|
.createTimelineEntityFilters(limit, createdTimeStart,
|
||||||
|
createdTimeEnd, null, null, null, null, null, null, fromId),
|
||||||
|
TimelineReaderWebServicesUtils
|
||||||
|
.createTimelineDataToRetrieve(null, metricsToRetrieve, fields,
|
||||||
|
null, null, null));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
handleException(e, url, startTime,
|
handleException(e, url, startTime,
|
||||||
"createdTime start/end or limit or fromId");
|
"createdTime start/end or limit or fromId");
|
||||||
|
@ -1435,7 +1457,6 @@ public class TimelineReaderWebServices {
|
||||||
long startTime = Time.monotonicNow();
|
long startTime = Time.monotonicNow();
|
||||||
init(res);
|
init(res);
|
||||||
TimelineReaderManager timelineReaderManager = getTimelineReaderManager();
|
TimelineReaderManager timelineReaderManager = getTimelineReaderManager();
|
||||||
Configuration config = timelineReaderManager.getConfig();
|
|
||||||
Set<TimelineEntity> entities = null;
|
Set<TimelineEntity> entities = null;
|
||||||
try {
|
try {
|
||||||
DateRange range = parseDateRange(dateRange);
|
DateRange range = parseDateRange(dateRange);
|
||||||
|
@ -1455,19 +1476,9 @@ public class TimelineReaderWebServices {
|
||||||
long endTime = Time.monotonicNow();
|
long endTime = Time.monotonicNow();
|
||||||
if (entities == null) {
|
if (entities == null) {
|
||||||
entities = Collections.emptySet();
|
entities = Collections.emptySet();
|
||||||
} else if (isDisplayEntityPerUserFilterEnabled(config)) {
|
} else {
|
||||||
Set<TimelineEntity> userEntities = new LinkedHashSet<>();
|
checkAccess(timelineReaderManager, callerUGI, entities,
|
||||||
userEntities.addAll(entities);
|
FlowActivityEntity.USER_INFO_KEY, true);
|
||||||
for (TimelineEntity entity : userEntities) {
|
|
||||||
if (entity.getInfo() != null) {
|
|
||||||
String userId =
|
|
||||||
(String) entity.getInfo().get(FlowActivityEntity.USER_INFO_KEY);
|
|
||||||
if (!validateAuthUserWithEntityUser(timelineReaderManager, callerUGI,
|
|
||||||
userId)) {
|
|
||||||
entities.remove(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
LOG.info("Processed URL " + url +
|
LOG.info("Processed URL " + url +
|
||||||
" (Took " + (endTime - startTime) + " ms.)");
|
" (Took " + (endTime - startTime) + " ms.)");
|
||||||
|
@ -1552,6 +1563,7 @@ public class TimelineReaderWebServices {
|
||||||
TimelineReaderWebServicesUtils.createTimelineDataToRetrieve(
|
TimelineReaderWebServicesUtils.createTimelineDataToRetrieve(
|
||||||
confsToRetrieve, metricsToRetrieve, fields, metricsLimit,
|
confsToRetrieve, metricsToRetrieve, fields, metricsLimit,
|
||||||
metricsTimeStart, metricsTimeEnd));
|
metricsTimeStart, metricsTimeEnd));
|
||||||
|
checkAccessForAppEntity(entity, callerUGI);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
handleException(e, url, startTime, "flowrunid");
|
handleException(e, url, startTime, "flowrunid");
|
||||||
}
|
}
|
||||||
|
@ -1722,6 +1734,7 @@ public class TimelineReaderWebServices {
|
||||||
TimelineReaderWebServicesUtils.createTimelineDataToRetrieve(
|
TimelineReaderWebServicesUtils.createTimelineDataToRetrieve(
|
||||||
confsToRetrieve, metricsToRetrieve, fields, metricsLimit,
|
confsToRetrieve, metricsToRetrieve, fields, metricsLimit,
|
||||||
metricsTimeStart, metricsTimeEnd));
|
metricsTimeStart, metricsTimeEnd));
|
||||||
|
checkAccessForAppEntity(entity, callerUGI);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
handleException(e, url, startTime, "flowrunid");
|
handleException(e, url, startTime, "flowrunid");
|
||||||
}
|
}
|
||||||
|
@ -1852,6 +1865,8 @@ public class TimelineReaderWebServices {
|
||||||
if (context == null) {
|
if (context == null) {
|
||||||
throw new BadRequestException("Incorrect UID " + uId);
|
throw new BadRequestException("Incorrect UID " + uId);
|
||||||
}
|
}
|
||||||
|
// TODO to be removed or modified once ACL story is played
|
||||||
|
checkAccess(timelineReaderManager, callerUGI, context.getUserId());
|
||||||
context.setEntityType(TimelineEntityType.YARN_APPLICATION.toString());
|
context.setEntityType(TimelineEntityType.YARN_APPLICATION.toString());
|
||||||
entities = timelineReaderManager.getEntities(context,
|
entities = timelineReaderManager.getEntities(context,
|
||||||
TimelineReaderWebServicesUtils.createTimelineEntityFilters(
|
TimelineReaderWebServicesUtils.createTimelineEntityFilters(
|
||||||
|
@ -3343,6 +3358,7 @@ public class TimelineReaderWebServices {
|
||||||
TimelineReaderWebServicesUtils.createTimelineDataToRetrieve(
|
TimelineReaderWebServicesUtils.createTimelineDataToRetrieve(
|
||||||
confsToRetrieve, metricsToRetrieve, fields, metricsLimit,
|
confsToRetrieve, metricsToRetrieve, fields, metricsLimit,
|
||||||
metricsTimeStart, metricsTimeEnd));
|
metricsTimeStart, metricsTimeEnd));
|
||||||
|
checkAccessForSubAppEntities(entities,callerUGI);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
handleException(e, url, startTime,
|
handleException(e, url, startTime,
|
||||||
"createdTime start/end or limit");
|
"createdTime start/end or limit");
|
||||||
|
@ -3410,6 +3426,7 @@ public class TimelineReaderWebServices {
|
||||||
TimelineReaderWebServicesUtils.createTimelineDataToRetrieve(
|
TimelineReaderWebServicesUtils.createTimelineDataToRetrieve(
|
||||||
confsToRetrieve, metricsToRetrieve, fields, metricsLimit,
|
confsToRetrieve, metricsToRetrieve, fields, metricsLimit,
|
||||||
metricsTimeStart, metricsTimeEnd));
|
metricsTimeStart, metricsTimeEnd));
|
||||||
|
checkAccessForSubAppEntities(entities,callerUGI);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
handleException(e, url, startTime, "");
|
handleException(e, url, startTime, "");
|
||||||
}
|
}
|
||||||
|
@ -3422,7 +3439,7 @@ public class TimelineReaderWebServices {
|
||||||
return entities;
|
return entities;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isDisplayEntityPerUserFilterEnabled(Configuration config) {
|
static boolean isDisplayEntityPerUserFilterEnabled(Configuration config) {
|
||||||
return !config
|
return !config
|
||||||
.getBoolean(YarnConfiguration.TIMELINE_SERVICE_READ_AUTH_ENABLED,
|
.getBoolean(YarnConfiguration.TIMELINE_SERVICE_READ_AUTH_ENABLED,
|
||||||
YarnConfiguration.DEFAULT_TIMELINE_SERVICE_READ_AUTH_ENABLED)
|
YarnConfiguration.DEFAULT_TIMELINE_SERVICE_READ_AUTH_ENABLED)
|
||||||
|
@ -3430,8 +3447,76 @@ public class TimelineReaderWebServices {
|
||||||
.getBoolean(YarnConfiguration.FILTER_ENTITY_LIST_BY_USER, false);
|
.getBoolean(YarnConfiguration.FILTER_ENTITY_LIST_BY_USER, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO to be removed or modified once ACL story is played
|
||||||
|
private void checkAccessForSubAppEntities(Set<TimelineEntity> entities,
|
||||||
|
UserGroupInformation callerUGI) throws Exception {
|
||||||
|
if (entities != null && entities.size() > 0
|
||||||
|
&& isDisplayEntityPerUserFilterEnabled(
|
||||||
|
getTimelineReaderManager().getConfig())) {
|
||||||
|
TimelineReaderContext timelineReaderContext = null;
|
||||||
|
TimelineEntity entity = entities.iterator().next();
|
||||||
|
String fromId =
|
||||||
|
(String) entity.getInfo().get(TimelineReaderUtils.FROMID_KEY);
|
||||||
|
timelineReaderContext =
|
||||||
|
TimelineFromIdConverter.SUB_APPLICATION_ENTITY_FROMID
|
||||||
|
.decodeUID(fromId);
|
||||||
|
checkAccess(getTimelineReaderManager(), callerUGI,
|
||||||
|
timelineReaderContext.getDoAsUser());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO to be removed or modified once ACL story is played
|
||||||
|
private void checkAccessForAppEntity(TimelineEntity entity,
|
||||||
|
UserGroupInformation callerUGI) throws Exception {
|
||||||
|
if (entity != null && isDisplayEntityPerUserFilterEnabled(
|
||||||
|
getTimelineReaderManager().getConfig())) {
|
||||||
|
String fromId =
|
||||||
|
(String) entity.getInfo().get(TimelineReaderUtils.FROMID_KEY);
|
||||||
|
TimelineReaderContext timelineReaderContext =
|
||||||
|
TimelineFromIdConverter.APPLICATION_FROMID.decodeUID(fromId);
|
||||||
|
checkAccess(getTimelineReaderManager(), callerUGI,
|
||||||
|
timelineReaderContext.getUserId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO to be removed or modified once ACL story is played
|
||||||
|
private void checkAccessForGenericEntity(TimelineEntity entity,
|
||||||
|
UserGroupInformation callerUGI) throws Exception {
|
||||||
|
if (entity != null && isDisplayEntityPerUserFilterEnabled(
|
||||||
|
getTimelineReaderManager().getConfig())) {
|
||||||
|
String fromId =
|
||||||
|
(String) entity.getInfo().get(TimelineReaderUtils.FROMID_KEY);
|
||||||
|
TimelineReaderContext timelineReaderContext =
|
||||||
|
TimelineFromIdConverter.GENERIC_ENTITY_FROMID.decodeUID(fromId);
|
||||||
|
checkAccess(getTimelineReaderManager(), callerUGI,
|
||||||
|
timelineReaderContext.getUserId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO to be removed or modified once ACL story is played
|
||||||
|
private void checkAccessForGenericEntities(Set<TimelineEntity> entities,
|
||||||
|
UserGroupInformation callerUGI, String entityType) throws Exception {
|
||||||
|
if (entities != null && entities.size() > 0
|
||||||
|
&& isDisplayEntityPerUserFilterEnabled(
|
||||||
|
getTimelineReaderManager().getConfig())) {
|
||||||
|
TimelineReaderContext timelineReaderContext = null;
|
||||||
|
TimelineEntity entity = entities.iterator().next();
|
||||||
|
String uid =
|
||||||
|
(String) entity.getInfo().get(TimelineReaderUtils.FROMID_KEY);
|
||||||
|
if (TimelineEntityType.YARN_APPLICATION.matches(entityType)) {
|
||||||
|
timelineReaderContext =
|
||||||
|
TimelineFromIdConverter.APPLICATION_FROMID.decodeUID(uid);
|
||||||
|
} else {
|
||||||
|
timelineReaderContext =
|
||||||
|
TimelineFromIdConverter.GENERIC_ENTITY_FROMID.decodeUID(uid);
|
||||||
|
}
|
||||||
|
checkAccess(getTimelineReaderManager(), callerUGI,
|
||||||
|
timelineReaderContext.getUserId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO to be removed/modified once ACL story has played
|
// TODO to be removed/modified once ACL story has played
|
||||||
private boolean validateAuthUserWithEntityUser(
|
static boolean validateAuthUserWithEntityUser(
|
||||||
TimelineReaderManager readerManager, UserGroupInformation ugi,
|
TimelineReaderManager readerManager, UserGroupInformation ugi,
|
||||||
String entityUser) {
|
String entityUser) {
|
||||||
String authUser = TimelineReaderWebServicesUtils.getUserName(ugi);
|
String authUser = TimelineReaderWebServicesUtils.getUserName(ugi);
|
||||||
|
@ -3442,4 +3527,41 @@ public class TimelineReaderWebServices {
|
||||||
}
|
}
|
||||||
return (readerManager.checkAccess(ugi) || authUser.equals(requestedUser));
|
return (readerManager.checkAccess(ugi) || authUser.equals(requestedUser));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO to be removed/modified once ACL story has played
|
||||||
|
static boolean checkAccess(TimelineReaderManager readerManager,
|
||||||
|
UserGroupInformation ugi, String entityUser) {
|
||||||
|
if (isDisplayEntityPerUserFilterEnabled(readerManager.getConfig())) {
|
||||||
|
if (!validateAuthUserWithEntityUser(readerManager, ugi, entityUser)) {
|
||||||
|
String userName = ugi.getShortUserName();
|
||||||
|
String msg = "User " + userName
|
||||||
|
+ " is not allowed to read TimelineService V2 data.";
|
||||||
|
throw new ForbiddenException(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO to be removed or modified once ACL story is played
|
||||||
|
static void checkAccess(TimelineReaderManager readerManager,
|
||||||
|
UserGroupInformation callerUGI, Set<TimelineEntity> entities,
|
||||||
|
String entityUserKey, boolean verifyForAllEntity) {
|
||||||
|
if (entities.size() > 0 && isDisplayEntityPerUserFilterEnabled(
|
||||||
|
readerManager.getConfig())) {
|
||||||
|
Set<TimelineEntity> userEntities = new LinkedHashSet<>();
|
||||||
|
userEntities.addAll(entities);
|
||||||
|
for (TimelineEntity entity : userEntities) {
|
||||||
|
if (entity.getInfo() != null) {
|
||||||
|
String userId = (String) entity.getInfo().get(entityUserKey);
|
||||||
|
if (!validateAuthUserWithEntityUser(readerManager, callerUGI,
|
||||||
|
userId)) {
|
||||||
|
entities.remove(entity);
|
||||||
|
if (!verifyForAllEntity) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,154 @@
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.timelineservice.reader;
|
||||||
|
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.security.UserGroupInformation;
|
||||||
|
import org.apache.hadoop.yarn.api.records.timelineservice.TimelineEntity;
|
||||||
|
import org.apache.hadoop.yarn.conf.YarnConfiguration;
|
||||||
|
import org.apache.hadoop.yarn.webapp.ForbiddenException;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class TestTimelineReaderWebServicesBasicAcl {
|
||||||
|
|
||||||
|
private TimelineReaderManager manager;
|
||||||
|
private static String adminUser = "admin";
|
||||||
|
private static UserGroupInformation adminUgi =
|
||||||
|
UserGroupInformation.createRemoteUser(adminUser);
|
||||||
|
private Configuration config;
|
||||||
|
|
||||||
|
@Before public void setUp() throws Exception {
|
||||||
|
config = new YarnConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After public void tearDown() throws Exception {
|
||||||
|
if (manager != null) {
|
||||||
|
manager.stop();
|
||||||
|
manager = null;
|
||||||
|
}
|
||||||
|
config = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test public void testTimelineReaderManagerAclsWhenDisabled()
|
||||||
|
throws Exception {
|
||||||
|
config.setBoolean(YarnConfiguration.YARN_ACL_ENABLE, false);
|
||||||
|
config.set(YarnConfiguration.YARN_ADMIN_ACL, adminUser);
|
||||||
|
manager = new TimelineReaderManager(null);
|
||||||
|
manager.init(config);
|
||||||
|
manager.start();
|
||||||
|
|
||||||
|
// when acls are disabled, always return true
|
||||||
|
Assert.assertTrue(manager.checkAccess(null));
|
||||||
|
|
||||||
|
// filter is disabled, so should return false
|
||||||
|
Assert.assertFalse(
|
||||||
|
TimelineReaderWebServices.isDisplayEntityPerUserFilterEnabled(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test public void testTimelineReaderManagerAclsWhenEnabled()
|
||||||
|
throws Exception {
|
||||||
|
Configuration config = new YarnConfiguration();
|
||||||
|
config.setBoolean(YarnConfiguration.YARN_ACL_ENABLE, true);
|
||||||
|
config.setBoolean(YarnConfiguration.FILTER_ENTITY_LIST_BY_USER, true);
|
||||||
|
config.set(YarnConfiguration.YARN_ADMIN_ACL, adminUser);
|
||||||
|
manager = new TimelineReaderManager(null);
|
||||||
|
manager.init(config);
|
||||||
|
manager.start();
|
||||||
|
|
||||||
|
String user1 = "user1";
|
||||||
|
String user2 = "user2";
|
||||||
|
UserGroupInformation user1Ugi =
|
||||||
|
UserGroupInformation.createRemoteUser(user1);
|
||||||
|
UserGroupInformation user2Ugi =
|
||||||
|
UserGroupInformation.createRemoteUser(user2);
|
||||||
|
|
||||||
|
// false because ugi is null
|
||||||
|
Assert.assertFalse(TimelineReaderWebServices
|
||||||
|
.validateAuthUserWithEntityUser(manager, null, user1));
|
||||||
|
|
||||||
|
// incoming ugi is admin asking for entity owner user1
|
||||||
|
Assert.assertTrue(
|
||||||
|
TimelineReaderWebServices.checkAccess(manager, adminUgi, user1));
|
||||||
|
|
||||||
|
// incoming ugi is admin asking for entity owner user1
|
||||||
|
Assert.assertTrue(
|
||||||
|
TimelineReaderWebServices.checkAccess(manager, adminUgi, user2));
|
||||||
|
|
||||||
|
// incoming ugi is non-admin i.e user1Ugi asking for entity owner user2
|
||||||
|
try {
|
||||||
|
TimelineReaderWebServices.checkAccess(manager, user1Ugi, user2);
|
||||||
|
Assert.fail("user1Ugi is not allowed to view user2");
|
||||||
|
} catch (ForbiddenException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
|
||||||
|
// incoming ugi is non-admin i.e user2Ugi asking for entity owner user1
|
||||||
|
try {
|
||||||
|
TimelineReaderWebServices.checkAccess(manager, user1Ugi, user2);
|
||||||
|
Assert.fail("user2Ugi is not allowed to view user1");
|
||||||
|
} catch (ForbiddenException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
|
||||||
|
String userKey = "user";
|
||||||
|
// incoming ugi is admin asking for entities
|
||||||
|
Set<TimelineEntity> entities = createEntities(10, userKey);
|
||||||
|
TimelineReaderWebServices
|
||||||
|
.checkAccess(manager, adminUgi, entities, userKey, true);
|
||||||
|
// admin is allowed to view other entities
|
||||||
|
Assert.assertTrue(entities.size() == 10);
|
||||||
|
|
||||||
|
// incoming ugi is user1Ugi asking for entities
|
||||||
|
// only user1 entities are allowed to view
|
||||||
|
entities = createEntities(5, userKey);
|
||||||
|
TimelineReaderWebServices
|
||||||
|
.checkAccess(manager, user1Ugi, entities, userKey, true);
|
||||||
|
Assert.assertTrue(entities.size() == 1);
|
||||||
|
Assert
|
||||||
|
.assertEquals(user1, entities.iterator().next().getInfo().get(userKey));
|
||||||
|
|
||||||
|
// incoming ugi is user2Ugi asking for entities
|
||||||
|
// only user2 entities are allowed to view
|
||||||
|
entities = createEntities(8, userKey);
|
||||||
|
TimelineReaderWebServices
|
||||||
|
.checkAccess(manager, user2Ugi, entities, userKey, true);
|
||||||
|
Assert.assertTrue(entities.size() == 1);
|
||||||
|
Assert
|
||||||
|
.assertEquals(user2, entities.iterator().next().getInfo().get(userKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<TimelineEntity> createEntities(int noOfUsers, String userKey) {
|
||||||
|
Set<TimelineEntity> entities = new LinkedHashSet<>();
|
||||||
|
for (int i = 0; i < noOfUsers; i++) {
|
||||||
|
TimelineEntity e = new TimelineEntity();
|
||||||
|
e.setType("user" + i);
|
||||||
|
e.setId("user" + i);
|
||||||
|
e.getInfo().put(userKey, "user" + i);
|
||||||
|
entities.add(e);
|
||||||
|
}
|
||||||
|
return entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue