This is an automated email from the ASF dual-hosted git repository.
pradeepagrawal8184 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git
The following commit(s) were added to refs/heads/master by this push:
new d6b244343 RANGER-5812: Extend service config validation to
lookupResource and saved-service check (#1266)
d6b244343 is described below
commit d6b244343a3f51c0f5ca24fcba9855e702514901
Author: PradeeP AgrawaL <[email protected]>
AuthorDate: Fri Oct 16 18:52:32 2026 +0530
RANGER-5812: Extend service config validation to lookupResource and
saved-service check (#1266)
- Refactor validateConfig SSRF checks into validateOutboundServiceConfigs
and reuse
for lookupResource before Admin initiates outbound resource lookups.
- Enforce service-level hasAccess in ServiceREST.lookupResource.
- Add optional ranger.serviceconfig.validate.require.saved.service
validateConfig
hardening (default false).
---
.../java/org/apache/ranger/biz/ServiceMgr.java | 337 ++++++++++++--
.../java/org/apache/ranger/rest/ServiceREST.java | 9 +
.../conf.dist/ranger-admin-default-site.xml | 6 +
.../java/org/apache/ranger/biz/TestServiceMgr.java | 495 +++++++++++++++++++++
.../org/apache/ranger/rest/TestServiceREST.java | 51 +++
5 files changed, 870 insertions(+), 28 deletions(-)
diff --git a/security-admin/src/main/java/org/apache/ranger/biz/ServiceMgr.java
b/security-admin/src/main/java/org/apache/ranger/biz/ServiceMgr.java
index 515dd98d9..cf9ff309c 100755
--- a/security-admin/src/main/java/org/apache/ranger/biz/ServiceMgr.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/ServiceMgr.java
@@ -59,7 +59,9 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
@@ -79,6 +81,8 @@ public class ServiceMgr {
private static final String NAME_RULES =
"hadoop.security.auth_to_local";
private static final String HOST_NAME = "ranger.service.host";
+ public static final String
PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE =
"ranger.serviceconfig.validate.require.saved.service";
+
private static final long _DefaultTimeoutValue_Lookp = 1000; // 1
s
private static final long _DefaultTimeoutValue_ValidateConfig = 10000; //
10 s
@@ -137,15 +141,17 @@ public List<String> lookupResource(String serviceName,
ResourceLookupContext con
}
}
- RangerBaseService svc = null;
+ if (service == null) {
+ throw new Exception("Service not found: " + serviceName);
+ }
- if (service != null) {
- Map<String, String> newConfigs =
rangerSvcService.getConfigsWithDecryptedPassword(service);
+ Map<String, String> newConfigs =
rangerSvcService.getConfigsWithDecryptedPassword(service);
- service.setConfigs(newConfigs);
+ service.setConfigs(newConfigs);
- svc = getRangerServiceByService(service, svcStore);
- }
+ validateOutboundServiceConfigs(newConfigs);
+
+ RangerBaseService svc = getRangerServiceByService(service, svcStore);
LOG.debug("==> ServiceMgr.lookupResource for Service: ({}Context:
{})", svc, context);
@@ -204,29 +210,9 @@ public VXResponse validateConfig(RangerService service,
ServiceStore svcStore) t
LOG.debug("==> ServiceMgr.validateConfig for Service: ({})", svc);
- // check if service configs contains localhost/127.0.0.1
if (service != null && service.getConfigs() != null) {
- for (Map.Entry<String, String> entry :
service.getConfigs().entrySet()) {
- String configValue = entry.getValue();
- if (configValue != null && !configValue.trim().isEmpty()) {
- String userInfo = extractUserInfo(configValue);
- if (userInfoContainsBlockedHost(userInfo)) {
- throw new Exception("Invalid value for configuration "
+ entry.getKey() + ": userinfo contains blocked host (blocked host detected)");
- }
-
- String host = extractHost(configValue);
- if (host != null) {
- if (isBlockedHost(host)) {
- throw new Exception("Invalid value for
configuration " + entry.getKey() + ": host " + host + " is not allowed (blocked
host detected)");
- }
- } else {
- String lowerVal = configValue.toLowerCase().trim();
- if (lowerVal.contains("localhost") ||
lowerVal.contains("127.0.0.1") || lowerVal.contains("0.0.0.0") ||
lowerVal.contains("::1")) {
- throw new Exception("Invalid value for
configuration " + entry.getKey() + ": contains blocked keywords but could not
be parsed securely.");
- }
- }
- }
- }
+ enforceSavedServiceEndpointConfigs(service);
+ validateOutboundServiceConfigs(service.getConfigs());
}
if (svc != null) {
@@ -460,6 +446,301 @@ Long parseLong(String str) {
}
}
+ private void enforceSavedServiceEndpointConfigs(RangerService service)
throws Exception {
+ if
(!Boolean.parseBoolean(PropertiesUtil.getProperty(PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE,
"false"))) {
+ return;
+ }
+
+ String serviceName = service.getName();
+ if (StringUtils.isBlank(serviceName)) {
+ return;
+ }
+
+ RangerService savedService = svcDBStore.getServiceByName(serviceName);
+ if (savedService == null) {
+ throw new Exception("Configuration validation requires a saved
service: " + serviceName);
+ }
+
+ if (StringUtils.isNotBlank(service.getType()) &&
StringUtils.isNotBlank(savedService.getType())
+ && !StringUtils.equals(service.getType(),
savedService.getType())) {
+ throw new Exception("Configuration validation service type does
not match saved service: " + serviceName);
+ }
+
+ Map<String, String> savedConfigs =
rangerSvcService.getConfigsWithDecryptedPassword(savedService);
+ if (savedConfigs == null) {
+ savedConfigs = Collections.emptyMap();
+ }
+
+ for (Map.Entry<String, String> entry :
service.getConfigs().entrySet()) {
+ String configKey = entry.getKey();
+ String requestValue = entry.getValue();
+ if (requestValue == null || requestValue.trim().isEmpty()) {
+ continue;
+ }
+
+ String savedValue = savedConfigs.get(configKey);
+ boolean savedIsEndpoint = savedValue != null &&
isConnectionEndpoint(savedValue);
+ boolean requestIsEndpoint = isConnectionEndpoint(requestValue);
+
+ if (!savedIsEndpoint && !requestIsEndpoint) {
+ continue;
+ }
+
+ if (savedValue == null) {
+ throw new Exception("Invalid value for configuration " +
configKey + ": endpoint property is not present in saved service
configuration");
+ }
+
+ if (!savedIsEndpoint) {
+ throw new Exception("Invalid value for configuration " +
configKey + ": endpoint must match the saved service configuration");
+ }
+
+ ConnectionEndpointPin savedPin =
parseConnectionEndpointPin(configKey, savedValue);
+ ConnectionEndpointPin requestPin =
parseConnectionEndpointPin(configKey, requestValue);
+ if (!requestPin.matches(savedPin)) {
+ throw new Exception("Invalid value for configuration " +
configKey + ": endpoint must match the saved service configuration");
+ }
+ }
+ }
+
+ private static boolean isConnectionEndpoint(String value) {
+ if (value == null) {
+ return false;
+ }
+
+ String trimmed = value.trim();
+ return looksLikeConnectionUrl(trimmed) || looksLikeJdbcAtHost(trimmed)
|| looksLikeHostPortEndpoint(trimmed);
+ }
+
+ private static boolean looksLikeConnectionUrl(String value) {
+ return value != null && value.contains("://");
+ }
+
+ private static boolean looksLikeJdbcAtHost(String value) {
+ if (StringUtils.isBlank(value) || looksLikeConnectionUrl(value)) {
+ return false;
+ }
+
+ return value.startsWith("jdbc:") && value.contains("@");
+ }
+
+ private static boolean looksLikeHostPortEndpoint(String value) {
+ if (StringUtils.isBlank(value) || looksLikeConnectionUrl(value) ||
value.contains("@")) {
+ return false;
+ }
+
+ if (value.contains(",")) {
+ for (String segment : value.split(",")) {
+ if (!looksLikeHostPortSegment(segment.trim())) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ return looksLikeHostPortSegment(value.trim());
+ }
+
+ private static boolean looksLikeHostPortSegment(String segment) {
+ if (StringUtils.isBlank(segment)) {
+ return false;
+ }
+
+ if (segment.startsWith("[")) {
+ int closeBracket = segment.indexOf(']');
+ if (closeBracket <= 0) {
+ return false;
+ }
+
+ if (closeBracket + 1 >= segment.length() ||
segment.charAt(closeBracket + 1) != ':') {
+ return false;
+ }
+
+ return StringUtils.isNumeric(segment.substring(closeBracket + 2));
+ }
+
+ int colon = segment.lastIndexOf(':');
+ if (colon <= 0 || colon >= segment.length() - 1) {
+ return false;
+ }
+
+ return StringUtils.isNumeric(segment.substring(colon + 1));
+ }
+
+ private static ConnectionEndpointPin parseConnectionEndpointPin(String
configKey, String rawValue) throws Exception {
+ String trimmed = rawValue.trim();
+ if (looksLikeConnectionUrl(trimmed)) {
+ return parseConnectionUrlPin(configKey, trimmed);
+ }
+
+ if (looksLikeJdbcAtHost(trimmed)) {
+ int at = trimmed.lastIndexOf('@');
+ String afterAt = trimmed.substring(at + 1);
+ if (afterAt.startsWith("//")) {
+ return parseConnectionUrlPin(configKey, "jdbc://" +
afterAt.substring(2));
+ }
+
+ List<String> authorityHostPorts =
parseCommaSeparatedHostPorts(afterAt);
+ if (authorityHostPorts.isEmpty()) {
+ throw new Exception("Invalid value for configuration " +
configKey + ": connection URL cannot be parsed securely for endpoint pinning");
+ }
+
+ return new ConnectionEndpointPin("jdbc-at", null,
authorityHostPorts, "");
+ }
+
+ if (looksLikeHostPortEndpoint(trimmed)) {
+ List<String> authorityHostPorts =
parseCommaSeparatedHostPorts(trimmed);
+ if (authorityHostPorts.isEmpty()) {
+ throw new Exception("Invalid value for configuration " +
configKey + ": connection URL cannot be parsed securely for endpoint pinning");
+ }
+
+ return new ConnectionEndpointPin("", null, authorityHostPorts, "");
+ }
+
+ throw new Exception("Invalid value for configuration " + configKey +
": connection URL cannot be parsed securely for endpoint pinning");
+ }
+
+ private static ConnectionEndpointPin parseConnectionUrlPin(String
configKey, String rawValue) throws Exception {
+ String trimmed = rawValue.trim();
+ int schemeSep = trimmed.indexOf("://");
+ if (schemeSep < 0) {
+ throw new Exception("Invalid value for configuration " + configKey
+ ": connection URL cannot be parsed securely for endpoint pinning");
+ }
+
+ String scheme = trimmed.substring(0,
schemeSep).toLowerCase(Locale.ROOT);
+ String remainder = trimmed.substring(schemeSep + 3);
+
+ String path = "";
+ String authority = remainder;
+ int pathStart = remainder.indexOf('/');
+ if (pathStart >= 0) {
+ authority = remainder.substring(0, pathStart);
+ path =
normalizePathForPinning(remainder.substring(pathStart));
+ }
+
+ String userInfo = null;
+ String hostPortPart = authority;
+ if (authority.contains("@")) {
+ int at = authority.lastIndexOf('@');
+ userInfo = authority.substring(0, at);
+ hostPortPart = authority.substring(at + 1);
+ }
+
+ List<String> authorityHostPorts =
parseCommaSeparatedHostPorts(hostPortPart);
+ if (authorityHostPorts.isEmpty() && StringUtils.isBlank(hostPortPart))
{
+ if (StringUtils.isBlank(path) || "/".equals(path)) {
+ throw new Exception("Invalid value for configuration " +
configKey + ": connection URL cannot be parsed securely for endpoint pinning");
+ }
+ } else if (authorityHostPorts.isEmpty()) {
+ throw new Exception("Invalid value for configuration " + configKey
+ ": connection URL cannot be parsed securely for endpoint pinning");
+ }
+
+ return new ConnectionEndpointPin(scheme, userInfo, authorityHostPorts,
path);
+ }
+
+ private static String normalizePathForPinning(String path) {
+ if (path == null) {
+ return "";
+ }
+ int cut = path.length();
+ for (int i = 0; i < path.length(); i++) {
+ char c = path.charAt(i);
+ if (c == '?' || c == '#') {
+ cut = i;
+ break;
+ }
+ }
+ return path.substring(0, cut);
+ }
+
+ private static List<String> parseCommaSeparatedHostPorts(String
hostPortPart) {
+ List<String> ret = new ArrayList<>();
+ if (StringUtils.isBlank(hostPortPart)) {
+ return ret;
+ }
+
+ for (String segment : hostPortPart.split(",")) {
+ String normalized = normalizeHostPortSegment(segment.trim());
+ if (StringUtils.isNotBlank(normalized)) {
+ ret.add(normalized);
+ }
+ }
+
+ return ret;
+ }
+
+ private static String normalizeHostPortSegment(String segment) {
+ if (StringUtils.isBlank(segment)) {
+ return segment;
+ }
+
+ if (segment.startsWith("[")) {
+ int closeBracket = segment.indexOf(']');
+ if (closeBracket > 0) {
+ String host = segment.substring(1,
closeBracket).toLowerCase(Locale.ROOT);
+ String portSuffix = closeBracket + 1 < segment.length() ?
segment.substring(closeBracket + 1) : "";
+ return host + portSuffix;
+ }
+ }
+
+ int colonCount = StringUtils.countMatches(segment, ":");
+ if (colonCount == 1) {
+ int colon = segment.indexOf(':');
+ return segment.substring(0, colon).toLowerCase(Locale.ROOT) +
segment.substring(colon);
+ }
+
+ return segment.toLowerCase(Locale.ROOT);
+ }
+
+ private static final class ConnectionEndpointPin {
+ private final String scheme;
+ private final String userInfo;
+ private final List<String> authorityHostPorts;
+ private final String path;
+
+ private ConnectionEndpointPin(String scheme, String userInfo,
List<String> authorityHostPorts, String path) {
+ this.scheme = scheme;
+ this.userInfo = StringUtils.isBlank(userInfo) ? null :
userInfo;
+ this.authorityHostPorts = authorityHostPorts == null ?
Collections.emptyList() : authorityHostPorts;
+ this.path = path == null ? "" : path;
+ }
+
+ private boolean matches(ConnectionEndpointPin saved) {
+ return Objects.equals(scheme, saved.scheme)
+ && Objects.equals(userInfo, saved.userInfo)
+ && Objects.equals(path, saved.path)
+ && authorityHostPorts.equals(saved.authorityHostPorts);
+ }
+ }
+
+ private static void validateOutboundServiceConfigs(Map<String, String>
configs) throws Exception {
+ if (configs == null) {
+ return;
+ }
+
+ for (Map.Entry<String, String> entry : configs.entrySet()) {
+ String configValue = entry.getValue();
+ if (configValue != null && !configValue.trim().isEmpty()) {
+ String userInfo = extractUserInfo(configValue);
+ if (userInfoContainsBlockedHost(userInfo)) {
+ throw new Exception("Invalid value for configuration " +
entry.getKey() + ": userinfo contains blocked host (blocked host detected)");
+ }
+
+ String host = extractHost(configValue);
+ if (host != null) {
+ if (isBlockedHost(host)) {
+ throw new Exception("Invalid value for configuration "
+ entry.getKey() + ": host " + host + " is not allowed (blocked host
detected)");
+ }
+ } else {
+ String lowerVal = configValue.toLowerCase().trim();
+ if (lowerVal.contains("localhost") ||
lowerVal.contains("127.0.0.1") || lowerVal.contains("0.0.0.0") ||
lowerVal.contains("::1")) {
+ throw new Exception("Invalid value for configuration "
+ entry.getKey() + ": contains blocked keywords but could not be parsed
securely.");
+ }
+ }
+ }
+ }
+ }
+
private static URI toConfigUri(String urlString) throws Exception {
String processedUrl = urlString.trim().replaceFirst("(?i)^jdbc:", "");
if (!processedUrl.contains("://")) {
diff --git
a/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java
b/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java
index 5197b2aff..665cd44f1 100644
--- a/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java
+++ b/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java
@@ -1135,6 +1135,15 @@ public List<String>
lookupResource(@PathParam("serviceName") String serviceName,
RangerPerfTracer perf = null;
try {
+ XXService xxService =
daoManager.getXXService().findByName(serviceName);
+ if (xxService == null) {
+ throw
restErrorUtil.createRESTException(HttpServletResponse.SC_NOT_FOUND, "Not
found", true);
+ }
+ if (!Boolean.TRUE.equals(bizUtil.hasAccess(xxService, null))) {
+ throw
restErrorUtil.createRESTException(HttpServletResponse.SC_FORBIDDEN,
+ "Operation denied. User is not permitted to lookup
resources for service " + serviceName, true);
+ }
+
if (RangerPerfTracer.isPerfTraceEnabled(PERF_LOG)) {
perf = RangerPerfTracer.getPerfTracer(PERF_LOG,
"ServiceREST.lookupResource(serviceName=" + serviceName + ")");
}
diff --git
a/security-admin/src/main/resources/conf.dist/ranger-admin-default-site.xml
b/security-admin/src/main/resources/conf.dist/ranger-admin-default-site.xml
index aa71f7dff..4f7bc87ac 100644
--- a/security-admin/src/main/resources/conf.dist/ranger-admin-default-site.xml
+++ b/security-admin/src/main/resources/conf.dist/ranger-admin-default-site.xml
@@ -508,6 +508,12 @@
<description></description>
</property>
+ <property>
+ <name>ranger.serviceconfig.validate.require.saved.service</name>
+ <value>false</value>
+ <description>When true, validateConfig for a named service
requires a saved service record and rejects endpoint URLs that differ from the
saved configuration.</description>
+ </property>
+
<property>
<name>ranger.timed.executor.max.threadpool.size</name>
<value>10</value>
diff --git
a/security-admin/src/test/java/org/apache/ranger/biz/TestServiceMgr.java
b/security-admin/src/test/java/org/apache/ranger/biz/TestServiceMgr.java
index ecf16c510..b0f075076 100644
--- a/security-admin/src/test/java/org/apache/ranger/biz/TestServiceMgr.java
+++ b/security-admin/src/test/java/org/apache/ranger/biz/TestServiceMgr.java
@@ -17,6 +17,7 @@
package org.apache.ranger.biz;
+import org.apache.ranger.common.PropertiesUtil;
import org.apache.ranger.common.TimedExecutor;
import org.apache.ranger.db.XXGroupUserDao;
import org.apache.ranger.plugin.model.RangerRole;
@@ -53,6 +54,7 @@
import java.util.stream.Stream;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -69,6 +71,20 @@ private static void setField(Object target, Class<?>
declaring, String fieldName
f.set(target, value);
}
+ private static void wireValidateConfigSuccessPipeline(ServiceMgr mgr,
ServiceStore store, String serviceType) throws Exception {
+ TimedExecutor exec = mock(TimedExecutor.class);
+ setField(mgr, ServiceMgr.class, "timedExecutor", exec);
+
+ RangerServiceDef def = new RangerServiceDef();
+ def.setName(serviceType);
+ def.setImplClass(RangerDefaultService.class.getName());
+ when(store.getServiceDefByName(serviceType)).thenReturn(def);
+
+ Map<String, Object> resp = new HashMap<>();
+ resp.put("connectivityStatus", true);
+ when(exec.timedTask(any(ServiceMgr.ValidateCallable.class), anyLong(),
any())).thenReturn(resp);
+ }
+
@Test
public void test01_parseLong_andTimeoutFallbacks() throws Exception {
ServiceMgr mgr = new ServiceMgr();
@@ -385,4 +401,483 @@ public void
test13_validateConfig_allowsLegitimateExternalUrls() throws Exceptio
Assertions.assertNotNull(response);
Assertions.assertNotEquals(VXResponse.STATUS_ERROR,
response.getStatusCode());
}
+
+ @ParameterizedTest
+ @MethodSource("ssrfBypassConfigValues")
+ public void test14_lookupResource_rejectsSSRFBypassAttempts(String
configKey, String configValue) throws Exception {
+ ServiceMgr mgr = new ServiceMgr();
+ ServiceDBStore svcDBStore = mock(ServiceDBStore.class);
+ RangerServiceService svcService = mock(RangerServiceService.class);
+ TimedExecutor exec = mock(TimedExecutor.class);
+ setField(mgr, ServiceMgr.class, "svcDBStore", svcDBStore);
+ setField(mgr, ServiceMgr.class, "rangerSvcService", svcService);
+ setField(mgr, ServiceMgr.class, "timedExecutor", exec);
+
+ Map<String, String> bypassConfigs = new HashMap<>();
+ bypassConfigs.put(configKey, configValue);
+
+ RangerService svc = new RangerService();
+ svc.setType("knox");
+ svc.setName("knox-test");
+ svc.setConfigs(bypassConfigs);
+
+ when(svcDBStore.getServiceByName("knox-test")).thenReturn(svc);
+
when(svcService.getConfigsWithDecryptedPassword(any(RangerService.class))).thenReturn(bypassConfigs);
+
+ ResourceLookupContext ctx = new ResourceLookupContext();
+ Exception thrown = Assertions.assertThrows(Exception.class, () ->
mgr.lookupResource("knox-test", ctx, null));
+ Assertions.assertTrue(
+ thrown.getMessage().contains("is not allowed") ||
thrown.getMessage().contains("blocked host detected"),
+ "Unexpected message: " + thrown.getMessage());
+ }
+
+ @Test
+ public void
test15_validateConfig_requireSavedService_rejectsMissingService() throws
Exception {
+ ServiceMgr mgr = new ServiceMgr();
+ ServiceDBStore db = mock(ServiceDBStore.class);
+ RangerServiceService svcService = mock(RangerServiceService.class);
+ setField(mgr, ServiceMgr.class, "svcDBStore", db);
+ setField(mgr, ServiceMgr.class, "rangerSvcService", svcService);
+
+
PropertiesUtil.getPropertiesMap().put(ServiceMgr.PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE,
"true");
+ try {
+ Map<String, String> cfg = new HashMap<>();
+ cfg.put("knox.url", "https://knox.example.com:8443/gateway");
+
+ RangerService svc = new RangerService();
+ svc.setName("knox-prod");
+ svc.setType("knox");
+ svc.setConfigs(cfg);
+
+
when(svcService.getConfigsWithDecryptedPassword(any(RangerService.class))).thenReturn(cfg);
+ when(db.getServiceByName("knox-prod")).thenReturn(null);
+
+ Exception thrown = Assertions.assertThrows(Exception.class, () ->
mgr.validateConfig(svc, null));
+ Assertions.assertTrue(thrown.getMessage().contains("requires a
saved service"));
+ } finally {
+
PropertiesUtil.getPropertiesMap().remove(ServiceMgr.PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE);
+ }
+ }
+
+ @Test
+ public void
test16_validateConfig_requireSavedService_rejectsEndpointMismatch() throws
Exception {
+ ServiceMgr mgr = new ServiceMgr();
+ ServiceDBStore db = mock(ServiceDBStore.class);
+ RangerServiceService svcService = mock(RangerServiceService.class);
+ setField(mgr, ServiceMgr.class, "svcDBStore", db);
+ setField(mgr, ServiceMgr.class, "rangerSvcService", svcService);
+
+
PropertiesUtil.getPropertiesMap().put(ServiceMgr.PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE,
"true");
+ try {
+ Map<String, String> savedCfg = new HashMap<>();
+ savedCfg.put("knox.url", "https://knox.example.com:8443/gateway");
+
+ Map<String, String> requestCfg = new HashMap<>();
+ requestCfg.put("knox.url",
"https://attacker.example.com:8443/gateway");
+
+ RangerService saved = new RangerService();
+ saved.setName("knox-prod");
+ saved.setType("knox");
+ saved.setConfigs(savedCfg);
+
+ RangerService request = new RangerService();
+ request.setName("knox-prod");
+ request.setType("knox");
+ request.setConfigs(requestCfg);
+
+ when(db.getServiceByName("knox-prod")).thenReturn(saved);
+
when(svcService.getConfigsWithDecryptedPassword(saved)).thenReturn(savedCfg);
+
when(svcService.getConfigsWithDecryptedPassword(request)).thenReturn(requestCfg);
+
+ Exception thrown = Assertions.assertThrows(Exception.class, () ->
mgr.validateConfig(request, null));
+ Assertions.assertTrue(thrown.getMessage().contains("must match the
saved service configuration"));
+ } finally {
+
PropertiesUtil.getPropertiesMap().remove(ServiceMgr.PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE);
+ }
+ }
+
+ @Test
+ public void
test17_validateConfig_requireSavedService_allowsMatchingEndpoint() throws
Exception {
+ ServiceMgr mgr = new ServiceMgr();
+ ServiceDBStore db = mock(ServiceDBStore.class);
+ RangerServiceService svcService = mock(RangerServiceService.class);
+ ServiceStore store = mock(ServiceStore.class);
+ setField(mgr, ServiceMgr.class, "svcDBStore", db);
+ setField(mgr, ServiceMgr.class, "rangerSvcService", svcService);
+ wireValidateConfigSuccessPipeline(mgr, store, "knox");
+
+
PropertiesUtil.getPropertiesMap().put(ServiceMgr.PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE,
"true");
+ try {
+ Map<String, String> cfg = new HashMap<>();
+ cfg.put("knox.url", "https://knox.example.com:8443/gateway");
+ cfg.put("username", "admin");
+
+ RangerService saved = new RangerService();
+ saved.setName("knox-prod");
+ saved.setType("knox");
+ saved.setConfigs(cfg);
+
+ RangerService request = new RangerService();
+ request.setName("knox-prod");
+ request.setType("knox");
+ request.setConfigs(new HashMap<>(cfg));
+
+ when(db.getServiceByName("knox-prod")).thenReturn(saved);
+
when(svcService.getConfigsWithDecryptedPassword(any(RangerService.class)))
+ .thenAnswer(invocation -> new
HashMap<>(invocation.<RangerService>getArgument(0).getConfigs()));
+
+ VXResponse response = mgr.validateConfig(request, store);
+ Assertions.assertNotNull(response);
+ Assertions.assertEquals(VXResponse.STATUS_SUCCESS,
response.getStatusCode());
+ } finally {
+
PropertiesUtil.getPropertiesMap().remove(ServiceMgr.PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE);
+ }
+ }
+
+ @Test
+ public void
test18_validateConfig_requireSavedService_rejectsPortOrPathChange() throws
Exception {
+ ServiceMgr mgr = new ServiceMgr();
+ ServiceDBStore db = mock(ServiceDBStore.class);
+ RangerServiceService svcService = mock(RangerServiceService.class);
+ setField(mgr, ServiceMgr.class, "svcDBStore", db);
+ setField(mgr, ServiceMgr.class, "rangerSvcService", svcService);
+
+
PropertiesUtil.getPropertiesMap().put(ServiceMgr.PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE,
"true");
+ try {
+ Map<String, String> savedCfg = new HashMap<>();
+ savedCfg.put("knox.url", "https://knox.example.com:8443/gateway");
+
+ RangerService saved = new RangerService();
+ saved.setName("knox-prod");
+ saved.setType("knox");
+ saved.setConfigs(savedCfg);
+
+ when(db.getServiceByName("knox-prod")).thenReturn(saved);
+
when(svcService.getConfigsWithDecryptedPassword(saved)).thenReturn(savedCfg);
+
+ Map<String, String> wrongPort = new HashMap<>();
+ wrongPort.put("knox.url", "https://knox.example.com:9/gateway");
+ RangerService requestPort = new RangerService();
+ requestPort.setName("knox-prod");
+ requestPort.setType("knox");
+ requestPort.setConfigs(wrongPort);
+
when(svcService.getConfigsWithDecryptedPassword(requestPort)).thenReturn(wrongPort);
+
+ Exception portEx = Assertions.assertThrows(Exception.class, () ->
mgr.validateConfig(requestPort, null));
+ Assertions.assertTrue(portEx.getMessage().contains("must match the
saved service configuration"));
+
+ Map<String, String> wrongPath = new HashMap<>();
+ wrongPath.put("knox.url", "https://knox.example.com:8443/other");
+ RangerService requestPath = new RangerService();
+ requestPath.setName("knox-prod");
+ requestPath.setType("knox");
+ requestPath.setConfigs(wrongPath);
+
when(svcService.getConfigsWithDecryptedPassword(requestPath)).thenReturn(wrongPath);
+
+ Exception pathEx = Assertions.assertThrows(Exception.class, () ->
mgr.validateConfig(requestPath, null));
+ Assertions.assertTrue(pathEx.getMessage().contains("must match the
saved service configuration"));
+ } finally {
+
PropertiesUtil.getPropertiesMap().remove(ServiceMgr.PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE);
+ }
+ }
+
+ @Test
+ public void
test19_validateConfig_requireSavedService_rejectsUserInfoBypass() throws
Exception {
+ ServiceMgr mgr = new ServiceMgr();
+ ServiceDBStore db = mock(ServiceDBStore.class);
+ RangerServiceService svcService = mock(RangerServiceService.class);
+ setField(mgr, ServiceMgr.class, "svcDBStore", db);
+ setField(mgr, ServiceMgr.class, "rangerSvcService", svcService);
+
+
PropertiesUtil.getPropertiesMap().put(ServiceMgr.PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE,
"true");
+ try {
+ Map<String, String> savedCfg = new HashMap<>();
+ savedCfg.put("knox.url", "https://knox.example.com:8443/gateway");
+
+ Map<String, String> requestCfg = new HashMap<>();
+ requestCfg.put("knox.url",
"https://[email protected]:8443/gateway");
+
+ RangerService saved = new RangerService();
+ saved.setName("knox-prod");
+ saved.setType("knox");
+ saved.setConfigs(savedCfg);
+
+ RangerService request = new RangerService();
+ request.setName("knox-prod");
+ request.setType("knox");
+ request.setConfigs(requestCfg);
+
+ when(db.getServiceByName("knox-prod")).thenReturn(saved);
+
when(svcService.getConfigsWithDecryptedPassword(saved)).thenReturn(savedCfg);
+
when(svcService.getConfigsWithDecryptedPassword(request)).thenReturn(requestCfg);
+
+ Exception thrown = Assertions.assertThrows(Exception.class, () ->
mgr.validateConfig(request, null));
+ Assertions.assertTrue(thrown.getMessage().contains("must match the
saved service configuration"));
+ } finally {
+
PropertiesUtil.getPropertiesMap().remove(ServiceMgr.PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE);
+ }
+ }
+
+ @Test
+ public void
test20_validateConfig_requireSavedService_rejectsJdbcMultiHostMismatch() throws
Exception {
+ ServiceMgr mgr = new ServiceMgr();
+ ServiceDBStore db = mock(ServiceDBStore.class);
+ RangerServiceService svcService = mock(RangerServiceService.class);
+ setField(mgr, ServiceMgr.class, "svcDBStore", db);
+ setField(mgr, ServiceMgr.class, "rangerSvcService", svcService);
+
+
PropertiesUtil.getPropertiesMap().put(ServiceMgr.PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE,
"true");
+ try {
+ Map<String, String> savedCfg = new HashMap<>();
+ savedCfg.put("jdbc.url", "jdbc:mysql://knox.example.com:3306/db");
+
+ Map<String, String> requestCfg = new HashMap<>();
+ requestCfg.put("jdbc.url",
"jdbc:mysql://knox.example.com:3306,evil.example.com:3306/db");
+
+ RangerService saved = new RangerService();
+ saved.setName("hive-prod");
+ saved.setType("hive");
+ saved.setConfigs(savedCfg);
+
+ RangerService request = new RangerService();
+ request.setName("hive-prod");
+ request.setType("hive");
+ request.setConfigs(requestCfg);
+
+ when(db.getServiceByName("hive-prod")).thenReturn(saved);
+
when(svcService.getConfigsWithDecryptedPassword(saved)).thenReturn(savedCfg);
+
when(svcService.getConfigsWithDecryptedPassword(request)).thenReturn(requestCfg);
+
+ Exception thrown = Assertions.assertThrows(Exception.class, () ->
mgr.validateConfig(request, null));
+ Assertions.assertTrue(thrown.getMessage().contains("must match the
saved service configuration"));
+ } finally {
+
PropertiesUtil.getPropertiesMap().remove(ServiceMgr.PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE);
+ }
+ }
+
+ @Test
+ public void
test21_validateConfig_requireSavedService_allowsUsernameChange() throws
Exception {
+ ServiceMgr mgr = new ServiceMgr();
+ ServiceDBStore db = mock(ServiceDBStore.class);
+ RangerServiceService svcService = mock(RangerServiceService.class);
+ ServiceStore store = mock(ServiceStore.class);
+ setField(mgr, ServiceMgr.class, "svcDBStore", db);
+ setField(mgr, ServiceMgr.class, "rangerSvcService", svcService);
+ wireValidateConfigSuccessPipeline(mgr, store, "knox");
+
+
PropertiesUtil.getPropertiesMap().put(ServiceMgr.PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE,
"true");
+ try {
+ Map<String, String> savedCfg = new HashMap<>();
+ savedCfg.put("knox.url", "https://knox.example.com:8443/gateway");
+ savedCfg.put("username", "admin");
+
+ Map<String, String> requestCfg = new HashMap<>(savedCfg);
+ requestCfg.put("username", "admin2");
+
+ RangerService saved = new RangerService();
+ saved.setName("knox-prod");
+ saved.setType("knox");
+ saved.setConfigs(savedCfg);
+
+ RangerService request = new RangerService();
+ request.setName("knox-prod");
+ request.setType("knox");
+ request.setConfigs(requestCfg);
+
+ when(db.getServiceByName("knox-prod")).thenReturn(saved);
+
when(svcService.getConfigsWithDecryptedPassword(any(RangerService.class)))
+ .thenAnswer(invocation -> new
HashMap<>(invocation.<RangerService>getArgument(0).getConfigs()));
+
+ VXResponse response = mgr.validateConfig(request, store);
+ Assertions.assertNotNull(response);
+ Assertions.assertEquals(VXResponse.STATUS_SUCCESS,
response.getStatusCode());
+ } finally {
+
PropertiesUtil.getPropertiesMap().remove(ServiceMgr.PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE);
+ }
+ }
+
+ @Test
+ public void
test22_validateConfig_requireSavedService_rejectsOpaqueJdbcUrlChange() throws
Exception {
+ ServiceMgr mgr = new ServiceMgr();
+ ServiceDBStore db = mock(ServiceDBStore.class);
+ RangerServiceService svcService = mock(RangerServiceService.class);
+ setField(mgr, ServiceMgr.class, "svcDBStore", db);
+ setField(mgr, ServiceMgr.class, "rangerSvcService", svcService);
+
+
PropertiesUtil.getPropertiesMap().put(ServiceMgr.PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE,
"true");
+ try {
+ Map<String, String> savedCfg = new HashMap<>();
+ savedCfg.put("jdbc.url",
"jdbc:mysql:loadbalance://knox.example.com:3306/db");
+
+ Map<String, String> requestCfg = new HashMap<>();
+ requestCfg.put("jdbc.url",
"jdbc:mysql:loadbalance://evil.example.com:3306/db");
+
+ RangerService saved = new RangerService();
+ saved.setName("hive-prod");
+ saved.setType("hive");
+ saved.setConfigs(savedCfg);
+
+ RangerService request = new RangerService();
+ request.setName("hive-prod");
+ request.setType("hive");
+ request.setConfigs(requestCfg);
+
+ when(db.getServiceByName("hive-prod")).thenReturn(saved);
+
when(svcService.getConfigsWithDecryptedPassword(saved)).thenReturn(savedCfg);
+
when(svcService.getConfigsWithDecryptedPassword(request)).thenReturn(requestCfg);
+
+ Exception thrown = Assertions.assertThrows(Exception.class, () ->
mgr.validateConfig(request, null));
+ Assertions.assertTrue(thrown.getMessage().contains("must match the
saved service configuration"));
+ } finally {
+
PropertiesUtil.getPropertiesMap().remove(ServiceMgr.PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE);
+ }
+ }
+
+ @Test
+ public void
test23_validateConfig_requireSavedService_rejectsHive2PathWhenHostNotParsed()
throws Exception {
+ ServiceMgr mgr = new ServiceMgr();
+ ServiceDBStore db = mock(ServiceDBStore.class);
+ RangerServiceService svcService = mock(RangerServiceService.class);
+ setField(mgr, ServiceMgr.class, "svcDBStore", db);
+ setField(mgr, ServiceMgr.class, "rangerSvcService", svcService);
+
+
PropertiesUtil.getPropertiesMap().put(ServiceMgr.PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE,
"true");
+ try {
+ Map<String, String> savedCfg = new HashMap<>();
+ savedCfg.put("jdbc.url", "jdbc:hive2:///default");
+
+ Map<String, String> requestCfg = new HashMap<>();
+ requestCfg.put("jdbc.url", "jdbc:hive2:///attacker");
+
+ RangerService saved = new RangerService();
+ saved.setName("hive-prod");
+ saved.setType("hive");
+ saved.setConfigs(savedCfg);
+
+ RangerService request = new RangerService();
+ request.setName("hive-prod");
+ request.setType("hive");
+ request.setConfigs(requestCfg);
+
+ when(db.getServiceByName("hive-prod")).thenReturn(saved);
+
when(svcService.getConfigsWithDecryptedPassword(saved)).thenReturn(savedCfg);
+
when(svcService.getConfigsWithDecryptedPassword(request)).thenReturn(requestCfg);
+
+ Exception thrown = Assertions.assertThrows(Exception.class, () ->
mgr.validateConfig(request, null));
+ Assertions.assertTrue(thrown.getMessage().contains("must match the
saved service configuration"));
+ } finally {
+
PropertiesUtil.getPropertiesMap().remove(ServiceMgr.PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE);
+ }
+ }
+
+ @Test
+ public void
test24_validateConfig_requireSavedService_rejectsHostPortWithoutScheme() throws
Exception {
+ ServiceMgr mgr = new ServiceMgr();
+ ServiceDBStore db = mock(ServiceDBStore.class);
+ RangerServiceService svcService = mock(RangerServiceService.class);
+ setField(mgr, ServiceMgr.class, "svcDBStore", db);
+ setField(mgr, ServiceMgr.class, "rangerSvcService", svcService);
+
+
PropertiesUtil.getPropertiesMap().put(ServiceMgr.PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE,
"true");
+ try {
+ Map<String, String> savedCfg = new HashMap<>();
+ savedCfg.put("knox.url", "https://knox.example.com:8443/gateway");
+
+ Map<String, String> requestCfg = new HashMap<>();
+ requestCfg.put("knox.url", "evil.example.com:8443");
+
+ RangerService saved = new RangerService();
+ saved.setName("knox-prod");
+ saved.setType("knox");
+ saved.setConfigs(savedCfg);
+
+ RangerService request = new RangerService();
+ request.setName("knox-prod");
+ request.setType("knox");
+ request.setConfigs(requestCfg);
+
+ when(db.getServiceByName("knox-prod")).thenReturn(saved);
+
when(svcService.getConfigsWithDecryptedPassword(saved)).thenReturn(savedCfg);
+
when(svcService.getConfigsWithDecryptedPassword(request)).thenReturn(requestCfg);
+
+ Exception thrown = Assertions.assertThrows(Exception.class, () ->
mgr.validateConfig(request, null));
+ Assertions.assertTrue(thrown.getMessage().contains("must match the
saved service configuration"));
+ } finally {
+
PropertiesUtil.getPropertiesMap().remove(ServiceMgr.PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE);
+ }
+ }
+
+ @Test
+ public void
test25_validateConfig_requireSavedService_rejectsOracleThinHostChange() throws
Exception {
+ ServiceMgr mgr = new ServiceMgr();
+ ServiceDBStore db = mock(ServiceDBStore.class);
+ RangerServiceService svcService = mock(RangerServiceService.class);
+ setField(mgr, ServiceMgr.class, "svcDBStore", db);
+ setField(mgr, ServiceMgr.class, "rangerSvcService", svcService);
+
+
PropertiesUtil.getPropertiesMap().put(ServiceMgr.PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE,
"true");
+ try {
+ Map<String, String> savedCfg = new HashMap<>();
+ savedCfg.put("jdbc.url",
"jdbc:oracle:thin:@knox.example.com:1521:ORCL");
+
+ Map<String, String> requestCfg = new HashMap<>();
+ requestCfg.put("jdbc.url",
"jdbc:oracle:thin:@evil.example.com:1521:ORCL");
+
+ RangerService saved = new RangerService();
+ saved.setName("oracle-prod");
+ saved.setType("oracle");
+ saved.setConfigs(savedCfg);
+
+ RangerService request = new RangerService();
+ request.setName("oracle-prod");
+ request.setType("oracle");
+ request.setConfigs(requestCfg);
+
+ when(db.getServiceByName("oracle-prod")).thenReturn(saved);
+
when(svcService.getConfigsWithDecryptedPassword(saved)).thenReturn(savedCfg);
+
when(svcService.getConfigsWithDecryptedPassword(request)).thenReturn(requestCfg);
+
+ Exception thrown = Assertions.assertThrows(Exception.class, () ->
mgr.validateConfig(request, null));
+ Assertions.assertTrue(thrown.getMessage().contains("must match the
saved service configuration"));
+ } finally {
+
PropertiesUtil.getPropertiesMap().remove(ServiceMgr.PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE);
+ }
+ }
+
+ @Test
+ public void
test26_validateConfig_requireSavedService_rejectsZkQuorumChange() throws
Exception {
+ ServiceMgr mgr = new ServiceMgr();
+ ServiceDBStore db = mock(ServiceDBStore.class);
+ RangerServiceService svcService = mock(RangerServiceService.class);
+ setField(mgr, ServiceMgr.class, "svcDBStore", db);
+ setField(mgr, ServiceMgr.class, "rangerSvcService", svcService);
+
+
PropertiesUtil.getPropertiesMap().put(ServiceMgr.PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE,
"true");
+ try {
+ Map<String, String> savedCfg = new HashMap<>();
+ savedCfg.put("zookeeper.connect",
"zk1.example.com:2181,zk2.example.com:2181");
+
+ Map<String, String> requestCfg = new HashMap<>();
+ requestCfg.put("zookeeper.connect",
"zk1.example.com:2181,evil.example.com:2181");
+
+ RangerService saved = new RangerService();
+ saved.setName("kafka-prod");
+ saved.setType("kafka");
+ saved.setConfigs(savedCfg);
+
+ RangerService request = new RangerService();
+ request.setName("kafka-prod");
+ request.setType("kafka");
+ request.setConfigs(requestCfg);
+
+ when(db.getServiceByName("kafka-prod")).thenReturn(saved);
+
when(svcService.getConfigsWithDecryptedPassword(saved)).thenReturn(savedCfg);
+
when(svcService.getConfigsWithDecryptedPassword(request)).thenReturn(requestCfg);
+
+ Exception thrown = Assertions.assertThrows(Exception.class, () ->
mgr.validateConfig(request, null));
+ Assertions.assertTrue(thrown.getMessage().contains("must match the
saved service configuration"));
+ } finally {
+
PropertiesUtil.getPropertiesMap().remove(ServiceMgr.PROP_SERVICE_CONFIG_VALIDATE_REQUIRE_SAVED_SERVICE);
+ }
+ }
}
diff --git
a/security-admin/src/test/java/org/apache/ranger/rest/TestServiceREST.java
b/security-admin/src/test/java/org/apache/ranger/rest/TestServiceREST.java
index b08a4f52d..bc208f314 100644
--- a/security-admin/src/test/java/org/apache/ranger/rest/TestServiceREST.java
+++ b/security-admin/src/test/java/org/apache/ranger/rest/TestServiceREST.java
@@ -633,10 +633,56 @@ public void test13lookupResource() throws Exception {
ResourceLookupContext context = new ResourceLookupContext();
context.setResourceName(serviceName);
context.setUserInput("HDFS");
+ XXService xService = xService();
+ XXServiceDao xServiceDao = Mockito.mock(XXServiceDao.class);
+ Mockito.when(daoManager.getXXService()).thenReturn(xServiceDao);
+ Mockito.when(xServiceDao.findByName(serviceName)).thenReturn(xService);
+ Mockito.when(bizUtil.hasAccess(xService, null)).thenReturn(true);
List<String> list = serviceREST.lookupResource(serviceName, context);
Assertions.assertNotNull(list);
}
+ @Test
+ public void test13bLookupResourceNotFound() throws Exception {
+ String serviceName = "missing_service";
+ ResourceLookupContext context = new ResourceLookupContext();
+ XXServiceDao xServiceDao = Mockito.mock(XXServiceDao.class);
+
+ Mockito.when(daoManager.getXXService()).thenReturn(xServiceDao);
+ Mockito.when(xServiceDao.findByName(serviceName)).thenReturn(null);
+
Mockito.when(restErrorUtil.createRESTException(Mockito.eq(HttpServletResponse.SC_NOT_FOUND),
Mockito.eq("Not found"), Mockito.eq(true)))
+ .thenReturn(new
WebApplicationException(HttpServletResponse.SC_NOT_FOUND));
+
+ WebApplicationException exception =
Assertions.assertThrows(WebApplicationException.class,
+ () -> serviceREST.lookupResource(serviceName, context));
+
+ Assertions.assertEquals(HttpServletResponse.SC_NOT_FOUND,
exception.getResponse().getStatus());
+
Mockito.verify(restErrorUtil).createRESTException(HttpServletResponse.SC_NOT_FOUND,
"Not found", true);
+ Mockito.verify(serviceMgr,
Mockito.never()).lookupResource(Mockito.anyString(), Mockito.any(),
Mockito.any());
+ }
+
+ @Test
+ public void test13cLookupResourceForbiddenWhenNoAccess() throws Exception {
+ String serviceName = "HDFS_1";
+ ResourceLookupContext context = new ResourceLookupContext();
+ XXService xService = xService();
+ XXServiceDao xServiceDao = Mockito.mock(XXServiceDao.class);
+ String deniedMsg = "Operation denied. User is not
permitted to lookup resources for service " + serviceName;
+
+ Mockito.when(daoManager.getXXService()).thenReturn(xServiceDao);
+ Mockito.when(xServiceDao.findByName(serviceName)).thenReturn(xService);
+ Mockito.when(bizUtil.hasAccess(xService, null)).thenReturn(false);
+
Mockito.when(restErrorUtil.createRESTException(Mockito.eq(HttpServletResponse.SC_FORBIDDEN),
Mockito.eq(deniedMsg), Mockito.eq(true)))
+ .thenReturn(new
WebApplicationException(HttpServletResponse.SC_FORBIDDEN));
+
+ WebApplicationException exception =
Assertions.assertThrows(WebApplicationException.class,
+ () -> serviceREST.lookupResource(serviceName, context));
+
+ Assertions.assertEquals(HttpServletResponse.SC_FORBIDDEN,
exception.getResponse().getStatus());
+
Mockito.verify(restErrorUtil).createRESTException(HttpServletResponse.SC_FORBIDDEN,
deniedMsg, true);
+ Mockito.verify(serviceMgr,
Mockito.never()).lookupResource(Mockito.anyString(), Mockito.any(),
Mockito.any());
+ }
+
@Test
public void test14grantAccess() throws Exception {
HttpServletRequest request =
Mockito.mock(HttpServletRequest.class);
@@ -3302,6 +3348,11 @@ public void test116LookupResourceWithException() throws
Exception {
context.setResourceName(serviceName);
context.setUserInput("HDFS");
+ XXService xService = xService();
+ XXServiceDao xServiceDao = Mockito.mock(XXServiceDao.class);
+ Mockito.when(daoManager.getXXService()).thenReturn(xServiceDao);
+ Mockito.when(xServiceDao.findByName(serviceName)).thenReturn(xService);
+ Mockito.when(bizUtil.hasAccess(xService, null)).thenReturn(true);
Mockito.when(serviceMgr.lookupResource(serviceName, context,
svcStore)).thenThrow(new RuntimeException("Lookup failed"));
Mockito.when(restErrorUtil.createRESTException(Mockito.anyString())).thenReturn(new
WebApplicationException());