Skip to content

Commit f98a149

Browse files
authored
RANGER-5475: add JWT support in RangerRESTClient (#831)
1 parent fe379d0 commit f98a149

3 files changed

Lines changed: 116 additions & 30 deletions

File tree

agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTClient.java

Lines changed: 112 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
2323
import com.sun.jersey.api.client.Client;
2424
import com.sun.jersey.api.client.ClientHandlerException;
25+
import com.sun.jersey.api.client.ClientRequest;
2526
import com.sun.jersey.api.client.ClientResponse;
2627
import com.sun.jersey.api.client.WebResource;
2728
import com.sun.jersey.api.client.config.ClientConfig;
@@ -48,9 +49,11 @@
4849
import javax.ws.rs.core.Cookie;
4950
import javax.ws.rs.core.Response;
5051

52+
import java.io.BufferedReader;
5153
import java.io.File;
5254
import java.io.FileInputStream;
5355
import java.io.FileNotFoundException;
56+
import java.io.FileReader;
5457
import java.io.IOException;
5558
import java.io.InputStream;
5659
import java.security.KeyManagementException;
@@ -82,8 +85,10 @@ public class RangerRESTClient {
8285
public static final String RANGER_SSL_KEYMANAGER_ALGO_TYPE = KeyManagerFactory.getDefaultAlgorithm();
8386
public static final String RANGER_SSL_TRUSTMANAGER_ALGO_TYPE = TrustManagerFactory.getDefaultAlgorithm();
8487
public static final String RANGER_SSL_CONTEXT_ALGO_TYPE = "TLSv1.2";
88+
public static final String JWT_HEADER_PREFIX = "Bearer ";
8589

8690
private final List<String> configuredURLs;
91+
private final String propertyPrefix;
8792
private String mUrl;
8893
private final String mSslConfigFileName;
8994
private String mUsername;
@@ -104,30 +109,26 @@ public class RangerRESTClient {
104109
private int lastKnownActiveUrlIndex;
105110
private volatile Client client;
106111
private volatile Client cookieAuthClient;
112+
private ClientFilter jwtAuthFilter;
107113
private ClientFilter basicAuthFilter;
108114

109115
public RangerRESTClient(String url, String sslConfigFileName, Configuration config) {
110-
mUrl = url;
111-
mSslConfigFileName = sslConfigFileName;
112-
configuredURLs = StringUtil.getURLs(mUrl);
116+
this(url, sslConfigFileName, config, getPropertyPrefix(config));
117+
}
118+
119+
public RangerRESTClient(String url, String sslConfigFileName, Configuration config, String propertyPrefix) {
120+
mUrl = url;
121+
mSslConfigFileName = sslConfigFileName;
122+
configuredURLs = StringUtil.getURLs(mUrl);
123+
this.propertyPrefix = propertyPrefix;
124+
113125
if (StringUtil.isEmpty(url)) {
114126
throw new IllegalArgumentException("Ranger URL is null or empty. Likely caused by incorrect configuration");
115127
} else {
116128
setLastKnownActiveUrlIndex((new Random()).nextInt(getConfiguredURLs().size()));
117129
}
118-
init(config);
119-
}
120-
121-
protected static WebResource setQueryParams(WebResource webResource, Map<String, String> params) {
122-
WebResource ret = webResource;
123-
124-
if (webResource != null && params != null) {
125-
for (Map.Entry<String, String> entry : params.entrySet()) {
126-
ret = ret.queryParam(entry.getKey(), entry.getValue());
127-
}
128-
}
129130

130-
return ret;
131+
init(config);
131132
}
132133

133134
public String getUrl() {
@@ -684,6 +685,22 @@ protected void setTrustStoreType(String mTrustStoreType) {
684685
this.mTrustStoreType = mTrustStoreType;
685686
}
686687

688+
protected static WebResource setQueryParams(WebResource webResource, Map<String, String> params) {
689+
WebResource ret = webResource;
690+
691+
if (webResource != null && params != null) {
692+
for (Map.Entry<String, String> entry : params.entrySet()) {
693+
ret = ret.queryParam(entry.getKey(), entry.getValue());
694+
}
695+
}
696+
697+
return ret;
698+
}
699+
700+
private static String getPropertyPrefix(Configuration config) {
701+
return (config instanceof RangerPluginConfig) ? ((RangerPluginConfig) config).getPropertyPrefix() : "ranger.plugin";
702+
}
703+
687704
private Client getCookieAuthClient() {
688705
Client ret = cookieAuthClient;
689706

@@ -694,6 +711,10 @@ private Client getCookieAuthClient() {
694711
if (ret == null) {
695712
cookieAuthClient = buildClient();
696713

714+
if (jwtAuthFilter != null) {
715+
cookieAuthClient.removeFilter(jwtAuthFilter);
716+
}
717+
697718
if (basicAuthFilter != null) {
698719
cookieAuthClient.removeFilter(basicAuthFilter);
699720
}
@@ -732,8 +753,11 @@ private Client buildClient() {
732753
client = Client.create(config);
733754
}
734755

735-
if (basicAuthFilter != null && !client.isFilterPresent(basicAuthFilter)) {
736-
client.addFilter(basicAuthFilter);
756+
// use JWT if present
757+
ClientFilter authFilter = jwtAuthFilter != null ? jwtAuthFilter : basicAuthFilter;
758+
759+
if (authFilter != null && !client.isFilterPresent(authFilter)) {
760+
client.addFilter(authFilter);
737761
}
738762

739763
// Set Connection Timeout and ReadTime for the PolicyRefresh
@@ -743,6 +767,23 @@ private Client buildClient() {
743767
return client;
744768
}
745769

770+
private void setJWTFilter(String jwtAsString) {
771+
if (StringUtils.isNotBlank(jwtAsString)) {
772+
LOG.info("Registering JWT auth header in REST client");
773+
774+
jwtAuthFilter = new ClientFilter() {
775+
@Override
776+
public ClientResponse handle(ClientRequest clientRequest) throws ClientHandlerException {
777+
clientRequest.getHeaders().add("Authorization", JWT_HEADER_PREFIX + jwtAsString);
778+
779+
return getNext().handle(clientRequest);
780+
}
781+
};
782+
} else {
783+
jwtAuthFilter = null;
784+
}
785+
}
786+
746787
private void setBasicAuthFilter(String username, String password) {
747788
if (StringUtils.isNotEmpty(username) && StringUtils.isNotEmpty(password)) {
748789
basicAuthFilter = new HTTPBasicAuthFilter(username, password);
@@ -780,22 +821,67 @@ private void init(Configuration config) {
780821
}
781822
}
782823

783-
final String pluginPropertyPrefix;
784-
785-
if (config instanceof RangerPluginConfig) {
786-
pluginPropertyPrefix = ((RangerPluginConfig) config).getPropertyPrefix();
787-
} else {
788-
pluginPropertyPrefix = "ranger.plugin";
789-
}
824+
String jwtAsString = fetchJWT(propertyPrefix, config);
825+
String username = config.get(propertyPrefix + ".policy.rest.client.username");
826+
String password = config.get(propertyPrefix + ".policy.rest.client.password");
790827

791-
String username = config.get(pluginPropertyPrefix + ".policy.rest.client.username");
792-
String password = config.get(pluginPropertyPrefix + ".policy.rest.client.password");
828+
setJWTFilter(jwtAsString);
793829

794830
if (StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password)) {
795831
setBasicAuthFilter(username, password);
796832
}
797833
}
798834

835+
private String fetchJWT(String propertyPrefix, Configuration config) {
836+
final String jwtSrc = config.get(propertyPrefix + ".policy.rest.client.jwt.source");
837+
838+
if (StringUtils.isNotEmpty(jwtSrc)) {
839+
switch (jwtSrc) {
840+
case "env":
841+
String jwtEnvVar = config.get(propertyPrefix + ".policy.rest.client.jwt.env");
842+
if (StringUtils.isNotEmpty(jwtEnvVar)) {
843+
String jwt = System.getenv(jwtEnvVar);
844+
if (StringUtils.isNotBlank(jwt)) {
845+
return jwt;
846+
}
847+
}
848+
break;
849+
case "file":
850+
String jwtFilePath = config.get(propertyPrefix + ".policy.rest.client.jwt.file");
851+
if (StringUtils.isNotEmpty(jwtFilePath)) {
852+
File jwtFile = new File(jwtFilePath);
853+
if (jwtFile.exists()) {
854+
try (BufferedReader reader = new BufferedReader(new FileReader(jwtFile))) {
855+
String line = null;
856+
while ((line = reader.readLine()) != null) {
857+
if (StringUtils.isNotBlank(line) && !line.startsWith("#")) {
858+
return line;
859+
}
860+
}
861+
} catch (IOException e) {
862+
LOG.error("Failed to read JWT from file: {}", jwtFilePath, e);
863+
}
864+
}
865+
}
866+
break;
867+
case "cred":
868+
String credFilePath = config.get(propertyPrefix + ".policy.rest.client.jwt.cred.file");
869+
String credAlias = config.get(propertyPrefix + ".policy.rest.client.jwt.cred.alias");
870+
if (StringUtils.isNotEmpty(credFilePath) && StringUtils.isNotEmpty(credAlias)) {
871+
String jwt = RangerCredentialProvider.getInstance().getCredentialString(credFilePath, credAlias);
872+
if (StringUtils.isNotBlank(jwt)) {
873+
return jwt;
874+
}
875+
}
876+
break;
877+
}
878+
} else {
879+
LOG.info("JWT source not configured, proceeding without JWT");
880+
}
881+
882+
return null;
883+
}
884+
799885
private boolean isSslEnabled(String url) {
800886
return !StringUtils.isEmpty(url) && url.toLowerCase().startsWith("https");
801887
}

tagsync/src/main/java/org/apache/ranger/tagsync/sink/tagadmin/TagAdminRESTSink.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public boolean initialize(Properties properties) {
8585
LOG.debug("isKerberized={}", isKerberized);
8686

8787
if (StringUtils.isNotBlank(restUrl)) {
88-
tagRESTClient = new RangerRESTClient(restUrl, sslConfigFile, TagSyncConfig.getInstance());
88+
tagRESTClient = new RangerRESTClient(restUrl, sslConfigFile, TagSyncConfig.getInstance(), "ranger.tagsync");
8989

9090
if (!isKerberized) {
9191
tagRESTClient.setBasicAuthInfo(userName, password);

ugsync/src/main/java/org/apache/ranger/unixusersync/process/RangerUgSyncRESTClient.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@
3737

3838
public class RangerUgSyncRESTClient extends RangerRESTClient {
3939
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) {
40-
super(policyMgrBaseUrls, "", UserGroupSyncConfig.getInstance().getConfig());
40+
super(policyMgrBaseUrls, "", UserGroupSyncConfig.getInstance().getConfig(), "ranger.usersync");
4141

42-
String authKerberos = "kerberos";
42+
boolean isKerberized = "kerberos".equalsIgnoreCase(authenticationType) && SecureClientLogin.isKerberosCredentialExists(principal, keytab);
4343

44-
if (!(authKerberos.equalsIgnoreCase(authenticationType) && SecureClientLogin.isKerberosCredentialExists(principal, keytab))) {
44+
if (!isKerberized) {
4545
setBasicAuthInfo(polMgrUsername, polMgrPassword);
4646
}
4747

0 commit comments

Comments
 (0)