1919package org .apache .polaris .service .quarkus .it ;
2020
2121import static org .assertj .core .api .Assertions .assertThat ;
22+ import static org .assertj .core .api .Assertions .assertThatThrownBy ;
2223
2324import com .auth0 .jwt .JWT ;
2425import com .auth0 .jwt .algorithms .Algorithm ;
2526import io .quarkus .test .junit .QuarkusTest ;
2627import io .quarkus .test .junit .QuarkusTestProfile ;
2728import io .quarkus .test .junit .TestProfile ;
29+ import io .smallrye .common .annotation .Identifier ;
30+ import jakarta .inject .Inject ;
2831import java .io .IOException ;
2932import java .time .Instant ;
3033import java .util .Map ;
34+ import org .apache .iceberg .exceptions .NotAuthorizedException ;
35+ import org .apache .iceberg .rest .ErrorHandlers ;
3136import org .apache .iceberg .rest .HTTPClient ;
3237import org .apache .iceberg .rest .RESTClient ;
3338import org .apache .iceberg .rest .auth .AuthConfig ;
3439import org .apache .iceberg .rest .auth .OAuth2Util ;
40+ import org .apache .iceberg .rest .responses .OAuthTokenResponse ;
41+ import org .apache .polaris .service .auth .TokenBrokerFactory ;
3542import org .apache .polaris .service .it .env .ClientCredentials ;
3643import org .apache .polaris .service .it .env .PolarisApiEndpoints ;
3744import org .apache .polaris .service .it .test .PolarisApplicationIntegrationTest ;
@@ -53,8 +60,12 @@ public Map<String, String> getConfigOverrides() {
5360 }
5461 }
5562
63+ @ Inject
64+ @ Identifier ("rsa-key-pair" )
65+ TokenBrokerFactory tokenBrokerFactory ;
66+
5667 @ Test
57- public void testIcebergRestApiRefreshToken (
68+ public void testIcebergRestApiRefreshExpiredToken (
5869 PolarisApiEndpoints endpoints , ClientCredentials clientCredentials ) throws IOException {
5970 String path = endpoints .catalogApiEndpoint () + "/v1/oauth/tokens" ;
6071 try (RESTClient client =
@@ -82,4 +93,91 @@ public void testIcebergRestApiRefreshToken(
8293 assertThat (JWT .decode (session .token ()).getExpiresAtAsInstant ()).isAfter (Instant .EPOCH );
8394 }
8495 }
96+
97+ @ Test
98+ public void testIcebergRestApiRefreshValidToken (
99+ PolarisApiEndpoints endpoints , ClientCredentials clientCredentials ) throws IOException {
100+ String path = endpoints .catalogApiEndpoint () + "/v1/oauth/tokens" ;
101+ try (RESTClient client =
102+ HTTPClient .builder (Map .of ())
103+ .withHeader (endpoints .realmHeaderName (), endpoints .realmId ())
104+ .uri (path )
105+ .build ()) {
106+ var response =
107+ client .postForm (
108+ path ,
109+ Map .of (
110+ "grant_type" ,
111+ "client_credentials" ,
112+ "scope" ,
113+ "PRINCIPAL_ROLE:ALL" ,
114+ "client_id" ,
115+ clientCredentials .clientId (),
116+ "client_secret" ,
117+ clientCredentials .clientSecret ()),
118+ OAuthTokenResponse .class ,
119+ Map .of (),
120+ ErrorHandlers .oauthErrorHandler ());
121+ String token = response .token ();
122+ var authConfig =
123+ AuthConfig .builder ()
124+ .credential (clientCredentials .clientId () + ":" + clientCredentials .clientSecret ())
125+ .scope ("PRINCIPAL_ROLE:ALL" )
126+ .oauth2ServerUri (path )
127+ .token (token )
128+ .build ();
129+ var parentSession = new OAuth2Util .AuthSession (Map .of (), authConfig );
130+ var session = OAuth2Util .AuthSession .fromAccessToken (client , null , token , 0L , parentSession );
131+ session .refresh (client );
132+ assertThat (session .token ()).isNotEqualTo (token );
133+ assertThat (JWT .decode (session .token ()).getExpiresAtAsInstant ()).isAfter (Instant .now ());
134+ }
135+ }
136+
137+ @ Test
138+ public void testIcebergRestApiInvalidToken (
139+ PolarisApiEndpoints endpoints , ClientCredentials clientCredentials ) throws IOException {
140+ String path = endpoints .catalogApiEndpoint () + "/v1/oauth/tokens" ;
141+ try (RESTClient client =
142+ HTTPClient .builder (Map .of ())
143+ .withHeader (endpoints .realmHeaderName (), endpoints .realmId ())
144+ .uri (path )
145+ .build ()) {
146+ var response =
147+ client .postForm (
148+ path ,
149+ Map .of (
150+ "grant_type" ,
151+ "client_credentials" ,
152+ "scope" ,
153+ "PRINCIPAL_ROLE:ALL" ,
154+ "client_id" ,
155+ clientCredentials .clientId (),
156+ "client_secret" ,
157+ clientCredentials .clientSecret ()),
158+ OAuthTokenResponse .class ,
159+ Map .of (),
160+ ErrorHandlers .oauthErrorHandler ());
161+ String token = response .token ();
162+ // mimics OAUth2Util.AuthSession refreshing the token
163+ assertThatThrownBy (
164+ () ->
165+ client .postForm (
166+ path ,
167+ Map .of (
168+ "grant_type" ,
169+ "urn:ietf:params:oauth:grant-type:token-exchange" ,
170+ "scope" ,
171+ "PRINCIPAL_ROLE:ALL" ,
172+ "subject_token" ,
173+ "invalid" ,
174+ "subject_token_type" ,
175+ "urn:ietf:params:oauth:token-type:access_token" ),
176+ OAuthTokenResponse .class ,
177+ Map .of ("Authorization" , "Bearer " + token ),
178+ ErrorHandlers .oauthErrorHandler ()))
179+ .isInstanceOf (NotAuthorizedException .class )
180+ .hasMessageContaining ("invalid_client" );
181+ }
182+ }
85183}
0 commit comments