diff --git a/pom.xml b/pom.xml
index ad2156bc0..89740e8f8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -50,19 +50,21 @@
xAzureSQLDB - - - - For tests not compatible with Azure SQL Database - -
xAzureSQLDW - - - - For tests not compatible with Azure Data Warehouse -
xAzureSQLMI - - - - For tests not compatible with Azure SQL Managed Instance
- NTLM - - - - - - - For tests using NTLM Authentication mode (excluded by default)
- reqExternalSetup - For tests requiring external setup (excluded by default)
- clientCertAuth - - For tests requiring client certificate authentication setup (excluded by default)
+ NTLM - - - For tests using NTLM Authentication mode (excluded by default)
+ kerberos - - - - For tests using Kerberos authentication (excluded by default)
+ reqExternalSetup - For tests requiring external setup (excluded by default)
+ clientCertAuth - - For tests requiring client certificate authentication
+ setup (excluded by default) - - - - - - - - - - - - - - - - - - - - - - -
+ requireSecret - For tests requiring setting up secrets manually
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Default testing enabled with SQL Server 2019 (SQLv15) -->
- xSQLv12,xSQLv15,NTLM,MSI,reqExternalSetup,clientCertAuth,fedAuth
-
+ xSQLv12,xSQLv15,NTLM,MSI,reqExternalSetup,clientCertAuth,fedAuth,kerberos,requireSecret
6.0.0
- 4.3.6
+ 4.9.2
1.4.3
5.0.0
4.9.3
@@ -232,7 +234,30 @@
test
-
+
+
+ central
+ https://sqlclientdrivers.pkgs.visualstudio.com/public/_packaging/mssql-jdbc/maven/v1
+
+ true
+
+
+ true
+
+
+
+
+
+ central
+ https://sqlclientdrivers.pkgs.visualstudio.com/public/_packaging/mssql-jdbc/maven/v1
+
+ true
+
+
+ true
+
+
+
jre8
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/AESetup.java b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/AESetup.java
index dbd270451..ca71de69e 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/AESetup.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/AESetup.java
@@ -177,17 +177,25 @@ static void setAEConnectionString(String serverName, String url, String protocol
if (!isSqlLinux() && null != serverName && null != url && null != protocol) {
enclaveProperties = "serverName=" + serverName + ";" + Constants.ENCLAVE_ATTESTATIONURL + "=" + url + ";"
+ Constants.ENCLAVE_ATTESTATIONPROTOCOL + "=" + protocol;
- AETestConnectionString = connectionString + ";sendTimeAsDateTime=false" + ";columnEncryptionSetting=enabled"
- + ";" + enclaveProperties;
+ AETestConnectionString = connectionString + ";sendTimeAsDateTime=false;columnEncryptionSetting=enabled;"
+ + enclaveProperties;
// show progress if testing multiple servers
if (enclaveServer.length > 1) {
System.out.println("Testing enclave: " + enclaveProperties);
}
+
+ // remove the password in connection string
+ // this is necessary as updateDataSource will only use 1st occurrence
+ String password = getConfiguredProperty("enclaveServerPassword");
+ AETestConnectionString = TestUtils.addOrOverrideProperty(AETestConnectionString, Constants.PASSWORD,
+ password);
} else {
- AETestConnectionString = connectionString + ";sendTimeAsDateTime=false"
- + ";columnEncryptionSetting=enabled";
+ AETestConnectionString = connectionString + ";sendTimeAsDateTime=false;columnEncryptionSetting=enabled;";
}
+
+ // TODO: update AE test servers to support
+ AETestConnectionString += ";encrypt=false;trustServerCertificate=true;";
}
@BeforeAll
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/BulkCopySendTemporalDataTypesAsStringAETest.java b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/BulkCopySendTemporalDataTypesAsStringAETest.java
index d8552ad3d..6dd177ab5 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/BulkCopySendTemporalDataTypesAsStringAETest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/BulkCopySendTemporalDataTypesAsStringAETest.java
@@ -57,6 +57,7 @@
@Tag(Constants.xSQLv12)
@Tag(Constants.xAzureSQLDB)
@Tag(Constants.xAzureSQLDW)
+@Tag(Constants.reqExternalSetup)
public class BulkCopySendTemporalDataTypesAsStringAETest extends AESetup {
static String inputFile = "BulkCopyCSVSendTemporalDataTypesAsStringForBulkCopy.csv";
static String encoding = "UTF-8";
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/CallableStatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/CallableStatementTest.java
index 7fd94b498..0b9f0a4f0 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/CallableStatementTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/CallableStatementTest.java
@@ -51,6 +51,7 @@
@Tag(Constants.xSQLv12)
@Tag(Constants.xAzureSQLDW)
@Tag(Constants.xAzureSQLDB)
+@Tag(Constants.reqExternalSetup)
public class CallableStatementTest extends AESetup {
private static String multiStatementsProcedure = AbstractSQLGenerator
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/EnclaveTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/EnclaveTest.java
index 9b136afd3..b142cd1dd 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/EnclaveTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/EnclaveTest.java
@@ -42,6 +42,7 @@
@Tag(Constants.xAzureSQLDW)
@Tag(Constants.xAzureSQLDB)
@Tag(Constants.reqExternalSetup)
+@Tag(Constants.requireSecret)
public class EnclaveTest extends AESetup {
/**
* Tests basic connection.
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/JDBCEncryptionDecryptionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/JDBCEncryptionDecryptionTest.java
index d4c2771c5..e54011872 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/JDBCEncryptionDecryptionTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/JDBCEncryptionDecryptionTest.java
@@ -29,10 +29,6 @@
import com.azure.identity.ClientSecretCredentialBuilder;
import java.util.Set;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
@@ -64,6 +60,7 @@
@Tag(Constants.xSQLv12)
@Tag(Constants.xAzureSQLDW)
@Tag(Constants.xAzureSQLDB)
+@Tag(Constants.reqExternalSetup)
public class JDBCEncryptionDecryptionTest extends AESetup {
private boolean nullable = false;
@@ -105,8 +102,7 @@ public void testJksName(String serverName, String url, String protocol) throws E
public void testAkvName(String serverName, String url, String protocol) throws Exception {
setAEConnectionString(serverName, url, protocol);
- SQLServerColumnEncryptionAzureKeyVaultProvider akv = new SQLServerColumnEncryptionAzureKeyVaultProvider(
- applicationClientID, applicationKey);
+ SQLServerColumnEncryptionAzureKeyVaultProvider akv = akvProvider;
String keystoreName = "keystoreName";
akv.setName(keystoreName);
assertTrue(akv.getName().equals(keystoreName), "AKV name: " + akv.getName() + " keystoreName: " + keystoreName);
@@ -136,6 +132,7 @@ public void testBadJks(String serverName, String url, String protocol) throws Ex
@SuppressWarnings("unused")
@ParameterizedTest
@MethodSource("enclaveParams")
+ @Tag(Constants.requireSecret)
public void testBadAkvCallback(String serverName, String url, String protocol) throws Exception {
setAEConnectionString(serverName, url, protocol);
@@ -209,8 +206,7 @@ public void testJksBadEncryptColumnEncryptionKey(String serverName, String url,
public void testAkvBadEncryptColumnEncryptionKey(String serverName, String url, String protocol) throws Exception {
setAEConnectionString(serverName, url, protocol);
- SQLServerColumnEncryptionAzureKeyVaultProvider akv = null;
- akv = new SQLServerColumnEncryptionAzureKeyVaultProvider(applicationClientID, applicationKey);
+ SQLServerColumnEncryptionAzureKeyVaultProvider akv = akvProvider;
// null encryptedColumnEncryptionKey
try {
@@ -288,8 +284,7 @@ public void testJksDecryptColumnEncryptionKey(String serverName, String url, Str
public void testAkvDecryptColumnEncryptionKey(String serverName, String url, String protocol) throws Exception {
setAEConnectionString(serverName, url, protocol);
- SQLServerColumnEncryptionAzureKeyVaultProvider akv = null;
- akv = new SQLServerColumnEncryptionAzureKeyVaultProvider(applicationClientID, applicationKey);
+ SQLServerColumnEncryptionAzureKeyVaultProvider akv = akvProvider;
// null akvpath
try {
@@ -2268,6 +2263,7 @@ void testNumerics(SQLServerStatement stmt, String cekName, String[][] table, Str
@ParameterizedTest
@MethodSource("enclaveParams")
@Tag(Constants.reqExternalSetup)
+ @Tag(Constants.requireSecret)
public void testAkvNameWithAuthCallback(String serverName, String url, String protocol) throws Exception {
setAEConnectionString(serverName, url, protocol);
@@ -2286,6 +2282,7 @@ public void testAkvNameWithAuthCallback(String serverName, String url, String pr
@ParameterizedTest
@MethodSource("enclaveParams")
@Tag(Constants.reqExternalSetup)
+ @Tag(Constants.requireSecret)
public void testAkvNameWithTokenCredential(String serverName, String url, String protocol) throws Exception {
setAEConnectionString(serverName, url, protocol);
@@ -2307,6 +2304,7 @@ public void testAkvNameWithTokenCredential(String serverName, String url, String
@ParameterizedTest
@MethodSource("enclaveParams")
@Tag(Constants.reqExternalSetup)
+ @Tag(Constants.requireSecret)
public void testAkvBadEncryptColumnEncryptionKeyWithAuthCallback(String serverName, String url,
String protocol) throws Exception {
setAEConnectionString(serverName, url, protocol);
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/MSITest.java b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/MSITest.java
index 7eec04b70..88cfa2593 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/MSITest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/MSITest.java
@@ -18,6 +18,10 @@
import java.util.Map;
import java.util.Properties;
+import com.azure.identity.CredentialUnavailableException;
+import com.azure.identity.ManagedIdentityCredential;
+import com.azure.identity.ManagedIdentityCredentialBuilder;
+
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
@@ -43,6 +47,7 @@
*/
@RunWith(JUnitPlatform.class)
@Tag(Constants.MSI)
+@Tag(Constants.requireSecret)
public class MSITest extends AESetup {
/*
@@ -365,11 +370,20 @@ private void testNumericAKV(String connStr) throws SQLException {
@BeforeEach
public void registerAKVProvider() throws Exception {
+ try {
// unregister the custom providers registered in AESetup
SQLServerConnection.unregisterColumnEncryptionKeyStoreProviders();
Map map = new HashMap();
- if (null != applicationClientID && null != applicationKey) {
+ if (null != akvProviderManagedClientId) {
+ System.out.println("ManagedIdentityCredential: registering akvProvider");
+
+ ManagedIdentityCredential credential = new ManagedIdentityCredentialBuilder()
+ .clientId(akvProviderManagedClientId).build();
+ akvProvider = new SQLServerColumnEncryptionAzureKeyVaultProvider(credential);
+ map.put(Constants.AZURE_KEY_VAULT_NAME, akvProvider);
+ System.out.println("ManagedIdentityCredential: registered akvProvider");
+ } else if (null != applicationClientID && null != applicationKey) {
File file = null;
try {
file = new File(Constants.MSSQL_JDBC_PROPERTIES);
@@ -386,8 +400,12 @@ public void registerAKVProvider() throws Exception {
file.delete();
}
}
+ System.out.println("applicationClientID: registered akvProvider");
}
SQLServerConnection.registerColumnEncryptionKeyStoreProviders(map);
+ } catch (Exception e) {
+ System.out.println("MSITest registerAKVProvider exception: " +e.getMessage());
+ }
}
}
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/MultiUserAKVTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/MultiUserAKVTest.java
index b597c2052..3852b606e 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/MultiUserAKVTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/MultiUserAKVTest.java
@@ -35,6 +35,8 @@
import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.RunWith;
+import com.azure.identity.ManagedIdentityCredential;
+import com.azure.identity.ManagedIdentityCredentialBuilder;
import com.microsoft.sqlserver.jdbc.RandomUtil;
import com.microsoft.sqlserver.jdbc.SQLServerColumnEncryptionAzureKeyVaultProvider;
import com.microsoft.sqlserver.jdbc.SQLServerColumnEncryptionKeyStoreProvider;
@@ -58,6 +60,7 @@
@Tag(Constants.xSQLv12)
@Tag(Constants.xAzureSQLDW)
@Tag(Constants.xAzureSQLDB)
+@Tag(Constants.reqExternalSetup)
public class MultiUserAKVTest extends AESetup {
private static Map requiredKeyStoreProvider = new HashMap<>();
@@ -99,7 +102,7 @@ public static void testCleanUp() throws Exception {
tempMap.put(Constants.CUSTOM_KEYSTORE_NAME, jksProvider);
}
- if (null != akvProvider && null != applicationClientID && null != applicationKey) {
+ if (null != akvProvider) {
tempMap.put(Constants.AZURE_KEY_VAULT_NAME, akvProvider);
}
@@ -111,7 +114,7 @@ public static void testCleanUp() throws Exception {
@Test
@Tag(Constants.reqExternalSetup)
public void decryptedCekIsCachedDuringDecryption() throws Exception {
- SQLServerColumnEncryptionAzureKeyVaultProvider provider = createAKVProvider();
+ SQLServerColumnEncryptionAzureKeyVaultProvider provider = akvProvider;
if (null == provider) {
fail(TestResource.getResource("R_AKVProviderNull"));
@@ -151,8 +154,9 @@ public void decryptedCekIsCachedDuringDecryption() throws Exception {
@Test
@Tag(Constants.reqExternalSetup)
+ @Tag(Constants.requireSecret)
public void signatureVerificationResultIsCachedDuringVerification() throws Exception {
- SQLServerColumnEncryptionAzureKeyVaultProvider provider = createAKVProvider();
+ SQLServerColumnEncryptionAzureKeyVaultProvider provider = akvProvider;
if (provider == null) {
fail(TestResource.getResource("R_AKVProviderNull"));
@@ -184,7 +188,7 @@ public void signatureVerificationResultIsCachedDuringVerification() throws Excep
@Test
@Tag(Constants.reqExternalSetup)
public void cekCacheEntryIsEvictedAfterTtlExpires() throws Exception {
- SQLServerColumnEncryptionAzureKeyVaultProvider provider = createAKVProvider();
+ SQLServerColumnEncryptionAzureKeyVaultProvider provider = akvProvider;
if (provider == null) {
fail(TestResource.getResource("R_AKVProviderNull"));
@@ -212,7 +216,7 @@ public void cekCacheEntryIsEvictedAfterTtlExpires() throws Exception {
@Test
@Tag(Constants.reqExternalSetup)
public void cekCacheShouldBeDisabledWhenAkvProviderIsRegisteredGlobally() throws Exception {
- SQLServerColumnEncryptionAzureKeyVaultProvider provider = createAKVProvider();
+ SQLServerColumnEncryptionAzureKeyVaultProvider provider = akvProvider;
if (provider == null) {
fail(TestResource.getResource("R_AKVProviderNull"));
@@ -256,8 +260,9 @@ public void cekCacheShouldBeDisabledWhenAkvProviderIsRegisteredGlobally() throws
@Test
@Tag(Constants.reqExternalSetup)
+ @Tag(Constants.requireSecret)
public void testLocalCekCacheIsScopedToProvider() throws Exception {
- SQLServerColumnEncryptionAzureKeyVaultProvider provider = createAKVProvider();
+ SQLServerColumnEncryptionAzureKeyVaultProvider provider = akvProvider;
if (provider == null) {
fail(TestResource.getResource("R_AKVProviderNull"));
@@ -268,6 +273,8 @@ public void testLocalCekCacheIsScopedToProvider() throws Exception {
fail((new MessageFormat(TestResource.getResource("R_objectNullOrEmpty"))).format(msgArg));
}
+ SQLServerConnection.unregisterColumnEncryptionKeyStoreProviders();
+
SQLServerConnection.unregisterColumnEncryptionKeyStoreProviders();
Map providerMap = new HashMap();
providerMap.put(Constants.AZURE_KEY_VAULT_NAME, akvProvider);
@@ -325,7 +332,7 @@ public void testLocalCekCacheIsScopedToProvider() throws Exception {
}
fail(TestResource.getResource("R_expectedExceptionNotThrown"));
} catch (SQLServerException ex) {
- assertTrue(ex.getMessage().contains("AADSTS700016"));
+ org.junit.jupiter.api.Assertions.assertTrue(ex.getMessage().contains("AADSTS700016"), ex.getMessage());
}
} finally {
dropObject(AETestConnectionString, "TABLE", customProviderTableName);
@@ -608,31 +615,4 @@ private int getCacheSize(String methodName,
return (int) method.invoke(provider);
}
-
- private SQLServerColumnEncryptionAzureKeyVaultProvider createAKVProvider() throws Exception {
-
- SQLServerColumnEncryptionAzureKeyVaultProvider azureKeyVaultProvider = null;
-
- if (null != applicationClientID && null != applicationKey) {
- File file = null;
- try {
- file = new File(Constants.MSSQL_JDBC_PROPERTIES);
- try (OutputStream os = new FileOutputStream(file);) {
- Properties props = new Properties();
- // Append to the list of hardcoded endpoints
- props.setProperty(Constants.AKV_TRUSTED_ENDPOINTS_KEYWORD, ";vault.azure.net");
- props.store(os, "");
- }
- azureKeyVaultProvider = new SQLServerColumnEncryptionAzureKeyVaultProvider(applicationClientID,
- applicationKey);
-
- } finally {
- if (null != file) {
- file.delete();
- }
- }
- }
-
- return azureKeyVaultProvider;
- }
}
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/PrecisionScaleTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/PrecisionScaleTest.java
index 5343b1fcd..162fe8cef 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/PrecisionScaleTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/PrecisionScaleTest.java
@@ -42,6 +42,7 @@
@Tag(Constants.xSQLv12)
@Tag(Constants.xAzureSQLDW)
@Tag(Constants.xAzureSQLDB)
+@Tag(Constants.reqExternalSetup)
public class PrecisionScaleTest extends AESetup {
private static java.util.Date date = null;
private static int offsetFromGMT = 0;
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/RegressionAlwaysEncryptedTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/RegressionAlwaysEncryptedTest.java
index c30dc0e50..1644bfc59 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/RegressionAlwaysEncryptedTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/AlwaysEncrypted/RegressionAlwaysEncryptedTest.java
@@ -29,6 +29,7 @@
@Tag(Constants.xSQLv12)
@Tag(Constants.xAzureSQLDW)
@Tag(Constants.xAzureSQLDB)
+@Tag(Constants.reqExternalSetup)
public class RegressionAlwaysEncryptedTest extends AESetup {
static String numericTable[][] = {{"Bit", "bit"}, {"Tinyint", "tinyint"}, {"Smallint", "smallint"},};
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java
index 5330d3db0..d1b2e7177 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java
@@ -644,13 +644,22 @@ public void testIncorrectDatabase() throws SQLException {
assertTrue(timeDiff <= milsecs, form.format(msgArgs));
}
} catch (Exception e) {
- assertTrue(e.getMessage().contains(TestResource.getResource("R_cannotOpenDatabase")));
+ assertTrue(
+ e.getMessage().contains(TestResource.getResource("R_cannotOpenDatabase"))
+ || (TestUtils.getProperty(connectionString, "msiClientId") != null
+ && e.getMessage().toLowerCase()
+ .contains(TestResource.getResource("R_loginFailedMI").toLowerCase())),
+ e.getMessage());
timerEnd = System.currentTimeMillis();
}
}
@Test
public void testIncorrectUserName() throws SQLException {
+ String auth = TestUtils.getProperty(connectionString, "authentication");
+ org.junit.Assume.assumeTrue(auth != null
+ && (auth.equalsIgnoreCase("SqlPassword") || auth.equalsIgnoreCase("ActiveDirectoryPassword")));
+
long timerStart = 0;
long timerEnd = 0;
final long milsecs = threshHoldForNoRetryInMilliseconds;
@@ -668,13 +677,22 @@ public void testIncorrectUserName() throws SQLException {
assertTrue(timeDiff <= milsecs, form.format(msgArgs));
}
} catch (Exception e) {
- assertTrue(e.getMessage().contains(TestResource.getResource("R_loginFailed")));
+ assertTrue(
+ e.getMessage().contains(TestResource.getResource("R_loginFailed"))
+ || (TestUtils.getProperty(connectionString, "msiClientId") != null
+ && e.getMessage().toLowerCase()
+ .contains(TestResource.getResource("R_loginFailedMI").toLowerCase())),
+ e.getMessage());
timerEnd = System.currentTimeMillis();
}
}
@Test
public void testIncorrectPassword() throws SQLException {
+ String auth = TestUtils.getProperty(connectionString, "authentication");
+ org.junit.Assume.assumeTrue(auth != null
+ && (auth.equalsIgnoreCase("SqlPassword") || auth.equalsIgnoreCase("ActiveDirectoryPassword")));
+
long timerStart = 0;
long timerEnd = 0;
final long milsecs = threshHoldForNoRetryInMilliseconds;
@@ -692,7 +710,12 @@ public void testIncorrectPassword() throws SQLException {
assertTrue(timeDiff <= milsecs, form.format(msgArgs));
}
} catch (Exception e) {
- assertTrue(e.getMessage().contains(TestResource.getResource("R_loginFailed")));
+ assertTrue(
+ e.getMessage().contains(TestResource.getResource("R_loginFailed"))
+ || (TestUtils.getProperty(connectionString, "msiClientId") != null
+ && e.getMessage().toLowerCase()
+ .contains(TestResource.getResource("R_loginFailedMI").toLowerCase())),
+ e.getMessage());
timerEnd = System.currentTimeMillis();
}
}
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java
index dd664bc72..3182654f9 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java
@@ -199,7 +199,9 @@ protected Object[][] getContents() {
{"R_AKVProviderNull", "The Azure key store provider is null."},
{"R_objectNullOrEmpty", "The {0} is null or empty."},
{"R_cekDecryptionFailed", "Failed to decrypt a column encryption key using key store provider: {0}."},
+ {"R_failedFedauth", "Failed to acquire fedauth token: "},
{"R_connectTimedOut", "connect timed out"},
{"R_queryCanceled", "The query was canceled."},
+ {"R_loginFailedMI", "Login failed for user ''"},
{"R_sessionKilled", "Cannot continue the execution because the session is in the kill state"}};
}
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/TimeoutTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/TimeoutTest.java
index ba084cb8f..cde1e6c31 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/TimeoutTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/TimeoutTest.java
@@ -168,87 +168,103 @@ public void testConnectRetryBadServer() {
// Test connect retry for database error
@Test
public void testConnectRetryServerError() {
- long timerEnd = 0;
+ String auth = TestUtils.getProperty(connectionString, "authentication");
+ org.junit.Assume.assumeTrue(auth != null
+ && (auth.equalsIgnoreCase("SqlPassword") || auth.equalsIgnoreCase("ActiveDirectoryPassword")));
+
+ long totalTime = 0;
long timerStart = System.currentTimeMillis();
+ int interval = defaultTimeout; // long interval so we can tell if there was a retry
+ long timeout = defaultTimeout * 2; // long loginTimeout to accommodate the long interval
- // non existent database with interval < loginTimeout this will generate a 4060 transient error and retry
- int connectRetryCount = new Random().nextInt(256);
- int connectRetryInterval = new Random().nextInt(defaultTimeout) + 1;
+ // non existent database with interval < loginTimeout this will generate a 4060 transient error and retry 1 time
try (Connection con = PrepUtil.getConnection(
TestUtils.addOrOverrideProperty(connectionString, "database", RandomUtil.getIdentifier("database"))
- + ";logintimeout=" + defaultTimeout + ";connectRetryCount=" + connectRetryCount
- + ";connectRetryInterval=" + connectRetryInterval)) {
+ + ";loginTimeout=" + timeout + ";connectRetryCount=" + 1 + ";connectRetryInterval=" + interval
+ + ";transparentNetworkIPResolution=false")) {
fail(TestResource.getResource("R_shouldNotConnect"));
} catch (Exception e) {
- assertTrue((e.getMessage().contains(TestResource.getResource("R_cannotOpenDatabase")))
- || ((isSqlAzure() || isSqlAzureDW())
- ? e.getMessage().contains(
- TestResource.getResource("R_connectTimedOut"))
- : false),
+ totalTime = System.currentTimeMillis() - timerStart;
+
+ assertTrue((e.getMessage().toLowerCase().contains(
+ TestResource.getResource("R_cannotOpenDatabase").toLowerCase())) || (TestUtils.getProperty(
+ connectionString, "msiClientId") != null && e.getMessage().toLowerCase()
+ .contains(TestResource.getResource("R_loginFailedMI").toLowerCase())) || ((isSqlAzure() || isSqlAzureDW()) ? e.getMessage().toLowerCase()
+ .contains(TestResource.getResource("R_connectTimedOut").toLowerCase()) : false),
e.getMessage());
- timerEnd = System.currentTimeMillis();
}
-
- // connect + all retries should always be <= loginTimeout
- verifyTimeout(timerEnd - timerStart, defaultTimeout);
}
// Test connect retry for database error using Datasource
@Test
public void testConnectRetryServerErrorDS() {
- long timerEnd = 0;
- long timerStart = System.currentTimeMillis();
+ String auth = TestUtils.getProperty(connectionString, "authentication");
+ org.junit.Assume.assumeTrue(auth != null
+ && (auth.equalsIgnoreCase("SqlPassword") || auth.equalsIgnoreCase("ActiveDirectoryPassword")));
- // non existent database with interval < loginTimeout this will generate a 4060 transient error and retry
- int connectRetryCount = new Random().nextInt(256);
- int connectRetryInterval = new Random().nextInt(defaultTimeout) + 1;
+ long totalTime = 0;
+ long timerStart = System.currentTimeMillis();
+ int interval = defaultTimeout; // long interval so we can tell if there was a retry
+ long loginTimeout = defaultTimeout * 2; // long loginTimeout to accommodate the long interval
+ // non existent database with interval < loginTimeout this will generate a 4060 transient error and retry 1 time
SQLServerDataSource ds = new SQLServerDataSource();
String connectStr = TestUtils.addOrOverrideProperty(connectionString, "database",
- RandomUtil.getIdentifier("database")) + ";logintimeout=" + defaultTimeout + ";connectRetryCount="
- + connectRetryCount + ";connectRetryInterval=" + connectRetryInterval;
+ RandomUtil.getIdentifier("database")) + ";logintimeout=" + loginTimeout + ";connectRetryCount=1"
+ + ";connectRetryInterval=" + interval;
updateDataSource(connectStr, ds);
try (Connection con = PrepUtil.getConnection(connectStr)) {
fail(TestResource.getResource("R_shouldNotConnect"));
} catch (Exception e) {
- assertTrue((e.getMessage().contains(TestResource.getResource("R_cannotOpenDatabase")))
- || ((isSqlAzure() || isSqlAzureDW())
- ? e.getMessage().contains(
- TestResource.getResource("R_connectTimedOut"))
- : false),
+ assertTrue(
+ (e.getMessage().toLowerCase()
+ .contains(TestResource.getResource("R_cannotOpenDatabase").toLowerCase()))
+ || (TestUtils.getProperty(connectionString, "msiClientId") != null && e.getMessage()
+ .toLowerCase().contains(TestResource.getResource("R_loginFailedMI").toLowerCase()))
+ || ((isSqlAzure() || isSqlAzureDW()) ? e.getMessage().toLowerCase()
+ .contains(TestResource.getResource("R_connectTimedOut").toLowerCase()) : false),
e.getMessage());
- timerEnd = System.currentTimeMillis();
+ totalTime = System.currentTimeMillis() - timerStart;
}
- // connect + all retries should always be <= loginTimeout
- verifyTimeout(timerEnd - timerStart, defaultTimeout);
+ // 1 retry should be at least 1 interval long but < 2 intervals
+ assertTrue(TimeUnit.SECONDS.toMillis(interval) < totalTime,
+ "interval: " + TimeUnit.SECONDS.toMillis(interval) + " total time: " + totalTime);
+ assertTrue(totalTime < TimeUnit.SECONDS.toMillis(2 * interval),
+ "total time: " + totalTime + " 2 * interval: " + TimeUnit.SECONDS.toMillis(2 * interval));
}
// Test connect retry for database error with loginTimeout
@Test
public void testConnectRetryTimeout() {
- long timerEnd = 0;
+ long totalTime = 0;
long timerStart = System.currentTimeMillis();
+ int interval = defaultTimeout; // long interval so we can tell if there was a retry
int loginTimeout = 2;
- // non existent server with very short loginTimeout so there is no time to do all retries
+ // non existent database with very short loginTimeout so there is no time to do any retry
try (Connection con = PrepUtil.getConnection(
TestUtils.addOrOverrideProperty(connectionString, "database", RandomUtil.getIdentifier("database"))
- + "connectRetryCount=" + (new Random().nextInt(256)) + ";connectRetryInterval="
- + (new Random().nextInt(defaultTimeout - 1) + 1) + ";loginTimeout=" + loginTimeout)) {
+ + "connectRetryCount=" + (new Random().nextInt(256)) + ";connectRetryInterval=" + interval
+ + ";loginTimeout=" + loginTimeout)) {
fail(TestResource.getResource("R_shouldNotConnect"));
} catch (Exception e) {
- assertTrue((e.getMessage().contains(TestResource.getResource("R_cannotOpenDatabase")))
- || ((isSqlAzure() || isSqlAzureDW())
- ? e.getMessage().contains(
- TestResource.getResource("R_connectTimedOut"))
- : false),
+ totalTime = System.currentTimeMillis() - timerStart;
+
+ assertTrue(
+ (e.getMessage().toLowerCase()
+ .contains(TestResource.getResource("R_cannotOpenDatabase").toLowerCase()))
+ || (TestUtils.getProperty(connectionString, "msiClientId") != null && e.getMessage()
+ .toLowerCase().contains(TestResource.getResource("R_loginFailedMI").toLowerCase()))
+ || ((isSqlAzure() || isSqlAzureDW()) ? e.getMessage().toLowerCase()
+ .contains(TestResource.getResource("R_connectTimedOut").toLowerCase()) : false),
e.getMessage());
- timerEnd = System.currentTimeMillis();
}
- verifyTimeout(timerEnd - timerStart, loginTimeout);
+ // if there was a retry then it would take at least 1 interval long, so if < interval means there were no retries
+ assertTrue(totalTime < TimeUnit.SECONDS.toMillis(interval),
+ "total time: " + totalTime + " interval: " + TimeUnit.SECONDS.toMillis(interval));
}
@Test
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/XADataSourceTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/XADataSourceTest.java
index 7b867f23a..0fe9a1e12 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/XADataSourceTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/XADataSourceTest.java
@@ -7,7 +7,6 @@
import javax.sql.XAConnection;
-import com.microsoft.sqlserver.jdbc.TestUtils;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
@@ -22,11 +21,10 @@
@RunWith(JUnitPlatform.class)
@Tag(Constants.reqExternalSetup)
public class XADataSourceTest extends AbstractTest {
- private static String connectionUrlSSL = connectionString + ";encrypt=true;trustServerCertificate=false;";
+ private static String connectionUrlSSL = connectionString;
@BeforeAll
public static void setupTests() throws Exception {
- connectionString = TestUtils.addOrOverrideProperty(connectionString,"trustServerCertificate", "true");
setConnection();
}
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/ConcurrentLoginTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/ConcurrentLoginTest.java
index 6b8509978..cde66ae2e 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/ConcurrentLoginTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/ConcurrentLoginTest.java
@@ -23,6 +23,7 @@
@RunWith(JUnitPlatform.class)
@Tag(Constants.fedAuth)
+@Tag(Constants.requireSecret)
public class ConcurrentLoginTest extends FedauthCommon {
final AtomicReference throwableRef = new AtomicReference();
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/ConnectionSuspensionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/ConnectionSuspensionTest.java
index c23d6f813..559da4074 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/ConnectionSuspensionTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/ConnectionSuspensionTest.java
@@ -31,6 +31,7 @@
@RunWith(JUnitPlatform.class)
@Tag("slow")
@Tag(Constants.fedAuth)
+@Tag(Constants.requireSecret)
public class ConnectionSuspensionTest extends FedauthCommon {
static String charTable = TestUtils.escapeSingleQuotes(
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/ErrorMessageTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/ErrorMessageTest.java
index 0630c05c5..efa35e500 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/ErrorMessageTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/ErrorMessageTest.java
@@ -26,6 +26,7 @@
@RunWith(JUnitPlatform.class)
@Tag(Constants.fedAuth)
+@Tag(Constants.requireSecret)
public class ErrorMessageTest extends FedauthCommon {
String badUserName = "abc" + azureUserName;
@@ -402,8 +403,11 @@ public void testADPasswordWrongPasswordWithConnectionStringUserName() throws SQL
assertTrue(INVALID_EXCEPTION_MSG + ": " + e.getMessage(), e.getMessage()
.contains(ERR_MSG_FAILED_AUTHENTICATE + " the user " + azureUserName
+ " in Active Directory (Authentication=ActiveDirectoryPassword).")
- && (e.getCause().getCause().getMessage().toLowerCase().contains("invalid username or password")
- || e.getCause().getCause().getMessage().contains(ERR_MSG_SIGNIN_TOO_MANY)));
+ && e.getCause().getCause().getMessage().toLowerCase().contains("invalid username or password")
+ || e.getCause().getCause().getMessage().contains(ERR_MSG_SIGNIN_TOO_MANY)
+ || e.getCause().getCause().getMessage().contains(ERR_FAULT_ID3342)
+ || e.getMessage().contains(ERR_MSG_REQUEST_THROTTLED)
+ || e.getMessage().contains(ERR_FAULT_AUTH_FAIL));
}
}
@@ -427,8 +431,11 @@ public void testADPasswordWrongPasswordWithDatasource() throws SQLException {
assertTrue(INVALID_EXCEPTION_MSG + ": " + e.getMessage(), e.getMessage()
.contains(ERR_MSG_FAILED_AUTHENTICATE + " the user " + azureUserName
+ " in Active Directory (Authentication=ActiveDirectoryPassword).")
- && (e.getCause().getCause().getMessage().toLowerCase().contains("invalid username or password")
- || e.getCause().getCause().getMessage().contains(ERR_MSG_SIGNIN_TOO_MANY)));
+ && e.getCause().getCause().getMessage().toLowerCase().contains("invalid username or password")
+ || e.getCause().getCause().getMessage().contains(ERR_MSG_SIGNIN_TOO_MANY)
+ || e.getCause().getCause().getMessage().contains(ERR_FAULT_ID3342)
+ || e.getMessage().contains(ERR_MSG_REQUEST_THROTTLED)
+ || e.getMessage().contains(ERR_FAULT_AUTH_FAIL));
}
}
@@ -446,8 +453,11 @@ public void testADPasswordWrongPasswordWithConnectionStringUser() throws SQLExce
assertTrue(INVALID_EXCEPTION_MSG + ": " + e.getMessage(), e.getMessage()
.contains(ERR_MSG_FAILED_AUTHENTICATE + " the user " + azureUserName
+ " in Active Directory (Authentication=ActiveDirectoryPassword).")
- && (e.getCause().getCause().getMessage().toLowerCase().contains("invalid username or password")
- || e.getCause().getCause().getMessage().contains(ERR_MSG_SIGNIN_TOO_MANY)));
+ && e.getCause().getCause().getMessage().toLowerCase().contains("invalid username or password")
+ || e.getCause().getCause().getMessage().contains(ERR_MSG_SIGNIN_TOO_MANY)
+ || e.getCause().getCause().getMessage().contains(ERR_FAULT_ID3342)
+ || e.getMessage().contains(ERR_MSG_REQUEST_THROTTLED)
+ || e.getMessage().contains(ERR_FAULT_AUTH_FAIL));
}
}
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/FedauthCommon.java b/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/FedauthCommon.java
index de29d9158..d9b3be8c3 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/FedauthCommon.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/FedauthCommon.java
@@ -7,7 +7,15 @@
import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
+import com.azure.core.credential.AccessToken;
+import com.azure.core.credential.TokenRequestContext;
+import com.azure.identity.ManagedIdentityCredential;
+import com.azure.identity.ManagedIdentityCredentialBuilder;
+import com.microsoft.aad.msal4j.ClientCredentialFactory;
+import com.microsoft.aad.msal4j.ClientCredentialParameters;
+import com.microsoft.aad.msal4j.ConfidentialClientApplication;
import com.microsoft.aad.msal4j.IAuthenticationResult;
+import com.microsoft.aad.msal4j.MsalThrottlingException;
import com.microsoft.aad.msal4j.PublicClientApplication;
import com.microsoft.aad.msal4j.UserNamePasswordParameters;
import java.sql.Connection;
@@ -42,11 +50,7 @@ public class FedauthCommon extends AbstractTest {
static String azureUserName = null;
static String azurePassword = null;
static String azureGroupUserName = null;
- static String azureAADPrincipialId = null;
- static String azureAADPrincipialSecret = null;
-
static boolean enableADIntegrated = false;
-
static String spn = null;
static String stsurl = null;
static String fedauthClientId = null;
@@ -72,6 +76,8 @@ public class FedauthCommon extends AbstractTest {
static final String ERR_MSG_HAS_CLOSED = TestResource.getResource("R_hasClosed");
static final String ERR_MSG_HAS_BEEN_CLOSED = TestResource.getResource("R_hasBeenClosed");
static final String ERR_MSG_SIGNIN_TOO_MANY = TestResource.getResource("R_signinTooManyTimes");
+ static final String ERR_FAULT_ID3342 = "FaultMessage: ID3242";
+ static final String ERR_FAULT_AUTH_FAIL = "FaultMessage: Authentication Failure";
static final String ERR_MSG_NOT_AUTH_AND_IS = TestUtils.R_BUNDLE
.getString("R_SetAuthenticationWhenIntegratedSecurityTrue");
static final String ERR_MSG_NOT_AUTH_AND_USER_PASSWORD = TestUtils.R_BUNDLE
@@ -80,6 +86,8 @@ public class FedauthCommon extends AbstractTest {
static final String ERR_MSG_RESULTSET_IS_CLOSED = TestUtils.R_BUNDLE.getString("R_resultsetClosed");
static final String ERR_MSG_SOCKET_CLOSED = TestResource.getResource("R_socketClosed");
static final String ERR_TCPIP_CONNECTION = TestResource.getResource("R_tcpipConnectionToHost");
+ static final String ERR_MSG_REQUEST_THROTTLED = "Request was throttled";
+ static final String ERR_FAILED_FEDAUTH = TestResource.getResource("R_failedFedauth");
enum SqlAuthentication {
NotSpecified,
@@ -124,8 +132,6 @@ public static void getConfigs() throws Exception {
azureUserName = getConfiguredProperty("azureUserName");
azurePassword = getConfiguredProperty("azurePassword");
azureGroupUserName = getConfiguredProperty("azureGroupUserName");
- azureAADPrincipialId = getConfiguredProperty("AADSecurePrincipalId");
- azureAADPrincipialSecret = getConfiguredProperty("AADSecurePrincipalSecret");
String prop = getConfiguredProperty("enableADIntegrated");
enableADIntegrated = (null != prop && prop.equalsIgnoreCase("true")) ? true : false;
@@ -153,22 +159,50 @@ public static void getConfigs() throws Exception {
*
*/
static void getFedauthInfo() {
- try {
+ int retry = 0;
+ long interval = THROTTLE_RETRY_INTERVAL;
+ ManagedIdentityCredential credential = new ManagedIdentityCredentialBuilder()
+ .clientId(akvProviderManagedClientId).build();
+
+ while (retry <= THROTTLE_RETRY_COUNT) {
+ try {
+ TokenRequestContext requestContext = new TokenRequestContext()
+ .setScopes(Collections.singletonList(spn + "/.default"));
+
+ AccessToken token = credential.getToken(requestContext).block();
+
+ if (token != null) {
+ secondsBeforeExpiration = TimeUnit.MILLISECONDS
+ .toSeconds(token.getExpiresAt().toInstant().toEpochMilli() - new Date().getTime());
+ accessToken = token.getToken();
+ }
- final PublicClientApplication clientApplication = PublicClientApplication.builder(fedauthClientId)
- .executorService(Executors.newFixedThreadPool(1)).authority(stsurl).build();
- final CompletableFuture future = clientApplication
- .acquireToken(UserNamePasswordParameters.builder(Collections.singleton(spn + "/.default"),
- azureUserName, azurePassword.toCharArray()).build());
+ retry = THROTTLE_RETRY_COUNT + 1;
+ } catch (MsalThrottlingException te) {
+ interval = te.retryInMs();
+ if (!checkForRetry(te, retry++, interval)) {
+ te.printStackTrace();
+ fail(ERR_FAILED_FEDAUTH + "no more retries: " + te.getMessage());
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ fail(ERR_FAILED_FEDAUTH + e.getMessage());
+ }
+ }
+ }
- final IAuthenticationResult authenticationResult = future.get();
+ static boolean checkForRetry(Exception e, int retry, long interval) {
+ if (retry > THROTTLE_RETRY_COUNT) {
+ return false;
+ }
+ try {
+ Thread.sleep(interval);
+ } catch (InterruptedException ex) {
+ e.printStackTrace();
- secondsBeforeExpiration = TimeUnit.MILLISECONDS
- .toSeconds(authenticationResult.expiresOnDate().getTime() - new Date().getTime());
- accessToken = authenticationResult.accessToken();
- } catch (Exception e) {
- fail(e.getMessage());
+ fail(ERR_FAILED_FEDAUTH + ex.getMessage());
}
+ return true;
}
void testUserName(Connection conn, String user, SqlAuthentication authentication) throws SQLException {
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/FedauthTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/FedauthTest.java
index 8d8cb5b5d..ec239e6b1 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/FedauthTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/FedauthTest.java
@@ -17,6 +17,7 @@
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
+import java.text.MessageFormat;
import java.util.Properties;
import org.junit.jupiter.api.AfterAll;
@@ -36,6 +37,7 @@
@RunWith(JUnitPlatform.class)
@Tag(Constants.fedAuth)
+@Tag(Constants.requireSecret)
public class FedauthTest extends FedauthCommon {
static String charTable = TestUtils
.escapeSingleQuotes(AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("JDBC_FedAuthTest")));
@@ -253,10 +255,11 @@ public void testCorrectAccessTokenDS() throws SQLException {
* @deprecated
*/
@Test
+ @Tag(Constants.requireSecret)
public void testAADServicePrincipalAuthDeprecated() {
String url = "jdbc:sqlserver://" + azureServer + ";database=" + azureDatabase + ";authentication="
- + SqlAuthentication.ActiveDirectoryServicePrincipal + ";AADSecurePrincipalId=" + azureAADPrincipialId
- + ";AADSecurePrincipalSecret=" + azureAADPrincipialSecret;
+ + SqlAuthentication.ActiveDirectoryServicePrincipal + ";AADSecurePrincipalId=" + applicationClientID
+ + ";AADSecurePrincipalSecret=" + applicationKey;
String urlEncrypted = url + ";encrypt=true;trustServerCertificate=true;";
SQLServerDataSource ds = new SQLServerDataSource();
updateDataSource(url, ds);
@@ -275,10 +278,11 @@ public void testAADServicePrincipalAuthDeprecated() {
* encryption.
*/
@Test
+ @Tag(Constants.requireSecret)
public void testAADServicePrincipalAuth() {
String url = "jdbc:sqlserver://" + azureServer + ";database=" + azureDatabase + ";authentication="
- + SqlAuthentication.ActiveDirectoryServicePrincipal + ";Username=" + azureAADPrincipialId + ";Password="
- + azureAADPrincipialSecret;
+ + SqlAuthentication.ActiveDirectoryServicePrincipal + ";Username=" + applicationClientID + ";Password="
+ + applicationKey;
String urlEncrypted = url + ";encrypt=true;trustServerCertificate=true;";
SQLServerDataSource ds = new SQLServerDataSource();
updateDataSource(url, ds);
@@ -296,36 +300,36 @@ public void testAADServicePrincipalAuth() {
* Test invalid connection property combinations when using AAD Service Principal Authentication.
*/
@Test
+ @Tag(Constants.requireSecret)
public void testAADServicePrincipalAuthWrong() {
String baseUrl = "jdbc:sqlserver://" + azureServer + ";database=" + azureDatabase + ";authentication="
+ SqlAuthentication.ActiveDirectoryServicePrincipal + ";";
// Wrong AADSecurePrincipalSecret provided.
- String url = baseUrl + "AADSecurePrincipalId=" + azureAADPrincipialId + ";AADSecurePrincipalSecret=wrongSecret";
+ String url = baseUrl + "AADSecurePrincipalId=" + applicationClientID + ";AADSecurePrincipalSecret=wrongSecret";
validateException(url, "R_MSALExecution");
// Wrong AADSecurePrincipalId provided.
- url = baseUrl + "AADSecurePrincipalId=wrongId;AADSecurePrincipalSecret=" + azureAADPrincipialSecret;
+ url = baseUrl + "AADSecurePrincipalId=wrongId;AADSecurePrincipalSecret=" + applicationKey;
validateException(url, "R_MSALExecution");
// AADSecurePrincipalSecret/password not provided.
- url = baseUrl + "AADSecurePrincipalId=" + azureAADPrincipialId;
+ url = baseUrl + "AADSecurePrincipalId=" + applicationClientID;
validateException(url, "R_NoUserPasswordForActiveServicePrincipal");
- url = baseUrl + "Username=" + azureAADPrincipialId;
+ url = baseUrl + "Username=" + applicationClientID;
validateException(url, "R_NoUserPasswordForActiveServicePrincipal");
// AADSecurePrincipalId/username not provided.
- url = baseUrl + "AADSecurePrincipalSecret=" + azureAADPrincipialSecret;
+ url = baseUrl + "AADSecurePrincipalSecret=" + applicationKey;
validateException(url, "R_NoUserPasswordForActiveServicePrincipal");
- url = baseUrl + "password=" + azureAADPrincipialSecret;
+ url = baseUrl + "password=" + applicationKey;
validateException(url, "R_NoUserPasswordForActiveServicePrincipal");
// Both AADSecurePrincipalId/username and AADSecurePrincipalSecret/password not provided.
validateException(baseUrl, "R_NoUserPasswordForActiveServicePrincipal");
// both username/password and AADSecurePrincipalId/AADSecurePrincipalSecret provided
- url = baseUrl + "Username=" + azureAADPrincipialId + ";password=" + azureAADPrincipialSecret
- + ";AADSecurePrincipalId=" + azureAADPrincipialId + ";AADSecurePrincipalSecret="
- + azureAADPrincipialSecret;
+ url = baseUrl + "Username=" + applicationClientID + ";password=" + applicationKey + ";AADSecurePrincipalId="
+ + applicationClientID + ";AADSecurePrincipalSecret=" + applicationKey;
validateException(url, "R_BothUserPasswordandDeprecated");
}
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/FedauthWithAE.java b/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/FedauthWithAE.java
index a34d4ee32..47478d2fc 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/FedauthWithAE.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/FedauthWithAE.java
@@ -23,6 +23,8 @@
import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.RunWith;
+import com.azure.identity.ManagedIdentityCredential;
+import com.azure.identity.ManagedIdentityCredentialBuilder;
import com.microsoft.sqlserver.jdbc.RandomUtil;
import com.microsoft.sqlserver.jdbc.SQLServerColumnEncryptionAzureKeyVaultProvider;
import com.microsoft.sqlserver.jdbc.SQLServerColumnEncryptionJavaKeyStoreProvider;
@@ -282,16 +284,17 @@ private SQLServerColumnEncryptionKeyStoreProvider setupKeyStoreProvider_JKS() th
private SQLServerColumnEncryptionKeyStoreProvider setupKeyStoreProvider_AKV() throws SQLServerException {
SQLServerConnection.unregisterColumnEncryptionKeyStoreProviders();
- return registerAKVProvider(
- new SQLServerColumnEncryptionAzureKeyVaultProvider(applicationClientID, applicationKey));
+ return registerAKVProvider();
}
- private SQLServerColumnEncryptionKeyStoreProvider registerAKVProvider(
- SQLServerColumnEncryptionKeyStoreProvider provider) throws SQLServerException {
- Map map1 = new HashMap();
- map1.put(provider.getName(), provider);
- SQLServerConnection.registerColumnEncryptionKeyStoreProviders(map1);
- return provider;
+ private SQLServerColumnEncryptionKeyStoreProvider registerAKVProvider() throws SQLServerException {
+ Map map = new HashMap();
+ ManagedIdentityCredential credential = new ManagedIdentityCredentialBuilder()
+ .clientId(akvProviderManagedClientId).build();
+ akvProvider = new SQLServerColumnEncryptionAzureKeyVaultProvider(credential);
+ map.put(Constants.AZURE_KEY_VAULT_NAME, akvProvider);
+ SQLServerConnection.registerColumnEncryptionKeyStoreProviders(map);
+ return akvProvider;
}
private void createCMK(String cmkName, String keyStoreName, String keyPath, Statement stmt) throws SQLException {
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/PooledConnectionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/PooledConnectionTest.java
index 884b6c276..6dc98ee2a 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/PooledConnectionTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/PooledConnectionTest.java
@@ -39,6 +39,7 @@
@RunWith(JUnitPlatform.class)
@Tag("slow")
@Tag(Constants.fedAuth)
+@Tag(Constants.requireSecret)
public class PooledConnectionTest extends FedauthCommon {
static String charTable = TestUtils.escapeSingleQuotes(
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/fips/FipsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/fips/FipsTest.java
index a486e3c27..9a02cd956 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/fips/FipsTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/fips/FipsTest.java
@@ -62,13 +62,18 @@ public void fipsTrustServerCertificateTest() throws Exception {
*/
@Test
public void fipsEncryptTest() throws Exception {
+ // test doesn't apply to managed identity as encrypt is set to on by default
+ String auth = TestUtils.getProperty(connectionString, "authentication");
+ org.junit.Assume.assumeTrue(auth != null && !(auth.equalsIgnoreCase("ActiveDirectoryManagedIdentity")
+ || auth.equalsIgnoreCase("ActiveDirectoryMSI")));
+
Properties props = buildConnectionProperties();
props.setProperty(Constants.ENCRYPT, Boolean.FALSE.toString());
try (Connection con = PrepUtil.getConnection(connectionString, props)) {
Assertions.fail(TestResource.getResource("R_expectedExceptionNotThrown"));
} catch (SQLException e) {
Assertions.assertTrue(e.getMessage().contains(TestResource.getResource("R_invalidFipsConfig")),
- TestResource.getResource("R_invalidEncrypt"));
+ TestResource.getResource("R_invalidTrustCert") + ": " + e.getMessage());
}
}
@@ -116,6 +121,11 @@ public void fipsDataSourcePropertyTest() throws Exception {
*/
@Test
public void fipsDatSourceEncrypt() {
+ // test doesn't apply to managed identity as encrypt is set to on by default
+ String auth = TestUtils.getProperty(connectionString, "authentication");
+ org.junit.Assume.assumeTrue(auth != null && !(auth.equalsIgnoreCase("ActiveDirectoryManagedIdentity")
+ || auth.equalsIgnoreCase("ActiveDirectoryMSI")));
+
SQLServerDataSource ds = new SQLServerDataSource();
setDataSourceProperties(ds);
ds.setEncrypt(false);
@@ -124,7 +134,7 @@ public void fipsDatSourceEncrypt() {
Assertions.fail(TestResource.getResource("R_expectedExceptionNotThrown"));
} catch (SQLException e) {
Assertions.assertTrue(e.getMessage().contains(TestResource.getResource("R_invalidFipsConfig")),
- TestResource.getResource("R_invalidEncrypt"));
+ TestResource.getResource("R_invalidEncrypt") + ": " + e.getMessage());
}
}
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/BasicConnectionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/BasicConnectionTest.java
index 3a6c7dd08..6204ec61c 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/BasicConnectionTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/BasicConnectionTest.java
@@ -49,15 +49,36 @@ public void testBasicReconnectDefault() throws SQLException {
}
@Test
- public void testBasicConnectionAAD() throws SQLException {
- String azureServer = getConfiguredProperty("azureServer");
- String azureDatabase = getConfiguredProperty("azureDatabase");
- String azureUserName = getConfiguredProperty("azureUserName");
- String azurePassword = getConfiguredProperty("azurePassword");
- org.junit.Assume.assumeTrue(azureServer != null && !azureServer.isEmpty());
-
- basicReconnect("jdbc:sqlserver://" + azureServer + ";database=" + azureDatabase + ";user=" + azureUserName
- + ";password=" + azurePassword + ";loginTimeout=30;Authentication=ActiveDirectoryPassword");
+ @Tag(Constants.fedAuth)
+ public void testBasicConnectionAAD() throws Exception {
+ // retry since this could fail due to server throttling
+ int retry = 1;
+ while (retry <= THROTTLE_RETRY_COUNT) {
+ try {
+ String azureServer = getConfiguredProperty("azureServer");
+ String azureDatabase = getConfiguredProperty("azureDatabase");
+ String azureUserName = getConfiguredProperty("azureUserName");
+ String azurePassword = getConfiguredProperty("azurePassword");
+ org.junit.Assume.assumeTrue(azureServer != null && !azureServer.isEmpty());
+
+ basicReconnect("jdbc:sqlserver://" + azureServer + ";database=" + azureDatabase + ";user="
+ + azureUserName + ";password=" + azurePassword
+ + ";loginTimeout=90;Authentication=ActiveDirectoryPassword");
+ retry = THROTTLE_RETRY_COUNT + 1;
+ } catch (Exception e) {
+ if (e.getMessage().matches(TestUtils.formatErrorMsg("R_crClientAllRecoveryAttemptsFailed"))) {
+ System.out.println(e.getMessage() + ". Recovery failed, retry #" + retry + " in "
+ + THROTTLE_RETRY_INTERVAL + " ms");
+
+ Thread.sleep(THROTTLE_RETRY_INTERVAL);
+ retry++;
+ } else {
+ e.printStackTrace();
+
+ fail(e.getMessage());
+ }
+ }
+ }
}
@Test
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/ReflectiveTests.java b/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/ReflectiveTests.java
index e2206deb9..1a4f9dd4b 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/ReflectiveTests.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/ReflectiveTests.java
@@ -77,13 +77,18 @@ public void testDefaultTimeout() throws SQLException {
}
/*
- * Default retry count is 1. Expect timeout to be just above login timeout.
+ * Default retry count is 1 (for non-Azure). Expect timeout to be just above login timeout.
*/
@Test
+ @Tag(Constants.xAzureSQLDB)
+ @Tag(Constants.xAzureSQLDW)
public void testDefaultRetry() throws SQLException {
Map m = new HashMap<>();
m.put("loginTimeout", "5");
- timeoutVariations(m, 6000, Optional.empty());
+
+ // ensure count is not set to something else as this test assumes exactly just 1 retry
+ // this is only true for non-Azure as retry counts gets auto changed for Azure servers
+ timeoutVariations(m, 15000, Optional.empty());
}
/*
@@ -109,7 +114,7 @@ public void testQueryTimeout() throws SQLException {
m.put("queryTimeout", "10");
m.put("loginTimeout", "65535");
m.put("connectRetryCount", "1");
- timeoutVariations(m, 12000, Optional.empty());
+ timeoutVariations(m, 14000, Optional.empty());
}
/*
@@ -123,7 +128,7 @@ public void testValidRetryWindow() throws SQLException {
m.put("loginTimeout", "5");
m.put("connectRetryCount", "2");
m.put("connectRetryInterval", "10");
- timeoutVariations(m, 25000, Optional.of("R_crClientAllRecoveryAttemptsFailed"));
+ timeoutVariations(m, 28000, Optional.of("R_crClientAllRecoveryAttemptsFailed"));
}
@Test
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/ResultSetsWithResiliencyTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/ResultSetsWithResiliencyTest.java
index 1147cbe79..663f9d599 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/ResultSetsWithResiliencyTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/ResultSetsWithResiliencyTest.java
@@ -167,93 +167,6 @@ public void testAdaptiveBufferingWithPartiallyBufferedResultSet() throws SQLExce
}
}
- /*
- * Test killing a session while retrieving result set should result in exception thrown
- */
- @Test
- public void testKillSession() throws Exception {
- // setup test with big tables
- String table1 = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("killSessionTestTable1"));
- String table2 = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("killSessionTestTable2"));
- String table3 = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("killSessionTestTable3"));
-
- try (Connection c = DriverManager.getConnection(connectionString); Statement s = c.createStatement();
- PreparedStatement ps1 = c.prepareStatement("INSERT INTO " + table1 + " values (?)");
- PreparedStatement ps2 = c.prepareStatement("INSERT INTO " + table2 + " values (?)");
- PreparedStatement ps3 = c.prepareStatement("INSERT INTO " + table3 + " values (?)")) {
- TestUtils.dropTableIfExists(table1, s);
- TestUtils.dropTableIfExists(table2, s);
- TestUtils.dropTableIfExists(table3, s);
- s.execute("CREATE TABLE " + table1
- + " (ID int primary key IDENTITY(1,1) NOT NULL, NAME varchar(255) NOT NULL); CREATE TABLE " + table2
- + " (ID int primary key IDENTITY(1,1) NOT NULL, NAME varchar(255) NOT NULL); CREATE TABLE " + table3
- + "( ID int primary key IDENTITY(1,1) NOT NULL, NAME varchar(255) NOT NULL);");
-
- for (int i = 0; i < 1000; i++) {
- ps1.setString(1, "value" + i);
- ps2.setString(1, "value" + i);
- ps3.setString(1, "value" + i);
-
- ps1.addBatch();
- ps2.addBatch();
- ps3.addBatch();
- }
-
- ps1.executeBatch();
- ps2.executeBatch();
- ps3.executeBatch();
-
- c.commit();
-
- try (Connection c2 = DriverManager.getConnection(connectionString)) {
- int sessionId = ResiliencyUtils.getSessionId(c2);
-
- Runnable r1 = () -> {
- try {
- ResiliencyUtils.killConnection(sessionId, connectionString, c2, 10);
- } catch (Exception e) {
- fail(e.getMessage());;
- }
- };
-
- Thread t1 = new Thread(r1);
- t1.start();
-
- // execute query which takes a long time and kill session in another thread
- try (PreparedStatement ps = c2.prepareStatement("SELECT e1.* FROM " + table1 + " e1, " + table2
- + " e2, " + table3 + " e3, " + table1
- + " e4 where e1.name = 'abc' or e2.name = 'def'or e3.name = 'ghi' or e4.name = 'xxx' and e1.name not in (select name FROM "
- + table2 + ") and e2.name not in (select name FROM " + table1
- + " ) and e3.name not in (SELECT name FROM " + table2
- + ") and e4.name not in (SELECT name FROM " + table3 + ");");
- ResultSet rs = ps.executeQuery()) {
-
- fail(TestResource.getResource("R_expectedExceptionNotThrown"));
- } catch (SQLException e) {
- // may get different error message depending on SQL servers.
- // Local servers will report a TDS error where as Azure servers will have a DONE error
- if (!(e.getMessage().matches(TestUtils.formatErrorMsg("R_serverError"))
- || e.getMessage().contains(TestResource.getResource("R_sessionKilled"))
- || e.getMessage().contains(TestResource.getResource("R_connectionReset")))) {
- e.printStackTrace();
- }
-
- assertTrue(
- e.getMessage().matches(TestUtils.formatErrorMsg("R_serverError"))
- || e.getMessage().contains(TestResource.getResource("R_sessionKilled"))
- || e.getMessage().contains(TestResource.getResource("R_connectionReset")),
- e.getMessage());
- }
- t1.join();
-
- } finally {
- TestUtils.dropTableIfExists(table1, s);
- TestUtils.dropTableIfExists(table2, s);
- TestUtils.dropTableIfExists(table3, s);
- }
- }
- }
-
@Test
public void testResultSetClientCursorInitializerOnDone() throws SQLException {
try (Connection con = ResiliencyUtils.getConnection(connectionString); Statement stmt = con.createStatement()) {
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/SQLServerErrorTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/SQLServerErrorTest.java
index 834e6da49..5318236a8 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/SQLServerErrorTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/SQLServerErrorTest.java
@@ -43,6 +43,11 @@ public static void setupTests() throws Exception {
@Test
@Tag(Constants.xAzureSQLDW)
public void testLoginFailedError() {
+ // test to remove password only valid for password auth
+ String auth = TestUtils.getProperty(connectionString, "authentication");
+ org.junit.Assume.assumeTrue(auth != null
+ && (auth.equalsIgnoreCase("SqlPassword") || auth.equalsIgnoreCase("ActiveDirectoryPassword")));
+
SQLServerDataSource ds = new SQLServerDataSource();
ds.setURL(connectionString);
ds.setLoginTimeout(loginTimeOutInSeconds);
diff --git a/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java b/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java
index 2bacd129b..9df82e517 100644
--- a/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java
+++ b/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java
@@ -32,6 +32,11 @@
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
+import com.azure.identity.ClientSecretCredential;
+import com.azure.identity.ClientSecretCredentialBuilder;
+import com.azure.identity.ManagedIdentityCredential;
+import com.azure.identity.ManagedIdentityCredentialBuilder;
+import com.microsoft.aad.msal4j.ConfidentialClientApplication;
import com.microsoft.sqlserver.jdbc.ISQLServerDataSource;
import com.microsoft.sqlserver.jdbc.SQLServerColumnEncryptionAzureKeyVaultProvider;
import com.microsoft.sqlserver.jdbc.SQLServerColumnEncryptionJavaKeyStoreProvider;
@@ -60,6 +65,7 @@ public abstract class AbstractTest {
protected static String applicationKey = null;
protected static String tenantID;
protected static String[] keyIDs = null;
+ protected static String akvProviderManagedClientId = null;
protected static String[] enclaveServer = null;
protected static String[] enclaveAttestationUrl = null;
@@ -108,6 +114,9 @@ public abstract class AbstractTest {
protected static boolean isWindows = System.getProperty("os.name").startsWith("Windows");
+ protected static final int THROTTLE_RETRY_COUNT = 3; // max number of throttling retries
+ protected static final int THROTTLE_RETRY_INTERVAL = 60000; // default throttling retry interval in ms
+
public static Properties properties = null;
/**
@@ -133,6 +142,9 @@ public static void setup() throws Exception {
applicationClientID = getConfiguredProperty("applicationClientID");
applicationKey = getConfiguredProperty("applicationKey");
+
+ akvProviderManagedClientId = getConfiguredProperty("akvProviderManagedClientId");
+
tenantID = getConfiguredProperty("tenantID");
trustServerCertificate = getConfiguredProperty("trustServerCertificate", "true");
@@ -177,7 +189,12 @@ public static void setup() throws Exception {
map.put(Constants.CUSTOM_KEYSTORE_NAME, jksProvider);
}
- if (null == akvProvider && null != applicationClientID && null != applicationKey) {
+ if (null == akvProvider && null != akvProviderManagedClientId) {
+ ManagedIdentityCredential credential = new ManagedIdentityCredentialBuilder()
+ .clientId(akvProviderManagedClientId).build();
+ akvProvider = new SQLServerColumnEncryptionAzureKeyVaultProvider(credential);
+ map.put(Constants.AZURE_KEY_VAULT_NAME, akvProvider);
+ } else if (null == akvProvider && null != applicationClientID && null != applicationKey) {
File file = null;
try {
file = new File(Constants.MSSQL_JDBC_PROPERTIES);
diff --git a/src/test/java/com/microsoft/sqlserver/testframework/Constants.java b/src/test/java/com/microsoft/sqlserver/testframework/Constants.java
index acac806cb..92efb79ca 100644
--- a/src/test/java/com/microsoft/sqlserver/testframework/Constants.java
+++ b/src/test/java/com/microsoft/sqlserver/testframework/Constants.java
@@ -43,6 +43,7 @@ private Constants() {}
public static final String reqExternalSetup = "reqExternalSetup";
public static final String clientCertAuth = "clientCertAuth";
public static final String fedAuth = "fedAuth";
+ public static final String requireSecret = "requireSecret";
public static final ThreadLocalRandom RANDOM = ThreadLocalRandom.current();
public static final Logger LOGGER = Logger.getLogger("AbstractTest");