2222import com .fasterxml .jackson .jaxrs .json .JacksonJsonProvider ;
2323import com .sun .jersey .api .client .Client ;
2424import com .sun .jersey .api .client .ClientHandlerException ;
25+ import com .sun .jersey .api .client .ClientRequest ;
2526import com .sun .jersey .api .client .ClientResponse ;
2627import com .sun .jersey .api .client .WebResource ;
2728import com .sun .jersey .api .client .config .ClientConfig ;
4849import javax .ws .rs .core .Cookie ;
4950import javax .ws .rs .core .Response ;
5051
52+ import java .io .BufferedReader ;
5153import java .io .File ;
5254import java .io .FileInputStream ;
5355import java .io .FileNotFoundException ;
56+ import java .io .FileReader ;
5457import java .io .IOException ;
5558import java .io .InputStream ;
5659import 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 }
0 commit comments