This is an automated email from the ASF dual-hosted git repository. btellier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 767aefd50676170ee7e4b19b6ae90f090d3bcbe5 Author: Amichai Rothman <amich...@amichais.net> AuthorDate: Sun Jun 15 18:30:32 2025 +0300 [ENHANCEMENT] add token expiration test --- .../org/apache/james/jwt/JwtTokenVerifierTest.java | 53 +++++++++++++++++++++- .../james/webadmin/authentication/JwtFilter.java | 4 +- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/server/protocols/jwt/src/test/java/org/apache/james/jwt/JwtTokenVerifierTest.java b/server/protocols/jwt/src/test/java/org/apache/james/jwt/JwtTokenVerifierTest.java index 8e6e0b1e9a..f9e7f32005 100644 --- a/server/protocols/jwt/src/test/java/org/apache/james/jwt/JwtTokenVerifierTest.java +++ b/server/protocols/jwt/src/test/java/org/apache/james/jwt/JwtTokenVerifierTest.java @@ -47,7 +47,19 @@ class JwtTokenVerifierTest { "NZ3DUR3lfBFbvYGQyFyib+e4MY1yWkj3sumMl1wdUB4lKLHLIRv9X1xCqvbSHEtq\n" + "zoZF4vgBYx0VmuJslwIDAQAB\n" + "-----END PUBLIC KEY-----"; - + + public static final String PRIVATE_PEM_KEY_EC = // use to generate tokens for future tests + "-----BEGIN EC PRIVATE KEY-----\n" + + "MHcCAQEEIL3oeBhM7D1DttaWfGUOsurEuFG1XcN1JDLijqTOcTuqoAoGCCqGSM49\n" + + "AwEHoUQDQgAE35E+lPVu46nAdTySey4ilUxO76RGG62SxqAdZb8B57t1ShzKC8U8\n" + + "qG+azeT9edpTao7dSRmgK3V83GAPAI88Ag==\n" + + "-----END EC PRIVATE KEY-----\n"; + public static final String PUBLIC_PEM_KEY_EC = + "-----BEGIN PUBLIC KEY-----\n" + + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE35E+lPVu46nAdTySey4ilUxO76RG\n" + + "G62SxqAdZb8B57t1ShzKC8U8qG+azeT9edpTao7dSRmgK3V83GAPAI88Ag==\n" + + "-----END PUBLIC KEY-----\n"; + private static final String VALID_TOKEN_WITHOUT_ADMIN = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.T04BTk" + "LXkJj24coSZkK13RfG25lpvmSl2MJ7N10KpBk9_-95EGYZdog-BDAn3PJzqVw52z-Bwjh4VOj1-j7cURu0cT4jXehhUrlCxS4n7QHZD" + "N_bsEYGu7KzjWTpTsUiHe-rN7izXVFxDGG1TGwlmBCBnPW-EFCf9ylUsJi0r2BKNdaaPRfMIrHptH1zJBkkUziWpBN1RNLjmvlAUf49" + @@ -59,17 +71,32 @@ class JwtTokenVerifierTest { "4H0a6L87BYppxVW701zaZ6dNxRMvHnjLBBWnPsC2B0rkkr2hEL2zfz7sb-iNGV-J4ICx97t8-TfQ5rz3VOX0FwdusPL_rJtmlGEGRivPkR6_aBe1" + "kQnvMlwpqF_3ox58EUqYJk6lK_6rjKEV3Xfre31IMpuQUy6c7TKc95sL2-13cknelTierBEmZ00RzTtv9SHIEfzZTfaUK2Wm0PvnQjmU2nIdEvU" + "EqE-jrM3yYXcQzoO-YTQnEhdl-iqbCfmEpYkl2Bx3eIq7gRxxnr7BPsX6HrCB0w"; + private static final String VALID_TOKEN_ADMIN_FALSE = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbkBvcGVu" + "LXBhYXMub3JnIiwiYWRtaW4iOmZhbHNlLCJpYXQiOjE0ODkwNDA4Njd9.reQc3DiVvbQHF08oW1qOUyDJyv3tfzDNk8jhVZequiCdOI9vXnRlOe" + "-yDYktd4WT8MYhqY7MgS-wR0vO9jZFv8ZCgd_MkKCvCO0HmMjP5iQPZ0kqGkgWUH7X123tfR38MfbCVAdPDba-K3MfkogV1xvDhlkPScFr_6MxE" + "xtedOK2JnQZn7t9sUzSrcyjWverm7gZkPptkIVoS8TsEeMMME5vFXe_nqkEG69q3kuBUm_33tbR5oNS0ZGZKlG9r41lHBjyf9J1xN4UYV8n866d" + "a7RPPCzshIWUtO0q9T2umWTnp-6OnOdBCkndrZmRR6pPxsD5YL0_77Wq8KT_5__fGA"; + + private static final String VALID_TOKEN_NO_EXPIRATION = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyQGV4YW" + + "1wbGUuY29tIiwiYWRtaW4iOnRydWV9.YSIWBGBcNv0-9ztJI2toAY5c6lgUo445l4C4smDlwVxIqe7N3lmgpwvq1BPI7NQFtNrJ6M2ZzMTK" + + "DUr1SlV0fQ"; + + private static final String VALID_TOKEN_EXPIRED = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyQGV4YW1wbGUu" + + "Y29tIiwiYWRtaW4iOnRydWUsImV4cCI6OTQ2Njg0ODAwfQ.j12z_REiEdMAgmZT4_XTZ-bsn8Yh-61f2ibraLU-7Pp9JTKPsmr63Ah5c5Zn" + + "gnqVwBKE6wuNPNnIh3E3GqA5Yg"; + + private static final String VALID_TOKEN_NOT_EXPIRED = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyQGV4YW1w" + + "bGUuY29tIiwiYWRtaW4iOnRydWUsImV4cCI6NDA3MDkwODgwMH0.BNa7bF8twlRycfz_8K8IPLd-0kNPAIe1DozKtibX6JR1PIpaHlRJT1U" + + "NdNc7Xw-qFJrLt8aepNN7Bu7C-EjKJQ"; + // Generated on https://jwt.io/ private static final String TOKEN_NONE_ALGORITHM = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwi" + "bmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.2XijNOVI9LXP9nWf-oj2SEWWNlcwmxzlQNGK1WdaWcQ"; private static final String TOKEN_NONE_ALGORITHM_NO_SIGNATURE = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwi" + "bmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9."; private JwtTokenVerifier sut; + private JwtTokenVerifier sutEc; @BeforeAll static void init() { @@ -80,6 +107,9 @@ class JwtTokenVerifierTest { void setup() { DefaultPublicKeyProvider pubKeyProvider = new DefaultPublicKeyProvider(new JwtConfiguration(ImmutableList.of(PUBLIC_PEM_KEY)), new PublicKeyReader()); sut = new JwtTokenVerifier(pubKeyProvider); + + pubKeyProvider = new DefaultPublicKeyProvider(new JwtConfiguration(ImmutableList.of(PUBLIC_PEM_KEY_EC)), new PublicKeyReader()); + sutEc = new JwtTokenVerifier(pubKeyProvider); } @Test @@ -220,4 +250,25 @@ class JwtTokenVerifierTest { assertThat(authorized).isFalse(); } + @Test + void verifyShouldWorkIfNoExpiration() { + boolean authorized = sutEc.verifyAndExtractLogin(VALID_TOKEN_NO_EXPIRATION).isPresent(); + + assertThat(authorized).isTrue(); + } + + @Test + void verifyShouldWorkIfNotExpired() { + boolean authorized = sutEc.verifyAndExtractLogin(VALID_TOKEN_NOT_EXPIRED).isPresent(); + + assertThat(authorized).isTrue(); + } + + @Test + void verifyShouldFailIfExpired() { + boolean authorized = sutEc.verifyAndExtractLogin(VALID_TOKEN_EXPIRED).isPresent(); + + assertThat(authorized).isFalse(); + } + } diff --git a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/authentication/JwtFilter.java b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/authentication/JwtFilter.java index 43bb410775..9464e8235f 100644 --- a/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/authentication/JwtFilter.java +++ b/server/protocols/webadmin/webadmin-core/src/main/java/org/apache/james/webadmin/authentication/JwtFilter.java @@ -52,7 +52,7 @@ public class JwtFilter implements AuthenticationFilter { .map(value -> value.substring(AUTHORIZATION_HEADER_PREFIX.length())); checkHeaderPresent(bearer); - String login = retrieveUser(bearer); + String login = retrieveUser(bearer); // includes standard token verification: format, signature, expiration, etc. checkIsAdmin(bearer); request.attribute(LOGIN, login); @@ -67,7 +67,7 @@ public class JwtFilter implements AuthenticationFilter { private String retrieveUser(Optional<String> bearer) { return jwtTokenVerifier.verifyAndExtractLogin(bearer.get()) - .orElseThrow(() -> halt(HttpStatus.UNAUTHORIZED_401, "Invalid Bearer header.")); + .orElseThrow(() -> halt(HttpStatus.UNAUTHORIZED_401, "Invalid token.")); } private void checkIsAdmin(Optional<String> bearer) { --------------------------------------------------------------------- To unsubscribe, e-mail: notifications-unsubscr...@james.apache.org For additional commands, e-mail: notifications-h...@james.apache.org