This repository has been archived by the owner on Aug 28, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 457
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Tests for AADAppRoleAuthenticationFilter and UserPrincipalManagerAudience.
- Loading branch information
1 parent
4831169
commit 65717a3
Showing
2 changed files
with
310 additions
and
0 deletions.
There are no files selected for viewing
161 changes: 161 additions & 0 deletions
161
...java/com/microsoft/azure/spring/autoconfigure/aad/AADAppRoleAuthenticationFilterTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
/** | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See LICENSE in the project root for | ||
* license information. | ||
*/ | ||
package com.microsoft.azure.spring.autoconfigure.aad; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
import static org.junit.Assert.assertNotNull; | ||
import static org.junit.Assert.assertNull; | ||
import static org.junit.Assert.assertThat; | ||
import static org.junit.Assert.assertTrue; | ||
import static org.mockito.ArgumentMatchers.any; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.verify; | ||
import static org.mockito.Mockito.when; | ||
|
||
import com.nimbusds.jose.JOSEException; | ||
import com.nimbusds.jose.JWSAlgorithm; | ||
import com.nimbusds.jose.JWSHeader.Builder; | ||
import com.nimbusds.jose.JWSObject; | ||
import com.nimbusds.jose.Payload; | ||
import com.nimbusds.jose.proc.BadJOSEException; | ||
import com.nimbusds.jwt.JWTClaimsSet; | ||
import com.nimbusds.jwt.proc.BadJWTException; | ||
import java.io.IOException; | ||
import java.text.ParseException; | ||
import java.util.Arrays; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.Set; | ||
import javax.servlet.FilterChain; | ||
import javax.servlet.ServletException; | ||
import javax.servlet.ServletRequest; | ||
import javax.servlet.ServletResponse; | ||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
import net.minidev.json.JSONArray; | ||
import org.assertj.core.api.Assertions; | ||
import org.hamcrest.CoreMatchers; | ||
import org.junit.Test; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.security.core.Authentication; | ||
import org.springframework.security.core.authority.SimpleGrantedAuthority; | ||
import org.springframework.security.core.context.SecurityContext; | ||
import org.springframework.security.core.context.SecurityContextHolder; | ||
|
||
public class AADAppRoleAuthenticationFilterTest { | ||
|
||
public static final String TOKEN = "dummy-token"; | ||
|
||
private final UserPrincipalManager userPrincipalManager; | ||
private final HttpServletRequest request; | ||
private final HttpServletResponse response; | ||
private final SimpleGrantedAuthority roleAdmin; | ||
private final SimpleGrantedAuthority roleUser; | ||
private final AADAppRoleStatelessAuthenticationFilter filter; | ||
|
||
private UserPrincipal createUserPrincipal(Collection<String> roles) { | ||
final JSONArray claims = new JSONArray(); | ||
claims.addAll(roles); | ||
final JWTClaimsSet jwtClaimsSet = new JWTClaimsSet.Builder() | ||
.subject("john doe") | ||
.claim("roles", claims) | ||
.build(); | ||
final JWSObject jwsObject = new JWSObject(new Builder(JWSAlgorithm.RS256).build(), | ||
new Payload(jwtClaimsSet.toString())); | ||
return new UserPrincipal(jwsObject, jwtClaimsSet); | ||
} | ||
|
||
public AADAppRoleAuthenticationFilterTest() { | ||
userPrincipalManager = mock(UserPrincipalManager.class); | ||
request = mock(HttpServletRequest.class); | ||
response = mock(HttpServletResponse.class); | ||
roleAdmin = new SimpleGrantedAuthority("ROLE_admin"); | ||
roleUser = new SimpleGrantedAuthority("ROLE_user"); | ||
filter = new AADAppRoleStatelessAuthenticationFilter(userPrincipalManager); | ||
} | ||
|
||
@Test | ||
public void testDoFilterGoodCase() | ||
throws ParseException, JOSEException, BadJOSEException, ServletException, IOException { | ||
final UserPrincipal dummyPrincipal = createUserPrincipal(Arrays.asList("user", "admin")); | ||
|
||
when(request.getHeader(HttpHeaders.AUTHORIZATION)).thenReturn("Bearer " + TOKEN); | ||
when(userPrincipalManager.buildUserPrincipal(TOKEN)).thenReturn(dummyPrincipal); | ||
|
||
// Check in subsequent filter that authentication is available! | ||
final FilterChain filterChain = new FilterChain() { | ||
@Override | ||
public void doFilter(ServletRequest request, ServletResponse response) | ||
throws IOException, ServletException { | ||
final SecurityContext context = SecurityContextHolder.getContext(); | ||
assertNotNull(context); | ||
final Authentication authentication = context.getAuthentication(); | ||
assertNotNull(authentication); | ||
assertTrue("User should be authenticated!", authentication.isAuthenticated()); | ||
assertEquals(dummyPrincipal, authentication.getPrincipal()); | ||
Assertions.assertThat((Collection<SimpleGrantedAuthority>) authentication.getAuthorities()) | ||
.containsExactlyInAnyOrder(roleAdmin, roleUser); | ||
} | ||
}; | ||
|
||
filter.doFilterInternal(request, response, filterChain); | ||
|
||
verify(userPrincipalManager).buildUserPrincipal(TOKEN); | ||
assertNull("Authentication has not been cleaned up!", SecurityContextHolder.getContext().getAuthentication()); | ||
} | ||
|
||
@Test(expected = ServletException.class) | ||
public void testDoFilterShouldRethrowJWTException() | ||
throws ParseException, JOSEException, BadJOSEException, ServletException, IOException { | ||
|
||
when(request.getHeader(HttpHeaders.AUTHORIZATION)).thenReturn("Bearer " + TOKEN); | ||
when(userPrincipalManager.buildUserPrincipal(any())).thenThrow(new BadJWTException("bad token")); | ||
|
||
filter.doFilterInternal(request, response, mock(FilterChain.class)); | ||
} | ||
|
||
@Test | ||
public void testDoFilterAddsDefaultRole() | ||
throws ParseException, JOSEException, BadJOSEException, ServletException, IOException { | ||
|
||
final UserPrincipal dummyPrincipal = createUserPrincipal(Collections.emptyList()); | ||
|
||
when(request.getHeader(HttpHeaders.AUTHORIZATION)).thenReturn("Bearer " + TOKEN); | ||
when(userPrincipalManager.buildUserPrincipal(TOKEN)).thenReturn(dummyPrincipal); | ||
|
||
// Check in subsequent filter that authentication is available and default roles are filled. | ||
final FilterChain filterChain = new FilterChain() { | ||
@Override | ||
public void doFilter(ServletRequest request, ServletResponse response) | ||
throws IOException, ServletException { | ||
final SecurityContext context = SecurityContextHolder.getContext(); | ||
assertNotNull(context); | ||
final Authentication authentication = context.getAuthentication(); | ||
assertNotNull(authentication); | ||
assertTrue("User should be authenticated!", authentication.isAuthenticated()); | ||
final SimpleGrantedAuthority expectedDefaultRole = new SimpleGrantedAuthority("ROLE_USER"); | ||
Assertions.assertThat((Collection<SimpleGrantedAuthority>) authentication.getAuthorities()) | ||
.containsExactlyInAnyOrder(expectedDefaultRole); | ||
} | ||
}; | ||
|
||
filter.doFilterInternal(request, response, filterChain); | ||
|
||
verify(userPrincipalManager).buildUserPrincipal(TOKEN); | ||
assertNull("Authentication has not been cleaned up!", SecurityContextHolder.getContext().getAuthentication()); | ||
} | ||
|
||
@Test | ||
public void testRolesToGrantedAuthoritiesShouldConvertRolesAndFilterNulls() { | ||
final JSONArray roles = new JSONArray().appendElement("user").appendElement(null).appendElement("ADMIN"); | ||
final AADAppRoleStatelessAuthenticationFilter filter = new AADAppRoleStatelessAuthenticationFilter(null); | ||
final Set<SimpleGrantedAuthority> result = filter.rolesToGrantedAuthorities(roles); | ||
assertThat("Set should contain the two granted authority 'ROLE_user' and 'ROLE_ADMIN'", result, | ||
CoreMatchers.hasItems(new SimpleGrantedAuthority("ROLE_user"), | ||
new SimpleGrantedAuthority("ROLE_ADMIN"))); | ||
} | ||
|
||
} |
149 changes: 149 additions & 0 deletions
149
...t/java/com/microsoft/azure/spring/autoconfigure/aad/UserPrincipalManagerAudienceTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
/** | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See LICENSE in the project root for | ||
* license information. | ||
*/ | ||
package com.microsoft.azure.spring.autoconfigure.aad; | ||
|
||
import static org.assertj.core.api.Assertions.assertThatCode; | ||
import static org.mockito.ArgumentMatchers.anyString; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.when; | ||
|
||
import com.nimbusds.jose.JOSEException; | ||
import com.nimbusds.jose.JWSAlgorithm; | ||
import com.nimbusds.jose.JWSHeader; | ||
import com.nimbusds.jose.JWSSigner; | ||
import com.nimbusds.jose.crypto.RSASSASigner; | ||
import com.nimbusds.jose.jwk.JWKSet; | ||
import com.nimbusds.jose.jwk.RSAKey; | ||
import com.nimbusds.jose.util.Resource; | ||
import com.nimbusds.jose.util.ResourceRetriever; | ||
import com.nimbusds.jwt.JWTClaimsSet; | ||
import com.nimbusds.jwt.SignedJWT; | ||
import java.io.IOException; | ||
import java.security.KeyPair; | ||
import java.security.KeyPairGenerator; | ||
import java.security.NoSuchAlgorithmException; | ||
import java.security.interfaces.RSAPrivateKey; | ||
import java.security.interfaces.RSAPublicKey; | ||
import java.time.Instant; | ||
import java.util.Date; | ||
import org.junit.Before; | ||
import org.junit.Test; | ||
|
||
public class UserPrincipalManagerAudienceTest { | ||
|
||
private static final String FAKE_CLIENT_ID = "dsflkjsdflkjsdf"; | ||
private static final String FAKE_APPLICATION_URI = "https://oihiugjuzfvbhg"; | ||
|
||
private JWSSigner signer; | ||
private String jwkString; | ||
private ResourceRetriever resourceRetriever; | ||
|
||
private ServiceEndpointsProperties serviceEndpointsProperties; | ||
private AADAuthenticationProperties aadAuthenticationProperties; | ||
private UserPrincipalManager userPrincipalManager; | ||
|
||
@Before | ||
public void setupKeys() throws NoSuchAlgorithmException, IOException { | ||
final KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA"); | ||
kpg.initialize(2048); | ||
|
||
final KeyPair kp = kpg.genKeyPair(); | ||
final RSAPrivateKey privateKey = (RSAPrivateKey) kp.getPrivate(); | ||
|
||
signer = new RSASSASigner(privateKey); | ||
|
||
final RSAKey rsaJWK = new RSAKey.Builder((RSAPublicKey) kp.getPublic()) | ||
.privateKey((RSAPrivateKey) kp.getPrivate()) | ||
.keyID("1") | ||
.build(); | ||
final JWKSet jwkSet = new JWKSet(rsaJWK); | ||
jwkString = jwkSet.toString(); | ||
|
||
resourceRetriever = url -> new Resource(jwkString, "application/json"); | ||
|
||
serviceEndpointsProperties = mock(ServiceEndpointsProperties.class); | ||
aadAuthenticationProperties = new AADAuthenticationProperties(); | ||
aadAuthenticationProperties.setClientId(FAKE_CLIENT_ID); | ||
aadAuthenticationProperties.setAppIdUri(FAKE_APPLICATION_URI); | ||
final ServiceEndpoints serviceEndpoints = new ServiceEndpoints(); | ||
serviceEndpoints.setAadKeyDiscoveryUri("file://dummy"); | ||
when(serviceEndpointsProperties.getServiceEndpoints(anyString())).thenReturn(serviceEndpoints); | ||
} | ||
|
||
@Test | ||
public void allowApplicationUriAsAudience() throws JOSEException { | ||
final JWTClaimsSet claimsSetOne = new JWTClaimsSet.Builder() | ||
.subject("foo") | ||
.issueTime(Date.from(Instant.now().minusSeconds(60))) | ||
.issuer("https://sts.windows.net/") | ||
.audience(FAKE_CLIENT_ID) | ||
.build(); | ||
final SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), claimsSetOne); | ||
signedJWT.sign(signer); | ||
|
||
final String orderTwo = signedJWT.serialize(); | ||
userPrincipalManager = new UserPrincipalManager(serviceEndpointsProperties, aadAuthenticationProperties, | ||
resourceRetriever, true); | ||
assertThatCode(() -> userPrincipalManager.buildUserPrincipal(orderTwo)) | ||
.doesNotThrowAnyException(); | ||
} | ||
|
||
@Test | ||
public void allowClientIdAsAudience() throws JOSEException { | ||
final JWTClaimsSet claimsSetOne = new JWTClaimsSet.Builder() | ||
.subject("foo") | ||
.issueTime(Date.from(Instant.now().minusSeconds(60))) | ||
.issuer("https://sts.windows.net/") | ||
.audience(FAKE_APPLICATION_URI) | ||
.build(); | ||
final SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), claimsSetOne); | ||
signedJWT.sign(signer); | ||
|
||
final String orderTwo = signedJWT.serialize(); | ||
userPrincipalManager = new UserPrincipalManager(serviceEndpointsProperties, aadAuthenticationProperties, | ||
resourceRetriever, true); | ||
assertThatCode(() -> userPrincipalManager.buildUserPrincipal(orderTwo)) | ||
.doesNotThrowAnyException(); | ||
} | ||
|
||
@Test | ||
public void failWithUnkownAudience() throws JOSEException { | ||
final JWTClaimsSet claimsSetOne = new JWTClaimsSet.Builder() | ||
.subject("foo") | ||
.issueTime(Date.from(Instant.now().minusSeconds(60))) | ||
.issuer("https://sts.windows.net/") | ||
.audience("unknown audience") | ||
.build(); | ||
final SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), claimsSetOne); | ||
signedJWT.sign(signer); | ||
|
||
final String orderTwo = signedJWT.serialize(); | ||
userPrincipalManager = new UserPrincipalManager(serviceEndpointsProperties, aadAuthenticationProperties, | ||
resourceRetriever, true); | ||
assertThatCode(() -> userPrincipalManager.buildUserPrincipal(orderTwo)) | ||
.hasMessageContaining("Invalid token audience."); | ||
} | ||
|
||
@Test | ||
public void failOnInvalidSiganture() throws JOSEException { | ||
final JWTClaimsSet claimsSetOne = new JWTClaimsSet.Builder() | ||
.subject("foo") | ||
.issueTime(Date.from(Instant.now().minusSeconds(60))) | ||
.issuer("https://sts.windows.net/") | ||
.audience(FAKE_APPLICATION_URI) | ||
.build(); | ||
final SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.RS256), claimsSetOne); | ||
signedJWT.sign(signer); | ||
|
||
final String orderTwo = signedJWT.serialize(); | ||
final String invalidToken = orderTwo.substring(0, orderTwo.length() - 5); | ||
|
||
userPrincipalManager = new UserPrincipalManager(serviceEndpointsProperties, aadAuthenticationProperties, | ||
resourceRetriever, true); | ||
assertThatCode(() -> userPrincipalManager.buildUserPrincipal(invalidToken)) | ||
.hasMessageContaining("JWT rejected: Invalid signature"); | ||
} | ||
} |