7
7
import static net .snowflake .client .core .SFTrustManager .resetOCSPResponseCacherServerURL ;
8
8
import static net .snowflake .client .jdbc .SnowflakeUtil .systemGetProperty ;
9
9
10
+ import com .fasterxml .jackson .core .JsonProcessingException ;
10
11
import com .fasterxml .jackson .databind .JsonNode ;
11
12
import com .fasterxml .jackson .databind .ObjectMapper ;
12
13
import com .google .common .base .Strings ;
@@ -271,7 +272,7 @@ static SFLoginOutput openSession(
271
272
AssertUtil .assertTrue (
272
273
loginInput .getUserName () != null , "missing user name for opening session" );
273
274
} else {
274
- // OAUTH needs either token or passord
275
+ // OAUTH needs either token or password
275
276
AssertUtil .assertTrue (
276
277
loginInput .getToken () != null || loginInput .getPassword () != null ,
277
278
"missing token or password for opening session" );
@@ -707,7 +708,7 @@ private static SFLoginOutput newSession(
707
708
// In RestRequest.execute(), socket timeout is replaced with auth timeout
708
709
// so we can renew the request within auth timeout.
709
710
// auth timeout within socket timeout is thrown without backoff,
710
- // and we need to update time remained in socket timeout here to control the
711
+ // and we need to update time remained in socket timeout here to control
711
712
// the actual socket timeout from customer setting.
712
713
if (loginInput .getSocketTimeoutInMillis () > 0 ) {
713
714
if (ex .issocketTimeoutNoBackoff ()) {
@@ -737,29 +738,7 @@ private static SFLoginOutput newSession(
737
738
break ;
738
739
}
739
740
740
- if (theString == null ) {
741
- if (lastRestException != null ) {
742
- logger .error (
743
- "Failed to open new session for user: {}, host: {}. Error: {}" ,
744
- loginInput .getUserName (),
745
- loginInput .getHostFromServerUrl (),
746
- lastRestException );
747
- throw lastRestException ;
748
- } else {
749
- SnowflakeSQLException exception =
750
- new SnowflakeSQLException (
751
- NO_QUERY_ID ,
752
- "empty authentication response" ,
753
- SqlState .CONNECTION_EXCEPTION ,
754
- ErrorCode .CONNECTION_ERROR .getMessageCode ());
755
- logger .error (
756
- "Failed to open new session for user: {}, host: {}. Error: {}" ,
757
- loginInput .getUserName (),
758
- loginInput .getHostFromServerUrl (),
759
- exception );
760
- throw exception ;
761
- }
762
- }
741
+ handleEmptyAuthResponse (theString , loginInput , lastRestException );
763
742
764
743
// general method, same as with data binding
765
744
JsonNode jsonNode = mapper .readTree (theString );
@@ -1201,22 +1180,11 @@ static void closeSession(SFLoginInput loginInput) throws SFException, SnowflakeS
1201
1180
private static String federatedFlowStep4 (
1202
1181
SFLoginInput loginInput , String ssoUrl , String oneTimeToken ) throws SnowflakeSQLException {
1203
1182
String responseHtml = "" ;
1204
- try {
1205
1183
1206
- final URL url = new URL (ssoUrl );
1207
- URI oktaGetUri =
1208
- new URIBuilder ()
1209
- .setScheme (url .getProtocol ())
1210
- .setHost (url .getHost ())
1211
- .setPath (url .getPath ())
1212
- .setParameter ("RelayState" , "%2Fsome%2Fdeep%2Flink" )
1213
- .setParameter ("onetimetoken" , oneTimeToken )
1214
- .build ();
1215
- HttpGet httpGet = new HttpGet (oktaGetUri );
1184
+ try {
1216
1185
1217
- HeaderGroup headers = new HeaderGroup ();
1218
- headers .addHeader (new BasicHeader (HttpHeaders .ACCEPT , "*/*" ));
1219
- httpGet .setHeaders (headers .getAllHeaders ());
1186
+ HttpGet httpGet = new HttpGet ();
1187
+ prepareFederatedFlowStep4Request (httpGet , ssoUrl , oneTimeToken );
1220
1188
1221
1189
responseHtml =
1222
1190
HttpUtil .executeGeneralRequest (
@@ -1276,26 +1244,7 @@ private static String federatedFlowStep3(SFLoginInput loginInput, String tokenUr
1276
1244
URL url = new URL (tokenUrl );
1277
1245
URI tokenUri = url .toURI ();
1278
1246
final HttpPost postRequest = new HttpPost (tokenUri );
1279
-
1280
- String userName ;
1281
- if (Strings .isNullOrEmpty (loginInput .getOKTAUserName ())) {
1282
- userName = loginInput .getUserName ();
1283
- } else {
1284
- userName = loginInput .getOKTAUserName ();
1285
- }
1286
- StringEntity params =
1287
- new StringEntity (
1288
- "{\" username\" :\" "
1289
- + userName
1290
- + "\" ,\" password\" :\" "
1291
- + loginInput .getPassword ()
1292
- + "\" }" );
1293
- postRequest .setEntity (params );
1294
-
1295
- HeaderGroup headers = new HeaderGroup ();
1296
- headers .addHeader (new BasicHeader (HttpHeaders .ACCEPT , "application/json" ));
1297
- headers .addHeader (new BasicHeader (HttpHeaders .CONTENT_TYPE , "application/json" ));
1298
- postRequest .setHeaders (headers .getAllHeaders ());
1247
+ setFederatedFlowStep3PostRequestAuthData (postRequest , loginInput );
1299
1248
1300
1249
final String idpResponse =
1301
1250
HttpUtil .executeRequestWithoutCookies (
@@ -1363,29 +1312,8 @@ private static void federatedFlowStep2(SFLoginInput loginInput, String tokenUrl,
1363
1312
private static JsonNode federatedFlowStep1 (SFLoginInput loginInput ) throws SnowflakeSQLException {
1364
1313
JsonNode dataNode = null ;
1365
1314
try {
1366
- URIBuilder fedUriBuilder = new URIBuilder (loginInput .getServerUrl ());
1367
- fedUriBuilder .setPath (SF_PATH_AUTHENTICATOR_REQUEST );
1368
- URI fedUrlUri = fedUriBuilder .build ();
1369
-
1370
- Map <String , Object > data = new HashMap <>();
1371
- data .put (ClientAuthnParameter .ACCOUNT_NAME .name (), loginInput .getAccountName ());
1372
- data .put (ClientAuthnParameter .AUTHENTICATOR .name (), loginInput .getAuthenticator ());
1373
- data .put (ClientAuthnParameter .CLIENT_APP_ID .name (), loginInput .getAppId ());
1374
- data .put (ClientAuthnParameter .CLIENT_APP_VERSION .name (), loginInput .getAppVersion ());
1375
-
1376
- ClientAuthnDTO authnData = new ClientAuthnDTO (data , null );
1377
- String json = mapper .writeValueAsString (authnData );
1378
-
1379
- // attach the login info json body to the post request
1380
- StringEntity input = new StringEntity (json , StandardCharsets .UTF_8 );
1381
- input .setContentType ("application/json" );
1382
- HttpPost postRequest = new HttpPost (fedUrlUri );
1383
- postRequest .setEntity (input );
1384
- postRequest .addHeader ("accept" , "application/json" );
1385
-
1386
- // Add headers for driver name and version
1387
- postRequest .addHeader (SF_HEADER_CLIENT_APP_ID , loginInput .getAppId ());
1388
- postRequest .addHeader (SF_HEADER_CLIENT_APP_VERSION , loginInput .getAppVersion ());
1315
+ StringEntity requestInput = prepareFederatedFlowStep1RequestInput (loginInput );
1316
+ HttpPost postRequest = prepareFederatedFlowStep1PostRequest (loginInput , requestInput );
1389
1317
1390
1318
final String gsResponse =
1391
1319
HttpUtil .executeGeneralRequest (
@@ -1395,6 +1323,7 @@ private static JsonNode federatedFlowStep1(SFLoginInput loginInput) throws Snowf
1395
1323
loginInput .getSocketTimeoutInMillis (),
1396
1324
0 ,
1397
1325
loginInput .getHttpClientSettingsKey ());
1326
+
1398
1327
logger .debug ("Authenticator-request response: {}" , gsResponse );
1399
1328
JsonNode jsonNode = mapper .readTree (gsResponse );
1400
1329
@@ -1790,12 +1719,148 @@ public static boolean isNewRetryStrategyRequest(HttpRequestBase request) {
1790
1719
URI requestURI = request .getURI ();
1791
1720
String requestPath = requestURI .getPath ();
1792
1721
if (requestPath != null ) {
1793
- if ( requestPath .equals (SF_PATH_LOGIN_REQUEST )
1722
+ return requestPath .equals (SF_PATH_LOGIN_REQUEST )
1794
1723
|| requestPath .equals (SF_PATH_AUTHENTICATOR_REQUEST )
1795
- || requestPath .equals (SF_PATH_TOKEN_REQUEST )) {
1796
- return true ;
1797
- }
1724
+ || requestPath .equals (SF_PATH_TOKEN_REQUEST );
1798
1725
}
1799
1726
return false ;
1800
1727
}
1728
+
1729
+ /**
1730
+ * Prepares an HTTP POST request for the first step of the federated authentication flow.
1731
+ *
1732
+ * @param loginInput The login information for the request.
1733
+ * @param inputData The JSON input data to include in the request.
1734
+ * @return An {@link HttpPost} object ready to execute the federated flow request.
1735
+ * @throws URISyntaxException If the constructed URI is invalid.
1736
+ */
1737
+ private static HttpPost prepareFederatedFlowStep1PostRequest (
1738
+ SFLoginInput loginInput , StringEntity inputData ) throws URISyntaxException {
1739
+ URIBuilder fedUriBuilder = new URIBuilder (loginInput .getServerUrl ());
1740
+ // TODO: if loginInput.serverUrl contains port or additional segments - it will be ignored and
1741
+ // overwritten here - to be fixed in SNOW-1922872
1742
+ fedUriBuilder .setPath (SF_PATH_AUTHENTICATOR_REQUEST );
1743
+ URI fedUrlUri = fedUriBuilder .build ();
1744
+
1745
+ HttpPost postRequest = new HttpPost (fedUrlUri );
1746
+ postRequest .setEntity (inputData );
1747
+ postRequest .addHeader ("accept" , "application/json" );
1748
+
1749
+ postRequest .addHeader (SF_HEADER_CLIENT_APP_ID , loginInput .getAppId ());
1750
+ postRequest .addHeader (SF_HEADER_CLIENT_APP_VERSION , loginInput .getAppVersion ());
1751
+
1752
+ return postRequest ;
1753
+ }
1754
+
1755
+ /**
1756
+ * Prepares the JSON input for the first step of the federated authentication flow.
1757
+ *
1758
+ * @param loginInput The login information for the request.
1759
+ * @return A {@link StringEntity} containing the JSON input for the request.
1760
+ * @throws JsonProcessingException If there is an error generating the JSON input.
1761
+ */
1762
+ private static StringEntity prepareFederatedFlowStep1RequestInput (SFLoginInput loginInput )
1763
+ throws JsonProcessingException {
1764
+ Map <String , Object > data = new HashMap <>();
1765
+ data .put (ClientAuthnParameter .ACCOUNT_NAME .name (), loginInput .getAccountName ());
1766
+ data .put (ClientAuthnParameter .AUTHENTICATOR .name (), loginInput .getAuthenticator ());
1767
+ data .put (ClientAuthnParameter .CLIENT_APP_ID .name (), loginInput .getAppId ());
1768
+ data .put (ClientAuthnParameter .CLIENT_APP_VERSION .name (), loginInput .getAppVersion ());
1769
+
1770
+ ClientAuthnDTO authnData = new ClientAuthnDTO (data , null );
1771
+ String json = mapper .writeValueAsString (authnData );
1772
+
1773
+ StringEntity input = new StringEntity (json , StandardCharsets .UTF_8 );
1774
+ input .setContentType ("application/json" );
1775
+ return input ;
1776
+ }
1777
+
1778
+ /**
1779
+ * Sets the authentication data for the third step of the federated authentication flow.
1780
+ *
1781
+ * @param postRequest The {@link HttpPost} request to update with authentication data.
1782
+ * @param loginInput The login information for the request.
1783
+ * @throws SnowflakeSQLException If an error occurs while preparing the request.
1784
+ */
1785
+ private static void setFederatedFlowStep3PostRequestAuthData (
1786
+ HttpPost postRequest , SFLoginInput loginInput ) throws SnowflakeSQLException {
1787
+ String userName =
1788
+ Strings .isNullOrEmpty (loginInput .getOKTAUserName ())
1789
+ ? loginInput .getUserName ()
1790
+ : loginInput .getOKTAUserName ();
1791
+ try {
1792
+ StringEntity params =
1793
+ new StringEntity (
1794
+ "{\" username\" :\" "
1795
+ + userName
1796
+ + "\" ,\" password\" :\" "
1797
+ + loginInput .getPassword ()
1798
+ + "\" }" );
1799
+ postRequest .setEntity (params );
1800
+
1801
+ HeaderGroup headers = new HeaderGroup ();
1802
+ headers .addHeader (new BasicHeader (HttpHeaders .ACCEPT , "application/json" ));
1803
+ headers .addHeader (new BasicHeader (HttpHeaders .CONTENT_TYPE , "application/json" ));
1804
+ postRequest .setHeaders (headers .getAllHeaders ());
1805
+ } catch (IOException ex ) {
1806
+ handleFederatedFlowError (loginInput , ex );
1807
+ }
1808
+ }
1809
+
1810
+ /**
1811
+ * Prepares an HTTP GET request for the fourth step of the federated authentication flow.
1812
+ *
1813
+ * @param retrieveSamlRequest The {@link HttpRequestBase} to update with the SAML request details.
1814
+ * @param ssoUrl The SSO URL to use for the request.
1815
+ * @param oneTimeToken The one-time token to include in the request.
1816
+ * @throws MalformedURLException If the SSO URL is malformed.
1817
+ * @throws URISyntaxException If the URI for the request cannot be built.
1818
+ */
1819
+ private static void prepareFederatedFlowStep4Request (
1820
+ HttpRequestBase retrieveSamlRequest , String ssoUrl , String oneTimeToken )
1821
+ throws MalformedURLException , URISyntaxException {
1822
+ final URL url = new URL (ssoUrl );
1823
+ URI oktaGetUri =
1824
+ new URIBuilder ()
1825
+ .setScheme (url .getProtocol ())
1826
+ .setHost (url .getHost ())
1827
+ .setPort (url .getPort ())
1828
+ .setPath (url .getPath ())
1829
+ .setParameter ("RelayState" , "%2Fsome%2Fdeep%2Flink" )
1830
+ .setParameter ("onetimetoken" , oneTimeToken )
1831
+ .build ();
1832
+ retrieveSamlRequest .setURI (oktaGetUri );
1833
+
1834
+ HeaderGroup headers = new HeaderGroup ();
1835
+ headers .addHeader (new BasicHeader (HttpHeaders .ACCEPT , "*/*" ));
1836
+ retrieveSamlRequest .setHeaders (headers .getAllHeaders ());
1837
+ }
1838
+
1839
+ private static void handleEmptyAuthResponse (
1840
+ String theString , SFLoginInput loginInput , Exception lastRestException )
1841
+ throws Exception , SFException {
1842
+ if (theString == null ) {
1843
+ if (lastRestException != null ) {
1844
+ logger .error (
1845
+ "Failed to open new session for user: {}, host: {}. Error: {}" ,
1846
+ loginInput .getUserName (),
1847
+ loginInput .getHostFromServerUrl (),
1848
+ lastRestException );
1849
+ throw lastRestException ;
1850
+ } else {
1851
+ SnowflakeSQLException exception =
1852
+ new SnowflakeSQLException (
1853
+ NO_QUERY_ID ,
1854
+ "empty authentication response" ,
1855
+ SqlState .CONNECTION_EXCEPTION ,
1856
+ ErrorCode .CONNECTION_ERROR .getMessageCode ());
1857
+ logger .error (
1858
+ "Failed to open new session for user: {}, host: {}. Error: {}" ,
1859
+ loginInput .getUserName (),
1860
+ loginInput .getHostFromServerUrl (),
1861
+ exception );
1862
+ throw exception ;
1863
+ }
1864
+ }
1865
+ }
1801
1866
}
0 commit comments