This is an automated email from the ASF dual-hosted git repository. pradeep pushed a commit to branch RANGER-4076_master in repository https://gitbox.apache.org/repos/asf/ranger.git
commit c5e458d50f10f2db0e3cfedc753dc23ec3dd5363 Author: Pradeep AgrawaL <[email protected]> AuthorDate: Thu Feb 5 18:46:16 2026 +0530 RANGER-5475: add JWT support in RangerRESTClient --- .../audit/utils/RangerJSONAuditWriterTest.java | 28 ++++-- .../ranger/plugin/util/RangerRESTClient.java | 104 ++++++++++++++++++--- .../tagsync/sink/tagadmin/TagAdminRESTSink.java | 2 +- .../process/RangerUgSyncRESTClient.java | 2 +- 4 files changed, 112 insertions(+), 24 deletions(-) diff --git a/agents-audit/core/src/test/java/org/apache/ranger/audit/utils/RangerJSONAuditWriterTest.java b/agents-audit/core/src/test/java/org/apache/ranger/audit/utils/RangerJSONAuditWriterTest.java index 18b93c8ba..88046089b 100644 --- a/agents-audit/core/src/test/java/org/apache/ranger/audit/utils/RangerJSONAuditWriterTest.java +++ b/agents-audit/core/src/test/java/org/apache/ranger/audit/utils/RangerJSONAuditWriterTest.java @@ -73,7 +73,9 @@ public void verifyAppendToFileWhenEnabledWithConfig() throws Exception { assertTrue(jsonAuditWriter.logJSON(Collections.singleton("Last log file will be opened in append mode and this event will be written"))); assertTrue(jsonAuditWriter.logJSON(Collections.singleton("This event will also be written in append mode"))); - jsonAuditWriter.fileSystem.deleteOnExit(jsonAuditWriter.auditPath); + if (jsonAuditWriter.auditPath != null) { + jsonAuditWriter.fileSystem.deleteOnExit(jsonAuditWriter.auditPath); + } } @Test @@ -120,8 +122,12 @@ public void verifyFileRolloverWithAppend() throws Exception { assertNotEquals(auditPath1, jsonAuditWriter.auditPath); // cleanup - jsonAuditWriter.fileSystem.deleteOnExit(auditPath1); - jsonAuditWriter.fileSystem.deleteOnExit(jsonAuditWriter.auditPath); + if (auditPath1 != null) { + jsonAuditWriter.fileSystem.deleteOnExit(auditPath1); + } + if (jsonAuditWriter.auditPath != null) { + jsonAuditWriter.fileSystem.deleteOnExit(jsonAuditWriter.auditPath); + } jsonAuditWriter.closeWriter(); } @@ -150,8 +156,12 @@ public void verifyNoAppendToFileWhenDisabledWithConfig() throws Exception { assertFalse(jsonAuditWriter.reUseLastLogFile); // cleanup - jsonAuditWriter.fileSystem.deleteOnExit(auditPath1); - jsonAuditWriter.fileSystem.deleteOnExit(jsonAuditWriter.auditPath); + if (auditPath1 != null) { + jsonAuditWriter.fileSystem.deleteOnExit(auditPath1); + } + if (jsonAuditWriter.auditPath != null) { + jsonAuditWriter.fileSystem.deleteOnExit(jsonAuditWriter.auditPath); + } } @Test @@ -175,8 +185,12 @@ public void verifyFileRolloverAfterThreshold() throws Exception { assertTrue(jsonAuditWriter.logJSON(Collections.singleton("Second file created since rollover happened!"))); // cleanup - jsonAuditWriter.fileSystem.deleteOnExit(auditPath1); - jsonAuditWriter.fileSystem.deleteOnExit(jsonAuditWriter.auditPath); + if (auditPath1 != null) { + jsonAuditWriter.fileSystem.deleteOnExit(auditPath1); + } + if (jsonAuditWriter.auditPath != null) { + jsonAuditWriter.fileSystem.deleteOnExit(jsonAuditWriter.auditPath); + } jsonAuditWriter.closeWriter(); } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTClient.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTClient.java index 1177f4bc6..da471e0c5 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTClient.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTClient.java @@ -48,9 +48,11 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.security.KeyManagementException; @@ -62,6 +64,7 @@ import java.security.cert.CertificateException; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Random; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -83,16 +86,18 @@ public class RangerRESTClient { public static final String RANGER_POLICYMGR_TRUSTSTORE_FILE_CREDENTIAL = "xasecure.policymgr.clientssl.truststore.credential.file"; public static final String RANGER_POLICYMGR_TRUSTSTORE_FILE_CREDENTIAL_ALIAS = "sslTrustStore"; public static final String RANGER_POLICYMGR_TRUSTSTORE_FILE_TYPE_DEFAULT = "jks"; - public static final String RANGER_SSL_KEYMANAGER_ALGO_TYPE = KeyManagerFactory.getDefaultAlgorithm(); public static final String RANGER_SSL_TRUSTMANAGER_ALGO_TYPE = TrustManagerFactory.getDefaultAlgorithm(); public static final String RANGER_SSL_CONTEXT_ALGO_TYPE = "TLSv1.2"; + public static final String JWT_HEADER_PREFIX = "Bearer "; + private String mUrl; private final String mSslConfigFileName; private String mUsername; private String mPassword; private boolean mIsSSL; + private String jwt; private String mKeyStoreURL; private String mKeyStoreAlias; private String mKeyStoreFile; @@ -108,11 +113,16 @@ public class RangerRESTClient { private int lastKnownActiveUrlIndex; private final List<String> configuredURLs; + private final String propertyPrefix; private volatile Client client; private volatile Client cookieAuthClient; public RangerRESTClient(String url, String sslConfigFileName, Configuration config) { + this(url, sslConfigFileName, config, getPropertyPrefix(config)); + } + + public RangerRESTClient(String url, String sslConfigFileName, Configuration config, String propertyPrefix) { mUrl = url; mSslConfigFileName = sslConfigFileName; configuredURLs = StringUtil.getURLs(mUrl); @@ -121,6 +131,7 @@ public RangerRESTClient(String url, String sslConfigFileName, Configuration conf } else { setLastKnownActiveUrlIndex((new Random()).nextInt(getConfiguredURLs().size())); } + this.propertyPrefix = propertyPrefix; init(config); } @@ -198,7 +209,7 @@ public Client getClient() { result = client; if (result == null) { - result = buildClient(true); + result = buildClient(); client = result; } } @@ -215,7 +226,7 @@ public Client getCookieAuthClient() { ret = cookieAuthClient; if (ret == null) { - cookieAuthClient = buildClient(true); + cookieAuthClient = buildClient(); //Pending : need to remove basic auth filter from client. ret = cookieAuthClient; } @@ -225,7 +236,11 @@ public Client getCookieAuthClient() { return ret; } - private Client buildClient(boolean isBasicAuth) { + private static String getPropertyPrefix(Configuration config) { + return (config instanceof RangerPluginConfig) ? ((RangerPluginConfig) config).getPropertyPrefix() : "ranger.plugin"; + } + + private Client buildClient() { RangerJersey2ClientBuilder.SafeClientBuilder clientBuilder; ClientConfig config = new ClientConfig(); @@ -263,11 +278,21 @@ public boolean verify(String urlHostName, SSLSession session) { // Validate that MOXy prevention is properly configured RangerJersey2ClientBuilder.validateAntiMoxyConfiguration(config); - if (isBasicAuth && StringUtils.isNotEmpty(mUsername) && StringUtils.isNotEmpty(mPassword)) { + final String authHeader; + if (StringUtils.isNotEmpty(jwt)) { // use JWT if present + authHeader = JWT_HEADER_PREFIX + jwt; + LOG.info("Registering JWT auth header in REST client"); + } else if (StringUtils.isNotEmpty(mUsername) && StringUtils.isNotEmpty(mPassword)) { + authHeader = "Basic " + java.util.Base64.getEncoder().encodeToString((mUsername + ":" + mPassword).getBytes()); + LOG.info("Registering Basic auth header in REST client"); + } else { + authHeader = null; + } + + if (StringUtils.isNotEmpty(authHeader)) { config.register(new javax.ws.rs.client.ClientRequestFilter() { @Override public void filter(javax.ws.rs.client.ClientRequestContext requestContext) { - String authHeader = "Basic " + java.util.Base64.getEncoder().encodeToString((mUsername + ":" + mPassword).getBytes()); requestContext.getHeaders().add("Authorization", authHeader); } }); @@ -314,16 +339,11 @@ private void init(Configuration config) { } } - final String pluginPropertyPrefix; - - if (config instanceof RangerPluginConfig) { - pluginPropertyPrefix = ((RangerPluginConfig) config).getPropertyPrefix(); - } else { - pluginPropertyPrefix = "ranger.plugin"; - } + Optional<String> jwtAsString = fetchJWT(propertyPrefix, config); + jwtAsString.ifPresent(s -> this.jwt = s); - String username = config.get(pluginPropertyPrefix + ".policy.rest.client.username"); - String password = config.get(pluginPropertyPrefix + ".policy.rest.client.password"); + String username = config.get(propertyPrefix + ".policy.rest.client.username"); + String password = config.get(propertyPrefix + ".policy.rest.client.password"); if (StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password)) { setBasicAuthInfo(username, password); @@ -334,6 +354,60 @@ private boolean isSsl(String url) { return !StringUtils.isEmpty(url) && url.toLowerCase().startsWith("https"); } + private Optional<String> fetchJWT(String propertyPrefix, Configuration config) { + Optional<String> ret = Optional.empty(); + String jwtSrc = config.get(propertyPrefix + ".policy.rest.client.jwt.source"); + + if (StringUtils.isNotEmpty(jwtSrc)) { + switch(jwtSrc) { + case "env": { + String jwtEnvVar = config.get(propertyPrefix + ".policy.rest.client.jwt.env"); + if (StringUtils.isNotEmpty(jwtEnvVar)){ + String jwt = System.getenv(jwtEnvVar); + if (StringUtils.isNotBlank(jwt)) { + ret = Optional.of(jwt); + } + } + break; + } + case "file": { + String jwtFilePath = config.get(propertyPrefix + ".policy.rest.client.jwt.file"); + if (StringUtils.isNotEmpty(jwtFilePath)){ + File jwtFile = new File(jwtFilePath); + if (jwtFile.exists()) { + try (BufferedReader reader = new BufferedReader(new FileReader(jwtFile))) { + String line = null; + while ((line = reader.readLine()) != null) { + if (StringUtils.isNotBlank(line) && !line.startsWith("#")) { + ret = Optional.of(line); + break; + } + } + } catch (IOException e) { + LOG.error("Failed to read JWT from file: {}", jwtFilePath, e); + } + } + } + break; + } + case "cred": { + String credFilePath = config.get(propertyPrefix + ".policy.rest.client.jwt.cred.file"); + String credAlias = config.get(propertyPrefix + ".policy.rest.client.jwt.cred.alias"); + if (StringUtils.isNotEmpty(credFilePath) && StringUtils.isNotEmpty(credAlias)){ + String jwt = RangerCredentialProvider.getInstance().getCredentialString(credFilePath, credAlias); + if (StringUtils.isNotBlank(jwt)) { + ret = Optional.of(jwt); + } + } + break; + } + } + } else { + LOG.info("JWT source not configured, proceeding without JWT"); + } + return ret; + } + private KeyManager[] getKeyManagers() { KeyManager[] kmList = null; diff --git a/tagsync/src/main/java/org/apache/ranger/tagsync/sink/tagadmin/TagAdminRESTSink.java b/tagsync/src/main/java/org/apache/ranger/tagsync/sink/tagadmin/TagAdminRESTSink.java index e232e55de..3862b0621 100644 --- a/tagsync/src/main/java/org/apache/ranger/tagsync/sink/tagadmin/TagAdminRESTSink.java +++ b/tagsync/src/main/java/org/apache/ranger/tagsync/sink/tagadmin/TagAdminRESTSink.java @@ -87,7 +87,7 @@ public boolean initialize(Properties properties) { LOG.debug("isKerberized={}", isKerberized); if (StringUtils.isNotBlank(restUrl)) { - tagRESTClient = new RangerRESTClient(restUrl, sslConfigFile, TagSyncConfig.getInstance()); + tagRESTClient = new RangerRESTClient(restUrl, sslConfigFile, TagSyncConfig.getInstance(), "ranger.tagsync"); if (!isKerberized) { tagRESTClient.setBasicAuthInfo(userName, password); diff --git a/ugsync/src/main/java/org/apache/ranger/unixusersync/process/RangerUgSyncRESTClient.java b/ugsync/src/main/java/org/apache/ranger/unixusersync/process/RangerUgSyncRESTClient.java index a6270239b..54553d2a9 100644 --- a/ugsync/src/main/java/org/apache/ranger/unixusersync/process/RangerUgSyncRESTClient.java +++ b/ugsync/src/main/java/org/apache/ranger/unixusersync/process/RangerUgSyncRESTClient.java @@ -33,7 +33,7 @@ public class RangerUgSyncRESTClient extends RangerRESTClient { public RangerUgSyncRESTClient(String policyMgrBaseUrls, String ugKeyStoreFile, String ugKeyStoreFilepwd, String ugKeyStoreType, String ugTrustStoreFile, String ugTrustStoreFilepwd, String ugTrustStoreType, String authenticationType, String principal, String keytab, String polMgrUsername, String polMgrPassword) { - super(policyMgrBaseUrls, "", UserGroupSyncConfig.getInstance().getConfig()); + super(policyMgrBaseUrls, "", UserGroupSyncConfig.getInstance().getConfig(), "ranger.usersync"); String authKerberos = "kerberos";
