diff --git a/knox-agent/pom.xml b/knox-agent/pom.xml index 040143967a..78fa54bcc4 100644 --- a/knox-agent/pom.xml +++ b/knox-agent/pom.xml @@ -28,6 +28,8 @@ Knox Security Plugin Knox Security Plugins + true + false 9.4.51.v20230217 UTF-8 diff --git a/knox-agent/src/main/java/org/apache/ranger/admin/client/RangerAdminJersey2RESTClient.java b/knox-agent/src/main/java/org/apache/ranger/admin/client/RangerAdminJersey2RESTClient.java index 04ba7a0c4c..e58814aee3 100644 --- a/knox-agent/src/main/java/org/apache/ranger/admin/client/RangerAdminJersey2RESTClient.java +++ b/knox-agent/src/main/java/org/apache/ranger/admin/client/RangerAdminJersey2RESTClient.java @@ -19,21 +19,35 @@ package org.apache.ranger.admin.client; -import java.io.UnsupportedEncodingException; -import java.lang.reflect.Type; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.security.PrivilegedExceptionAction; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; -import java.util.Set; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import org.apache.commons.lang.StringUtils; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.security.AccessControlException; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.ranger.audit.provider.MiscUtil; +import org.apache.ranger.authorization.utils.StringUtil; +import org.apache.ranger.plugin.util.GrantRevokeRequest; +import org.apache.ranger.plugin.util.RangerCommonConstants; +import org.apache.ranger.plugin.util.RangerPluginCapability; +import org.apache.ranger.plugin.util.RangerRESTUtils; +import org.apache.ranger.plugin.util.RangerRoles; +import org.apache.ranger.plugin.util.RangerServiceNotFoundException; +import org.apache.ranger.plugin.util.RangerSslHelper; +import org.apache.ranger.plugin.util.RangerUserStore; +import org.apache.ranger.plugin.util.ServicePolicies; +import org.apache.ranger.plugin.util.ServiceTags; +import org.apache.ranger.plugin.util.URLEncoderUtil; +import org.glassfish.jersey.client.ClientProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSession; import javax.ws.rs.ProcessingException; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; @@ -44,1126 +58,1113 @@ import javax.ws.rs.core.NewCookie; import javax.ws.rs.core.Response; -import org.apache.commons.lang.StringUtils; -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.security.AccessControlException; -import org.apache.hadoop.security.UserGroupInformation; -import org.apache.ranger.plugin.util.*; -import org.apache.ranger.audit.provider.MiscUtil; -import org.apache.ranger.authorization.utils.StringUtil; -import org.glassfish.jersey.client.ClientProperties; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonParseException; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Type; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.PrivilegedExceptionAction; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; public class RangerAdminJersey2RESTClient extends AbstractRangerAdminClient { + // none of the members are public -- this is only for testability. None of these is meant to be accessible + private static final Logger LOG = LoggerFactory.getLogger(RangerAdminJersey2RESTClient.class); + + private static final int MAX_PLUGIN_ID_LEN = 255; + + private final String pluginCapabilities = Long.toHexString(new RangerPluginCapability().getPluginCapabilities()); + + volatile Client client; + + boolean isSSL; + SSLContext sslContext; + HostnameVerifier hv; + String sslConfigFileName; + String serviceName; + String serviceNameUrlParam; + String clusterName; + boolean supportsPolicyDeltas; + boolean supportsTagDeltas; + String pluginId; + int restClientConnTimeOutMs; + int restClientReadTimeOutMs; + int restClientMaxRetryAttempts; + int restClientRetryIntervalMs; + + private int lastKnownActiveUrlIndex; + private List configURLs; + private boolean isRangerCookieEnabled; + private String rangerAdminCookieName; + private Cookie policyDownloadSessionId; + private boolean isValidPolicyDownloadSessionCookie; + private Cookie tagDownloadSessionId; + private boolean isValidTagDownloadSessionCookie; + private Cookie roleDownloadSessionId; + private boolean isValidRoleDownloadSessionCookie; + + @Override + public void init(String serviceName, String appId, String configPropertyPrefix, Configuration config) { + LOG.debug("==> RangerAdminJersey2RESTClient.init({})", configPropertyPrefix); + + super.init(serviceName, appId, configPropertyPrefix, config); + + this.serviceName = serviceName; + pluginId = getPluginId(serviceName, appId); + + String tmpUrl = config.get(configPropertyPrefix + ".policy.rest.url"); + + sslConfigFileName = config.get(configPropertyPrefix + ".policy.rest.ssl.config.file"); + restClientConnTimeOutMs = config.getInt(configPropertyPrefix + ".policy.rest.client.connection.timeoutMs", 120 * 1000); + restClientReadTimeOutMs = config.getInt(configPropertyPrefix + ".policy.rest.client.read.timeoutMs", 30 * 1000); + restClientMaxRetryAttempts = config.getInt(configPropertyPrefix + ".policy.rest.client.max.retry.attempts", 3); + restClientRetryIntervalMs = config.getInt(configPropertyPrefix + ".policy.rest.client.retry.interval.ms", 1 * 1000); + + clusterName = config.get(configPropertyPrefix + ".access.cluster.name", ""); + + if (StringUtil.isEmpty(clusterName)) { + clusterName = config.get(configPropertyPrefix + ".ambari.cluster.name", ""); + } + + supportsPolicyDeltas = config.getBoolean(configPropertyPrefix + RangerCommonConstants.PLUGIN_CONFIG_SUFFIX_POLICY_DELTA, RangerCommonConstants.PLUGIN_CONFIG_SUFFIX_POLICY_DELTA_DEFAULT); + supportsTagDeltas = config.getBoolean(configPropertyPrefix + RangerCommonConstants.PLUGIN_CONFIG_SUFFIX_TAG_DELTA, RangerCommonConstants.PLUGIN_CONFIG_SUFFIX_TAG_DELTA_DEFAULT); + isRangerCookieEnabled = config.getBoolean(configPropertyPrefix + ".policy.rest.client.cookie.enabled", RangerCommonConstants.POLICY_REST_CLIENT_SESSION_COOKIE_ENABLED); + rangerAdminCookieName = config.get(configPropertyPrefix + ".policy.rest.client.session.cookie.name", RangerCommonConstants.DEFAULT_COOKIE_NAME); + + configURLs = StringUtil.getURLs(tmpUrl); + this.lastKnownActiveUrlIndex = new Random().nextInt(configURLs.size()); + + String url = configURLs.get(this.lastKnownActiveUrlIndex); + + isSSL = isSsl(url); + + LOG.info("Init params: Base URL[{}], SSL Config filename[{}], ServiceName=[{}], SupportsPolicyDeltas=[{}], SupportsTagDeltas=[{}], ConfigURLs=[{}]", url, sslConfigFileName, this.serviceName, supportsPolicyDeltas, supportsTagDeltas, configURLs); + + client = getClient(); + + client.property(ClientProperties.CONNECT_TIMEOUT, restClientConnTimeOutMs); + client.property(ClientProperties.READ_TIMEOUT, restClientReadTimeOutMs); + + LOG.debug("<== RangerAdminJersey2RESTClient.init({}): {}", configPropertyPrefix, client); + + try { + this.serviceNameUrlParam = URLEncoderUtil.encodeURIParam(serviceName); + } catch (UnsupportedEncodingException e) { + LOG.warn("Unsupported encoding, serviceName={}", serviceName); + + this.serviceNameUrlParam = serviceName; + } + } + + @Override + public ServicePolicies getServicePoliciesIfUpdated(final long lastKnownVersion, final long lastActivationTimeInMillis) throws Exception { + LOG.debug("==> RangerAdminJersey2RESTClient.getServicePoliciesIfUpdated({}, {})", lastKnownVersion, lastActivationTimeInMillis); + + final ServicePolicies servicePolicies; + + if (isRangerCookieEnabled && policyDownloadSessionId != null && isValidPolicyDownloadSessionCookie) { + servicePolicies = getServicePoliciesIfUpdatedWithCookie(lastKnownVersion, lastActivationTimeInMillis); + } else { + servicePolicies = getServicePoliciesIfUpdatedWithCred(lastKnownVersion, lastActivationTimeInMillis); + } + + LOG.debug("<== RangerAdminJersey2RESTClient.getServicePoliciesIfUpdated({}, {}): {}", lastKnownVersion, lastActivationTimeInMillis, servicePolicies); + + return servicePolicies; + } + + @Override + public RangerRoles getRolesIfUpdated(final long lastKnowRoleVersion, final long lastActivationTimeInMillis) throws Exception { + LOG.debug("==> RangerAdminJersey2RESTClient.getRolesIfUpdated({}, {})", lastKnowRoleVersion, lastActivationTimeInMillis); + + final RangerRoles rangerRoles; + + if (isRangerCookieEnabled && roleDownloadSessionId != null && isValidRoleDownloadSessionCookie) { + rangerRoles = getRangerRolesIfUpdatedWithCookie(lastKnowRoleVersion, lastActivationTimeInMillis); + } else { + rangerRoles = getRangerRolesIfUpdatedWithCred(lastKnowRoleVersion, lastActivationTimeInMillis); + } + + LOG.debug("<== RangerAdminJersey2RESTClient.getRolesIfUpdated({}, {}): {}", lastKnowRoleVersion, lastActivationTimeInMillis, rangerRoles); + + return rangerRoles; + } + + @Override + public void grantAccess(GrantRevokeRequest request) throws Exception { + LOG.debug("==> RangerAdminRESTClient.grantAccess({})", request); + + Map queryParams = new HashMap<>(); + + queryParams.put(RangerRESTUtils.REST_PARAM_PLUGIN_ID, pluginId); + + String relativeURL = RangerRESTUtils.REST_URL_SERVICE_GRANT_ACCESS + serviceName; + Response response = get(queryParams, relativeURL); + int httpResponseCode = response == null ? -1 : response.getStatus(); + + switch (httpResponseCode) { + case -1: + LOG.warn("Unexpected: Null response from policy server while granting access! Returning null!"); + + throw new Exception("unknown error!"); + case 200: + LOG.debug("grantAccess() suceeded: HTTP status={}", httpResponseCode); + break; + case 401: + throw new AccessControlException(); + default: + String body = response.readEntity(String.class); + + LOG.warn("Unexpected: Received status[{}] with body[{}] form url[{}]", httpResponseCode, body, relativeURL); + + throw new Exception("HTTP status: " + httpResponseCode); + } + + LOG.debug("<== RangerAdminRESTClient.grantAccess({})", request); + } + + @Override + public void revokeAccess(GrantRevokeRequest request) throws Exception { + LOG.debug("==> RangerAdminRESTClient.grantAccess({})", request); + + Map queryParams = new HashMap<>(); + + queryParams.put(RangerRESTUtils.REST_PARAM_PLUGIN_ID, pluginId); + + String relativeURL = RangerRESTUtils.REST_URL_SERVICE_REVOKE_ACCESS + serviceName; + Response response = get(queryParams, relativeURL); + int httpResponseCode = response == null ? -1 : response.getStatus(); + + switch (httpResponseCode) { + case -1: + LOG.warn("Unexpected: Null response from policy server while granting access! Returning null!"); + + throw new Exception("unknown error!"); + case 200: + LOG.debug("grantAccess() suceeded: HTTP status={}", httpResponseCode); + break; + case 401: + throw new AccessControlException(); + default: + String body = response.readEntity(String.class); + + LOG.warn("Unexpected: Received status[{}] with body[{}] form url[{}]", httpResponseCode, body, relativeURL); + + throw new Exception("HTTP status: " + httpResponseCode); + } + + LOG.debug("<== RangerAdminRESTClient.grantAccess({})", request); + } + + @Override + public ServiceTags getServiceTagsIfUpdated(final long lastKnownVersion, final long lastActivationTimeInMillis) throws Exception { + LOG.debug("==> RangerAdminJersey2RESTClient.getServiceTagsIfUpdated({}, {})", lastKnownVersion, lastActivationTimeInMillis); + + final ServiceTags serviceTags; + + if (isRangerCookieEnabled && tagDownloadSessionId != null && isValidTagDownloadSessionCookie) { + serviceTags = getServiceTagsIfUpdatedWithCookie(lastKnownVersion, lastActivationTimeInMillis); + } else { + serviceTags = getServiceTagsIfUpdatedWithCred(lastKnownVersion, lastActivationTimeInMillis); + } + + LOG.debug("<== RangerAdminJersey2RESTClient.getServiceTagsIfUpdated({}, {}): {}", lastKnownVersion, lastActivationTimeInMillis, serviceTags); + + return serviceTags; + } + + @Override + public List getTagTypes(String pattern) throws Exception { + throw new Exception("RangerAdminjersey2RESTClient.getTagTypes() -- *** NOT IMPLEMENTED *** "); + } + + @Override + public RangerUserStore getUserStoreIfUpdated(long lastKnownUserStoreVersion, long lastActivationTimeInMillis) throws Exception { + LOG.debug("==> RangerAdminjersey2RESTClient.getUserStoreIfUpdated(lastKnownUserStoreVersion={}, lastActivationTimeInMillis={})", lastKnownUserStoreVersion, lastActivationTimeInMillis); + + final RangerUserStore ret; + final Response response; + final UserGroupInformation user = MiscUtil.getUGILoginUser(); + final boolean isSecureMode = isKerberosEnabled(user); + Map queryParams = new HashMap<>(); + + queryParams.put(RangerRESTUtils.REST_PARAM_LAST_KNOWN_USERSTORE_VERSION, Long.toString(lastKnownUserStoreVersion)); + queryParams.put(RangerRESTUtils.REST_PARAM_LAST_ACTIVATION_TIME, Long.toString(lastActivationTimeInMillis)); + queryParams.put(RangerRESTUtils.REST_PARAM_PLUGIN_ID, pluginId); + queryParams.put(RangerRESTUtils.REST_PARAM_CLUSTER_NAME, clusterName); + queryParams.put(RangerRESTUtils.REST_PARAM_CAPABILITIES, pluginCapabilities); + + if (isSecureMode) { + LOG.debug("Checking UserStore updated as user: {}", user); + + response = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> { + try { + String relativeURL = RangerRESTUtils.REST_URL_SERVICE_SERCURE_GET_USERSTORE + serviceNameUrlParam; + + return get(queryParams, relativeURL); + } catch (Exception e) { + LOG.error("Failed to get response", e); + } + + return null; + }); + } else { + LOG.debug("Checking UserStore updated as user: {}", user); + + String relativeURL = RangerRESTUtils.REST_URL_SERVICE_GET_USERSTORE + serviceNameUrlParam; + + response = get(queryParams, relativeURL); + } + + if (response == null || response.getStatus() == 304) { // NOT_MODIFIED + if (response == null) { + LOG.error("Error getting UserStore; Received NULL response!!. secureMode={}, user={}, serviceName={}", isSecureMode, user, serviceName); + } else { + String resp = response.hasEntity() ? response.readEntity(String.class) : null; + + LOG.debug("No change in UserStore. secureMode={}, user={}, response={}, serviceName={}, lastKnownUserStoreVersion={}, lastActivationTimeInMillis={}", + isSecureMode, user, resp, serviceName, lastKnownUserStoreVersion, lastActivationTimeInMillis); + } + + ret = null; + } else if (response.getStatus() == 200) { // OK + String body = response.readEntity(String.class); + + ret = getGson().fromJson(body, RangerUserStore.class); + } else if (response.getStatus() == 404) { // NOT_FOUND + ret = null; + + LOG.error("Error getting UserStore; service not found. secureMode={}, user={}, response={}, serviceName={}, lastKnownUserStoreVersion={}, lastActivationTimeInMillis={}", + isSecureMode, user, response.getStatus(), serviceName, lastKnownUserStoreVersion, lastActivationTimeInMillis); + + String exceptionMsg = response.hasEntity() ? response.readEntity(String.class) : null; + + RangerServiceNotFoundException.throwExceptionIfServiceNotFound(serviceName, exceptionMsg); + + LOG.warn("Received 404 error code with body:[{}], Ignoring", exceptionMsg); + } else { + String resp = response.hasEntity() ? response.readEntity(String.class) : null; + + LOG.warn("Error getting UserStore. secureMode={}, user={}, response={}, serviceName={}, lastKnownUserStoreVersion={}, lastActivationTimeInMillis={}", + isSecureMode, user, resp, serviceName, lastKnownUserStoreVersion, lastActivationTimeInMillis); + + ret = null; + } + + LOG.debug("<== RangerAdminjersey2RESTClient.getUserStoreIfUpdated(lastKnownUserStoreVersion={}, lastActivationTimeInMillis={}): ret={}", lastKnownUserStoreVersion, lastActivationTimeInMillis, ret); + + return ret; + } + + protected boolean shouldRetry(String currentUrl, int index, int retryAttemptCount, ProcessingException ex) { + LOG.warn("Failed to communicate with Ranger Admin. URL: {}. Error: {}", currentUrl, ex.getMessage()); + + boolean isLastUrl = index == (configURLs.size() - 1); + + // attempt retry after failure on the last url + boolean ret = isLastUrl && (retryAttemptCount < restClientMaxRetryAttempts); + + if (ret) { + LOG.warn("Waiting for {}ms before retry attempt #{}", restClientRetryIntervalMs, retryAttemptCount + 1); + + try { + Thread.sleep(restClientRetryIntervalMs); + } catch (InterruptedException excp) { + LOG.error("Failed while waiting to retry", excp); + } + } else if (isLastUrl) { + LOG.error("Failed to communicate with all Ranger Admin's URL's : [ {} ]", configURLs); + + throw new ProcessingException("Failed to communicate with all Ranger Admin's URL : [ " + configURLs + " ]", ex); + } + + return ret; + } + + // package level methods left so (and not private only for testability!) Not intended for use outside this class!! + Gson getGson() { + return new GsonBuilder() + .setPrettyPrinting() + // We get date from the policy manager as unix long! This deserializer exists to deal with it. Remove this class once we start send date/time per RFC 3339 + .registerTypeAdapter(Date.class, new GsonUnixDateDeserializer()) + .create(); + } + + Client getClient() { + Client result = client; + + if (result == null) { + synchronized (this) { + result = client; + + if (result == null) { + result = buildClient(); + client = result; + } + } + } + + return result; + } + + Client buildClient() { + if (isSSL) { + if (sslContext == null) { + RangerSslHelper sslHelper = new RangerSslHelper(sslConfigFileName); + + sslContext = sslHelper.createContext(); + } + + if (hv == null) { + hv = (urlHostName, session) -> session.getPeerHost().equals(urlHostName); + } + + client = ClientBuilder.newBuilder() + .sslContext(sslContext) + .hostnameVerifier(hv) + .build(); + } + + if (client == null) { + client = ClientBuilder.newClient(); + } + + return client; + } + + private Response get(Map queyParams, String relativeURL) { + Response response = null; + int startIndex = this.lastKnownActiveUrlIndex; + int currentIndex = 0; + int retryAttempt = 0; + + for (int index = 0; index < configURLs.size(); index++) { + try { + currentIndex = (startIndex + index) % configURLs.size(); + + WebTarget target = client.target(configURLs.get(currentIndex) + relativeURL); + + response = setQueryParams(target, queyParams).request(MediaType.APPLICATION_JSON_TYPE).get(); + + if (response != null) { + setLastKnownActiveUrlIndex(currentIndex); + break; + } + } catch (ProcessingException e) { + if (shouldRetry(configURLs.get(currentIndex), index, retryAttempt, e)) { + retryAttempt++; + + index = -1; // start from first url + } + } + } + + return response; + } + + private Response get(Map queyParams, String relativeURL, Cookie sessionId) { + Response response = null; + int startIndex = this.lastKnownActiveUrlIndex; + int currentIndex = 0; + int retryAttempt = 0; + + for (int index = 0; index < configURLs.size(); index++) { + try { + currentIndex = (startIndex + index) % configURLs.size(); + + WebTarget target = client.target(configURLs.get(currentIndex) + relativeURL); + + target = setQueryParams(target, queyParams); + + Invocation.Builder invocationBuilder = target.request(MediaType.APPLICATION_JSON_TYPE).cookie(sessionId); + + response = invocationBuilder.get(); + + if (response != null) { + setLastKnownActiveUrlIndex(currentIndex); + break; + } + } catch (ProcessingException e) { + if (shouldRetry(configURLs.get(currentIndex), index, retryAttempt, e)) { + retryAttempt++; + + index = -1; // start from first url + } + } + } + return response; + } + + private static WebTarget setQueryParams(WebTarget target, Map params) { + WebTarget ret = target; + + if (target != null && params != null) { + Set> entrySet = params.entrySet(); + + for (Map.Entry entry : entrySet) { + ret = ret.queryParam(entry.getKey(), entry.getValue()); + } + } + + return ret; + } + + private void setLastKnownActiveUrlIndex(int lastKnownActiveUrlIndex) { + this.lastKnownActiveUrlIndex = lastKnownActiveUrlIndex; + } + + private boolean isSsl(String url) { + return !StringUtils.isEmpty(url) && url.toLowerCase().startsWith("https"); + } + + private String getPluginId(String serviceName, String appId) { + String hostName; + + try { + hostName = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + LOG.error("ERROR: Unable to find hostname for the agent ", e); + + hostName = "unknownHost"; + } + + String ret = hostName + "-" + serviceName; + + if (!StringUtils.isEmpty(appId)) { + ret = appId + "@" + ret; + } + + if (ret.length() > MAX_PLUGIN_ID_LEN) { + ret = ret.substring(0, MAX_PLUGIN_ID_LEN); + } + + return ret; + } + + /* Policies Download from Ranger admin */ + private ServicePolicies getServicePoliciesIfUpdatedWithCred(final long lastKnownVersion, final long lastActivationTimeInMillis) throws Exception { + LOG.debug("==> RangerAdminJersey2RESTClient.getServicePoliciesWithCred({}, {})", lastKnownVersion, lastActivationTimeInMillis); + + final ServicePolicies ret; + final Response response = getRangerAdminPolicyDownloadResponse(lastKnownVersion, lastActivationTimeInMillis); + int httpResponseCode = response == null ? -1 : response.getStatus(); + String body = null; + + switch (httpResponseCode) { + case 200: + body = response.readEntity(String.class); + + LOG.debug("Response from 200 server: {}", body); + + Gson gson = getGson(); + + ret = gson.fromJson(body, ServicePolicies.class); + + setCookieReceivedFromCredSession(response); + + LOG.debug("Deserialized response to: {}", ret); + break; + case 304: + ret = null; + + setCookieReceivedFromCredSession(response); + + LOG.debug("Got response: 304. Ok. Returning null"); + break; + case -1: + ret = null; + policyDownloadSessionId = null; + + LOG.warn("Unexpected: Null response from policy server while trying to get policies! Returning null!"); + break; + case 404: + ret = null; + policyDownloadSessionId = null; + + if (response.hasEntity()) { + body = response.readEntity(String.class); + + if (StringUtils.isNotBlank(body)) { + RangerServiceNotFoundException.throwExceptionIfServiceNotFound(serviceName, body); + } + } + + LOG.warn("Received 404 error code with body:[{}], Ignoring", body); + break; + default: + ret = null; + policyDownloadSessionId = null; + body = response.readEntity(String.class); + + LOG.warn("Unexpected: Received status[{}] with body[{}] form url[{}]", httpResponseCode, body, getRelativeURL(isSecureMode())); + break; + } + + LOG.debug("<== RangerAdminJersey2RESTClient.getServicePoliciesWithCred({}, {}): {}", lastKnownVersion, lastActivationTimeInMillis, ret); + + return ret; + } + + private ServicePolicies getServicePoliciesIfUpdatedWithCookie(final long lastKnownVersion, final long lastActivationTimeInMillis) throws Exception { + LOG.debug("==> RangerAdminJersey2RESTClient.getServicePoliciesWithCookie({}, {})", lastKnownVersion, lastActivationTimeInMillis); + + final ServicePolicies ret; + final Response response = getRangerAdminPolicyDownloadResponse(lastKnownVersion, lastActivationTimeInMillis); + int httpResponseCode = response == null ? -1 : response.getStatus(); + String body = null; + + switch (httpResponseCode) { + case 200: + body = response.readEntity(String.class); + + LOG.debug("Response from 200 server: {}", body); + + Gson gson = getGson(); + + ret = gson.fromJson(body, ServicePolicies.class); + + checkAndResetSessionCookie(response); + + LOG.debug("Deserialized response to: {}", ret); + break; + case 304: + ret = null; + + checkAndResetSessionCookie(response); + + LOG.debug("Got response: 304. Ok. Returning null"); + break; + case -1: + ret = null; + policyDownloadSessionId = null; + isValidPolicyDownloadSessionCookie = false; + + LOG.warn("Unexpected: Null response from policy server while trying to get policies! Returning null!"); + break; + case 404: + ret = null; + policyDownloadSessionId = null; + isValidPolicyDownloadSessionCookie = false; + + if (response.hasEntity()) { + body = response.readEntity(String.class); + + if (StringUtils.isNotBlank(body)) { + RangerServiceNotFoundException.throwExceptionIfServiceNotFound(serviceName, body); + } + } + + LOG.warn("Received 404 error code with body:[{}], Ignoring", body); + break; + default: + ret = null; + policyDownloadSessionId = null; + isValidPolicyDownloadSessionCookie = false; + body = response.readEntity(String.class); + + LOG.warn("Unexpected: Received status[{}] with body[{}] form url[{}]", httpResponseCode, body, getRelativeURL(isSecureMode())); + break; + } + + LOG.debug("<== RangerAdminJersey2RESTClient.getServicePoliciesWithCookie({}, {}): {}", lastKnownVersion, lastActivationTimeInMillis, ret); + + return ret; + } + + private Response getRangerAdminPolicyDownloadResponse(final long lastKnownVersion, final long lastActivationTimeInMillis) throws Exception { + LOG.debug("==> RangerAdminJersey2RESTClient.getRangerAdminPolicyDownloadResponse({}, {})", lastKnownVersion, lastActivationTimeInMillis); + + final Response ret; + Map queryParams = new HashMap<>(); + + queryParams.put(RangerRESTUtils.REST_PARAM_LAST_KNOWN_POLICY_VERSION, Long.toString(lastKnownVersion)); + queryParams.put(RangerRESTUtils.REST_PARAM_LAST_ACTIVATION_TIME, Long.toString(lastActivationTimeInMillis)); + queryParams.put(RangerRESTUtils.REST_PARAM_PLUGIN_ID, pluginId); + queryParams.put(RangerRESTUtils.REST_PARAM_CLUSTER_NAME, clusterName); + queryParams.put(RangerRESTUtils.REST_PARAM_SUPPORTS_POLICY_DELTAS, Boolean.toString(supportsPolicyDeltas)); + queryParams.put(RangerRESTUtils.REST_PARAM_CAPABILITIES, pluginCapabilities); + + if (isSecureMode()) { + if (LOG.isDebugEnabled()) { + LOG.debug("Checking Service policy if updated as user : {}", MiscUtil.getUGILoginUser()); + } + + ret = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> get(queryParams, getRelativeURL(true), policyDownloadSessionId)); + } else { + LOG.debug("Checking Service policy if updated with old api call"); + + ret = get(queryParams, getRelativeURL(false), policyDownloadSessionId); + } + + LOG.debug("<== RangerAdminJersey2RESTClient.getRangerAdminPolicyDownloadResponse({}, {}): {}", lastKnownVersion, lastActivationTimeInMillis, ret); + + return ret; + } + + private String getRelativeURL(final boolean isSecureMode) { + final String ret; + + if (isSecureMode) { + ret = RangerRESTUtils.REST_URL_POLICY_GET_FOR_SECURE_SERVICE_IF_UPDATED + serviceName; + } else { + ret = RangerRESTUtils.REST_URL_POLICY_GET_FOR_SERVICE_IF_UPDATED + serviceName; + } + + return ret; + } + + private void checkAndResetSessionCookie(Response response) { + Map cookieMap = response.getCookies(); + Set cookieNames = cookieMap.keySet(); + + for (String cookieName : cookieNames) { + if (cookieName.equalsIgnoreCase(rangerAdminCookieName)) { + policyDownloadSessionId = cookieMap.get(cookieName); + isValidPolicyDownloadSessionCookie = (policyDownloadSessionId != null); + break; + } + } + } + + private void setCookieReceivedFromCredSession(Response response) { + if (isRangerCookieEnabled) { + Cookie sessionCookie = null; + Map cookieMap = response.getCookies(); + + // save cookie received from credentials session login + Set cookieNames = cookieMap.keySet(); + + for (String cookieName : cookieNames) { + if (cookieName.equalsIgnoreCase(rangerAdminCookieName)) { + sessionCookie = cookieMap.get(cookieName); + break; + } + } + + policyDownloadSessionId = sessionCookie; + isValidPolicyDownloadSessionCookie = (policyDownloadSessionId != null); + } + } + + /* Tags Download from Ranger admin */ + private ServiceTags getServiceTagsIfUpdatedWithCred(final long lastKnownVersion, final long lastActivationTimeInMillis) throws Exception { + LOG.debug("==> RangerAdminJersey2RESTClient.getServiceTagsIfUpdatedWithCred({}, {})", lastKnownVersion, lastActivationTimeInMillis); + + final ServiceTags ret; + final Response response = getTagsDownloadResponse(lastKnownVersion, lastActivationTimeInMillis); + int httpResponseCode = response == null ? -1 : response.getStatus(); + String body = null; + + switch (httpResponseCode) { + case 200: + body = response.readEntity(String.class); + + LOG.debug("Response from 200 server: {}", body); + + Gson gson = getGson(); + + ret = gson.fromJson(body, ServiceTags.class); + + setCookieReceivedFromTagDownloadSession(response); + + LOG.debug("Deserialized response to: {}", ret); + break; + case 304: + ret = null; + + setCookieReceivedFromTagDownloadSession(response); + + LOG.debug("Got response: 304. Ok. Returning null"); + break; + case -1: + ret = null; + tagDownloadSessionId = null; + + LOG.warn("Unexpected: Null response from tag server while trying to get tags! Returning null!"); + break; + case 404: + ret = null; + tagDownloadSessionId = null; + + if (response.hasEntity()) { + body = response.readEntity(String.class); + + if (StringUtils.isNotBlank(body)) { + RangerServiceNotFoundException.throwExceptionIfServiceNotFound(serviceName, body); + } + } + + LOG.warn("Received 404 error code with body:[{}], Ignoring", body); + break; + default: + ret = null; + tagDownloadSessionId = null; + body = response.readEntity(String.class); + + LOG.warn("Unexpected: Received status[{}] with body[{}] form url[{}]", httpResponseCode, body, getRelativeURLForTagDownload(isSecureMode())); + break; + } + + LOG.debug("<== RangerAdminJersey2RESTClient.getServiceTagsIfUpdatedWithCred({}, {}): {}", lastKnownVersion, lastActivationTimeInMillis, ret); + + return ret; + } + + private ServiceTags getServiceTagsIfUpdatedWithCookie(final long lastKnownVersion, final long lastActivationTimeInMillis) throws Exception { + LOG.debug("==> RangerAdminJersey2RESTClient.getServiceTagsIfUpdatedWithCookie({}, {})", lastKnownVersion, lastActivationTimeInMillis); + + final ServiceTags ret; + final Response response = getTagsDownloadResponse(lastKnownVersion, lastActivationTimeInMillis); + int httpResponseCode = response == null ? -1 : response.getStatus(); + String body = null; + + switch (httpResponseCode) { + case 200: + body = response.readEntity(String.class); + + LOG.debug("Response from 200 server: {}", body); + + Gson gson = getGson(); + + ret = gson.fromJson(body, ServiceTags.class); + + checkAndResetTagDownloadSessionCookie(response); + + LOG.debug("Deserialized response to: {}", ret); + break; + case 304: + ret = null; + + checkAndResetTagDownloadSessionCookie(response); + + LOG.debug("Got response: 304. Ok. Returning null"); + break; + case -1: + ret = null; + tagDownloadSessionId = null; + isValidTagDownloadSessionCookie = false; + + LOG.warn("Unexpected: Null response from tag server while trying to get tags! Returning null!"); + break; + case 404: + ret = null; + tagDownloadSessionId = null; + isValidTagDownloadSessionCookie = false; + + if (response.hasEntity()) { + body = response.readEntity(String.class); + + if (StringUtils.isNotBlank(body)) { + RangerServiceNotFoundException.throwExceptionIfServiceNotFound(serviceName, body); + } + } + + LOG.warn("Received 404 error code with body:[{}], Ignoring", body); + break; + default: + ret = null; + tagDownloadSessionId = null; + isValidTagDownloadSessionCookie = false; + body = response.readEntity(String.class); + + LOG.warn("Unexpected: Received status[{}] with body[{}] form url[{}]", httpResponseCode, body, ret); + break; + } + + LOG.debug("<== RangerAdminJersey2RESTClient.getServiceTagsIfUpdatedWithCookie({}, {}): {}", lastKnownVersion, lastActivationTimeInMillis, ret); + + return ret; + } + + private Response getTagsDownloadResponse(final long lastKnownVersion, final long lastActivationTimeInMillis) throws Exception { + LOG.debug("==> RangerAdminJersey2RESTClient.getTagsDownloadResponse({}, {})", lastKnownVersion, lastActivationTimeInMillis); + + final Response ret; + Map queryParams = new HashMap<>(); + + queryParams.put(RangerRESTUtils.LAST_KNOWN_TAG_VERSION_PARAM, Long.toString(lastKnownVersion)); + queryParams.put(RangerRESTUtils.REST_PARAM_LAST_ACTIVATION_TIME, Long.toString(lastActivationTimeInMillis)); + queryParams.put(RangerRESTUtils.REST_PARAM_PLUGIN_ID, pluginId); + queryParams.put(RangerRESTUtils.REST_PARAM_SUPPORTS_TAG_DELTAS, Boolean.toString(supportsTagDeltas)); + queryParams.put(RangerRESTUtils.REST_PARAM_CAPABILITIES, pluginCapabilities); + + if (isSecureMode()) { + if (LOG.isDebugEnabled()) { + LOG.debug("Checking Service tags if updated as user : {}", MiscUtil.getUGILoginUser()); + } + + ret = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> get(queryParams, getRelativeURLForTagDownload(true), tagDownloadSessionId)); + } else { + LOG.debug("Checking Service tags if updated with old api call"); + + ret = get(queryParams, getRelativeURLForTagDownload(false), tagDownloadSessionId); + } + + LOG.debug("<== RangerAdminJersey2RESTClient.getTagsDownloadResponse({}, {}): {}", lastKnownVersion, lastActivationTimeInMillis, ret); + + return ret; + } + + private String getRelativeURLForTagDownload(final boolean isSecureMode) { + final String ret; + + if (isSecureMode) { + ret = RangerRESTUtils.REST_URL_GET_SECURE_SERVICE_TAGS_IF_UPDATED + serviceName; + } else { + ret = RangerRESTUtils.REST_URL_GET_SERVICE_TAGS_IF_UPDATED + serviceName; + } + + return ret; + } + + private void checkAndResetTagDownloadSessionCookie(Response response) { + Map cookieMap = response.getCookies(); + Set cookieNames = cookieMap.keySet(); + + for (String cookieName : cookieNames) { + if (cookieName.equalsIgnoreCase(rangerAdminCookieName)) { + tagDownloadSessionId = cookieMap.get(cookieName); + isValidTagDownloadSessionCookie = (tagDownloadSessionId != null); + break; + } + } + } + + private void setCookieReceivedFromTagDownloadSession(Response response) { + if (isRangerCookieEnabled) { + Cookie sessionCookie = null; + Map cookieMap = response.getCookies(); + + // save cookie received from credentials session login + Set cookieNames = cookieMap.keySet(); + + for (String cookieName : cookieNames) { + if (cookieName.equalsIgnoreCase(rangerAdminCookieName)) { + sessionCookie = cookieMap.get(cookieName); + } + } + + tagDownloadSessionId = sessionCookie; + isValidTagDownloadSessionCookie = (tagDownloadSessionId != null); + } + } + + /* Role Download from Ranger Admin */ + private RangerRoles getRangerRolesIfUpdatedWithCred(final long lastKnownRoleVersion, final long lastActivationTimeInMillis) throws Exception { + LOG.debug("==> RangerAdminJersey2RESTClient.getRangerRolesIfUpdatedWithCred({}, {})", lastKnownRoleVersion, lastActivationTimeInMillis); + + final RangerRoles ret; + final Response response = getRoleDownloadResponse(lastKnownRoleVersion, lastActivationTimeInMillis); + int httpResponseCode = response == null ? -1 : response.getStatus(); + String body = null; + + switch (httpResponseCode) { + case 200: + body = response.readEntity(String.class); + + LOG.debug("Response from 200 server: {}", body); + + Gson gson = getGson(); + + ret = gson.fromJson(body, RangerRoles.class); + + setCookieReceivedFromRoleDownloadSession(response); + + LOG.debug("Deserialized response to: {}", ret); + break; + case 304: + ret = null; + + setCookieReceivedFromRoleDownloadSession(response); + LOG.debug("Got response: 304. Ok. Returning null"); + break; + case -1: + ret = null; + roleDownloadSessionId = null; + + LOG.warn("Unexpected: Null response from policy server while trying to get policies! Returning null!"); + break; + case 404: + ret = null; + roleDownloadSessionId = null; + + if (response.hasEntity()) { + body = response.readEntity(String.class); + + if (StringUtils.isNotBlank(body)) { + RangerServiceNotFoundException.throwExceptionIfServiceNotFound(serviceName, body); + } + } + + LOG.warn("Received 404 error code with body:[{}], Ignoring", body); + break; + default: + ret = null; + roleDownloadSessionId = null; + body = response.readEntity(String.class); + + LOG.warn("Unexpected: Received status[{}] with body[{}] form url[{}]", httpResponseCode, body, getRelativeURLForRoleDownload(isSecureMode())); + + break; + } + + LOG.debug("<== RangerAdminJersey2RESTClient.getRangerRolesIfUpdatedWithCred({}, {}): {}", lastKnownRoleVersion, lastActivationTimeInMillis, ret); + + return ret; + } + + private RangerRoles getRangerRolesIfUpdatedWithCookie(final long lastKnownRoleVersion, final long lastActivationTimeInMillis) throws Exception { + LOG.debug("==> RangerAdminJersey2RESTClient.getRangerRolesIfUpdatedWithCookie({}, {})", lastKnownRoleVersion, lastActivationTimeInMillis); + + final RangerRoles ret; + final Response response = getRoleDownloadResponse(lastKnownRoleVersion, lastActivationTimeInMillis); + int httpResponseCode = response == null ? -1 : response.getStatus(); + String body = null; + + switch (httpResponseCode) { + case 200: + body = response.readEntity(String.class); + + LOG.debug("Response from 200 server: {}", body); + + Gson gson = getGson(); + + ret = gson.fromJson(body, RangerRoles.class); + + checkAndResetRoleDownloadSessionCookie(response); + + LOG.debug("Deserialized response to: {}", ret); + break; + case 304: + ret = null; + + checkAndResetRoleDownloadSessionCookie(response); + + LOG.debug("Got response: 304. Ok. Returning null"); + break; + case -1: + ret = null; + roleDownloadSessionId = null; + isValidRoleDownloadSessionCookie = false; + + LOG.warn("Unexpected: Null response from policy server while trying to get policies! Returning null!"); + break; + case 404: + ret = null; + roleDownloadSessionId = null; + isValidRoleDownloadSessionCookie = false; + + if (response.hasEntity()) { + body = response.readEntity(String.class); + + if (StringUtils.isNotBlank(body)) { + RangerServiceNotFoundException.throwExceptionIfServiceNotFound(serviceName, body); + } + } + + LOG.warn("Received 404 error code with body:[{}], Ignoring", body); + break; + default: + ret = null; + roleDownloadSessionId = null; + isValidRoleDownloadSessionCookie = false; + body = response.readEntity(String.class); + + LOG.warn("Unexpected: Received status[{}] with body[{}] form url[{}]", httpResponseCode, body, getRelativeURLForRoleDownload(isSecureMode())); + + break; + } + + LOG.debug("<== RangerAdminJersey2RESTClient.getRangerRolesIfUpdatedWithCookie({}, {}): {}", lastKnownRoleVersion, lastActivationTimeInMillis, ret); + + return ret; + } + + private Response getRoleDownloadResponse(final long lastKnownRoleVersion, final long lastActivationTimeInMillis) throws Exception { + LOG.debug("==> RangerAdminJersey2RESTClient.getRoleDownloadResponse({}, {})", lastKnownRoleVersion, lastActivationTimeInMillis); + + final Response ret; + Map queryParams = new HashMap<>(); + + queryParams.put(RangerRESTUtils.REST_PARAM_LAST_KNOWN_ROLE_VERSION, Long.toString(lastKnownRoleVersion)); + queryParams.put(RangerRESTUtils.REST_PARAM_LAST_ACTIVATION_TIME, Long.toString(lastActivationTimeInMillis)); + queryParams.put(RangerRESTUtils.REST_PARAM_PLUGIN_ID, pluginId); + queryParams.put(RangerRESTUtils.REST_PARAM_CLUSTER_NAME, clusterName); + + if (isSecureMode()) { + if (LOG.isDebugEnabled()) { + LOG.debug("Checking Roles if updated as user : {}", MiscUtil.getUGILoginUser()); + } + + ret = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> get(queryParams, getRelativeURLForRoleDownload(true), roleDownloadSessionId)); + } else { + LOG.debug("Checking Roles if updated with old api call"); + + ret = get(queryParams, getRelativeURLForRoleDownload(false), roleDownloadSessionId); + } + + LOG.debug("<== RangerAdminJersey2RESTClient.getRoleDownloadResponse({}, {}): {}", lastKnownRoleVersion, lastActivationTimeInMillis, ret); + + return ret; + } - // none of the members are public -- this is only for testability. None of these is meant to be accessible - private static final Logger LOG = LoggerFactory.getLogger(RangerAdminJersey2RESTClient.class); - - boolean _isSSL = false; - volatile Client _client = null; - SSLContext _sslContext = null; - HostnameVerifier _hv; - String _sslConfigFileName = null; - String _serviceName = null; - String _serviceNameUrlParam = null; - String _clusterName = null; - boolean _supportsPolicyDeltas = false; - boolean _supportsTagDeltas = false; - String _pluginId = null; - int _restClientConnTimeOutMs; - int _restClientReadTimeOutMs; - int _restClientMaxRetryAttempts; - int _restClientRetryIntervalMs; - private int lastKnownActiveUrlIndex; - private List configURLs; - private boolean isRangerCookieEnabled; - private String rangerAdminCookieName; - private Cookie policyDownloadSessionId = null; - private boolean isValidPolicyDownloadSessionCookie = false; - private Cookie tagDownloadSessionId = null; - private boolean isValidTagDownloadSessionCookie = false; - private Cookie roleDownloadSessionId = null; - private boolean isValidRoleDownloadSessionCookie = false; - //private Map cookieMap = new HashMap<>(); - private final String pluginCapabilities = Long.toHexString(new RangerPluginCapability().getPluginCapabilities()); - private static final int MAX_PLUGIN_ID_LEN = 255; - - @Override - public void init(String serviceName, String appId, String configPropertyPrefix, Configuration config) { - if(LOG.isDebugEnabled()) { - LOG.debug("==> RangerAdminJersey2RESTClient.init(" + configPropertyPrefix + ")"); - } - - super.init(serviceName, appId, configPropertyPrefix, config); - - _serviceName = serviceName; - _pluginId = getPluginId(serviceName, appId); - String tmpUrl = config.get(configPropertyPrefix + ".policy.rest.url"); - _sslConfigFileName = config.get(configPropertyPrefix + ".policy.rest.ssl.config.file"); - _restClientConnTimeOutMs = config.getInt(configPropertyPrefix + ".policy.rest.client.connection.timeoutMs", 120 * 1000); - _restClientReadTimeOutMs = config.getInt(configPropertyPrefix + ".policy.rest.client.read.timeoutMs", 30 * 1000); - _restClientMaxRetryAttempts = config.getInt(configPropertyPrefix + ".policy.rest.client.max.retry.attempts", 3); - _restClientRetryIntervalMs = config.getInt(configPropertyPrefix + ".policy.rest.client.retry.interval.ms", 1 * 1000); - - _clusterName = config.get(configPropertyPrefix + ".access.cluster.name", ""); - if(StringUtil.isEmpty(_clusterName)){ - _clusterName =config.get(configPropertyPrefix + ".ambari.cluster.name", ""); - } - _supportsPolicyDeltas = config.getBoolean(configPropertyPrefix + RangerCommonConstants.PLUGIN_CONFIG_SUFFIX_POLICY_DELTA, RangerCommonConstants.PLUGIN_CONFIG_SUFFIX_POLICY_DELTA_DEFAULT); - _supportsTagDeltas = config.getBoolean(configPropertyPrefix + RangerCommonConstants.PLUGIN_CONFIG_SUFFIX_TAG_DELTA, RangerCommonConstants.PLUGIN_CONFIG_SUFFIX_TAG_DELTA_DEFAULT); - isRangerCookieEnabled = config.getBoolean(configPropertyPrefix + ".policy.rest.client.cookie.enabled", RangerCommonConstants.POLICY_REST_CLIENT_SESSION_COOKIE_ENABLED); - rangerAdminCookieName = config.get(configPropertyPrefix + ".policy.rest.client.session.cookie.name", RangerCommonConstants.DEFAULT_COOKIE_NAME); - - configURLs = StringUtil.getURLs(tmpUrl); - this.lastKnownActiveUrlIndex = new Random().nextInt(configURLs.size()); - String url = configURLs.get(this.lastKnownActiveUrlIndex); - _isSSL = isSsl(url); - LOG.info("Init params: " + String.format("Base URL[%s], SSL Config filename[%s], ServiceName=[%s], SupportsPolicyDeltas=[%s], ConfigURLs=[%s]", url, _sslConfigFileName, _serviceName, _supportsPolicyDeltas, _supportsTagDeltas, configURLs)); - - _client = getClient(); - _client.property(ClientProperties.CONNECT_TIMEOUT, _restClientConnTimeOutMs); - _client.property(ClientProperties.READ_TIMEOUT, _restClientReadTimeOutMs); - - if(LOG.isDebugEnabled()) { - LOG.debug("<== RangerAdminJersey2RESTClient.init(" + configPropertyPrefix + "): " + _client.toString()); - } - - try { - this._serviceNameUrlParam = URLEncoderUtil.encodeURIParam(serviceName); - } catch (UnsupportedEncodingException e) { - LOG.warn("Unsupported encoding, serviceName=" + serviceName); - this._serviceNameUrlParam = serviceName; - } - } - - @Override - public ServicePolicies getServicePoliciesIfUpdated(final long lastKnownVersion, final long lastActivationTimeInMillis) throws Exception { - if(LOG.isDebugEnabled()) { - LOG.debug("==> RangerAdminJersey2RESTClient.getServicePoliciesIfUpdated(" + lastKnownVersion + ", " + lastActivationTimeInMillis + ")"); - } - - final ServicePolicies servicePolicies; - - if (isRangerCookieEnabled && policyDownloadSessionId != null && isValidPolicyDownloadSessionCookie) { - servicePolicies = getServicePoliciesIfUpdatedWithCookie(lastKnownVersion, lastActivationTimeInMillis); - } else { - servicePolicies = getServicePoliciesIfUpdatedWithCred(lastKnownVersion, lastActivationTimeInMillis); - } - - if(LOG.isDebugEnabled()) { - LOG.debug("<== RangerAdminJersey2RESTClient.getServicePoliciesIfUpdated(" + lastKnownVersion + ", " + lastActivationTimeInMillis + "): " + servicePolicies); - } - return servicePolicies; - } - - @Override - public RangerRoles getRolesIfUpdated(final long lastKnowRoleVersion, final long lastActivationTimeInMillis) throws Exception { - if(LOG.isDebugEnabled()) { - LOG.debug("==> RangerAdminJersey2RESTClient.getRolesIfUpdated(" + lastKnowRoleVersion + ", " + lastActivationTimeInMillis + ")"); - } - - final RangerRoles rangerRoles; - - if (isRangerCookieEnabled && roleDownloadSessionId != null && isValidRoleDownloadSessionCookie) { - rangerRoles = getRangerRolesIfUpdatedWithCookie(lastKnowRoleVersion, lastActivationTimeInMillis); - } else { - rangerRoles = getRangerRolesIfUpdatedWithCred(lastKnowRoleVersion, lastActivationTimeInMillis); - } - - if(LOG.isDebugEnabled()) { - LOG.debug("<== RangerAdminJersey2RESTClient.getRolesIfUpdated(" + lastKnowRoleVersion + ", " + lastActivationTimeInMillis + "): " + rangerRoles); - } - return rangerRoles; - } - - @Override - public void grantAccess(GrantRevokeRequest request) throws Exception { - - if(LOG.isDebugEnabled()) { - LOG.debug("==> RangerAdminRESTClient.grantAccess(" + request + ")"); - } - - Map queryParams = new HashMap(); - queryParams.put(RangerRESTUtils.REST_PARAM_PLUGIN_ID, _pluginId); - - String relativeURL = RangerRESTUtils.REST_URL_SERVICE_GRANT_ACCESS + _serviceName; - Response response = get(queryParams, relativeURL); - - int httpResponseCode = response == null ? -1 : response.getStatus(); - - switch(httpResponseCode) { - case -1: - LOG.warn("Unexpected: Null response from policy server while granting access! Returning null!"); - throw new Exception("unknown error!"); - case 200: - LOG.debug("grantAccess() suceeded: HTTP status=" + httpResponseCode); - break; - case 401: - throw new AccessControlException(); - default: - String body = response.readEntity(String.class); - String message = String.format("Unexpected: Received status[%d] with body[%s] form url[%s]", httpResponseCode, body, relativeURL); - LOG.warn(message); - throw new Exception("HTTP status: " + httpResponseCode); - } - - if(LOG.isDebugEnabled()) { - LOG.debug("<== RangerAdminRESTClient.grantAccess(" + request + ")"); - } - } - - @Override - public void revokeAccess(GrantRevokeRequest request) throws Exception { - - if(LOG.isDebugEnabled()) { - LOG.debug("==> RangerAdminRESTClient.grantAccess(" + request + ")"); - } - - Map queryParams = new HashMap(); - queryParams.put(RangerRESTUtils.REST_PARAM_PLUGIN_ID, _pluginId); - - String relativeURL = RangerRESTUtils.REST_URL_SERVICE_REVOKE_ACCESS + _serviceName; - Response response = get(queryParams, relativeURL); - - int httpResponseCode = response == null ? -1 : response.getStatus(); - - switch(httpResponseCode) { - case -1: - LOG.warn("Unexpected: Null response from policy server while granting access! Returning null!"); - throw new Exception("unknown error!"); - case 200: - LOG.debug("grantAccess() suceeded: HTTP status=" + httpResponseCode); - break; - case 401: - throw new AccessControlException(); - default: - String body = response.readEntity(String.class); - String message = String.format("Unexpected: Received status[%d] with body[%s] form url[%s]", httpResponseCode, body, relativeURL); - LOG.warn(message); - throw new Exception("HTTP status: " + httpResponseCode); - } - - if(LOG.isDebugEnabled()) { - LOG.debug("<== RangerAdminRESTClient.grantAccess(" + request + ")"); - } - } - - @Override - public ServiceTags getServiceTagsIfUpdated(final long lastKnownVersion, final long lastActivationTimeInMillis) throws Exception { - if (LOG.isDebugEnabled()) { - LOG.debug("==> RangerAdminJersey2RESTClient.getServiceTagsIfUpdated(" + lastKnownVersion + ", " + lastActivationTimeInMillis + ")"); - } - - final ServiceTags serviceTags; - - if (isRangerCookieEnabled && tagDownloadSessionId != null && isValidTagDownloadSessionCookie) { - serviceTags = getServiceTagsIfUpdatedWithCookie(lastKnownVersion, lastActivationTimeInMillis); - } else { - serviceTags = getServiceTagsIfUpdatedWithCred(lastKnownVersion, lastActivationTimeInMillis); - } - - if (LOG.isDebugEnabled()) { - LOG.debug("<== RangerAdminJersey2RESTClient.getServiceTagsIfUpdated(" + lastKnownVersion + ", " + lastActivationTimeInMillis + "): " + serviceTags); - } - return serviceTags; - } - - @Override - public List getTagTypes(String pattern) throws Exception { - throw new Exception("RangerAdminjersey2RESTClient.getTagTypes() -- *** NOT IMPLEMENTED *** "); - } - - @Override - public RangerUserStore getUserStoreIfUpdated(long lastKnownUserStoreVersion, long lastActivationTimeInMillis) throws Exception { - if (LOG.isDebugEnabled()) { - LOG.debug("==> RangerAdminjersey2RESTClient.getUserStoreIfUpdated(lastKnownUserStoreVersion={}, lastActivationTimeInMillis={})", lastKnownUserStoreVersion, lastActivationTimeInMillis); - } - - final RangerUserStore ret; - final UserGroupInformation user = MiscUtil.getUGILoginUser(); - final boolean isSecureMode = isKerberosEnabled(user); - final Response response; - - Map queryParams = new HashMap(); - - queryParams.put(RangerRESTUtils.REST_PARAM_LAST_KNOWN_USERSTORE_VERSION, Long.toString(lastKnownUserStoreVersion)); - queryParams.put(RangerRESTUtils.REST_PARAM_LAST_ACTIVATION_TIME, Long.toString(lastActivationTimeInMillis)); - queryParams.put(RangerRESTUtils.REST_PARAM_PLUGIN_ID, _pluginId); - queryParams.put(RangerRESTUtils.REST_PARAM_CLUSTER_NAME, _clusterName); - queryParams.put(RangerRESTUtils.REST_PARAM_CAPABILITIES, pluginCapabilities); - - if (isSecureMode) { - if (LOG.isDebugEnabled()) { - LOG.debug("Checking UserStore updated as user: {}", user); - } - - response = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> { - try { - String relativeURL = RangerRESTUtils.REST_URL_SERVICE_SERCURE_GET_USERSTORE + _serviceNameUrlParam; - - return get(queryParams, relativeURL); - } catch (Exception e) { - LOG.error("Failed to get response", e); - } - - return null; - }); - } else { - if (LOG.isDebugEnabled()) { - LOG.debug("Checking UserStore updated as user: {}", user); - } - - String relativeURL = RangerRESTUtils.REST_URL_SERVICE_GET_USERSTORE + _serviceNameUrlParam; - - response = get(queryParams, relativeURL); - } - - if (response == null || response.getStatus() == 304) { // NOT_MODIFIED - if (response == null) { - LOG.error("Error getting UserStore; Received NULL response!!. secureMode={}, user={}, serviceName={}", isSecureMode, user, _serviceName); - } else { - String resp = response.hasEntity() ? response.readEntity(String.class) : null; - - if (LOG.isDebugEnabled()) { - LOG.debug("No change in UserStore. secureMode={}, user={}, response={}, serviceName={}, lastKnownUserStoreVersion={}, lastActivationTimeInMillis={}", - isSecureMode, user, resp, _serviceName, lastKnownUserStoreVersion, lastActivationTimeInMillis); - } - } - - ret = null; - } else if (response.getStatus() == 200) { // OK - String body = response.readEntity(String.class); - - ret = getGson().fromJson(body, RangerUserStore.class); - } else if (response.getStatus() == 404) { // NOT_FOUND - ret = null; - - LOG.error("Error getting UserStore; service not found. secureMode={}, user={}, response={}, serviceName={}, lastKnownUserStoreVersion={}, lastActivationTimeInMillis={}", - isSecureMode, user, response.getStatus(), _serviceName, lastKnownUserStoreVersion, lastActivationTimeInMillis); - - String exceptionMsg = response.hasEntity() ? response.readEntity(String.class) : null; - - RangerServiceNotFoundException.throwExceptionIfServiceNotFound(_serviceName, exceptionMsg); - - LOG.warn("Received 404 error code with body:[{}], Ignoring", exceptionMsg); - } else { - String resp = response.hasEntity() ? response.readEntity(String.class) : null; - - LOG.warn("Error getting UserStore. secureMode={}, user={}, response={}, serviceName={}, lastKnownUserStoreVersion={}, lastActivationTimeInMillis={}", - isSecureMode, user, resp, _serviceName, lastKnownUserStoreVersion, lastActivationTimeInMillis); - - ret = null; - } - - if (LOG.isDebugEnabled()) { - LOG.debug("<== RangerAdminjersey2RESTClient.getUserStoreIfUpdated(lastKnownUserStoreVersion={}, lastActivationTimeInMillis={}): ret={}", lastKnownUserStoreVersion, lastActivationTimeInMillis, ret); - } - - return ret; - } - - // We get date from the policy manager as unix long! This deserializer exists to deal with it. Remove this class once we start send date/time per RFC 3339 - public static class GsonUnixDateDeserializer implements JsonDeserializer { - - @Override - public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { - return new Date(json.getAsJsonPrimitive().getAsLong()); - } - - } - - // package level methods left so (and not private only for testability!) Not intended for use outside this class!! - Gson getGson() { - return new GsonBuilder() - .setPrettyPrinting() - // We get date from the policy manager as unix long! This deserializer exists to deal with it. Remove this class once we start send date/time per RFC 3339 - .registerTypeAdapter(Date.class, new GsonUnixDateDeserializer()) - .create(); - } - - Client getClient() { - Client result = _client; - if(result == null) { - synchronized(this) { - result = _client; - if(result == null) { - _client = result = buildClient(); - } - } - } - - return result; - } - - Client buildClient() { - - if (_isSSL) { - if (_sslContext == null) { - RangerSslHelper sslHelper = new RangerSslHelper(_sslConfigFileName); - _sslContext = sslHelper.createContext(); - } - if (_hv == null) { - _hv = new HostnameVerifier() { - public boolean verify(String urlHostName, SSLSession session) { - return session.getPeerHost().equals(urlHostName); - } - }; - } - _client = ClientBuilder.newBuilder() - .sslContext(_sslContext) - .hostnameVerifier(_hv) - .build(); - } - - if(_client == null) { - _client = ClientBuilder.newClient(); - } - - return _client; - } - - private Response get(Map queyParams, String relativeURL) { - Response response = null; - int startIndex = this.lastKnownActiveUrlIndex; - int currentIndex = 0; - int retryAttempt = 0; - - for (int index = 0; index < configURLs.size(); index++) { - try { - currentIndex = (startIndex + index) % configURLs.size(); - - WebTarget target = _client.target(configURLs.get(currentIndex) + relativeURL); - response = setQueryParams(target, queyParams).request(MediaType.APPLICATION_JSON_TYPE).get(); - if (response != null) { - setLastKnownActiveUrlIndex(currentIndex); - break; - } - } catch (ProcessingException e) { - if (shouldRetry(configURLs.get(currentIndex), index, retryAttempt, e)) { - retryAttempt++; - - index = -1; // start from first url - } - } - } - return response; - } - - private Response get(Map queyParams, String relativeURL, Cookie sessionId) { - Response response = null; - int startIndex = this.lastKnownActiveUrlIndex; - int currentIndex = 0; - int retryAttempt = 0; - - for (int index = 0; index < configURLs.size(); index++) { - try { - currentIndex = (startIndex + index) % configURLs.size(); - - WebTarget target = _client.target(configURLs.get(currentIndex)+relativeURL); - target = setQueryParams(target, queyParams); - Invocation.Builder invocationBuilder = target.request(MediaType.APPLICATION_JSON_TYPE).cookie(sessionId); - response = invocationBuilder.get(); - if (response != null) { - setLastKnownActiveUrlIndex(currentIndex); - break; - } - } catch (ProcessingException e) { - if (shouldRetry(configURLs.get(currentIndex), index, retryAttempt, e)) { - retryAttempt++; - - index = -1; // start from first url - } - } - } - return response; - } - - private static WebTarget setQueryParams(WebTarget target, Map params) { - WebTarget ret = target; - if (target != null && params != null) { - Set> entrySet = params.entrySet(); - for (Map.Entry entry : entrySet) { - ret = ret.queryParam(entry.getKey(), entry.getValue()); - } - } - return ret; - } - - private void setLastKnownActiveUrlIndex(int lastKnownActiveUrlIndex) { - this.lastKnownActiveUrlIndex = lastKnownActiveUrlIndex; - } - - private boolean isSsl(String url) { - return !StringUtils.isEmpty(url) && url.toLowerCase().startsWith("https"); - } - - private String getPluginId(String serviceName, String appId) { - String hostName = null; - - try { - hostName = InetAddress.getLocalHost().getHostName(); - } catch (UnknownHostException e) { - LOG.error("ERROR: Unable to find hostname for the agent ", e); - hostName = "unknownHost"; - } - - String ret = hostName + "-" + serviceName; - - if(! StringUtils.isEmpty(appId)) { - ret = appId + "@" + ret; - } - - if (ret.length() > MAX_PLUGIN_ID_LEN ) { - ret = ret.substring(0,MAX_PLUGIN_ID_LEN); - } - - return ret ; - } - - /* Policies Download from Ranger admin */ - private ServicePolicies getServicePoliciesIfUpdatedWithCred(final long lastKnownVersion, final long lastActivationTimeInMillis) throws Exception { - if (LOG.isDebugEnabled()) { - LOG.debug("==> RangerAdminJersey2RESTClient.getServicePoliciesWithCred(" + lastKnownVersion + ", " + lastActivationTimeInMillis + ")"); - } - - final ServicePolicies ret; - - final Response response = getRangerAdminPolicyDownloadResponse(lastKnownVersion, lastActivationTimeInMillis); - - int httpResponseCode = response == null ? -1 : response.getStatus(); - String body = null; - - switch (httpResponseCode) { - case 200: - body = response.readEntity(String.class); - - if (LOG.isDebugEnabled()) { - LOG.debug("Response from 200 server: " + body); - } - - Gson gson = getGson(); - ret = gson.fromJson(body, ServicePolicies.class); - setCookieReceivedFromCredSession(response); - - if (LOG.isDebugEnabled()) { - LOG.debug("Deserialized response to: " + ret); - } - break; - case 304: - ret = null; - setCookieReceivedFromCredSession(response); - LOG.debug("Got response: 304. Ok. Returning null"); - break; - case -1: - ret = null; - policyDownloadSessionId = null; - LOG.warn("Unexpected: Null response from policy server while trying to get policies! Returning null!"); - break; - case 404: - ret = null; - policyDownloadSessionId = null; - if (response.hasEntity()) { - body = response.readEntity(String.class); - if (StringUtils.isNotBlank(body)) { - RangerServiceNotFoundException.throwExceptionIfServiceNotFound(_serviceName, body); - } - } - LOG.warn("Received 404 error code with body:[" + body + "], Ignoring"); - break; - default: - ret = null; - policyDownloadSessionId = null; - body = response.readEntity(String.class); - LOG.warn(String.format("Unexpected: Received status[%d] with body[%s] form url[%s]", httpResponseCode, body, getRelativeURL(isSecureMode()))); - break; - } - - if (LOG.isDebugEnabled()) { - LOG.debug("<== RangerAdminJersey2RESTClient.getServicePoliciesWithCred(" + lastKnownVersion + ", " + lastActivationTimeInMillis + "): " + ret); - } - - return ret; - } - - private ServicePolicies getServicePoliciesIfUpdatedWithCookie(final long lastKnownVersion, final long lastActivationTimeInMillis) throws Exception { - if (LOG.isDebugEnabled()) { - LOG.debug("==> RangerAdminJersey2RESTClient.getServicePoliciesWithCookie(" + lastKnownVersion + ", " + lastActivationTimeInMillis + ")"); - } - - final ServicePolicies ret; - - final Response response = getRangerAdminPolicyDownloadResponse(lastKnownVersion, lastActivationTimeInMillis); - - int httpResponseCode = response == null ? -1 : response.getStatus(); - String body = null; - - switch (httpResponseCode) { - case 200: - body = response.readEntity(String.class); - - if (LOG.isDebugEnabled()) { - LOG.debug("Response from 200 server: " + body); - } - - Gson gson = getGson(); - ret = gson.fromJson(body, ServicePolicies.class); - checkAndResetSessionCookie(response); - - if (LOG.isDebugEnabled()) { - LOG.debug("Deserialized response to: " + ret); - } - break; - case 304: - ret = null; - checkAndResetSessionCookie(response); - LOG.debug("Got response: 304. Ok. Returning null"); - break; - case -1: - ret = null; - policyDownloadSessionId = null; - isValidPolicyDownloadSessionCookie = false; - LOG.warn("Unexpected: Null response from policy server while trying to get policies! Returning null!"); - break; - case 404: - ret = null; - policyDownloadSessionId = null; - isValidPolicyDownloadSessionCookie = false; - if (response.hasEntity()) { - body = response.readEntity(String.class); - if (StringUtils.isNotBlank(body)) { - RangerServiceNotFoundException.throwExceptionIfServiceNotFound(_serviceName, body); - } - } - LOG.warn("Received 404 error code with body:[" + body + "], Ignoring"); - break; - default: - ret = null; - policyDownloadSessionId = null; - isValidPolicyDownloadSessionCookie = false; - body = response.readEntity(String.class); - LOG.warn(String.format("Unexpected: Received status[%d] with body[%s] form url[%s]", httpResponseCode, body, getRelativeURL(isSecureMode()))); - break; - } - - if (LOG.isDebugEnabled()) { - LOG.debug("<== RangerAdminJersey2RESTClient.getServicePoliciesWithCookie(" + lastKnownVersion + ", " + lastActivationTimeInMillis + "): " + ret); - } - - return ret; - } - - private Response getRangerAdminPolicyDownloadResponse(final long lastKnownVersion, final long lastActivationTimeInMillis) throws Exception { - if (LOG.isDebugEnabled()) { - LOG.debug("==> RangerAdminJersey2RESTClient.getRangerAdminPolicyDownloadResponse(" + lastKnownVersion + ", " + lastActivationTimeInMillis + ")"); - } - - final Response ret; - - Map queryParams = new HashMap(); - queryParams.put(RangerRESTUtils.REST_PARAM_LAST_KNOWN_POLICY_VERSION, Long.toString(lastKnownVersion)); - queryParams.put(RangerRESTUtils.REST_PARAM_LAST_ACTIVATION_TIME, Long.toString(lastActivationTimeInMillis)); - queryParams.put(RangerRESTUtils.REST_PARAM_PLUGIN_ID, _pluginId); - queryParams.put(RangerRESTUtils.REST_PARAM_CLUSTER_NAME, _clusterName); - queryParams.put(RangerRESTUtils.REST_PARAM_SUPPORTS_POLICY_DELTAS, Boolean.toString(_supportsPolicyDeltas)); - queryParams.put(RangerRESTUtils.REST_PARAM_CAPABILITIES, pluginCapabilities); - - if (isSecureMode()) { - if (LOG.isDebugEnabled()) { - LOG.debug("Checking Service policy if updated as user : " + MiscUtil.getUGILoginUser()); - } - ret = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> get(queryParams, getRelativeURL(true), policyDownloadSessionId)); - } else { - if (LOG.isDebugEnabled()) { - LOG.debug("Checking Service policy if updated with old api call"); - } - ret = get(queryParams, getRelativeURL(false), policyDownloadSessionId); - } - - if (LOG.isDebugEnabled()) { - LOG.debug("<== RangerAdminJersey2RESTClient.getRangerAdminPolicyDownloadResponse(" + lastKnownVersion + ", " + lastActivationTimeInMillis + "): " + ret); - } - - return ret; - } - - private String getRelativeURL(final boolean isSecureMode) { - final String ret; - if (isSecureMode){ - ret = RangerRESTUtils.REST_URL_POLICY_GET_FOR_SECURE_SERVICE_IF_UPDATED + _serviceName; - } else { - ret = RangerRESTUtils.REST_URL_POLICY_GET_FOR_SERVICE_IF_UPDATED + _serviceName; - } - return ret; - } - - private void checkAndResetSessionCookie(Response response) { - Map cookieMap = response.getCookies(); - Set cookieNames = cookieMap.keySet(); - for (String cookieName : cookieNames) { - if (cookieName.equalsIgnoreCase(rangerAdminCookieName)) { - policyDownloadSessionId = cookieMap.get(cookieName); - isValidPolicyDownloadSessionCookie = (policyDownloadSessionId != null); - break; - } - } - } - - private void setCookieReceivedFromCredSession(Response response) { - if (isRangerCookieEnabled) { - Cookie sessionCookie = null; - Map cookieMap = response.getCookies(); - // save cookie received from credentials session login - Set cookieNames = cookieMap.keySet(); - for (String cookieName : cookieNames) { - if (cookieName.equalsIgnoreCase(rangerAdminCookieName)) { - sessionCookie = cookieMap.get(cookieName); - break; - } - } - policyDownloadSessionId = sessionCookie; - isValidPolicyDownloadSessionCookie = (policyDownloadSessionId != null); - } - } - - /* Tags Download from Ranger admin */ - private ServiceTags getServiceTagsIfUpdatedWithCred(final long lastKnownVersion, final long lastActivationTimeInMillis) throws Exception { - if (LOG.isDebugEnabled()) { - LOG.debug("==> RangerAdminJersey2RESTClient.getServiceTagsIfUpdatedWithCred(" + lastKnownVersion + ", " + lastActivationTimeInMillis + ")"); - } - - final ServiceTags ret; - - final Response response = getTagsDownloadResponse(lastKnownVersion, lastActivationTimeInMillis); - - int httpResponseCode = response == null ? -1 : response.getStatus(); - String body = null; - - switch (httpResponseCode) { - case 200: - body = response.readEntity(String.class); - - if (LOG.isDebugEnabled()) { - LOG.debug("Response from 200 server: " + body); - } - - Gson gson = getGson(); - ret = gson.fromJson(body, ServiceTags.class); - setCookieReceivedFromTagDownloadSession(response); - - if (LOG.isDebugEnabled()) { - LOG.debug("Deserialized response to: " + ret); - } - break; - case 304: - ret = null; - setCookieReceivedFromTagDownloadSession(response); - LOG.debug("Got response: 304. Ok. Returning null"); - break; - case -1: - ret = null; - tagDownloadSessionId = null; - LOG.warn("Unexpected: Null response from tag server while trying to get tags! Returning null!"); - break; - case 404: - ret = null; - tagDownloadSessionId = null; - if (response.hasEntity()) { - body = response.readEntity(String.class); - if (StringUtils.isNotBlank(body)) { - RangerServiceNotFoundException.throwExceptionIfServiceNotFound(_serviceName, body); - } - } - LOG.warn("Received 404 error code with body:[" + body + "], Ignoring"); - break; - default: - ret = null; - tagDownloadSessionId = null; - body = response.readEntity(String.class); - LOG.warn(String.format("Unexpected: Received status[%d] with body[%s] form url[%s]", httpResponseCode, body, getRelativeURLForTagDownload(isSecureMode()))); - break; - } - - if (LOG.isDebugEnabled()) { - LOG.debug("<== RangerAdminJersey2RESTClient.getServiceTagsIfUpdatedWithCred(" + lastKnownVersion + ", " + lastActivationTimeInMillis + "): " + ret); - } - - return ret; - } - - private ServiceTags getServiceTagsIfUpdatedWithCookie(final long lastKnownVersion, final long lastActivationTimeInMillis) throws Exception { - if (LOG.isDebugEnabled()) { - LOG.debug("==> RangerAdminJersey2RESTClient.getServiceTagsIfUpdatedWithCookie(" + lastKnownVersion + ", " + lastActivationTimeInMillis + ")"); - } - - final ServiceTags ret; - - final Response response = getTagsDownloadResponse(lastKnownVersion, lastActivationTimeInMillis); - - int httpResponseCode = response == null ? -1 : response.getStatus(); - String body = null; - - switch (httpResponseCode) { - case 200: - body = response.readEntity(String.class); - - if (LOG.isDebugEnabled()) { - LOG.debug("Response from 200 server: " + body); - } - - Gson gson = getGson(); - ret = gson.fromJson(body, ServiceTags.class); - checkAndResetTagDownloadSessionCookie(response); - - if (LOG.isDebugEnabled()) { - LOG.debug("Deserialized response to: " + ret); - } - break; - case 304: - ret = null; - checkAndResetTagDownloadSessionCookie(response); - LOG.debug("Got response: 304. Ok. Returning null"); - break; - case -1: - ret = null; - tagDownloadSessionId = null; - isValidTagDownloadSessionCookie = false; - LOG.warn("Unexpected: Null response from tag server while trying to get tags! Returning null!"); - break; - case 404: - ret = null; - tagDownloadSessionId = null; - isValidTagDownloadSessionCookie = false; - if (response.hasEntity()) { - body = response.readEntity(String.class); - if (StringUtils.isNotBlank(body)) { - RangerServiceNotFoundException.throwExceptionIfServiceNotFound(_serviceName, body); - } - } - LOG.warn("Received 404 error code with body:[" + body + "], Ignoring"); - break; - default: - ret = null; - tagDownloadSessionId = null; - isValidTagDownloadSessionCookie = false; - body = response.readEntity(String.class); - LOG.warn(String.format("Unexpected: Received status[%d] with body[%s] form url[%s]", httpResponseCode, body, ret)); - break; - } - - if (LOG.isDebugEnabled()) { - LOG.debug("<== RangerAdminJersey2RESTClient.getServiceTagsIfUpdatedWithCookie(" + lastKnownVersion + ", " + lastActivationTimeInMillis + "): " + ret); - } - - return ret; - } - - private Response getTagsDownloadResponse(final long lastKnownVersion, final long lastActivationTimeInMillis) throws Exception { - if (LOG.isDebugEnabled()) { - LOG.debug("==> RangerAdminJersey2RESTClient.getTagsDownloadResponse(" + lastKnownVersion + ", " + lastActivationTimeInMillis + ")"); - } - - final Response ret; - - Map queryParams = new HashMap(); - queryParams.put(RangerRESTUtils.LAST_KNOWN_TAG_VERSION_PARAM, Long.toString(lastKnownVersion)); - queryParams.put(RangerRESTUtils.REST_PARAM_LAST_ACTIVATION_TIME, Long.toString(lastActivationTimeInMillis)); - queryParams.put(RangerRESTUtils.REST_PARAM_PLUGIN_ID, _pluginId); - queryParams.put(RangerRESTUtils.REST_PARAM_SUPPORTS_TAG_DELTAS, Boolean.toString(_supportsTagDeltas)); - queryParams.put(RangerRESTUtils.REST_PARAM_CAPABILITIES, pluginCapabilities); - - if (isSecureMode()) { - if (LOG.isDebugEnabled()) { - LOG.debug("Checking Service tags if updated as user : " + MiscUtil.getUGILoginUser()); - } - ret = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> get(queryParams, getRelativeURLForTagDownload(true), tagDownloadSessionId)); - } else { - if (LOG.isDebugEnabled()) { - LOG.debug("Checking Service tags if updated with old api call"); - } - ret = get(queryParams, getRelativeURLForTagDownload(false), tagDownloadSessionId); - } - - if (LOG.isDebugEnabled()) { - LOG.debug("<== RangerAdminJersey2RESTClient.getTagsDownloadResponse(" + lastKnownVersion + ", " + lastActivationTimeInMillis + "): " + ret); - } - - return ret; - } - - private String getRelativeURLForTagDownload(final boolean isSecureMode) { - final String ret; - if (isSecureMode){ - ret = RangerRESTUtils.REST_URL_GET_SECURE_SERVICE_TAGS_IF_UPDATED + _serviceName; - } else { - ret = RangerRESTUtils.REST_URL_GET_SERVICE_TAGS_IF_UPDATED + _serviceName; - } - return ret; - } - - private void checkAndResetTagDownloadSessionCookie(Response response) { - Map cookieMap = response.getCookies(); - Set cookieNames = cookieMap.keySet(); - for (String cookieName : cookieNames) { - if (cookieName.equalsIgnoreCase(rangerAdminCookieName)) { - tagDownloadSessionId = cookieMap.get(cookieName); - isValidTagDownloadSessionCookie = (tagDownloadSessionId != null); - break; - } - } - } - - private void setCookieReceivedFromTagDownloadSession(Response response) { - if (isRangerCookieEnabled) { - Cookie sessionCookie = null; - Map cookieMap = response.getCookies(); - // save cookie received from credentials session login - Set cookieNames = cookieMap.keySet(); - for (String cookieName : cookieNames) { - if (cookieName.equalsIgnoreCase(rangerAdminCookieName)) { - sessionCookie = cookieMap.get(cookieName); - } - } - tagDownloadSessionId = sessionCookie; - isValidTagDownloadSessionCookie = (tagDownloadSessionId != null); - } - } - - /* Role Download from Ranger Admin */ - private RangerRoles getRangerRolesIfUpdatedWithCred(final long lastKnownRoleVersion, final long lastActivationTimeInMillis) throws Exception { - if (LOG.isDebugEnabled()) { - LOG.debug("==> RangerAdminJersey2RESTClient.getRangerRolesIfUpdatedWithCred(" + lastKnownRoleVersion + ", " + lastActivationTimeInMillis + ")"); - } - - final RangerRoles ret; - - final Response response = getRoleDownloadResponse(lastKnownRoleVersion, lastActivationTimeInMillis); - - int httpResponseCode = response == null ? -1 : response.getStatus(); - String body = null; - - switch (httpResponseCode) { - case 200: - body = response.readEntity(String.class); - - if (LOG.isDebugEnabled()) { - LOG.debug("Response from 200 server: " + body); - } - - Gson gson = getGson(); - ret = gson.fromJson(body, RangerRoles.class); - setCookieReceivedFromRoleDownloadSession(response); - - if (LOG.isDebugEnabled()) { - LOG.debug("Deserialized response to: " + ret); - } - break; - case 304: - ret = null; - setCookieReceivedFromRoleDownloadSession(response); - LOG.debug("Got response: 304. Ok. Returning null"); - break; - case -1: - ret = null; - roleDownloadSessionId = null; - LOG.warn("Unexpected: Null response from policy server while trying to get policies! Returning null!"); - break; - case 404: - ret = null; - roleDownloadSessionId = null; - if (response.hasEntity()) { - body = response.readEntity(String.class); - if (StringUtils.isNotBlank(body)) { - RangerServiceNotFoundException.throwExceptionIfServiceNotFound(_serviceName, body); - } - } - LOG.warn("Received 404 error code with body:[" + body + "], Ignoring"); - break; - default: - ret = null; - roleDownloadSessionId = null; - body = response.readEntity(String.class); - LOG.warn(String.format("Unexpected: Received status[%d] with body[%s] form url[%s]", httpResponseCode, body, getRelativeURLForRoleDownload(isSecureMode()))); - break; - } - - if (LOG.isDebugEnabled()) { - LOG.debug("<== RangerAdminJersey2RESTClient.getRangerRolesIfUpdatedWithCred(" + lastKnownRoleVersion + ", " + lastActivationTimeInMillis + "): " + ret); - } - - return ret; - } - - private RangerRoles getRangerRolesIfUpdatedWithCookie(final long lastKnownRoleVersion, final long lastActivationTimeInMillis) throws Exception { - if (LOG.isDebugEnabled()) { - LOG.debug("==> RangerAdminJersey2RESTClient.getRangerRolesIfUpdatedWithCookie(" + lastKnownRoleVersion + ", " + lastActivationTimeInMillis + ")"); - } - - final RangerRoles ret; - - final Response response = getRoleDownloadResponse(lastKnownRoleVersion, lastActivationTimeInMillis); - - int httpResponseCode = response == null ? -1 : response.getStatus(); - String body = null; - - switch (httpResponseCode) { - case 200: - body = response.readEntity(String.class); - - if (LOG.isDebugEnabled()) { - LOG.debug("Response from 200 server: " + body); - } - - Gson gson = getGson(); - ret = gson.fromJson(body, RangerRoles.class); - checkAndResetRoleDownloadSessionCookie(response); - if (LOG.isDebugEnabled()) { - LOG.debug("Deserialized response to: " + ret); - } - break; - case 304: - ret = null; - checkAndResetRoleDownloadSessionCookie(response); - LOG.debug("Got response: 304. Ok. Returning null"); - break; - case -1: - ret = null; - roleDownloadSessionId = null; - isValidRoleDownloadSessionCookie = false; - LOG.warn("Unexpected: Null response from policy server while trying to get policies! Returning null!"); - break; - case 404: - ret = null; - roleDownloadSessionId = null; - isValidRoleDownloadSessionCookie = false; - if (response.hasEntity()) { - body = response.readEntity(String.class); - if (StringUtils.isNotBlank(body)) { - RangerServiceNotFoundException.throwExceptionIfServiceNotFound(_serviceName, body); - } - } - LOG.warn("Received 404 error code with body:[" + body + "], Ignoring"); - break; - default: - ret = null; - roleDownloadSessionId = null; - isValidRoleDownloadSessionCookie = false; - body = response.readEntity(String.class); - LOG.warn(String.format("Unexpected: Received status[%d] with body[%s] form url[%s]", httpResponseCode, body, getRelativeURLForRoleDownload(isSecureMode()))); - break; - } - - if (LOG.isDebugEnabled()) { - LOG.debug("<== RangerAdminJersey2RESTClient.getRangerRolesIfUpdatedWithCookie(" + lastKnownRoleVersion + ", " + lastActivationTimeInMillis + "): " + ret); - } - - return ret; - } - - private Response getRoleDownloadResponse(final long lastKnownRoleVersion, final long lastActivationTimeInMillis) throws Exception { - if (LOG.isDebugEnabled()) { - LOG.debug("==> RangerAdminJersey2RESTClient.getRoleDownloadResponse(" + lastKnownRoleVersion + ", " + lastActivationTimeInMillis + ")"); - } - - final Response ret; - - Map queryParams = new HashMap(); - queryParams.put(RangerRESTUtils.REST_PARAM_LAST_KNOWN_ROLE_VERSION, Long.toString(lastKnownRoleVersion)); - queryParams.put(RangerRESTUtils.REST_PARAM_LAST_ACTIVATION_TIME, Long.toString(lastActivationTimeInMillis)); - queryParams.put(RangerRESTUtils.REST_PARAM_PLUGIN_ID, _pluginId); - queryParams.put(RangerRESTUtils.REST_PARAM_CLUSTER_NAME, _clusterName); - - if (isSecureMode()) { - if (LOG.isDebugEnabled()) { - LOG.debug("Checking Roles if updated as user : " + MiscUtil.getUGILoginUser()); - } - ret = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> get(queryParams, getRelativeURLForRoleDownload(true), roleDownloadSessionId)); - } else { - if (LOG.isDebugEnabled()) { - LOG.debug("Checking Roles if updated with old api call"); - } - ret = get(queryParams, getRelativeURLForRoleDownload(false), roleDownloadSessionId); - } - - if (LOG.isDebugEnabled()) { - LOG.debug("<== RangerAdminJersey2RESTClient.getRoleDownloadResponse(" + lastKnownRoleVersion + ", " + lastActivationTimeInMillis + "): " + ret); - } - - return ret; - } - - private String getRelativeURLForRoleDownload(final boolean isSecureMode) { - final String ret; - if (isSecureMode){ - ret = RangerRESTUtils.REST_URL_SERVICE_SERCURE_GET_USER_GROUP_ROLES + _serviceName; - } else { - ret = RangerRESTUtils.REST_URL_SERVICE_GET_USER_GROUP_ROLES + _serviceName; - } - return ret; - } - - private void checkAndResetRoleDownloadSessionCookie(Response response) { - Map cookieMap = response.getCookies(); - Set cookieNames = cookieMap.keySet(); - for (String cookieName : cookieNames) { - if (cookieName.equalsIgnoreCase(rangerAdminCookieName)) { - roleDownloadSessionId = cookieMap.get(cookieName); - isValidRoleDownloadSessionCookie = (roleDownloadSessionId != null); - break; - } - } - } - - private void setCookieReceivedFromRoleDownloadSession(Response response) { - if (isRangerCookieEnabled) { - Cookie sessionCookie = null; - Map cookieMap = response.getCookies(); - // save cookie received from credentials session login - Set cookieNames = cookieMap.keySet(); - for (String cookieName : cookieNames) { - if (cookieName.equalsIgnoreCase(rangerAdminCookieName)) { - sessionCookie = cookieMap.get(cookieName); - break; - } - } - roleDownloadSessionId = sessionCookie; - isValidRoleDownloadSessionCookie = (roleDownloadSessionId != null); - } - } - - protected boolean shouldRetry(String currentUrl, int index, int retryAttemptCount, ProcessingException ex) { - LOG.warn("Failed to communicate with Ranger Admin. URL: " + currentUrl + ". Error: " + ex.getMessage()); - - boolean isLastUrl = index == (configURLs.size() - 1); - - // attempt retry after failure on the last url - boolean ret = isLastUrl && (retryAttemptCount < _restClientMaxRetryAttempts); - - if (ret) { - LOG.warn("Waiting for " + _restClientRetryIntervalMs + "ms before retry attempt #" + (retryAttemptCount + 1)); - - try { - Thread.sleep(_restClientRetryIntervalMs); - } catch (InterruptedException excp) { - LOG.error("Failed while waiting to retry", excp); - } - } else if (isLastUrl) { - LOG.error("Failed to communicate with all Ranger Admin's URL's : [ " + configURLs + " ]"); - - throw new ProcessingException("Failed to communicate with all Ranger Admin's URL : [ "+ configURLs+" ]", ex); - } - - return ret; - } - - private boolean isSecureMode() { - return isKerberosEnabled(MiscUtil.getUGILoginUser()); - } + private String getRelativeURLForRoleDownload(final boolean isSecureMode) { + final String ret; + + if (isSecureMode) { + ret = RangerRESTUtils.REST_URL_SERVICE_SERCURE_GET_USER_GROUP_ROLES + serviceName; + } else { + ret = RangerRESTUtils.REST_URL_SERVICE_GET_USER_GROUP_ROLES + serviceName; + } + + return ret; + } + + private void checkAndResetRoleDownloadSessionCookie(Response response) { + Map cookieMap = response.getCookies(); + Set cookieNames = cookieMap.keySet(); + + for (String cookieName : cookieNames) { + if (cookieName.equalsIgnoreCase(rangerAdminCookieName)) { + roleDownloadSessionId = cookieMap.get(cookieName); + isValidRoleDownloadSessionCookie = (roleDownloadSessionId != null); + break; + } + } + } + + private void setCookieReceivedFromRoleDownloadSession(Response response) { + if (isRangerCookieEnabled) { + Cookie sessionCookie = null; + Map cookieMap = response.getCookies(); + + // save cookie received from credentials session login + Set cookieNames = cookieMap.keySet(); + + for (String cookieName : cookieNames) { + if (cookieName.equalsIgnoreCase(rangerAdminCookieName)) { + sessionCookie = cookieMap.get(cookieName); + break; + } + } + + roleDownloadSessionId = sessionCookie; + isValidRoleDownloadSessionCookie = (roleDownloadSessionId != null); + } + } + + private boolean isSecureMode() { + return isKerberosEnabled(MiscUtil.getUGILoginUser()); + } + + // We get date from the policy manager as unix long! This deserializer exists to deal with it. Remove this class once we start send date/time per RFC 3339 + public static class GsonUnixDateDeserializer implements JsonDeserializer { + @Override + public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + return new Date(json.getAsJsonPrimitive().getAsLong()); + } + } } diff --git a/knox-agent/src/main/java/org/apache/ranger/authorization/knox/KnoxRangerPlugin.java b/knox-agent/src/main/java/org/apache/ranger/authorization/knox/KnoxRangerPlugin.java index 5ce5a5374b..da798be0c4 100644 --- a/knox-agent/src/main/java/org/apache/ranger/authorization/knox/KnoxRangerPlugin.java +++ b/knox-agent/src/main/java/org/apache/ranger/authorization/knox/KnoxRangerPlugin.java @@ -19,9 +19,6 @@ package org.apache.ranger.authorization.knox; -import java.util.List; -import java.util.Set; - import org.apache.ranger.authorization.knox.KnoxRangerPlugin.KnoxConstants.AccessType; import org.apache.ranger.authorization.knox.KnoxRangerPlugin.KnoxConstants.PluginConfiguration; import org.apache.ranger.authorization.knox.KnoxRangerPlugin.KnoxConstants.ResourceName; @@ -31,104 +28,119 @@ import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl; import org.apache.ranger.plugin.service.RangerBasePlugin; +import java.util.List; +import java.util.Set; + public class KnoxRangerPlugin extends RangerBasePlugin { + boolean initialized; + + public KnoxRangerPlugin() { + super(PluginConfiguration.ServiceType, PluginConfiguration.AuditApplicationType); + } + + // must be synchronized so that accidental double init of plugin does not happen .. in case servlet instantiates multiple filters. + @Override + public synchronized void init() { + if (!initialized) { + // mandatory call to base plugin + super.init(); + // One time call to register the audit hander with the policy engine. + super.setResultProcessor(new RangerDefaultAuditHandler(getConfig())); + initialized = true; + } + } + + public static class RequestBuilder { + String service; + String topology; + String user; + Set groups; + String clientIp; + String remoteIp; + List forwardedAddresses; + + RequestBuilder service(String service) { + this.service = service; + return this; + } + + RequestBuilder topology(String topology) { + this.topology = topology; + return this; + } + + RequestBuilder user(String user) { + this.user = user; + return this; + } + + RequestBuilder groups(Set groups) { + this.groups = groups; + return this; + } + + RequestBuilder clientIp(String clientIp) { + this.clientIp = clientIp; + return this; + } + + RequestBuilder remoteIp(String remoteIp) { + this.remoteIp = remoteIp; + return this; + } + + RequestBuilder forwardedAddresses(List forwardedAddresses) { + this.forwardedAddresses = forwardedAddresses; + return this; + } + + void verifyBuildable() { + if (topology == null) { + throw new IllegalStateException("_topology can't be null!"); + } + if (service == null) { + throw new IllegalStateException("_service can't be null!"); + } + if (user == null) { + throw new IllegalStateException("_user can't be null!"); + } + } + + RangerAccessRequest build() { + // build resource + RangerAccessResourceImpl resource = new RangerAccessResourceImpl(); + resource.setValue(ResourceName.Service, service); + resource.setValue(ResourceName.Topology, topology); + // build request + RangerAccessRequestImpl request = new RangerAccessRequestImpl(); + request.setAction(AccessType.Allow); + request.setAccessType(AccessType.Allow); + request.setClientIPAddress(clientIp); + request.setUser(user); + request.setUserGroups(groups); + request.setResource(resource); + request.setRemoteIPAddress(remoteIp); + request.setForwardedAddresses(forwardedAddresses); + return request; + } + } + + public static class KnoxConstants { + // Plugin parameters + static class PluginConfiguration { + static final String ServiceType = "knox"; + static final String AuditApplicationType = "knox"; + } + + // must match the corresponding string used in service definition file + static class ResourceName { + static final String Topology = "topology"; + static final String Service = "service"; + } - boolean initialized = false; - public KnoxRangerPlugin() { - super(PluginConfiguration.ServiceType, PluginConfiguration.AuditApplicationType); - } - - // must be synchronized so that accidental double init of plugin does not happen .. in case servlet instantiates multiple filters. - @Override - synchronized public void init() { - if (!initialized) { - // mandatory call to base plugin - super.init(); - // One time call to register the audit hander with the policy engine. - super.setResultProcessor(new RangerDefaultAuditHandler(getConfig())); - initialized = true; - } - } - - public static class RequestBuilder { - String _service; - String _topology; - String _user; - Set _groups; - String _clientIp; - String _remoteIp; - List _forwardedAddresses; - - RequestBuilder service(String service) { - _service = service; - return this; - } - RequestBuilder topology(String topology) { - _topology = topology; - return this; - } - RequestBuilder user(String user) { - _user = user; - return this; - } - RequestBuilder groups(Set groups) { - _groups = groups; - return this; - } - RequestBuilder clientIp(String clientIp) { - _clientIp = clientIp; - return this; - } - RequestBuilder remoteIp(String remoteIp) { - _remoteIp = remoteIp; - return this; - } - RequestBuilder forwardedAddresses(List forwardedAddresses) { - _forwardedAddresses = forwardedAddresses; - return this; - } - void verifyBuildable() { - if (_topology == null) throw new IllegalStateException("_topology can't be null!"); - if (_service == null) throw new IllegalStateException("_service can't be null!"); - if (_user == null) throw new IllegalStateException("_user can't be null!"); - } - - RangerAccessRequest build() { - // build resource - RangerAccessResourceImpl resource = new RangerAccessResourceImpl(); - resource.setValue(ResourceName.Service, _service); - resource.setValue(ResourceName.Topology, _topology); - // build request - RangerAccessRequestImpl request = new RangerAccessRequestImpl(); - request.setAction(AccessType.Allow); - request.setAccessType(AccessType.Allow); - request.setClientIPAddress(_clientIp); - request.setUser(_user); - request.setUserGroups(_groups); - request.setResource(resource); - request.setRemoteIPAddress(_remoteIp); - request.setForwardedAddresses(_forwardedAddresses); - return request; - } - } - - public static class KnoxConstants { - - // Plugin parameters - static class PluginConfiguration { - static final String ServiceType = "knox"; - static final String AuditApplicationType = "knox"; - } - - // must match the corresponding string used in service definition file - static class ResourceName { - static final String Topology = "topology"; - static final String Service = "service"; - } - - // must match the corresponding string used in service definition file - static class AccessType { - static final String Allow = "allow"; - } - } + // must match the corresponding string used in service definition file + static class AccessType { + static final String Allow = "allow"; + } + } } diff --git a/knox-agent/src/main/java/org/apache/ranger/authorization/knox/RangerPDPKnoxFilter.java b/knox-agent/src/main/java/org/apache/ranger/authorization/knox/RangerPDPKnoxFilter.java index 2f3b436168..5d81a79037 100644 --- a/knox-agent/src/main/java/org/apache/ranger/authorization/knox/RangerPDPKnoxFilter.java +++ b/knox-agent/src/main/java/org/apache/ranger/authorization/knox/RangerPDPKnoxFilter.java @@ -6,9 +6,9 @@ * 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 - * + *

+ * 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. @@ -18,23 +18,6 @@ package org.apache.ranger.authorization.knox; -import java.io.IOException; -import java.security.AccessController; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import javax.security.auth.Subject; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - import org.apache.knox.gateway.filter.AbstractGatewayFilter; import org.apache.knox.gateway.security.GroupPrincipal; import org.apache.knox.gateway.security.ImpersonatedPrincipal; @@ -47,172 +30,173 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.security.auth.Subject; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import java.io.IOException; +import java.security.AccessController; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + public class RangerPDPKnoxFilter implements Filter { + private static final Logger LOG = LoggerFactory.getLogger(RangerPDPKnoxFilter.class); + private static final Logger PERF_KNOXAUTH_REQUEST_LOG = RangerPerfTracer.getPerfLogger("knoxauth.request"); + + private static final String KNOX_GATEWAY_JASS_CONFIG_SECTION = "com.sun.security.jgss.initiate"; + private static volatile KnoxRangerPlugin plugin; + + private String resourceRole; + + @Override + public void init(FilterConfig filterConfig) { + resourceRole = getInitParameter(filterConfig, "resource.role"); + + KnoxRangerPlugin me = plugin; + + if (me == null) { + synchronized (RangerPDPKnoxFilter.class) { + me = plugin; + + if (me == null) { + try { + MiscUtil.setUGIFromJAASConfig(KNOX_GATEWAY_JASS_CONFIG_SECTION); + + LOG.info("LoginUser = {}", MiscUtil.getUGILoginUser()); + } catch (Throwable t) { + LOG.error("Error while setting UGI for Knox Plugin...", t); + } + + LOG.info("Creating KnoxRangerPlugin"); + + plugin = new KnoxRangerPlugin(); + + plugin.init(); + } + } + } + } + + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + String sourceUrl = (String) request.getAttribute(AbstractGatewayFilter.SOURCE_REQUEST_CONTEXT_URL_ATTRIBUTE_NAME); + String topologyName = getTopologyName(sourceUrl); + String serviceName = getServiceName(); + + RangerPerfTracer perf = null; + + if (RangerPerfTracer.isPerfTraceEnabled(PERF_KNOXAUTH_REQUEST_LOG)) { + perf = RangerPerfTracer.getPerfTracer(PERF_KNOXAUTH_REQUEST_LOG, "RangerPDPKnoxFilter.doFilter(url=" + sourceUrl + ", topologyName=" + topologyName + ")"); + } + + Subject subject = Subject.getSubject(AccessController.getContext()); + Set primaryPrincipals = subject.getPrincipals(PrimaryPrincipal.class); + String primaryUser = null; + + if (!primaryPrincipals.isEmpty()) { + primaryUser = primaryPrincipals.stream().findFirst().get().getName(); + } + + String impersonatedUser = null; + Set impersonations = subject.getPrincipals(ImpersonatedPrincipal.class); + + if (!impersonations.isEmpty()) { + impersonatedUser = impersonations.stream().findFirst().get().getName(); + } + + String user = (impersonatedUser != null) ? impersonatedUser : primaryUser; + + LOG.debug("Checking access primaryUser: {}, impersonatedUser: {}, effectiveUser: {}", primaryUser, impersonatedUser, user); + + Set groupObjects = subject.getPrincipals(GroupPrincipal.class); + Set groups = new HashSet<>(); + + for (GroupPrincipal obj : groupObjects) { + groups.add(obj.getName()); + } + + String clientIp = request.getRemoteAddr(); + List forwardedAddresses = getForwardedAddresses(request); + + LOG.debug("Checking access primaryUser: {}, impersonatedUser: {}, effectiveUser: {}, groups: {}, clientIp: {}, remoteIp: {}, forwardedAddresses: {}", primaryUser, impersonatedUser, user, groups, clientIp, clientIp, forwardedAddresses); + + RangerAccessRequest accessRequest = new RequestBuilder().service(serviceName).topology(topologyName).user(user).groups(groups).clientIp(clientIp).remoteIp(clientIp).forwardedAddresses(forwardedAddresses).build(); + boolean accessAllowed = false; + + if (plugin != null) { + RangerAccessResult result = plugin.isAccessAllowed(accessRequest); + + accessAllowed = result != null && result.getIsAllowed(); + } + + LOG.debug("Access allowed: {}", accessAllowed); + + RangerPerfTracer.log(perf); + + if (accessAllowed) { + chain.doFilter(request, response); + } else { + sendForbidden((HttpServletResponse) response); + } + } + + public void destroy() { + } + + private String getInitParameter(FilterConfig filterConfig, String paramName) { + return filterConfig.getInitParameter(paramName.toLowerCase()); + } + + private List getForwardedAddresses(ServletRequest request) { + List forwardedAddresses = null; + + if (request instanceof HttpServletRequest) { + HttpServletRequest httpRequest = (HttpServletRequest) request; + String xForwardedFor = httpRequest.getHeader("X-Forwarded-For"); + + if (xForwardedFor != null) { + forwardedAddresses = Arrays.asList(xForwardedFor.split(",")); + } + } + + return forwardedAddresses; + } + + private void sendForbidden(HttpServletResponse res) { + sendErrorCode(res, 403); + } + + private void sendErrorCode(HttpServletResponse res, int code) { + try { + res.sendError(code); + } catch (IOException e) { + LOG.error("Error while redirecting: ", e); + } + } + + private String getTopologyName(String requestUrl) { + if (requestUrl == null) { + return null; + } + + String url = requestUrl.trim(); + String[] tokens = url.split("/"); + + if (tokens.length > 2) { + return tokens[2]; + } else { + return null; + } + } - private static final Logger LOG = LoggerFactory.getLogger(RangerPDPKnoxFilter.class); - - private static final Logger PERF_KNOXAUTH_REQUEST_LOG = RangerPerfTracer.getPerfLogger("knoxauth.request"); - - private static final String KNOX_GATEWAY_JASS_CONFIG_SECTION = "com.sun.security.jgss.initiate"; - - private String resourceRole = null; - private static volatile KnoxRangerPlugin plugin = null; - - @Override - public void init(FilterConfig filterConfig) throws ServletException { - resourceRole = getInitParameter(filterConfig, "resource.role"); - - KnoxRangerPlugin me = plugin; - - if(me == null) { - synchronized (RangerPDPKnoxFilter.class) { - me = plugin; - - if(me == null) { - try { - MiscUtil.setUGIFromJAASConfig(KNOX_GATEWAY_JASS_CONFIG_SECTION); - LOG.info("LoginUser=" + MiscUtil.getUGILoginUser()); - } catch (Throwable t) { - LOG.error("Error while setting UGI for Knox Plugin...", t); - } - - LOG.info("Creating KnoxRangerPlugin"); - plugin = new KnoxRangerPlugin(); - plugin.init(); - } - } - } - } - - private String getInitParameter(FilterConfig filterConfig, String paramName) { - return filterConfig.getInitParameter(paramName.toLowerCase()); - } - - public void destroy() { - } - - public void doFilter(ServletRequest request, ServletResponse response, - FilterChain chain) throws IOException, ServletException { - - String sourceUrl = (String) request - .getAttribute(AbstractGatewayFilter.SOURCE_REQUEST_CONTEXT_URL_ATTRIBUTE_NAME); - String topologyName = getTopologyName(sourceUrl); - String serviceName = getServiceName(); - - RangerPerfTracer perf = null; - - if(RangerPerfTracer.isPerfTraceEnabled(PERF_KNOXAUTH_REQUEST_LOG)) { - perf = RangerPerfTracer.getPerfTracer(PERF_KNOXAUTH_REQUEST_LOG, "RangerPDPKnoxFilter.doFilter(url=" + sourceUrl + ", topologyName=" + topologyName + ")"); - } - - Subject subject = Subject.getSubject(AccessController.getContext()); - - Set primaryPrincipals = subject.getPrincipals( - PrimaryPrincipal.class); - String primaryUser = null; - if (primaryPrincipals != null && primaryPrincipals.size() > 0) { - primaryUser = primaryPrincipals.stream().findFirst().get().getName(); - } - - String impersonatedUser = null; - Set impersonations = subject.getPrincipals( - ImpersonatedPrincipal.class); - if (impersonations != null && impersonations.size() > 0) { - impersonatedUser = impersonations.stream().findFirst().get().getName(); - } - - String user = (impersonatedUser != null) ? impersonatedUser - : primaryUser; - if (LOG.isDebugEnabled()) { - LOG.debug("Checking access primaryUser: " + primaryUser + ", impersonatedUser: " - + impersonatedUser + ", effectiveUser: " + user); - } - - Set groupObjects = subject.getPrincipals(GroupPrincipal.class); - Set groups = new HashSet(); - for (GroupPrincipal obj : groupObjects) { - groups.add(obj.getName()); - } - - String clientIp = request.getRemoteAddr(); - List forwardedAddresses = getForwardedAddresses(request); - - if (LOG.isDebugEnabled()) { - LOG.debug("Checking access primaryUser: " + primaryUser - + ", impersonatedUser: " + impersonatedUser - + ", effectiveUser: " + user + ", groups: " + groups - + ", clientIp: " + clientIp + ", remoteIp: " + clientIp + ", forwardedAddresses: " + forwardedAddresses); - } - - RangerAccessRequest accessRequest = new RequestBuilder() - .service(serviceName) - .topology(topologyName) - .user(user) - .groups(groups) - .clientIp(clientIp) - .remoteIp(clientIp) - .forwardedAddresses(forwardedAddresses) - .build(); - - boolean accessAllowed = false; - - if (plugin != null) { - RangerAccessResult result = plugin.isAccessAllowed(accessRequest); - - accessAllowed = result != null && result.getIsAllowed(); - } - - if (LOG.isDebugEnabled()) { - LOG.debug("Access allowed: " + accessAllowed); - } - - RangerPerfTracer.log(perf); - - if (accessAllowed) { - chain.doFilter(request, response); - } else { - sendForbidden((HttpServletResponse) response); - } - } - - private List getForwardedAddresses(ServletRequest request) { - List forwardedAddresses = null; - if (request instanceof HttpServletRequest) { - HttpServletRequest httpRequest = (HttpServletRequest) request; - String xForwardedFor = httpRequest.getHeader("X-Forwarded-For"); - if(xForwardedFor != null) { - forwardedAddresses = Arrays.asList(xForwardedFor.split(",")); - } - } - return forwardedAddresses; - } - - private void sendForbidden(HttpServletResponse res) { - sendErrorCode(res, 403); - } - - private void sendErrorCode(HttpServletResponse res, int code) { - try { - res.sendError(code); - } catch (IOException e) { - LOG.error("Error while redirecting:", e); - } - } - - private String getTopologyName(String requestUrl) { - if (requestUrl == null) { - return null; - } - String url = requestUrl.trim(); - String[] tokens = url.split("/"); - if (tokens.length > 2) { - return tokens[2]; - } else { - return null; - } - } - - private String getServiceName() { - return resourceRole; - } + private String getServiceName() { + return resourceRole; + } } diff --git a/knox-agent/src/main/java/org/apache/ranger/services/knox/RangerServiceKnox.java b/knox-agent/src/main/java/org/apache/ranger/services/knox/RangerServiceKnox.java index 2a0c6a5107..05e4855341 100644 --- a/knox-agent/src/main/java/org/apache/ranger/services/knox/RangerServiceKnox.java +++ b/knox-agent/src/main/java/org/apache/ranger/services/knox/RangerServiceKnox.java @@ -18,104 +18,103 @@ */ package org.apache.ranger.services.knox; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - +import org.apache.commons.lang.StringUtils; import org.apache.ranger.plugin.model.RangerPolicy; -import org.apache.ranger.plugin.model.RangerService; -import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItem; import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemAccess; +import org.apache.ranger.plugin.model.RangerService; +import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.service.RangerBaseService; import org.apache.ranger.plugin.service.ResourceLookupContext; import org.apache.ranger.services.knox.client.KnoxResourceMgr; -import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + public class RangerServiceKnox extends RangerBaseService { + private static final Logger LOG = LoggerFactory.getLogger(RangerServiceKnox.class); + + public static final String ACCESS_TYPE_ALLOW = "allow"; + + public RangerServiceKnox() { + super(); + } + + @Override + public void init(RangerServiceDef serviceDef, RangerService service) { + super.init(serviceDef, service); + } + + @Override + public Map validateConfig() { + Map ret = new HashMap<>(); + String serviceName = getServiceName(); + + LOG.debug("==> RangerServiceKnox.validateConfig Service: ({})", serviceName); + + if (configs != null) { + try { + ret = KnoxResourceMgr.validateConfig(serviceName, configs); + } catch (Exception e) { + LOG.error("<== RangerServiceKnox.validateConfig Error:{}", String.valueOf(e)); + + throw e; + } + } + + LOG.debug("<== RangerServiceKnox.validateConfig Response : ({})", ret); + + return ret; + } + + @Override + public List lookupResource(ResourceLookupContext context) { + List ret = new ArrayList<>(); + String serviceName = getServiceName(); + Map configs = getConfigs(); + + LOG.debug("==> RangerServiceKnox.lookupResource Context: ({})", context); + + if (context != null) { + try { + ret = KnoxResourceMgr.getKnoxResources(serviceName, configs, context); + } catch (Exception e) { + LOG.error("<== RangerServiceKnox.lookupResource Error : {}", String.valueOf(e)); + + throw e; + } + } + + LOG.debug("<== RangerServiceKnox.lookupResource Response: ({})", ret); + + return ret; + } + + @Override + public List getDefaultRangerPolicies() throws Exception { + LOG.debug("==> RangerServiceKnox.getDefaultRangerPolicies()"); + + List ret = super.getDefaultRangerPolicies(); + + for (RangerPolicy defaultPolicy : ret) { + if (defaultPolicy.getName().contains("all") && StringUtils.isNotBlank(lookUpUser)) { + RangerPolicyItem policyItemForLookupUser = new RangerPolicyItem(); + + policyItemForLookupUser.setUsers(Collections.singletonList(lookUpUser)); + policyItemForLookupUser.setAccesses(Collections.singletonList(new RangerPolicyItemAccess(ACCESS_TYPE_ALLOW))); + policyItemForLookupUser.setDelegateAdmin(false); + + defaultPolicy.addPolicyItem(policyItemForLookupUser); + } + } - private static final Logger LOG = LoggerFactory.getLogger(RangerServiceKnox.class); - public static final String ACCESS_TYPE_ALLOW = "allow"; - - public RangerServiceKnox() { - super(); - } - - @Override - public void init(RangerServiceDef serviceDef, RangerService service) { - super.init(serviceDef, service); - } - - @Override - public Map validateConfig() throws Exception { - Map ret = new HashMap(); - String serviceName = getServiceName(); - if(LOG.isDebugEnabled()) { - LOG.debug("==> RangerServiceKnox.validateConfig Service: (" + serviceName + " )"); - } - if ( configs != null) { - try { - ret = KnoxResourceMgr.validateConfig(serviceName, configs); - } catch (Exception e) { - LOG.error("<== RangerServiceKnox.validateConfig Error:" + e); - throw e; - } - } - if(LOG.isDebugEnabled()) { - LOG.debug("<== RangerServiceKnox.validateConfig Response : (" + ret + " )"); - } - return ret; - } - - @Override - public List getDefaultRangerPolicies() throws Exception { - if (LOG.isDebugEnabled()) { - LOG.debug("==> RangerServiceKnox.getDefaultRangerPolicies()"); - } - - List ret = super.getDefaultRangerPolicies(); - for (RangerPolicy defaultPolicy : ret) { - if (defaultPolicy.getName().contains("all") && StringUtils.isNotBlank(lookUpUser)) { - RangerPolicyItem policyItemForLookupUser = new RangerPolicyItem(); - policyItemForLookupUser.setUsers(Collections.singletonList(lookUpUser)); - policyItemForLookupUser.setAccesses(Collections.singletonList(new RangerPolicyItemAccess(ACCESS_TYPE_ALLOW))); - policyItemForLookupUser.setDelegateAdmin(false); - defaultPolicy.addPolicyItem(policyItemForLookupUser); - } - } - - if (LOG.isDebugEnabled()) { - LOG.debug("<== RangerServiceKnox.getDefaultRangerPolicies()"); - } - return ret; - } - - @Override - public List lookupResource(ResourceLookupContext context) throws Exception { - - List ret = new ArrayList(); - String serviceName = getServiceName(); - Map configs = getConfigs(); - if(LOG.isDebugEnabled()) { - LOG.debug("==> RangerServiceKnox.lookupResource Context: (" + context + ")"); - } - if (context != null) { - try { - ret = KnoxResourceMgr.getKnoxResources(serviceName, configs, context); - - } catch (Exception e) { - LOG.error( "<== RangerServiceKnox.lookupResource Error : " + e); - throw e; - } - } - if(LOG.isDebugEnabled()) { - LOG.debug("<== RangerServiceKnox.lookupResource Response: (" + ret + ")"); - } - return ret; - } + LOG.debug("<== RangerServiceKnox.getDefaultRangerPolicies()"); + return ret; + } } diff --git a/knox-agent/src/main/java/org/apache/ranger/services/knox/client/KnoxClient.java b/knox-agent/src/main/java/org/apache/ranger/services/knox/client/KnoxClient.java index 19407c0ece..7e06de932a 100644 --- a/knox-agent/src/main/java/org/apache/ranger/services/knox/client/KnoxClient.java +++ b/knox-agent/src/main/java/org/apache/ranger/services/knox/client/KnoxClient.java @@ -6,9 +6,9 @@ * 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 - * + *

