Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,14 @@
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Properties;
import org.hisp.dhis.external.conf.ConfigurationKey;
import org.hisp.dhis.http.HttpStatus;
import org.hisp.dhis.security.jwt.Dhis2JwtAuthenticationManagerResolver;
import org.hisp.dhis.security.oidc.DhisOidcClientRegistration;
import org.hisp.dhis.security.oidc.DhisOidcProviderRepository;
import org.hisp.dhis.security.oidc.GenericOidcProviderConfigParser;
import org.hisp.dhis.security.oidc.provider.GoogleProvider;
import org.hisp.dhis.test.webapi.ControllerWithJwtTokenAuthTestBase;
import org.hisp.dhis.test.webapi.json.domain.JsonError;
import org.hisp.dhis.test.webapi.json.domain.JsonUser;
Expand All @@ -63,31 +62,14 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;

/**
* @author Morten Svanæs <msvanaes@dhis2.org>
*/
class JwtBearerTokenTest extends ControllerWithJwtTokenAuthTestBase {
public static final String EXPIRED_GOOGLE_JWT_TOKEN =
"eyJhbGciOiJSUzI1NiIsImtpZCI6ImU4NzMyZGIwNjI4NzUxNTU1NjIx"
+ "M2I4MGFjYmNmZDA4Y2ZiMzAyYTkiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiO"
+ "iIxMDE5NDE3MDAyNTQ0LW1xYTdmbGs0bWpvaHJnc2JnOWJ0YTlidmx1b2o4NW8wLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwiY"
+ "XVkIjoiMTAxOTQxNzAwMjU0NC1tcWE3ZmxrNG1qb2hyZ3NiZzlidGE5YnZsdW9qODVvMC5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvb"
+ "SIsInN1YiI6IjExMDk3ODA1MDEyNzk2NzA2NTUwNiIsImVtYWlsIjoiZGhpczJvaWRjdXNlckBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZ"
+ "mllZCI6dHJ1ZSwiYXRfaGFzaCI6IkhXbTNXcXphM2p5TEFUZjNlU1pBNVEiLCJuYW1lIjoiZGhpczJvaWRjdXNlciBUZXN0ZXIiLCJwa"
+ "WN0dXJlIjoiaHR0cHM6Ly9saDMuZ29vZ2xldXNlcmNvbnRlbnQuY29tLy1oRmptUnhOQkJTWS9BQUFBQUFBQUFBSS9BQUFBQUFBQUFBQ"
+ "S9BTVp1dWNuQkdYVTF5X05fV25qSXJndHBpSXFWMl9ndll3L3M5Ni1jL3Bob3RvLmpwZyIsImdpdmVuX25hbWUiOiJkaGlzMm9pZGN1c"
+ "2VyIiwiZmFtaWx5X25hbWUiOiJUZXN0ZXIiLCJsb2NhbGUiOiJlbiIsImlhdCI6MTYxNDk1NzU5MCwiZXhwIjoxNjE0OTYxMTkwfQ.OC"
+ "m7hj4H-UqRpM_Xrfq58U3ZGI3k7-S3c4AslVAaMxKsNitsPDZ7oxs-FJT-E7uDqnp1wW5LyBLj8jfJZ4JnvuiNGZrvCCpR3m70_4mSgP"
+ "8VTjFFEijgfW1IIy_BWI8gDY6iCK7qgOATdYnCyJteWBMKRPr5wVSN05TT3xxLzsE7C5ViOzHAm2v6XrrsEhfcjNmwKmlljjpImTwtUS"
+ "TBS3DWoWsHaNqXfE3rO0M7231FWl2X0vk5oO-KycNoS1vDZLAvdf6QRJVnPMkQ6Cx5XSMSYEmUmFqM3Sj2ip0Q48hAe4ydzIgRWdGbzG"
+ "nMH3euqGWr4_G_EBvVqfVPnBF0YA";

public static final String GOOGLE_CLIENT_ID =
"1019417002544-mqa7flk4mjohrgsbg9bta9bvluoj85o0.apps.googleusercontent.com";

public static final String TEST_PROVIDER_ONE_URI = "testproviderone.com";

public static final String TEST_PROVIDER_ONE_NAME = "testproviderone";
Expand Down Expand Up @@ -130,6 +112,20 @@ private Jwt createJwt(String provider, String clientId, String mappingKey, Strin
return jwsEncoder.encode(joseHeader, jwtClaimsSet);
}

private Jwt createExpiredJwt(
String provider, String clientId, String mappingKey, String mappingValue) {
Instant expiresAt = Instant.ofEpochSecond(1614961190);
Instant issuedAt = expiresAt.minus(1, ChronoUnit.HOURS);
JoseHeader joseHeader = TestJoseHeaders.joseHeader(provider).build();
JwtClaimsSet jwtClaimsSet =
TestJwtClaimsSets.jwtClaimsSet(provider, clientId, mappingKey, mappingValue)
.issuedAt(issuedAt)
.notBefore(issuedAt)
.expiresAt(expiresAt)
.build();
return jwsEncoder.encode(joseHeader, jwtClaimsSet);
}

@Test
void testJwkEncodeEndDecode() {
Jwt encodedJws =
Expand Down Expand Up @@ -160,17 +156,36 @@ void testMalformedToken() {
GET("/me", JwtTokenHeader("NOT_A_JWT_TOKEN")));
}

@Test
void testExpiredToken() {
setupTestingProvider(CLIENT_ID_1, TEST_PROVIDER_ONE_NAME, TEST_PROVIDER_ONE_URI);
String tokenValue =
createExpiredJwt(TEST_PROVIDER_ONE_URI, CLIENT_ID_1, DEFAULT_MAPPING_CLAIM, DEFAULT_EMAIL)
.getTokenValue();
assertInvalidTokenError(
"An error occurred while attempting to decode the Jwt: Jwt expired at 2021-03-05T16:19:50Z",
GET("/me", JwtTokenHeader(tokenValue)));
}

/**
* This error happens when using ephemeral signing keys, so for example when you restart the
* server, the access tokens issued before the restart will no longer be valid.
* server, the access tokens issued before the restart will no longer be valid. Here it is
* simulated by signing the token with an algorithm (RS384) that the decoder does not accept, so
* no matching key is found.
*/
@Test
void testExpiredToken() {
dhis2JwtAuthenticationManagerResolver.setJwtDecoder(null);
setupGoogleProvider(GOOGLE_CLIENT_ID);
void testUnknownSigningKey() {
setupTestingProvider(CLIENT_ID_1, TEST_PROVIDER_ONE_NAME, TEST_PROVIDER_ONE_URI);
JoseHeader joseHeader =
TestJoseHeaders.joseHeader(SignatureAlgorithm.RS384, TEST_PROVIDER_ONE_URI).build();
JwtClaimsSet jwtClaimsSet =
TestJwtClaimsSets.jwtClaimsSet(
TEST_PROVIDER_ONE_URI, CLIENT_ID_1, DEFAULT_MAPPING_CLAIM, DEFAULT_EMAIL)
.build();
String tokenValue = jwsEncoder.encode(joseHeader, jwtClaimsSet).getTokenValue();
assertInvalidTokenError(
"An error occurred while attempting to decode the Jwt: Signed JWT rejected: Another algorithm expected, or no matching key(s) found",
GET("/me", JwtTokenHeader(EXPIRED_GOOGLE_JWT_TOKEN)));
GET("/me", JwtTokenHeader(tokenValue)));
}

@Test
Expand Down Expand Up @@ -199,14 +214,6 @@ private void assertInvalidTokenError(String expected, HttpResponse response) {
assertEquals(expected, error.getDevMessage());
}

private void setupGoogleProvider(String clientId) {
Properties config = new Properties();
config.put(ConfigurationKey.OIDC_PROVIDER_GOOGLE_CLIENT_ID.getKey(), clientId);
config.put(ConfigurationKey.OIDC_PROVIDER_GOOGLE_CLIENT_SECRET.getKey(), "secret");
DhisOidcClientRegistration parse = GoogleProvider.parse(config);
dhisOidcProviderRepository.addRegistration(parse);
}

private void setupTestingProvider(
String clientId, String providerName, final String providerURI) {
Properties config = new Properties();
Expand Down
Loading