1313import java .io .File ;
1414import java .io .FileInputStream ;
1515import java .io .FileOutputStream ;
16+ import java .io .IOException ;
17+ import java .io .InputStream ;
18+ import java .lang .reflect .Field ;
1619import java .net .URI ;
1720import java .security .KeyStore ;
1821import java .security .cert .CertificateFactory ;
2629import java .util .ArrayList ;
2730import java .util .Arrays ;
2831import java .util .Calendar ;
32+ import java .util .Date ;
33+ import java .util .HashSet ;
2934import java .util .List ;
3035import java .util .Locale ;
36+ import java .util .Properties ;
3137import java .util .ResourceBundle ;
32-
38+ import java .util .Set ;
39+ import java .util .concurrent .CompletableFuture ;
40+ import java .util .concurrent .ExecutorService ;
41+ import java .util .concurrent .Executors ;
42+ import java .util .concurrent .TimeUnit ;
43+
44+ import org .junit .Assert ;
45+
46+ import com .microsoft .aad .msal4j .ClientCredentialFactory ;
47+ import com .microsoft .aad .msal4j .ClientCredentialParameters ;
48+ import com .microsoft .aad .msal4j .ConfidentialClientApplication ;
49+ import com .microsoft .aad .msal4j .IAuthenticationResult ;
50+ import com .microsoft .aad .msal4j .IClientCredential ;
3351import com .microsoft .sqlserver .testframework .AbstractSQLGenerator ;
52+ import com .microsoft .sqlserver .testframework .Constants ;
3453import com .microsoft .sqlserver .testframework .PrepUtil ;
3554import com .microsoft .sqlserver .testframework .sqlType .SqlBigInt ;
3655import com .microsoft .sqlserver .testframework .sqlType .SqlBinary ;
@@ -75,6 +94,97 @@ public final class TestUtils {
7594 static final int ENGINE_EDITION_FOR_SQL_AZURE_DW = 6 ;
7695 static final int ENGINE_EDITION_FOR_SQL_AZURE_MI = 8 ;
7796
97+ public static final int TEST_TOKEN_EXPIRY_SECONDS = 120 ; // token expiry time in secs
98+
99+ public static String ACCESS_TOKEN_CALLBACK = null ;
100+
101+ static String applicationKey ;
102+ static String applicationClientID ;
103+
104+ static {
105+ try (InputStream input = new FileInputStream (Constants .CONFIG_PROPERTIES_FILE )) {
106+ Properties configProperties = new Properties ();
107+ configProperties .load (input );
108+ applicationKey = configProperties .getProperty ("applicationKey" );
109+ applicationClientID = configProperties .getProperty ("applicationClientID" );
110+ } catch (IOException e ) {
111+ // No config file found
112+ }
113+ }
114+
115+ public static boolean expireTokenToggle = false ;
116+
117+ public static final SQLServerAccessTokenCallback accessTokenCallback = new SQLServerAccessTokenCallback () {
118+ @ Override
119+ public SqlAuthenticationToken getAccessToken (String spn , String stsurl ) {
120+ String scope = spn + "/.default" ;
121+ Set <String > scopes = new HashSet <>();
122+ scopes .add (scope );
123+
124+ try {
125+ ExecutorService executorService = Executors .newSingleThreadExecutor ();
126+ IClientCredential credential = ClientCredentialFactory .createFromSecret (applicationKey );
127+ ConfidentialClientApplication clientApplication = ConfidentialClientApplication
128+ .builder (applicationClientID , credential ).executorService (executorService ).authority (stsurl )
129+ .build ();
130+ CompletableFuture <IAuthenticationResult > future = clientApplication
131+ .acquireToken (ClientCredentialParameters .builder (scopes ).build ());
132+
133+ IAuthenticationResult authenticationResult = future .get ();
134+ String accessToken = authenticationResult .accessToken ();
135+ long expiresOn = authenticationResult .expiresOnDate ().getTime ();
136+
137+ ACCESS_TOKEN_CALLBACK = accessToken ;
138+
139+ if (expireTokenToggle ) {
140+ Date now = new Date ();
141+ long minutesToExpireWithin = TEST_TOKEN_EXPIRY_SECONDS * 1000 ; // Expire within 2 minutes
142+ return new SqlAuthenticationToken (accessToken , now .getTime () + minutesToExpireWithin );
143+ } else {
144+ return new SqlAuthenticationToken (accessToken , expiresOn );
145+ }
146+ } catch (Exception e ) {
147+ fail (TestResource .getResource ("R_unexpectedException" ) + e .getMessage ());
148+ }
149+ return null ;
150+ }
151+ };
152+
153+ public static void setAccessTokenExpiry (Object con , String accessToken ) {
154+ Field fedAuthTokenField ;
155+ try {
156+ fedAuthTokenField = SQLServerConnection .class .getDeclaredField ("fedAuthToken" );
157+ fedAuthTokenField .setAccessible (true );
158+
159+ Date newExpiry = new Date (
160+ System .currentTimeMillis () + TimeUnit .SECONDS .toMillis (TEST_TOKEN_EXPIRY_SECONDS ));
161+ SqlAuthenticationToken newFedAuthToken = new SqlAuthenticationToken (accessToken , newExpiry );
162+ fedAuthTokenField .set (con , newFedAuthToken );
163+ } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e ) {
164+ Assert .fail ("Failed to set token expiry: " + e .getMessage ());
165+ }
166+ }
167+
168+ public static void setAccessTokenExpiry (Object con ) {
169+ Field fedAuthTokenField ;
170+ Field wrappedConnection ;
171+ try {
172+ fedAuthTokenField = SQLServerConnection .class .getDeclaredField ("fedAuthToken" );
173+ fedAuthTokenField .setAccessible (true );
174+
175+ wrappedConnection = SQLServerConnectionPoolProxy .class .getDeclaredField ("wrappedConnection" );
176+ wrappedConnection .setAccessible (true );
177+ Object wrappedConnectionObj = wrappedConnection .get (con );
178+
179+ Date newExpiry = new Date (
180+ System .currentTimeMillis () + TimeUnit .SECONDS .toMillis (TEST_TOKEN_EXPIRY_SECONDS ));
181+ SqlAuthenticationToken newFedAuthToken = new SqlAuthenticationToken (ACCESS_TOKEN_CALLBACK , newExpiry );
182+ fedAuthTokenField .set (wrappedConnectionObj , newFedAuthToken );
183+ } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e ) {
184+ Assert .fail ("Failed to set token expiry: " + e .getMessage ());
185+ }
186+ }
187+
78188 private TestUtils () {}
79189
80190 /**
@@ -127,6 +237,15 @@ public static boolean isAzureMI(Connection con) {
127237 return ((SQLServerConnection ) con ).isAzureMI ();
128238 }
129239
240+ /**
241+ * Checks if connection is established to Azure Synapse OnDemand server
242+ *
243+ */
244+ public static boolean isAzureSynapseOnDemand (Connection con ) {
245+ isAzure (con );
246+ return ((SQLServerConnection ) con ).isAzureSynapseOnDemandEndpoint ();
247+ }
248+
130249 /**
131250 * Checks if connection is established to server that supports AEv2.
132251 *
@@ -300,6 +419,18 @@ public static void dropTableIfExists(String tableName, java.sql.Statement stmt)
300419 dropObjectIfExists (tableName , "U" , stmt );
301420 }
302421
422+ public static void dropTableWithSchemaIfExists (String tableNameWithSchema ,
423+ java .sql .Statement stmt ) throws SQLException {
424+ stmt .execute (
425+ "IF OBJECT_ID('" + tableNameWithSchema + "', 'U') IS NOT NULL DROP TABLE " + tableNameWithSchema + ";" );
426+ }
427+
428+ public static void dropProcedureWithSchemaIfExists (String procedureWithSchema ,
429+ java .sql .Statement stmt ) throws SQLException {
430+ stmt .execute ("IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'" + procedureWithSchema
431+ + "') AND type in (N'P', N'PC')) DROP PROCEDURE " + procedureWithSchema + ";" );
432+ }
433+
303434 /**
304435 * Deletes the contents of a table.
305436 *
@@ -376,7 +507,8 @@ public static void dropTypeIfExists(String typeName, java.sql.Statement stmt) th
376507 * @throws SQLException
377508 */
378509 public static void dropUserDefinedTypeIfExists (String typeName , Statement stmt ) throws SQLException {
379- stmt .executeUpdate ("IF EXISTS (select * from sys.types where name = '" + escapeSingleQuotes (typeName ) + "') DROP TYPE " + typeName );
510+ stmt .executeUpdate ("IF EXISTS (select * from sys.types where name = '" + escapeSingleQuotes (typeName )
511+ + "') DROP TYPE " + typeName );
380512 }
381513
382514 /**
@@ -403,7 +535,31 @@ public static void dropDatabaseIfExists(String databaseName, String connectionSt
403535 */
404536 public static void dropSchemaIfExists (String schemaName , Statement stmt ) throws SQLException {
405537 stmt .execute ("if EXISTS (SELECT * FROM sys.schemas where name = '" + escapeSingleQuotes (schemaName )
406- + "') drop schema " + AbstractSQLGenerator .escapeIdentifier (schemaName ));
538+ + "') DROP SCHEMA" + AbstractSQLGenerator .escapeIdentifier (schemaName ));
539+ }
540+
541+ /**
542+ * mimic "DROP USER..."
543+ *
544+ * @param userName
545+ * @param stmt
546+ * @throws SQLException
547+ */
548+ public static void dropUserIfExists (String userName , Statement stmt ) throws SQLException {
549+ stmt .execute ("IF EXISTS (SELECT * FROM sys.sysusers where name = '" + escapeSingleQuotes (userName )
550+ + "') DROP USER " + AbstractSQLGenerator .escapeIdentifier (userName ));
551+ }
552+
553+ /**
554+ * mimic "DROP LOGIN..."
555+ *
556+ * @param userName
557+ * @param stmt
558+ * @throws SQLException
559+ */
560+ public static void dropLoginIfExists (String userName , Statement stmt ) throws SQLException {
561+ stmt .execute ("IF EXISTS (SELECT * FROM sys.sysusers where name = '" + escapeSingleQuotes (userName )
562+ + "') DROP LOGIN " + AbstractSQLGenerator .escapeIdentifier (userName ));
407563 }
408564
409565 /**
@@ -964,4 +1120,27 @@ private static java.security.cert.Certificate getCertificate(String certname) th
9641120 return cf .generateCertificate (is );
9651121 }
9661122 }
1123+
1124+ public static String getConnectionID (
1125+ SQLServerPooledConnection pc ) throws ClassNotFoundException , NoSuchFieldException , IllegalAccessException {
1126+ Class <?> pooledConnection = Class .forName ("com.microsoft.sqlserver.jdbc.SQLServerPooledConnection" );
1127+ Class <?> connection = Class .forName ("com.microsoft.sqlserver.jdbc.SQLServerConnection" );
1128+
1129+ Field physicalConnection = pooledConnection .getDeclaredField ("physicalConnection" );
1130+ Field traceID = connection .getDeclaredField ("traceID" );
1131+
1132+ physicalConnection .setAccessible (true );
1133+ traceID .setAccessible (true );
1134+
1135+ SQLServerConnection conn = (SQLServerConnection ) physicalConnection .get (pc );
1136+ return (String ) traceID .get (conn );
1137+ }
1138+
1139+ public static void freeProcCache (Statement stmt ) {
1140+ try {
1141+ stmt .execute ("DBCC FREEPROCCACHE" );
1142+ } catch (Exception e ) {
1143+ // ignore error - some tests fails due to permission issues from managed identity, this does not seem to affect tests
1144+ }
1145+ }
9671146}
0 commit comments