44 */
55package com .microsoft .sqlserver .jdbc .fedauth ;
66
7- import static org .junit .Assert .assertTrue ;
7+ import static org .junit .jupiter . api . Assertions .assertTrue ;
88import static org .junit .jupiter .api .Assertions .fail ;
99
10+ import com .microsoft .aad .msal4j .ClientCredentialFactory ;
11+ import com .microsoft .aad .msal4j .ClientCredentialParameters ;
12+ import com .microsoft .aad .msal4j .ConfidentialClientApplication ;
1013import com .microsoft .aad .msal4j .IAuthenticationResult ;
14+ import com .microsoft .aad .msal4j .IClientCredential ;
15+ import com .microsoft .aad .msal4j .ITokenCacheAccessAspect ;
16+ import com .microsoft .aad .msal4j .ITokenCacheAccessContext ;
1117import com .microsoft .aad .msal4j .MsalThrottlingException ;
12- import com .microsoft .aad .msal4j .PublicClientApplication ;
13- import com .microsoft .aad .msal4j .UserNamePasswordParameters ;
18+
1419import java .sql .Connection ;
1520import java .sql .PreparedStatement ;
1621import java .sql .ResultSet ;
1722import java .sql .SQLException ;
1823import java .sql .Statement ;
19- import java .util .Collections ;
2024import java .util .Date ;
25+ import java .util .HashSet ;
2126import java .util .Locale ;
27+ import java .util .Set ;
2228import java .util .concurrent .CompletableFuture ;
2329import java .util .concurrent .Executors ;
2430import java .util .concurrent .TimeUnit ;
31+ import java .util .concurrent .locks .Lock ;
32+ import java .util .concurrent .locks .ReentrantLock ;
2533import java .util .logging .LogManager ;
2634
2735import org .junit .jupiter .api .BeforeAll ;
3442import com .microsoft .sqlserver .testframework .AbstractTest ;
3543
3644
45+ @ Tag (Constants .fedAuth )
46+ class FedauthTokenCache implements ITokenCacheAccessAspect {
47+ private static FedauthTokenCache instance = new FedauthTokenCache ();
48+ private final Lock lock = new ReentrantLock ();
49+
50+ private FedauthTokenCache () {}
51+
52+ static FedauthTokenCache getInstance () {
53+ return instance ;
54+ }
55+
56+ private String cache = null ;
57+
58+ @ Override
59+ public void beforeCacheAccess (ITokenCacheAccessContext iTokenCacheAccessContext ) {
60+ lock .lock ();
61+ try {
62+ if (null != cache && null != iTokenCacheAccessContext && null != iTokenCacheAccessContext .tokenCache ()) {
63+ iTokenCacheAccessContext .tokenCache ().deserialize (cache );
64+ }
65+ } finally {
66+ lock .unlock ();
67+ }
68+ }
69+
70+ @ Override
71+ public void afterCacheAccess (ITokenCacheAccessContext iTokenCacheAccessContext ) {
72+ lock .lock ();
73+ try {
74+ if (null != iTokenCacheAccessContext && iTokenCacheAccessContext .hasCacheChanged ()
75+ && null != iTokenCacheAccessContext .tokenCache ())
76+ cache = iTokenCacheAccessContext .tokenCache ().serialize ();
77+ } finally {
78+ lock .unlock ();
79+ }
80+ }
81+
82+ static void clearUserTokenCache () {
83+ if (null != instance .cache && !instance .cache .isEmpty ()) {
84+ instance .cache = null ;
85+ }
86+ }
87+ }
88+
89+
3790@ Tag (Constants .fedAuth )
3891public class FedauthCommon extends AbstractTest {
3992
@@ -42,8 +95,6 @@ public class FedauthCommon extends AbstractTest {
4295 static String azureUserName = null ;
4396 static String azurePassword = null ;
4497 static String azureGroupUserName = null ;
45- static String azureAADPrincipalId = null ;
46- static String azureAADPrincipalSecret = null ;
4798
4899 static boolean enableADIntegrated = false ;
49100
@@ -55,6 +106,7 @@ public class FedauthCommon extends AbstractTest {
55106 static String fedauthClientId = null ;
56107 static long secondsBeforeExpiration = -1 ;
57108 static String accessToken = null ;
109+ static String certificatePassword = null ;
58110 static String [] fedauthJksPaths = null ;
59111 static String [] fedauthJksPathsLinux = null ;
60112 static String [] fedauthJavaKeyAliases = null ;
@@ -75,6 +127,8 @@ public class FedauthCommon extends AbstractTest {
75127 static final String ERR_MSG_HAS_CLOSED = TestResource .getResource ("R_hasClosed" );
76128 static final String ERR_MSG_HAS_BEEN_CLOSED = TestResource .getResource ("R_hasBeenClosed" );
77129 static final String ERR_MSG_SIGNIN_TOO_MANY = TestResource .getResource ("R_signinTooManyTimes" );
130+ static final String ERR_FAULT_ID3342 = "FaultMessage: ID3242" ;
131+ static final String ERR_FAULT_AUTH_FAIL = "FaultMessage: Authentication Failure" ;
78132 static final String ERR_MSG_NOT_AUTH_AND_IS = TestUtils .R_BUNDLE
79133 .getString ("R_SetAuthenticationWhenIntegratedSecurityTrue" );
80134 static final String ERR_MSG_NOT_AUTH_AND_USER_PASSWORD = TestUtils .R_BUNDLE
@@ -91,7 +145,8 @@ enum SqlAuthentication {
91145 SqlPassword ,
92146 ActiveDirectoryPassword ,
93147 ActiveDirectoryIntegrated ,
94- ActiveDirectoryServicePrincipal ;
148+ ActiveDirectoryServicePrincipal ,
149+ ActiveDirectoryServicePrincipalCertificate ;
95150
96151 static SqlAuthentication valueOfString (String value ) throws SQLServerException {
97152 SqlAuthentication method = null ;
@@ -124,8 +179,9 @@ public static void getConfigs() throws Exception {
124179 azureUserName = getConfiguredProperty ("azureUserName" );
125180 azurePassword = getConfiguredProperty ("azurePassword" );
126181 azureGroupUserName = getConfiguredProperty ("azureGroupUserName" );
127- azureAADPrincipalId = getConfiguredProperty ("AADSecurePrincipalId" );
128- azureAADPrincipalSecret = getConfiguredProperty ("AADSecurePrincipalSecret" );
182+
183+ // password for service principal certificate
184+ certificatePassword = getConfiguredProperty ("certificatePassword" );
129185
130186 String prop = getConfiguredProperty ("enableADIntegrated" );
131187 enableADIntegrated = (null != prop && prop .equalsIgnoreCase ("true" )) ? true : false ;
@@ -162,30 +218,34 @@ static void getFedauthInfo() {
162218 long interval = THROTTLE_RETRY_INTERVAL ;
163219 while (retry <= THROTTLE_RETRY_COUNT ) {
164220 try {
165- if (null == fedauthPcaApp ) {
166- fedauthPcaApp = PublicClientApplication .builder (fedauthClientId )
167- .executorService (Executors .newFixedThreadPool (1 )).authority (stsurl ).build ();
221+ Set <String > scopes = new HashSet <>();
222+ scopes .add (spn + "/.default" );
223+ if (null == fedauthClientApp ) {
224+ IClientCredential credential = ClientCredentialFactory .createFromSecret (applicationKey );
225+ fedauthClientApp = ConfidentialClientApplication .builder (applicationClientID , credential )
226+ .executorService (Executors .newFixedThreadPool (1 ))
227+ .setTokenCacheAccessAspect (FedauthTokenCache .getInstance ()).authority (stsurl ).build ();
168228 }
169229
170- final CompletableFuture <IAuthenticationResult > future = fedauthPcaApp
171- .acquireToken (UserNamePasswordParameters .builder (Collections .singleton (spn + "/.default" ),
172- azureUserName , azurePassword .toCharArray ()).build ());
230+ final CompletableFuture <IAuthenticationResult > future = fedauthClientApp
231+ .acquireToken (ClientCredentialParameters .builder (scopes ).build ());
173232
174233 final IAuthenticationResult authenticationResult = future .get ();
175234
176235 secondsBeforeExpiration = TimeUnit .MILLISECONDS
177236 .toSeconds (authenticationResult .expiresOnDate ().getTime () - new Date ().getTime ());
178237 accessToken = authenticationResult .accessToken ();
238+
179239 retry = THROTTLE_RETRY_COUNT + 1 ;
180240 } catch (MsalThrottlingException te ) {
181241 interval = te .retryInMs ();
182242 if (!checkForRetry (te , retry ++, interval )) {
243+ te .printStackTrace ();
183244 fail (ERR_FAILED_FEDAUTH + "no more retries: " + te .getMessage ());
184245 }
185246 } catch (Exception e ) {
186- if (!checkForRetry (e , retry ++, interval )) {
187- fail (ERR_FAILED_FEDAUTH + "no more retries: " + e .getMessage ());
188- }
247+ e .printStackTrace ();
248+ fail (ERR_FAILED_FEDAUTH + e .getMessage ());
189249 }
190250 }
191251 }
@@ -195,8 +255,6 @@ static boolean checkForRetry(Exception e, int retry, long interval) {
195255 return false ;
196256 }
197257 try {
198- System .out
199- .println (e .getMessage () + "Get FedAuth token failed, retry #" + retry + " in " + interval + " ms" );
200258 Thread .sleep (interval );
201259 } catch (InterruptedException ex ) {
202260 e .printStackTrace ();
@@ -209,11 +267,12 @@ static boolean checkForRetry(Exception e, int retry, long interval) {
209267 void testUserName (Connection conn , String user , SqlAuthentication authentication ) throws SQLException {
210268 try (Statement stmt = conn .createStatement (); ResultSet rs = stmt .executeQuery ("SELECT SUSER_SNAME()" )) {
211269 rs .next ();
212- if (SqlAuthentication .ActiveDirectoryIntegrated != authentication ) {
213- assertTrue (user .equals (rs .getString (1 )));
214- } else {
270+ if (SqlAuthentication .ActiveDirectoryPassword == authentication
271+ || SqlAuthentication .SqlPassword == authentication ) {
272+ assertTrue (user .equals (rs .getString (1 )), rs .getString (1 ));
273+ } else if (SqlAuthentication .ActiveDirectoryIntegrated == authentication ) {
215274 if (isWindows ) {
216- assertTrue (rs .getString (1 ).contains (System .getProperty ("user.name" )));
275+ assertTrue (rs .getString (1 ).contains (System .getProperty ("user.name" )), rs . getString ( 1 ) );
217276 } else {
218277 // cannot verify user in kerberos tickets so just check it's not empty
219278 assertTrue (!rs .getString (1 ).isEmpty ());
0 commit comments