+ * 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. @@ -18,413 +18,406 @@ package org.apache.ranger.services.knox.client; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Callable; -import java.util.concurrent.TimeUnit; - +import com.fasterxml.jackson.databind.JsonNode; +import com.sun.jersey.api.client.Client; +import com.sun.jersey.api.client.ClientResponse; +import com.sun.jersey.api.client.WebResource; +import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter; import org.apache.ranger.plugin.client.BaseClient; import org.apache.ranger.plugin.client.HadoopException; import org.apache.ranger.plugin.util.JsonUtilsV2; import org.apache.ranger.plugin.util.PasswordUtils; -import com.fasterxml.jackson.databind.JsonNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.sun.jersey.api.client.Client; -import com.sun.jersey.api.client.ClientResponse; -import com.sun.jersey.api.client.WebResource; -import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; public class KnoxClient { + private static final Logger LOG = LoggerFactory.getLogger(KnoxClient.class); + + private static final String EXPECTED_MIME_TYPE = "application/json"; + private static final String ERROR_MSG = " You can still save the repository and start creating policies, but you would not be able to use autocomplete for resource names. Check ranger_admin.log for more info."; - private static final String EXPECTED_MIME_TYPE = "application/json"; - private static final Logger LOG = LoggerFactory.getLogger(KnoxClient.class); + private final String knoxUrl; + private final String userName; + private final String password; - private String knoxUrl; - private String userName; - private String password; - - /* + /* Sample curl calls to Knox to discover topologies - curl -ivk -u : https://localhost:8443/gateway/admin/api/v1/topologies - curl -ivk -u : https://localhost:8443/gateway/admin/api/v1/topologies/admin - */ - - public KnoxClient(String knoxUrl, String userName, String password) { - LOG.debug("Constructed KnoxClient with knoxUrl: " + knoxUrl + - ", userName: " + userName); - this.knoxUrl = knoxUrl; - this.userName = userName; - this.password = password; - } - - public List getTopologyList(String topologyNameMatching,List knoxTopologyList) { - - // sample URI: https://hdp.example.com:8443/gateway/admin/api/v1/topologies - LOG.debug("Getting Knox topology list for topologyNameMatching : " + - topologyNameMatching); - List topologyList = new ArrayList(); - String errMsg = " You can still save the repository and start creating " - + "policies, but you would not be able to use autocomplete for " - + "resource names. Check ranger_admin.log for more info."; - if (topologyNameMatching == null || topologyNameMatching.trim().isEmpty()) { - topologyNameMatching = ""; - } - String decryptedPwd=null; - try { - decryptedPwd=PasswordUtils.decryptPassword(password); - } catch(Exception ex) { - LOG.info("Password decryption failed; trying knox connection with received password string"); - decryptedPwd=null; - } finally { - if (decryptedPwd==null) { - decryptedPwd=password; - } - } - try { - - Client client = null; - ClientResponse response = null; - - try { - client = Client.create(); - - client.addFilter(new HTTPBasicAuthFilter(userName, decryptedPwd)); - WebResource webResource = client.resource(knoxUrl); - response = webResource.accept(EXPECTED_MIME_TYPE) - .get(ClientResponse.class); - LOG.debug("Knox topology list response: " + response); - if (response != null) { - - if (response.getStatus() == 200) { - String jsonString = response.getEntity(String.class); - LOG.debug("Knox topology list response JSON string: "+ jsonString); - - JsonNode rootNode = JsonUtilsV2.getMapper().readTree(jsonString); - JsonNode topologyNode = rootNode.findValue("topology"); - if (topologyNode == null) { - return topologyList; - } - Iterator elements = topologyNode.elements(); - while (elements.hasNext()) { - JsonNode element = elements.next(); - JsonNode nameElement = element.get("name"); - if (nameElement != null) { - String topologyName = nameElement.asText(); - LOG.debug("Found Knox topologyName: " + topologyName); - if (knoxTopologyList != null && topologyName != null && knoxTopologyList.contains(topologyNameMatching)) { - continue; - } - if (topologyName != null && ( "*".equals(topologyNameMatching) || topologyName.startsWith(topologyNameMatching))) { - topologyList.add(topologyName); - } - } - - } - } else { - LOG.error("Got invalid REST response from: " + knoxUrl + ", responseStatus: " + response.getStatus()); - } - - } else { - String msgDesc = "Unable to get a valid response for " - + "getTopologyList() call for KnoxUrl : [" + knoxUrl - + "] - got null response."; - LOG.error(msgDesc); - HadoopException hdpException = new HadoopException(msgDesc); - hdpException.generateResponseDataMap(false, msgDesc, - msgDesc + errMsg, null, null); - throw hdpException; - } - - } finally { - if (response != null) { - response.close(); - } - if (client != null) { - client.destroy(); - } - } - } catch (HadoopException he) { - throw he; - } catch (Throwable t) { - String msgDesc = "Exception on REST call to KnoxUrl : " + knoxUrl + "."; - HadoopException hdpException = new HadoopException(msgDesc, t); - LOG.error(msgDesc, t); - - hdpException.generateResponseDataMap(false, - BaseClient.getMessage(t), msgDesc + errMsg, null, null); - throw hdpException; - } - if (LOG.isDebugEnabled()) { - LOG.debug("<== KnoxClient.getTopologyList() Topology Matching: " + topologyNameMatching + " Result : " + topologyList.toString()); - } - return topologyList; - } - - - public List getServiceList(List knoxTopologyList, String serviceNameMatching, List knoxServiceList) { - - // sample URI: .../admin/api/v1/topologies/ - if (LOG.isDebugEnabled()) { - LOG.debug("==> KnoxClient.getServiceList() Service Name: " + serviceNameMatching ); - } - List serviceList = new ArrayList(); - String errMsg = " You can still save the repository and start creating " - + "policies, but you would not be able to use autocomplete for " - + "resource names. Check ranger_admin.log for more info."; - if (serviceNameMatching == null || serviceNameMatching.trim().isEmpty()) { - serviceNameMatching = ""; - } - String decryptedPwd=null; - try { - decryptedPwd=PasswordUtils.decryptPassword(password); - } catch(Exception ex) { - LOG.info("Password decryption failed; trying knox connection with received password string"); - decryptedPwd=null; - } finally { - if (decryptedPwd==null) { - decryptedPwd=password; - } - } - try { - - Client client = null; - ClientResponse response = null; - - try { - client = Client.create(); - - client.addFilter(new HTTPBasicAuthFilter(userName, decryptedPwd)); - - for (String topologyName : knoxTopologyList) { - - WebResource webResource = client.resource(knoxUrl + "/" + topologyName); - - response = webResource.accept(EXPECTED_MIME_TYPE) - .get(ClientResponse.class); - LOG.debug("Knox service lookup response: " + response); - if (response != null) { - - if (response.getStatus() == 200) { - String jsonString = response.getEntity(String.class); - LOG.debug("Knox service lookup response JSON string: " + jsonString); - - JsonNode rootNode = JsonUtilsV2.getMapper().readTree(jsonString); - JsonNode topologyNode = rootNode.findValue("topology"); - if (topologyNode != null) { - JsonNode servicesNode = topologyNode.get("service"); - if (servicesNode != null) { - Iterator services = servicesNode.elements(); - while (services.hasNext()) { - JsonNode service = services.next(); - JsonNode serviceElement = service.get("role"); - if (serviceElement != null) { - String serviceName = serviceElement.asText(); - LOG.debug("Knox serviceName: " + serviceName); - if (serviceName == null || (knoxServiceList != null && knoxServiceList.contains(serviceName))){ - continue; - } - if (serviceName.startsWith(serviceNameMatching) || "*".equals(serviceNameMatching)) { - serviceList.add(serviceName); - } - } - } - } - } - } else { - LOG.error("Got invalid REST response from: " + knoxUrl + ", responsStatus: " + response.getStatus()); - } - - } else { - String msgDesc = "Unable to get a valid response for " - + "getServiceList() call for KnoxUrl : [" + knoxUrl - + "] - got null response."; - LOG.error(msgDesc); - HadoopException hdpException = new HadoopException(msgDesc); - hdpException.generateResponseDataMap(false, msgDesc, - msgDesc + errMsg, null, null); - throw hdpException; - } - } - } finally{ - if (response != null) { - response.close(); - } - if (client != null) { - client.destroy(); - } - } - } catch (HadoopException he) { - throw he; - } catch (Throwable t) { - String msgDesc = "Exception on REST call to KnoxUrl : " + knoxUrl + "."; - HadoopException hdpException = new HadoopException(msgDesc, t); - LOG.error(msgDesc, t); - - hdpException.generateResponseDataMap(false, - BaseClient.getMessage(t), msgDesc + errMsg, null, null); - throw hdpException; - } - return serviceList; - } - - public static void main(String[] args) { - - KnoxClient knoxClient = null; - - if (args.length != 3) { - System.err.println("USAGE: java " + KnoxClient.class.getName() - + " knoxUrl userName password [sslConfigFileName]"); - System.exit(1); - } - - knoxClient = new KnoxClient(args[0], args[1], args[2]); - List topologyList = knoxClient.getTopologyList("",null); - if ((topologyList == null) || topologyList.isEmpty()) { - System.out.println("No knox topologies found"); - } else { - List serviceList = knoxClient.getServiceList(topologyList,"*",null); - if ((serviceList == null) || serviceList.isEmpty()) { - System.out.println("No services found for knox topology: "); - } else { - for (String service : serviceList) { - System.out.println(" Found service for topology: " + service ); - } - } - } - } - - public static Map connectionTest(String serviceName, - Map configs) { - - String errMsg = " You can still save the repository and start creating " - + "policies, but you would not be able to use autocomplete for " - + "resource names. Check ranger_admin.log for more info."; - boolean connectivityStatus = false; - Map responseData = new HashMap(); - - KnoxClient knoxClient = getKnoxClient(serviceName, configs); - List strList = getKnoxResources(knoxClient, "", null,null,null); - - if (strList != null && (strList.size() != 0)) { - connectivityStatus = true; - } - - if (connectivityStatus) { - String successMsg = "ConnectionTest Successful"; - BaseClient.generateResponseDataMap(connectivityStatus, successMsg, successMsg, - null, null, responseData); - } else { - String failureMsg = "Unable to retrieve any topologies/services using given parameters."; - BaseClient.generateResponseDataMap(connectivityStatus, failureMsg, failureMsg + errMsg, - null, null, responseData); - } - - return responseData; - } - - public static KnoxClient getKnoxClient(String serviceName, - Map configs) { - KnoxClient knoxClient = null; - if(LOG.isDebugEnabled()){ - LOG.debug("Getting knoxClient for ServiceName: " + serviceName); - LOG.debug("configMap: " + configs); - } - String errMsg = " You can still save the repository and start creating " - + "policies, but you would not be able to use autocomplete for " - + "resource names. Check ranger_admin.log for more info."; - if ( configs != null && !configs.isEmpty()) { - String knoxUrl = configs.get("knox.url"); - String knoxAdminUser = configs.get("username"); - String knoxAdminPassword = configs.get("password"); - knoxClient = new KnoxClient(knoxUrl, knoxAdminUser, - knoxAdminPassword); - } else { - String msgDesc = "Could not connect as Connection ConfigMap is empty."; - LOG.error(msgDesc); - HadoopException hdpException = new HadoopException(msgDesc); - hdpException.generateResponseDataMap(false, msgDesc, msgDesc + errMsg, null, - null); - throw hdpException; - } - return knoxClient; - } - - public static List getKnoxResources(final KnoxClient knoxClient, - String topologyName, String serviceName, List knoxTopologyList, List knoxServiceList) { - - if (LOG.isDebugEnabled() ) { - LOG.debug("==> KnoxClient.getKnoxResource " + "topology: " + topologyName + "Service Name: " + serviceName); - } - - List resultList = new ArrayList(); - String errMsg = " You can still save the repository and start creating " - + "policies, but you would not be able to use autocomplete for " - + "resource names. Check ranger_admin.log for more info."; - - try { - if (knoxClient == null) { - // LOG.error("Unable to get knox resources: knoxClient is null"); - // return new ArrayList(); - String msgDesc = "Unable to get knox resources: knoxClient is null."; - LOG.error(msgDesc); - HadoopException hdpException = new HadoopException(msgDesc); - hdpException.generateResponseDataMap(false, msgDesc, msgDesc + errMsg, - null, null); - throw hdpException; - } - - final Callable> callableObj; - if (serviceName != null) { - final String finalServiceNameMatching = serviceName.trim(); - final List finalknoxServiceList = knoxServiceList; - final List finalTopologyList = knoxTopologyList; - callableObj = new Callable>() { - @Override - public List call() { - return knoxClient.getServiceList(finalTopologyList, - finalServiceNameMatching,finalknoxServiceList); - } - }; - - } else { - final String finalTopologyNameMatching = (topologyName == null) ? "" - : topologyName.trim(); - final List finalknoxTopologyList = knoxTopologyList; - callableObj = new Callable>() { - @Override - public List call() { - return knoxClient - .getTopologyList(finalTopologyNameMatching,finalknoxTopologyList); - } - }; - } - resultList = timedTask(callableObj, 5, TimeUnit.SECONDS); - - } catch (HadoopException he) { - throw he; - } catch (Exception e) { - String msgDesc = "Unable to get knox resources."; - LOG.error(msgDesc, e); - HadoopException hdpException = new HadoopException(msgDesc); - - hdpException.generateResponseDataMap(false, - BaseClient.getMessage(e), msgDesc + errMsg, null, null); - throw hdpException; - } - if (LOG.isDebugEnabled()) { - LOG.debug("<== KnoxClient.getKnoxResources() Result : "+ resultList ); - } - return resultList; - } - - public static T timedTask(Callable callableObj, long timeout, - TimeUnit timeUnit) throws Exception { - return callableObj.call(); - } + curl -ivk -u : https://localhost:8443/gateway/admin/api/v1/topologies + curl -ivk -u : https://localhost:8443/gateway/admin/api/v1/topologies/admin + */ + + public KnoxClient(String knoxUrl, String userName, String password) { + LOG.debug("Constructed KnoxClient with knoxUrl: {}, userName: {}", knoxUrl, userName); + + this.knoxUrl = knoxUrl; + this.userName = userName; + this.password = password; + } + + public static void main(String[] args) { + if (args.length != 3) { + System.err.println("USAGE: java " + KnoxClient.class.getName() + " knoxUrl userName password [sslConfigFileName]"); + + System.exit(1); + } + + KnoxClient knoxClient = new KnoxClient(args[0], args[1], args[2]); + List topologyList = knoxClient.getTopologyList("", null); + + if ((topologyList == null) || topologyList.isEmpty()) { + System.out.println("No knox topologies found"); + } else { + List serviceList = knoxClient.getServiceList(topologyList, "*", null); + + if ((serviceList == null) || serviceList.isEmpty()) { + System.out.println("No services found for knox topology: "); + } else { + for (String service : serviceList) { + System.out.println("Found service for topology: " + service); + } + } + } + } + + public static Map connectionTest(String serviceName, Map configs) { + boolean connectivityStatus = false; + Map responseData = new HashMap<>(); + KnoxClient knoxClient = getKnoxClient(serviceName, configs); + List strList = getKnoxResources(knoxClient, "", null, null, null); + + if (strList != null && !strList.isEmpty()) { + connectivityStatus = true; + } + + if (connectivityStatus) { + String successMsg = "ConnectionTest Successful"; + + BaseClient.generateResponseDataMap(connectivityStatus, successMsg, successMsg, null, null, responseData); + } else { + String failureMsg = "Unable to retrieve any topologies/services using given parameters."; + + BaseClient.generateResponseDataMap(connectivityStatus, failureMsg, failureMsg + ERROR_MSG, null, null, responseData); + } + + return responseData; + } + + public static KnoxClient getKnoxClient(String serviceName, Map configs) { + LOG.debug("Getting knoxClient for ServiceName: {}", serviceName); + LOG.debug("configMap: {}", configs); + + KnoxClient knoxClient; + + if (configs != null && !configs.isEmpty()) { + String knoxUrl = configs.get("knox.url"); + String knoxAdminUser = configs.get("username"); + String knoxAdminPassword = configs.get("password"); + + knoxClient = new KnoxClient(knoxUrl, knoxAdminUser, knoxAdminPassword); + } else { + String msgDesc = "Could not connect as Connection ConfigMap is empty."; + + LOG.error(msgDesc); + + HadoopException hdpException = new HadoopException(msgDesc); + + hdpException.generateResponseDataMap(false, msgDesc, msgDesc + ERROR_MSG, null, null); + + throw hdpException; + } + + return knoxClient; + } + + public static List getKnoxResources(final KnoxClient knoxClient, String topologyName, String serviceName, List knoxTopologyList, List knoxServiceList) { + LOG.debug("==> KnoxClient.getKnoxResource topology: {}Service Name: {}", topologyName, serviceName); + + List resultList; + + try { + if (knoxClient == null) { + // LOG.error("Unable to get knox resources: knoxClient is null"); + // return new ArrayList(); + String msgDesc = "Unable to get knox resources: knoxClient is null."; + + LOG.error(msgDesc); + + HadoopException hdpException = new HadoopException(msgDesc); + + hdpException.generateResponseDataMap(false, msgDesc, msgDesc + ERROR_MSG, null, null); + + throw hdpException; + } + + final Callable> callableObj; + + if (serviceName != null) { + final String finalServiceNameMatching = serviceName.trim(); + final List finalknoxServiceList = knoxServiceList; + final List finalTopologyList = knoxTopologyList; + + callableObj = () -> knoxClient.getServiceList(finalTopologyList, finalServiceNameMatching, finalknoxServiceList); + } else { + final String finalTopologyNameMatching = (topologyName == null) ? "" : topologyName.trim(); + final List finalknoxTopologyList = knoxTopologyList; + + callableObj = () -> knoxClient.getTopologyList(finalTopologyNameMatching, finalknoxTopologyList); + } + + resultList = timedTask(callableObj, 5, TimeUnit.SECONDS); + } catch (HadoopException he) { + throw he; + } catch (Exception e) { + String msgDesc = "Unable to get knox resources."; + + LOG.error(msgDesc, e); + + HadoopException hdpException = new HadoopException(msgDesc); + + hdpException.generateResponseDataMap(false, BaseClient.getMessage(e), msgDesc + ERROR_MSG, null, null); + + throw hdpException; + } + + LOG.debug("<== KnoxClient.getKnoxResources() Result : {}", resultList); + + return resultList; + } + + public static T timedTask(Callable callableObj, long timeout, TimeUnit timeUnit) throws Exception { + return callableObj.call(); + } + + public List getTopologyList(String topologyNameMatching, List knoxTopologyList) { + // sample URI: https://hdp.example.com:8443/gateway/admin/api/v1/topologies + LOG.debug("Getting Knox topology list for topologyNameMatching : {}", topologyNameMatching); + + List topologyList = new ArrayList<>(); + + if (topologyNameMatching == null || topologyNameMatching.trim().isEmpty()) { + topologyNameMatching = ""; + } + + String decryptedPwd = null; + + try { + decryptedPwd = PasswordUtils.decryptPassword(password); + } catch (Exception ex) { + LOG.info("Password decryption failed; trying knox connection with received password string"); + } finally { + if (decryptedPwd == null) { + decryptedPwd = password; + } + } + + try { + Client client = null; + ClientResponse response = null; + + try { + client = Client.create(); + + client.addFilter(new HTTPBasicAuthFilter(userName, decryptedPwd)); + + WebResource webResource = client.resource(knoxUrl); + + response = webResource.accept(EXPECTED_MIME_TYPE).get(ClientResponse.class); + + LOG.debug("Knox topology list response: {}", response); + + if (response != null) { + if (response.getStatus() == 200) { + String jsonString = response.getEntity(String.class); + + LOG.debug("Knox topology list response JSON string: {}", jsonString); + + JsonNode rootNode = JsonUtilsV2.getMapper().readTree(jsonString); + JsonNode topologyNode = rootNode.findValue("topology"); + + if (topologyNode == null) { + return topologyList; + } + + Iterator elements = topologyNode.elements(); + + while (elements.hasNext()) { + JsonNode element = elements.next(); + JsonNode nameElement = element.get("name"); + + if (nameElement != null) { + String topologyName = nameElement.asText(); + + LOG.debug("Found Knox topologyName: {}", topologyName); + + if (knoxTopologyList != null && topologyName != null && knoxTopologyList.contains(topologyNameMatching)) { + continue; + } + + if (topologyName != null && ("*".equals(topologyNameMatching) || topologyName.startsWith(topologyNameMatching))) { + topologyList.add(topologyName); + } + } + } + } else { + LOG.error("Got invalid REST response from: {}, responseStatus: {}", knoxUrl, response.getStatus()); + } + } else { + String msgDesc = "Unable to get a valid response for getTopologyList() call for KnoxUrl : [" + knoxUrl + "] - got null response."; + + LOG.error(msgDesc); + + HadoopException hdpException = new HadoopException(msgDesc); + + hdpException.generateResponseDataMap(false, msgDesc, msgDesc + ERROR_MSG, null, null); + + throw hdpException; + } + } finally { + if (response != null) { + response.close(); + } + + if (client != null) { + client.destroy(); + } + } + } catch (HadoopException he) { + throw he; + } catch (Throwable t) { + String msgDesc = "Exception on REST call to KnoxUrl : " + knoxUrl + "."; + + HadoopException hdpException = new HadoopException(msgDesc, t); + + LOG.error(msgDesc, t); + + hdpException.generateResponseDataMap(false, BaseClient.getMessage(t), msgDesc + ERROR_MSG, null, null); + + throw hdpException; + } + + LOG.debug("<== KnoxClient.getTopologyList() Topology Matching: {} Result : {}", topologyNameMatching, topologyList); + + return topologyList; + } + + public List getServiceList(List knoxTopologyList, String serviceNameMatching, List knoxServiceList) { + // sample URI: .../admin/api/v1/topologies/ + LOG.debug("==> KnoxClient.getServiceList() Service Name: {}", serviceNameMatching); + + List serviceList = new ArrayList<>(); + + if (serviceNameMatching == null || serviceNameMatching.trim().isEmpty()) { + serviceNameMatching = ""; + } + + String decryptedPwd = null; + + try { + decryptedPwd = PasswordUtils.decryptPassword(password); + } catch (Exception ex) { + LOG.info("Password decryption failed; trying knox connection with received password string"); + } finally { + if (decryptedPwd == null) { + decryptedPwd = password; + } + } + + try { + Client client = null; + ClientResponse response = null; + + try { + client = Client.create(); + + client.addFilter(new HTTPBasicAuthFilter(userName, decryptedPwd)); + + for (String topologyName : knoxTopologyList) { + WebResource webResource = client.resource(knoxUrl + "/" + topologyName); + + response = webResource.accept(EXPECTED_MIME_TYPE).get(ClientResponse.class); + + LOG.debug("Knox service lookup response: {}", response); + + if (response != null) { + if (response.getStatus() == 200) { + String jsonString = response.getEntity(String.class); + + LOG.debug("Knox service lookup response JSON string: {}", jsonString); + + JsonNode rootNode = JsonUtilsV2.getMapper().readTree(jsonString); + JsonNode topologyNode = rootNode.findValue("topology"); + + if (topologyNode != null) { + JsonNode servicesNode = topologyNode.get("service"); + + if (servicesNode != null) { + Iterator services = servicesNode.elements(); + + while (services.hasNext()) { + JsonNode service = services.next(); + JsonNode serviceElement = service.get("role"); + + if (serviceElement != null) { + String serviceName = serviceElement.asText(); + + LOG.debug("Knox serviceName: {}", serviceName); + + if (serviceName == null || (knoxServiceList != null && knoxServiceList.contains(serviceName))) { + continue; + } + + if (serviceName.startsWith(serviceNameMatching) || "*".equals(serviceNameMatching)) { + serviceList.add(serviceName); + } + } + } + } + } + } else { + LOG.error("Got invalid REST response from: {}, responsStatus: {}", knoxUrl, response.getStatus()); + } + } else { + String msgDesc = "Unable to get a valid response for getServiceList() call for KnoxUrl : [" + knoxUrl + "] - got null response."; + + LOG.error(msgDesc); + + HadoopException hdpException = new HadoopException(msgDesc); + + hdpException.generateResponseDataMap(false, msgDesc, msgDesc + ERROR_MSG, null, null); + + throw hdpException; + } + } + } finally { + if (response != null) { + response.close(); + } + + if (client != null) { + client.destroy(); + } + } + } catch (HadoopException he) { + throw he; + } catch (Throwable t) { + String msgDesc = "Exception on REST call to KnoxUrl : " + knoxUrl + "."; + + HadoopException hdpException = new HadoopException(msgDesc, t); + + LOG.error(msgDesc, t); + + hdpException.generateResponseDataMap(false, BaseClient.getMessage(t), msgDesc + ERROR_MSG, null, null); + throw hdpException; + } + return serviceList; + } } diff --git a/knox-agent/src/main/java/org/apache/ranger/services/knox/client/KnoxConnectionMgr.java b/knox-agent/src/main/java/org/apache/ranger/services/knox/client/KnoxConnectionMgr.java index eb02397718..27901548d2 100644 --- a/knox-agent/src/main/java/org/apache/ranger/services/knox/client/KnoxConnectionMgr.java +++ b/knox-agent/src/main/java/org/apache/ranger/services/knox/client/KnoxConnectionMgr.java @@ -19,76 +19,76 @@ package org.apache.ranger.services.knox.client; -import java.util.Map; - import org.apache.ranger.plugin.model.RangerService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; - +import java.util.Map; public class KnoxConnectionMgr { + private static final Logger LOG = LoggerFactory.getLogger(KnoxConnectionMgr.class); + + public KnoxClient getKnoxClientbyService(RangerService service) { + LOG.debug("Getting knoxClient for ServiceName: {}", service); + + KnoxClient knoxClient = null; + + if (service != null) { + Map configs = service.getConfigs(); + + knoxClient = getKnoxClientByConfig(configs); + } + + return knoxClient; + } + + public KnoxClient getKnoxClientByConfig(final Map configs) { + KnoxClient knoxClient = null; + + if (configs == null) { + LOG.error("Connection Config is empty"); + } else { + String knoxUrl = configs.get("knox.url"); + String knoxAdminUser = configs.get("username"); + String knoxAdminPassword = configs.get("password"); + + knoxClient = new KnoxClient(knoxUrl, knoxAdminUser, knoxAdminPassword); + } + + return knoxClient; + } + + public KnoxClient getKnoxClient(String serviceName, Map configs) { + KnoxClient knoxClient = null; + + LOG.debug("Getting knoxClient for datasource: {} configMap: {}", serviceName, configs); + + if (configs == null) { + LOG.error("Connection ConfigMap is empty"); + } else { + String knoxUrl = configs.get("knox.url"); + String knoxAdminUser = configs.get("username"); + String knoxAdminPassword = configs.get("password"); + + knoxClient = new KnoxClient(knoxUrl, knoxAdminUser, knoxAdminPassword); + } + + return knoxClient; + } + + public KnoxClient getKnoxClient(final String knoxUrl, String knoxAdminUser, String knoxAdminPassword) { + KnoxClient knoxClient = null; + + if (knoxUrl == null || knoxUrl.isEmpty()) { + LOG.error("Can not create KnoxClient: knoxUrl is empty"); + } else if (knoxAdminUser == null || knoxAdminUser.isEmpty()) { + LOG.error("Can not create KnoxClient: knoxAdminUser is empty"); + } else if (knoxAdminPassword == null || knoxAdminPassword.isEmpty()) { + LOG.error("Can not create KnoxClient: knoxAdminPassword is empty"); + } else { + knoxClient = new KnoxClient(knoxUrl, knoxAdminUser, knoxAdminPassword); + } - private static final Logger LOG = LoggerFactory.getLogger(KnoxConnectionMgr.class); - - public KnoxClient getKnoxClientbyService(RangerService service) { - KnoxClient knoxClient = null; - Map configs = null; - - if(LOG.isDebugEnabled()) { - LOG.debug("Getting knoxClient for ServiceName: " + service.toString()); - } - - if (service != null) { - configs = service.getConfigs(); - knoxClient = getKnoxClientByConfig(configs); - } - return knoxClient; - } - - public KnoxClient getKnoxClientByConfig( final Map configs) { - KnoxClient knoxClient = null; - if (configs == null) { - LOG.error("Connection Config is empty"); - - } else { - - String knoxUrl = configs.get("knox.url"); - String knoxAdminUser = configs.get("username"); - String knoxAdminPassword = configs.get("password"); - knoxClient = new KnoxClient(knoxUrl, knoxAdminUser, knoxAdminPassword); - } - return knoxClient; - } - - public KnoxClient getKnoxClient(String serviceName, - Map configs) { - KnoxClient knoxClient = null; - LOG.debug("Getting knoxClient for datasource: " + serviceName + - "configMap: " + configs); - if (configs == null) { - LOG.error("Connection ConfigMap is empty"); - } else { - String knoxUrl = configs.get("knox.url"); - String knoxAdminUser = configs.get("username"); - String knoxAdminPassword = configs.get("password"); - knoxClient = new KnoxClient(knoxUrl, knoxAdminUser, knoxAdminPassword); - } - return knoxClient; - } - - - public KnoxClient getKnoxClient(final String knoxUrl, String knoxAdminUser, String knoxAdminPassword) { - KnoxClient knoxClient = null; - if (knoxUrl == null || knoxUrl.isEmpty()) { - LOG.error("Can not create KnoxClient: knoxUrl is empty"); - } else if (knoxAdminUser == null || knoxAdminUser.isEmpty()) { - LOG.error("Can not create KnoxClient: knoxAdminUser is empty"); - } else if (knoxAdminPassword == null || knoxAdminPassword.isEmpty()) { - LOG.error("Can not create KnoxClient: knoxAdminPassword is empty"); - } else { - knoxClient = new KnoxClient(knoxUrl, knoxAdminUser, knoxAdminPassword); - } - return knoxClient; - } + return knoxClient; + } } diff --git a/knox-agent/src/main/java/org/apache/ranger/services/knox/client/KnoxResourceMgr.java b/knox-agent/src/main/java/org/apache/ranger/services/knox/client/KnoxResourceMgr.java index 163fede87d..f3a8079ce5 100644 --- a/knox-agent/src/main/java/org/apache/ranger/services/knox/client/KnoxResourceMgr.java +++ b/knox-agent/src/main/java/org/apache/ranger/services/knox/client/KnoxResourceMgr.java @@ -19,93 +19,97 @@ package org.apache.ranger.services.knox.client; -import java.util.List; -import java.util.Map; - import org.apache.ranger.plugin.service.ResourceLookupContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.List; +import java.util.Map; public class KnoxResourceMgr { + private static final Logger LOG = LoggerFactory.getLogger(KnoxResourceMgr.class); + + private static final String TOPOLOGY = "topology"; + private static final String SERVICE = "service"; + + private KnoxResourceMgr() { + // to block instantiation + } + + public static Map validateConfig(String serviceName, Map configs) { + LOG.debug("==> KnoxResourceMgr.testConnection ServiceName: {} Configs{}", serviceName, configs); + + Map ret; + + try { + ret = KnoxClient.connectionTest(serviceName, configs); + } catch (Exception e) { + LOG.error("<== KnoxResourceMgr.connectionTest Error: {}", String.valueOf(e)); + + throw e; + } + + LOG.debug("<== KnoxResourceMgr.HdfsResourceMgr Result : {}", ret); + + return ret; + } + + public static List getKnoxResources(String serviceName, Map configs, ResourceLookupContext context) { + String userInput = context.getUserInput(); + String resource = context.getResourceName(); + Map> resourceMap = context.getResources(); + List resultList = null; + List knoxTopologyList = null; + List knoxServiceList = null; + String knoxTopologyName = null; + String knoxServiceName = null; + + if (userInput != null && resource != null) { + if (resourceMap != null && !resourceMap.isEmpty()) { + knoxTopologyList = resourceMap.get(TOPOLOGY); + knoxServiceList = resourceMap.get(SERVICE); + } + + switch (resource.trim().toLowerCase()) { + case TOPOLOGY: + knoxTopologyName = userInput; + break; + case SERVICE: + knoxServiceName = userInput; + break; + default: + break; + } + } + + String knoxUrl = configs.get("knox.url"); + String knoxAdminUser = configs.get("username"); + String knoxAdminPassword = configs.get("password"); + + if (knoxUrl == null || knoxUrl.isEmpty()) { + LOG.error("Unable to get knox resources: knoxUrl is empty"); + + return resultList; + } else if (knoxAdminUser == null || knoxAdminUser.isEmpty()) { + LOG.error("Unable to get knox resources: knoxAdminUser is empty"); + + return resultList; + } else if (knoxAdminPassword == null || knoxAdminPassword.isEmpty()) { + LOG.error("Unable to get knox resources: knoxAdminPassword is empty"); + + return resultList; + } + + LOG.debug("<== KnoxResourceMgr.getKnoxResources() knoxUrl: {} knoxAdminUser: {} topologyName: {} KnoxServiceName: {}", knoxUrl, knoxAdminUser, knoxTopologyName, knoxServiceName); + + final KnoxClient knoxClient = new KnoxConnectionMgr().getKnoxClient(knoxUrl, knoxAdminUser, knoxAdminPassword); + + if (knoxClient != null) { + synchronized (knoxClient) { + resultList = KnoxClient.getKnoxResources(knoxClient, knoxTopologyName, knoxServiceName, knoxTopologyList, knoxServiceList); + } + } - private static final Logger LOG = LoggerFactory.getLogger(KnoxResourceMgr.class); - - private static final String TOPOLOGY = "topology"; - private static final String SERVICE = "service"; - - public static Map validateConfig(String serviceName, Map configs) throws Exception { - Map ret = null; - if (LOG.isDebugEnabled()) { - LOG.debug("==> KnoxResourceMgr.testConnection ServiceName: "+ serviceName + "Configs" + configs ); - } - try { - ret = KnoxClient.connectionTest(serviceName, configs); - } catch (Exception e) { - LOG.error("<== KnoxResourceMgr.connectionTest Error: " + e); - throw e; - } - - if(LOG.isDebugEnabled()) { - LOG.debug("<== KnoxResourceMgr.HdfsResourceMgr Result : "+ ret ); - } - return ret; - } - - public static List getKnoxResources(String serviceName, Map configs, ResourceLookupContext context) throws Exception { - - - String userInput = context.getUserInput(); - String resource = context.getResourceName(); - Map> resourceMap = context.getResources(); - List resultList = null; - List knoxTopologyList = null; - List knoxServiceList = null; - String knoxTopologyName = null; - String knoxServiceName = null; - - if ( userInput != null && resource != null) { - if ( resourceMap != null && !resourceMap.isEmpty() ) { - knoxTopologyList = resourceMap.get(TOPOLOGY); - knoxServiceList = resourceMap.get(SERVICE); - } - switch (resource.trim().toLowerCase()) { - case TOPOLOGY: - knoxTopologyName = userInput; - break; - case SERVICE: - knoxServiceName = userInput; - break; - default: - break; - } - } - - String knoxUrl = configs.get("knox.url"); - String knoxAdminUser = configs.get("username"); - String knoxAdminPassword = configs.get("password"); - - if (knoxUrl == null || knoxUrl.isEmpty()) { - LOG.error("Unable to get knox resources: knoxUrl is empty"); - return resultList; - } else if (knoxAdminUser == null || knoxAdminUser.isEmpty()) { - LOG.error("Unable to get knox resources: knoxAdminUser is empty"); - return resultList; - } else if (knoxAdminPassword == null || knoxAdminPassword.isEmpty()) { - LOG.error("Unable to get knox resources: knoxAdminPassword is empty"); - return resultList; - } - - if(LOG.isDebugEnabled()) { - LOG.debug("<== KnoxResourceMgr.getKnoxResources() knoxUrl: "+ knoxUrl + " knoxAdminUser: " + knoxAdminUser + " topologyName: " + knoxTopologyName + " KnoxServiceName: " + knoxServiceName); - } - - final KnoxClient knoxClient = new KnoxConnectionMgr().getKnoxClient(knoxUrl, knoxAdminUser, knoxAdminPassword); - if ( knoxClient != null) { - synchronized(knoxClient) { - resultList = KnoxClient.getKnoxResources(knoxClient, knoxTopologyName, knoxServiceName,knoxTopologyList,knoxServiceList); - } - } - return resultList; - } + return resultList; + } } diff --git a/knox-agent/src/test/java/org/apache/ranger/services/knox/KnoxRangerTest.java b/knox-agent/src/test/java/org/apache/ranger/services/knox/KnoxRangerTest.java index 764192172a..5b1063924c 100644 --- a/knox-agent/src/test/java/org/apache/ranger/services/knox/KnoxRangerTest.java +++ b/knox-agent/src/test/java/org/apache/ranger/services/knox/KnoxRangerTest.java @@ -6,9 +6,9 @@ * 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 - * + *

+ * 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. @@ -17,40 +17,39 @@ */ package org.apache.ranger.services.knox; -import static io.restassured.RestAssured.given; -import static org.hamcrest.CoreMatchers.is; - -import java.io.File; -import java.io.IOException; -import java.nio.file.FileSystems; -import java.nio.file.Path; - +import com.mycila.xmltool.XMLDoc; +import com.mycila.xmltool.XMLTag; +import io.restassured.http.ContentType; +import io.restassured.response.ValidatableResponse; import org.apache.commons.io.IOUtils; +import org.apache.http.HttpStatus; import org.apache.knox.gateway.GatewayTestConfig; import org.apache.knox.gateway.GatewayTestDriver; -import org.apache.http.HttpStatus; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; -import com.mycila.xmltool.XMLDoc; -import com.mycila.xmltool.XMLTag; +import java.io.File; +import java.io.IOException; +import java.nio.file.FileSystems; +import java.nio.file.Path; -import io.restassured.http.ContentType; -import io.restassured.response.ValidatableResponse; +import static io.restassured.RestAssured.given; +import static org.hamcrest.CoreMatchers.is; /** * Test Apache Knox secured by Apache Ranger. */ public class KnoxRangerTest { - - private static GatewayTestDriver driver = new GatewayTestDriver(); + private static final GatewayTestDriver driver = new GatewayTestDriver(); @BeforeClass public static void setupSuite() throws Exception { driver.setResourceBase(KnoxRangerTest.class); driver.setupLdap(0); + GatewayTestConfig config = new GatewayTestConfig(); + driver.setupService("WEBHDFS", "http://localhost:50070/webhdfs", "/cluster/webhdfs", true); driver.setupService("STORM", "http://localhost:8477", "/cluster/storm", true); driver.setupService("SOLR", "http://localhost:8983", "/cluster/solr", true); @@ -65,69 +64,6 @@ public static void cleanupSuite() throws Exception { driver.cleanup(); } - /** - * Creates a topology that is deployed to the gateway instance for the test suite. - * Note that this topology is shared by all of the test methods in this suite. - * @return A populated XML structure for a topology file. - */ - private static XMLTag createTopology() { - XMLTag xml = XMLDoc.newDocument( true ) - .addRoot( "topology" ) - .addTag( "gateway" ) - .addTag( "provider" ) - .addTag( "role" ).addText( "webappsec" ) - .addTag("name").addText("WebAppSec") - .addTag("enabled").addText("true") - .addTag( "param" ) - .addTag("name").addText("csrf.enabled") - .addTag("value").addText("true").gotoParent().gotoParent() - .addTag("provider") - .addTag("role").addText("authentication") - .addTag("name").addText("ShiroProvider") - .addTag("enabled").addText("true") - .addTag( "param" ) - .addTag("name").addText("main.ldapRealm") - .addTag("value").addText("org.apache.hadoop.gateway.shirorealm.KnoxLdapRealm").gotoParent() - .addTag( "param" ) - .addTag( "name" ).addText( "main.ldapRealm.userDnTemplate" ) - .addTag( "value" ).addText( "uid={0},ou=people,dc=hadoop,dc=apache,dc=org" ).gotoParent() - .addTag( "param" ) - .addTag( "name" ).addText( "main.ldapRealm.contextFactory.url" ) - .addTag( "value" ).addText(driver.getLdapUrl() ).gotoParent() - .addTag( "param" ) - .addTag( "name" ).addText( "main.ldapRealm.contextFactory.authenticationMechanism" ) - .addTag( "value" ).addText( "simple" ).gotoParent() - .addTag( "param" ) - .addTag( "name" ).addText( "urls./**" ) - .addTag( "value" ).addText( "authcBasic" ).gotoParent().gotoParent() - .addTag("provider") - .addTag("role").addText("identity-assertion") - .addTag("enabled").addText("true") - .addTag("name").addText("Default").gotoParent() - .addTag("provider") - .addTag( "role" ).addText( "authorization" ) - .addTag("name").addText("XASecurePDPKnox") - .addTag( "enabled" ).addText( "true" ) - .gotoRoot() - .addTag("service") - .addTag("role").addText("WEBHDFS") - .addTag("url").addText(driver.getRealUrl("WEBHDFS")).gotoParent() - .addTag("service") - .addTag("role").addText("STORM") - .addTag("url").addText(driver.getRealUrl("STORM")).gotoParent() - .addTag("service") - .addTag("role").addText("WEBHBASE") - .addTag("url").addText(driver.getRealUrl("WEBHBASE")).gotoParent() - .addTag("service") - .addTag("role").addText("KAFKA") - .addTag("url").addText(driver.getRealUrl("KAFKA")).gotoParent() - .addTag("service") - .addTag("role").addText("SOLR") - .addTag("url").addText(driver.getRealUrl("SOLR")).gotoParent() - .gotoRoot(); - return xml; - } - @Test public void testHDFSAllowed() throws IOException { makeWebHDFSInvocation(HttpStatus.SC_OK, "alice", "password"); @@ -159,12 +95,12 @@ public void testHBaseNotAllowed() throws Exception { } @Test - public void testKafkaAllowed() throws IOException { + public void testKafkaAllowed() { makeKafkaInvocation(HttpStatus.SC_OK, "alice", "password"); } @Test - public void testKafkaNotAllowed() throws IOException { + public void testKafkaNotAllowed() { makeKafkaInvocation(HttpStatus.SC_FORBIDDEN, "bob", "password"); } @@ -178,143 +114,208 @@ public void testSolrNotAllowed() throws Exception { makeSolrInvocation(HttpStatus.SC_FORBIDDEN, "bob", "password"); } - private void makeWebHDFSInvocation(int statusCode, String user, String password) throws IOException { + /** + * Creates a topology that is deployed to the gateway instance for the test suite. + * Note that this topology is shared by all of the test methods in this suite. + * + * @return A populated XML structure for a topology file. + */ + private static XMLTag createTopology() { + return XMLDoc.newDocument(true) + .addRoot("topology") + .addTag("gateway") + .addTag("provider") + .addTag("role").addText("webappsec") + .addTag("name").addText("WebAppSec") + .addTag("enabled").addText("true") + .addTag("param") + .addTag("name").addText("csrf.enabled") + .addTag("value").addText("true").gotoParent().gotoParent() + .addTag("provider") + .addTag("role").addText("authentication") + .addTag("name").addText("ShiroProvider") + .addTag("enabled").addText("true") + .addTag("param") + .addTag("name").addText("main.ldapRealm") + .addTag("value").addText("org.apache.hadoop.gateway.shirorealm.KnoxLdapRealm").gotoParent() + .addTag("param") + .addTag("name").addText("main.ldapRealm.userDnTemplate") + .addTag("value").addText("uid={0},ou=people,dc=hadoop,dc=apache,dc=org").gotoParent() + .addTag("param") + .addTag("name").addText("main.ldapRealm.contextFactory.url") + .addTag("value").addText(driver.getLdapUrl()).gotoParent() + .addTag("param") + .addTag("name").addText("main.ldapRealm.contextFactory.authenticationMechanism") + .addTag("value").addText("simple").gotoParent() + .addTag("param") + .addTag("name").addText("urls./**") + .addTag("value").addText("authcBasic").gotoParent().gotoParent() + .addTag("provider") + .addTag("role").addText("identity-assertion") + .addTag("enabled").addText("true") + .addTag("name").addText("Default").gotoParent() + .addTag("provider") + .addTag("role").addText("authorization") + .addTag("name").addText("XASecurePDPKnox") + .addTag("enabled").addText("true") + .gotoRoot() + .addTag("service") + .addTag("role").addText("WEBHDFS") + .addTag("url").addText(driver.getRealUrl("WEBHDFS")).gotoParent() + .addTag("service") + .addTag("role").addText("STORM") + .addTag("url").addText(driver.getRealUrl("STORM")).gotoParent() + .addTag("service") + .addTag("role").addText("WEBHBASE") + .addTag("url").addText(driver.getRealUrl("WEBHBASE")).gotoParent() + .addTag("service") + .addTag("role").addText("KAFKA") + .addTag("url").addText(driver.getRealUrl("KAFKA")).gotoParent() + .addTag("service") + .addTag("role").addText("SOLR") + .addTag("url").addText(driver.getRealUrl("SOLR")).gotoParent() + .gotoRoot(); + } + private void makeWebHDFSInvocation(int statusCode, String user, String password) throws IOException { String basedir = System.getProperty("basedir"); + if (basedir == null) { basedir = new File(".").getCanonicalPath(); } + Path path = FileSystems.getDefault().getPath(basedir, "/src/test/resources/webhdfs-liststatus-test.json"); driver.getMock("WEBHDFS") - .expect() - .method( "GET" ) - .pathInfo( "/v1/hdfstest" ) - .queryParam( "op", "LISTSTATUS" ) - .respond() - .status( HttpStatus.SC_OK ) - .content( IOUtils.toByteArray( path.toUri() ) ) - .contentType( "application/json" ); + .expect() + .method("GET") + .pathInfo("/v1/hdfstest") + .queryParam("op", "LISTSTATUS") + .respond() + .status(HttpStatus.SC_OK) + .content(IOUtils.toByteArray(path.toUri())) + .contentType("application/json"); ValidatableResponse response = given() - .log().all() - .auth().preemptive().basic( user, password ) - .header("X-XSRF-Header", "jksdhfkhdsf") - .queryParam( "op", "LISTSTATUS" ) - .when() - .get( driver.getUrl("WEBHDFS") + "/v1/hdfstest" ) - .then() - .statusCode(statusCode) - .log().body(); + .log().all() + .auth().preemptive().basic(user, password) + .header("X-XSRF-Header", "jksdhfkhdsf") + .queryParam("op", "LISTSTATUS") + .when() + .get(driver.getUrl("WEBHDFS") + "/v1/hdfstest") + .then() + .statusCode(statusCode) + .log().body(); if (statusCode == HttpStatus.SC_OK) { - response.body( "FileStatuses.FileStatus[0].pathSuffix", is ("dir") ); + response.body("FileStatuses.FileStatus[0].pathSuffix", is("dir")); } } private void makeStormUIInvocation(int statusCode, String user, String password) throws IOException { String basedir = System.getProperty("basedir"); + if (basedir == null) { basedir = new File(".").getCanonicalPath(); } + Path path = FileSystems.getDefault().getPath(basedir, "/src/test/resources/cluster-configuration.json"); driver.getMock("STORM") - .expect() - .method("GET") - .pathInfo("/api/v1/cluster/configuration") - .respond() - .status(HttpStatus.SC_OK) - .content(IOUtils.toByteArray( path.toUri() )) - .contentType("application/json"); + .expect() + .method("GET") + .pathInfo("/api/v1/cluster/configuration") + .respond() + .status(HttpStatus.SC_OK) + .content(IOUtils.toByteArray(path.toUri())) + .contentType("application/json"); given() - .auth().preemptive().basic(user, password) - .header("X-XSRF-Header", "jksdhfkhdsf") - .header("Accept", "application/json") - .when().get( driver.getUrl("STORM") + "/api/v1/cluster/configuration") - .then() - .log().all() - .statusCode(statusCode); - - } + .auth().preemptive().basic(user, password) + .header("X-XSRF-Header", "jksdhfkhdsf") + .header("Accept", "application/json") + .when().get(driver.getUrl("STORM") + "/api/v1/cluster/configuration") + .then() + .log().all() + .statusCode(statusCode); + } private void makeHBaseInvocation(int statusCode, String user, String password) throws IOException { String basedir = System.getProperty("basedir"); + if (basedir == null) { basedir = new File(".").getCanonicalPath(); } - Path path = FileSystems.getDefault().getPath(basedir, "/src/test/resources/webhbase-table-list.xml"); + Path path = FileSystems.getDefault().getPath(basedir, "/src/test/resources/webhbase-table-list.xml"); driver.getMock("WEBHBASE") - .expect() - .method( "GET" ) - .pathInfo( "/" ) - .header( "Accept", ContentType.XML.toString() ) - .respond() - .status( HttpStatus.SC_OK ) - .content( IOUtils.toByteArray( path.toUri() ) ) - .contentType( ContentType.XML.toString() ); + .expect() + .method("GET") + .pathInfo("/") + .header("Accept", ContentType.XML.toString()) + .respond() + .status(HttpStatus.SC_OK) + .content(IOUtils.toByteArray(path.toUri())) + .contentType(ContentType.XML.toString()); given() - .log().all() - .auth().preemptive().basic( user, password ) - .header("X-XSRF-Header", "jksdhfkhdsf") - .header( "Accept", ContentType.XML.toString() ) - .when().get( driver.getUrl("WEBHBASE") ) - .then() - .statusCode( statusCode ) - .log().body(); + .log().all() + .auth().preemptive().basic(user, password) + .header("X-XSRF-Header", "jksdhfkhdsf") + .header("Accept", ContentType.XML.toString()) + .when().get(driver.getUrl("WEBHBASE")) + .then() + .statusCode(statusCode) + .log().body(); } - private void makeKafkaInvocation(int statusCode, String user, String password) throws IOException { - + private void makeKafkaInvocation(int statusCode, String user, String password) { driver.getMock("KAFKA") - .expect() - .method( "GET" ) - .pathInfo( "/topics" ) - .respond() - .status( HttpStatus.SC_OK ); + .expect() + .method("GET") + .pathInfo("/topics") + .respond() + .status(HttpStatus.SC_OK); given() - .log().all() - .auth().preemptive().basic( user, password ) - .header("X-XSRF-Header", "jksdhfkhdsf") - .when() - .get( driver.getUrl("KAFKA") + "/topics" ) - .then() - .statusCode(statusCode) - .log().body(); - + .log().all() + .auth().preemptive().basic(user, password) + .header("X-XSRF-Header", "jksdhfkhdsf") + .when() + .get(driver.getUrl("KAFKA") + "/topics") + .then() + .statusCode(statusCode) + .log().body(); } private void makeSolrInvocation(int statusCode, String user, String password) throws IOException { String basedir = System.getProperty("basedir"); + if (basedir == null) { basedir = new File(".").getCanonicalPath(); } + Path path = FileSystems.getDefault().getPath(basedir, "/src/test/resources/query_response.xml"); driver.getMock("SOLR") - .expect() - .method("GET") - .pathInfo("/gettingstarted/select") - .queryParam("q", "author_s:William+Shakespeare") - .respond() - .status(HttpStatus.SC_OK) - .content(IOUtils.toByteArray( path.toUri() )) - .contentType("application/json"); + .expect() + .method("GET") + .pathInfo("/gettingstarted/select") + .queryParam("q", "author_s:William+Shakespeare") + .respond() + .status(HttpStatus.SC_OK) + .content(IOUtils.toByteArray(path.toUri())) + .contentType("application/json"); given() - .auth().preemptive().basic(user, password) - .header("X-XSRF-Header", "jksdhfkhdsf") - .header("Accept", "application/json") - .when().get( driver.getUrl("SOLR") - + "/gettingstarted/select?q=author_s:William+Shakespeare") - .then() - .log().all() - .statusCode(statusCode); - + .auth().preemptive().basic(user, password) + .header("X-XSRF-Header", "jksdhfkhdsf") + .header("Accept", "application/json") + .when().get(driver.getUrl("SOLR") + + "/gettingstarted/select?q=author_s:William+Shakespeare") + .then() + .log().all() + .statusCode(statusCode); } } diff --git a/knox-agent/src/test/java/org/apache/ranger/services/knox/RangerAdminClientImpl.java b/knox-agent/src/test/java/org/apache/ranger/services/knox/RangerAdminClientImpl.java index 0ab9205bef..fcf6a1a430 100644 --- a/knox-agent/src/test/java/org/apache/ranger/services/knox/RangerAdminClientImpl.java +++ b/knox-agent/src/test/java/org/apache/ranger/services/knox/RangerAdminClientImpl.java @@ -17,43 +17,40 @@ package org.apache.ranger.services.knox; -import java.io.File; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.util.List; - import org.apache.hadoop.thirdparty.com.google.common.base.Charsets; import org.apache.ranger.admin.client.AbstractRangerAdminClient; import org.apache.ranger.plugin.util.ServicePolicies; import org.apache.ranger.plugin.util.ServiceTags; +import java.io.File; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.util.List; /** * A test implementation of the RangerAdminClient interface that just reads policies in from a file and returns them */ public class RangerAdminClientImpl extends AbstractRangerAdminClient { - private final static String cacheFilename = "knox-policies.json"; + private static final String cacheFilename = "knox-policies.json"; public ServicePolicies getServicePoliciesIfUpdated(long lastKnownVersion, long lastActivationTimeInMillis) throws Exception { - String basedir = System.getProperty("basedir"); + if (basedir == null) { basedir = new File(".").getCanonicalPath(); } - java.nio.file.Path cachePath = FileSystems.getDefault().getPath(basedir, "/src/test/resources/" + cacheFilename); - byte[] cacheBytes = Files.readAllBytes(cachePath); + java.nio.file.Path cachePath = FileSystems.getDefault().getPath(basedir, "/src/test/resources/" + cacheFilename); + byte[] cacheBytes = Files.readAllBytes(cachePath); return gson.fromJson(new String(cacheBytes, Charsets.UTF_8), ServicePolicies.class); } - public ServiceTags getServiceTagsIfUpdated(long lastKnownVersion, long lastActivationTimeInMillis) throws Exception { + public ServiceTags getServiceTagsIfUpdated(long lastKnownVersion, long lastActivationTimeInMillis) { return null; } - public List getTagTypes(String tagTypePattern) throws Exception { + public List getTagTypes(String tagTypePattern) { return null; } - - -} \ No newline at end of file +}