From 2681c4dea9dff3f3c75be72cd5f93513c779bc00 Mon Sep 17 00:00:00 2001 From: Nirusu Date: Mon, 6 Jan 2025 21:31:16 +0100 Subject: [PATCH 1/3] Add support for high-precision decimal TimeClaims --- .../model/jose/TimeClaimFactory.java | 32 +++++++++++-------- .../model/jose/JWSTimeClaimTest.java | 6 ++-- .../jwteditor/model/jose/TimeClaimTest.java | 16 +++++----- .../jwteditor/presenter/InformationTest.java | 13 ++++---- 4 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/main/java/com/blackberry/jwteditor/model/jose/TimeClaimFactory.java b/src/main/java/com/blackberry/jwteditor/model/jose/TimeClaimFactory.java index a33bebe..b2110f9 100644 --- a/src/main/java/com/blackberry/jwteditor/model/jose/TimeClaimFactory.java +++ b/src/main/java/com/blackberry/jwteditor/model/jose/TimeClaimFactory.java @@ -21,6 +21,7 @@ import org.json.JSONException; import org.json.JSONObject; +import java.math.BigDecimal; import java.time.Instant; import java.time.ZonedDateTime; import java.time.format.DateTimeParseException; @@ -34,8 +35,8 @@ public class TimeClaimFactory { - public static TimeClaim fromEpochSeconds(TimeClaimType type, String value, Long epochSeconds) { - return new TimeClaim(type, value, dateTimeValue(epochSeconds)); + public static TimeClaim fromEpochSeconds(TimeClaimType type, String value) { + return new TimeClaim(type, value, dateTimeValue(value)); } public static TimeClaim fromIsoDateTime(TimeClaimType type, String value) { @@ -56,10 +57,8 @@ static List fromPayloadJson(String payloadJson) { .map(type -> { String value = jsonObject.get(type.name).toString(); - if (value.matches("\\d+")) { - Long epochSeconds = numberValue(jsonObject, type.name); - - return fromEpochSeconds(type, value, epochSeconds); + if (value.matches("\\d+(.\\d+)?")) { + return fromEpochSeconds(type, value); } else { return fromIsoDateTime(type, value); } @@ -76,21 +75,26 @@ private static Optional parsePayload(String payloadJson) { } } - private static Long numberValue(JSONObject jsonObject, String name) { - try { - return jsonObject.getLong(name); - } catch (JSONException e) { + private static ZonedDateTime dateTimeValue(String numberValue) { + if (numberValue == null) { return null; } - } - private static ZonedDateTime dateTimeValue(Long numberValue) { - if (numberValue == null || numberValue < 0) { + // Try to parse string time value as BigDecimal to keep the floating point precision + BigDecimal epochSecondsDecimal = new BigDecimal(numberValue); + + // Truncate decimal part of the value to get the seconds + long seconds = epochSecondsDecimal.longValue(); + if (seconds < 0) { return null; } - Instant instant = Instant.ofEpochSecond(numberValue); + // Get decimal part as nanoseconds + long nanoseconds = epochSecondsDecimal.subtract(new BigDecimal(seconds)) + .movePointRight(9) + .longValue(); + Instant instant = Instant.ofEpochSecond(seconds, nanoseconds); return ZonedDateTime.ofInstant(instant, UTC); } diff --git a/src/test/java/com/blackberry/jwteditor/model/jose/JWSTimeClaimTest.java b/src/test/java/com/blackberry/jwteditor/model/jose/JWSTimeClaimTest.java index 0fb578b..9258103 100644 --- a/src/test/java/com/blackberry/jwteditor/model/jose/JWSTimeClaimTest.java +++ b/src/test/java/com/blackberry/jwteditor/model/jose/JWSTimeClaimTest.java @@ -46,7 +46,7 @@ void givenJWSWithNoTimeClaims_thenTimeClaimsIsEmpty() throws ParseException { void givenJWSWithExpTimeClaims_thenTimeClaimsCorrect(String data) throws ParseException { JWS jws = JWSFactory.parse(data); - assertThat(jws.claims().timeClaims()).containsExactly(TimeClaimFactory.fromEpochSeconds(EXPIRATION_TIME, "1716239022", 1716239022L)); + assertThat(jws.claims().timeClaims()).containsExactly(TimeClaimFactory.fromEpochSeconds(EXPIRATION_TIME, "1716239022")); } private static Stream jwsWithValidExpValues() { @@ -113,7 +113,7 @@ void givenJWSWithExpTimeClaims_whenExpiryDateIsInThePast_thenTimeClaimNotValid(S void givenJWSWithNbfTimeClaims_thenTimeClaimsCorrect(String data) throws ParseException { JWS jws = JWSFactory.parse(data); - assertThat(jws.claims().timeClaims()).containsExactly(TimeClaimFactory.fromEpochSeconds(NOT_BEFORE_TIME, "1716239022", 1716239022L)); + assertThat(jws.claims().timeClaims()).containsExactly(TimeClaimFactory.fromEpochSeconds(NOT_BEFORE_TIME, "1716239022")); } private static Stream jwsWithValidNbfValues() { @@ -186,7 +186,7 @@ private static Stream jwsWithValidIatValues() { void givenJWSWithIatTimeClaims_thenTimeClaimsCorrect(String data) throws ParseException { JWS jws = JWSFactory.parse(data); - assertThat(jws.claims().timeClaims()).containsExactly(TimeClaimFactory.fromEpochSeconds(ISSUED_AT_TIME, "1716239022", 1716239022L)); + assertThat(jws.claims().timeClaims()).containsExactly(TimeClaimFactory.fromEpochSeconds(ISSUED_AT_TIME, "1716239022")); } private static Stream jwsWithValidIatDatesInThePast() { diff --git a/src/test/java/com/blackberry/jwteditor/model/jose/TimeClaimTest.java b/src/test/java/com/blackberry/jwteditor/model/jose/TimeClaimTest.java index 24d2bc4..9a190a4 100644 --- a/src/test/java/com/blackberry/jwteditor/model/jose/TimeClaimTest.java +++ b/src/test/java/com/blackberry/jwteditor/model/jose/TimeClaimTest.java @@ -36,7 +36,7 @@ class TimeClaimTest { @EnumSource(TimeClaimType.class) @ParameterizedTest void invalidEpochTime(TimeClaimType type) { - TimeClaim timeClaim = TimeClaimFactory.fromEpochSeconds(type, "-1", -1L); + TimeClaim timeClaim = TimeClaimFactory.fromEpochSeconds(type, "-1"); assertThat(timeClaim.type()).isEqualTo(type); assertThat(timeClaim.value()).isEqualTo("-1"); @@ -92,7 +92,7 @@ static Stream epochDateTimes() { void testIssuedAtTimeClaimsForEpochTimes(long epochDateTime, String expectedDateTimeString) { String epochDateTimeValue = Long.toString(epochDateTime); - TimeClaim timeClaim = TimeClaimFactory.fromEpochSeconds(ISSUED_AT_TIME, epochDateTimeValue, epochDateTime); + TimeClaim timeClaim = TimeClaimFactory.fromEpochSeconds(ISSUED_AT_TIME, epochDateTimeValue); assertThat(timeClaim.type()).isEqualTo(ISSUED_AT_TIME); assertThat(timeClaim.value()).isEqualTo(epochDateTimeValue); @@ -107,7 +107,7 @@ void testExpiredDatesInThePastAreInvalid() { long epochSeconds = Instant.now().getEpochSecond() - 1; String epochSecondsValue = Long.toString(epochSeconds); - TimeClaim timeClaim = TimeClaimFactory.fromEpochSeconds(EXPIRATION_TIME, epochSecondsValue, epochSeconds); + TimeClaim timeClaim = TimeClaimFactory.fromEpochSeconds(EXPIRATION_TIME, epochSecondsValue); assertThat(timeClaim.type()).isEqualTo(EXPIRATION_TIME); assertThat(timeClaim.value()).isEqualTo(epochSecondsValue); @@ -122,7 +122,7 @@ void testExpiredDatesInTheFutureAreValid() { long epochSeconds = Instant.now().getEpochSecond() + 5; String epochSecondsValue = Long.toString(epochSeconds); - TimeClaim timeClaim = TimeClaimFactory.fromEpochSeconds(EXPIRATION_TIME, epochSecondsValue, epochSeconds); + TimeClaim timeClaim = TimeClaimFactory.fromEpochSeconds(EXPIRATION_TIME, epochSecondsValue); assertThat(timeClaim.type()).isEqualTo(EXPIRATION_TIME); assertThat(timeClaim.value()).isEqualTo(epochSecondsValue); @@ -137,7 +137,7 @@ void testIssuedAtDatesInThePastAreValid() { long epochSeconds = Instant.now().getEpochSecond() - 1; String epochSecondsValue = Long.toString(epochSeconds); - TimeClaim timeClaim = TimeClaimFactory.fromEpochSeconds(ISSUED_AT_TIME, epochSecondsValue, epochSeconds); + TimeClaim timeClaim = TimeClaimFactory.fromEpochSeconds(ISSUED_AT_TIME, epochSecondsValue); assertThat(timeClaim.type()).isEqualTo(ISSUED_AT_TIME); assertThat(timeClaim.value()).isEqualTo(epochSecondsValue); @@ -152,7 +152,7 @@ void testIssuedAtDatesInTheFutureAreInvalid() { long epochSeconds = Instant.now().getEpochSecond() + 5; String epochSecondsValue = Long.toString(epochSeconds); - TimeClaim timeClaim = TimeClaimFactory.fromEpochSeconds(ISSUED_AT_TIME, epochSecondsValue, epochSeconds); + TimeClaim timeClaim = TimeClaimFactory.fromEpochSeconds(ISSUED_AT_TIME, epochSecondsValue); assertThat(timeClaim.type()).isEqualTo(ISSUED_AT_TIME); assertThat(timeClaim.value()).isEqualTo(epochSecondsValue); @@ -166,7 +166,7 @@ void testNotBeforeDatesInThePastAreValid() { long epochSeconds = Instant.now().getEpochSecond() - 1; String epochSecondsValue = Long.toString(epochSeconds); - TimeClaim timeClaim = TimeClaimFactory.fromEpochSeconds(NOT_BEFORE_TIME, epochSecondsValue, epochSeconds); + TimeClaim timeClaim = TimeClaimFactory.fromEpochSeconds(NOT_BEFORE_TIME, epochSecondsValue); assertThat(timeClaim.type()).isEqualTo(NOT_BEFORE_TIME); assertThat(timeClaim.value()).isEqualTo(epochSecondsValue); @@ -181,7 +181,7 @@ void testNotBeforeDatesInTheFutureAreInvalid() { long epochSeconds = Instant.now().getEpochSecond() + 5; String epochSecondsValue = Long.toString(epochSeconds); - TimeClaim timeClaim = TimeClaimFactory.fromEpochSeconds(NOT_BEFORE_TIME, epochSecondsValue, epochSeconds); + TimeClaim timeClaim = TimeClaimFactory.fromEpochSeconds(NOT_BEFORE_TIME, epochSecondsValue); assertThat(timeClaim.type()).isEqualTo(NOT_BEFORE_TIME); assertThat(timeClaim.value()).isEqualTo(epochSecondsValue); diff --git a/src/test/java/com/blackberry/jwteditor/presenter/InformationTest.java b/src/test/java/com/blackberry/jwteditor/presenter/InformationTest.java index 90dfc0b..1ae8221 100644 --- a/src/test/java/com/blackberry/jwteditor/presenter/InformationTest.java +++ b/src/test/java/com/blackberry/jwteditor/presenter/InformationTest.java @@ -36,15 +36,14 @@ class InformationTest { static Stream data() { return Stream.of( arguments(new TimeClaim(ISSUED_AT_TIME, "isogeny", null), "Issued At - invalid value: isogeny", true), - arguments(TimeClaimFactory.fromEpochSeconds(ISSUED_AT_TIME, "1516239022", 1516239022L), "Issued At - Thu Jan 18 2018 01:30:22", false), - arguments(TimeClaimFactory.fromEpochSeconds(ISSUED_AT_TIME, "1516239022", 2516239022L), "Issued At - Sun Sep 26 2049 03:17:02", true), + arguments(TimeClaimFactory.fromEpochSeconds(ISSUED_AT_TIME, "1516239022"), "Issued At - Thu Jan 18 2018 01:30:22", false), + arguments(TimeClaimFactory.fromEpochSeconds(ISSUED_AT_TIME, "2516239022"), "Issued At - Sun Sep 26 2049 03:17:02", true), arguments(new TimeClaim(NOT_BEFORE_TIME, "isogeny", null), "Not Before - invalid value: isogeny", true), - arguments(TimeClaimFactory.fromEpochSeconds(NOT_BEFORE_TIME, "1516239022", 1516239022L), "Not Before - Thu Jan 18 2018 01:30:22", false), - arguments(TimeClaimFactory.fromEpochSeconds(NOT_BEFORE_TIME, "1516239022", 2516239022L), "Not Before - Sun Sep 26 2049 03:17:02", true), + arguments(TimeClaimFactory.fromEpochSeconds(NOT_BEFORE_TIME, "1516239022"), "Not Before - Thu Jan 18 2018 01:30:22", false), + arguments(TimeClaimFactory.fromEpochSeconds(NOT_BEFORE_TIME, "2516239022"), "Not Before - Sun Sep 26 2049 03:17:02", true), arguments(new TimeClaim(EXPIRATION_TIME, "isogeny", null), "Expiration Time - invalid value: isogeny", true), - arguments(TimeClaimFactory.fromEpochSeconds(EXPIRATION_TIME, "1516239022", 1516239022L), "Expiration Time - Thu Jan 18 2018 01:30:22", true), - arguments(TimeClaimFactory.fromEpochSeconds(EXPIRATION_TIME, "1516239022", 2516239022L), "Expiration Time - Sun Sep 26 2049 03:17:02", false) - ); + arguments(TimeClaimFactory.fromEpochSeconds(EXPIRATION_TIME, "1516239022"), "Expiration Time - Thu Jan 18 2018 01:30:22", true), + arguments(TimeClaimFactory.fromEpochSeconds(EXPIRATION_TIME, "2516239022"), "Expiration Time - Sun Sep 26 2049 03:17:02", false)); } @MethodSource("data") From 6e09489409c8cb85dd5c964dc29432f8ed1ae761 Mon Sep 17 00:00:00 2001 From: Nirusu Date: Sat, 11 Jan 2025 00:38:43 +0100 Subject: [PATCH 2/3] Add tests for high-precision decimal TimeClaims --- .../model/jose/JWSTimeClaimTest.java | 55 ++++++++++++++++--- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/src/test/java/com/blackberry/jwteditor/model/jose/JWSTimeClaimTest.java b/src/test/java/com/blackberry/jwteditor/model/jose/JWSTimeClaimTest.java index 9258103..482ab24 100644 --- a/src/test/java/com/blackberry/jwteditor/model/jose/JWSTimeClaimTest.java +++ b/src/test/java/com/blackberry/jwteditor/model/jose/JWSTimeClaimTest.java @@ -53,6 +53,8 @@ private static Stream jwsWithValidExpValues() { return Stream.of( "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiZXhwIjoyMzE2MjM5MDIyfQ.nmCDcUHT-yLgrRG1LKMH9E2FQs2xWcB8CncxoqKdQEQ", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiZXhwIjoiMjMxNjIzOTAyMiJ9.kDOLZJTcFVwrSVSDeFK31EIx7Q4e4Ya33iNCk4QZ10c", + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiZXhwIjoyMzE2MjM5MDIyLjEyMzQ1Nn0.H1-2jQa-px7ubm0npoG1-cWzDWNOh2IDQlsz4-Wwcyk", + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiZXhwIjoiMjMxNjIzOTAyMi4xMjM0NTYifQ.MST0_jbQJRT5p_HE8jiutwo1UJlno_G57TcizmbCS4k", "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiZXhwIjoiMjAzNi0xMi0xOVQxNjozOTo1Ny0wODowMCJ9." ); } @@ -72,7 +74,8 @@ private static Stream jwsWithInvalidExpValues() { return Stream.of( "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiZXhwIjoiemVvbGl0ZSJ9.Vsy0uiJVpA17ys9DRFWldEcgiut_N5QhvHCRRcp8Xow", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiZXhwIjpudWxsfQ.JsGUzIiM3S7cZF3LjYg-S6rYz4VXK-GBpGELzNJKOgY", - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiZXhwIjotMTIzNDV9.BCgzT_CEurIxa0MxbS9seJ62lgfJT54P7AQpUkp65GE" + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiZXhwIjotMTIzNDV9.BCgzT_CEurIxa0MxbS9seJ62lgfJT54P7AQpUkp65GE", + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiZXhwIjotMTIzNDUuNjc4OX0.y5y6TxxnzznRjSms67yg9O-U4t_RwV9wBz588gqB99M" ); } @@ -90,7 +93,9 @@ void givenJWSWithExpTimeClaims_whenExpiryDateInvalid_thenTimeClaimNotValid(Strin private static Stream jwsWithExpValuesInThePast() { return Stream.of( "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJleHAiOjE1MTYyMzkwMjJ9.mVGXFv3OuwtuZPsdaf_oGUYm2uOH-T-JRTDQE1c10q0", - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJleHAiOjB9.eVVhRMapPD77WuMWIHKBqYw0cjJHC49On-mHEuonYjk" + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJleHAiOjE1MTYyMzkwMjIuMTIzNDU2fQ.Nx2K7VSNCFGSOzLvB4cKyqUJfnYJUekVx3kvctsLYZk", + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJleHAiOjB9.eVVhRMapPD77WuMWIHKBqYw0cjJHC49On-mHEuonYjk", + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJleHAiOjB9.z4UM0Z_ZONsVCh4Cw9NyNKy3GN9PmBWCvVo6Dw12CiY" ); } @@ -108,14 +113,24 @@ void givenJWSWithExpTimeClaims_whenExpiryDateIsInThePast_thenTimeClaimNotValid(S @ParameterizedTest @ValueSource(strings = { "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwibmJmIjoxNzE2MjM5MDIyfQ.mZ_wGwRA0BBp_m6LJOPOBfwMosKrhTf9DbnKZYTzBzg", - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwibmJmIjoiMTcxNjIzOTAyMiJ9.JD7t6jE6sOzuaFi5Lj5e3RhnoRDDWW9QHv_U3bFgEKM" - }) + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwibmJmIjoiMTcxNjIzOTAyMiJ9.JD7t6jE6sOzuaFi5Lj5e3RhnoRDDWW9QHv_U3bFgEKM"}) void givenJWSWithNbfTimeClaims_thenTimeClaimsCorrect(String data) throws ParseException { JWS jws = JWSFactory.parse(data); assertThat(jws.claims().timeClaims()).containsExactly(TimeClaimFactory.fromEpochSeconds(NOT_BEFORE_TIME, "1716239022")); } + @ParameterizedTest + @ValueSource(strings = { + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwibmJmIjoxNzE2MjM5MDIyLjEyMzQ1Nn0.22WbREJsZuvAaRiGNXSENHg0JC81QL5nUxKpWOtVSR8", + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwibmJmIjoiMTcxNjIzOTAyMi4xMjM0NTYifQ.B_WI9vnukq93HeQ1xeWqQms4zZU0BAWL9irQdhoGfh0" + }) + void givenJWSWithNbfTimeClaimsMicroseconds_thenTimeClaimsCorrect(String data) throws ParseException { + JWS jws = JWSFactory.parse(data); + + assertThat(jws.claims().timeClaims()).containsExactly(TimeClaimFactory.fromEpochSeconds(NOT_BEFORE_TIME, "1716239022.123456")); + } + private static Stream jwsWithValidNbfValues() { return Stream.of( "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwibmJmIjoxNzE2MjM5MDIyfQ.mZ_wGwRA0BBp_m6LJOPOBfwMosKrhTf9DbnKZYTzBzg", @@ -123,6 +138,8 @@ private static Stream jwsWithValidNbfValues() { "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJuYmYiOjE1MTYyMzkwMjJ9.1OhNEYnVM64dytXGZ1kj_Z3fV73xGFxWyba52S5r7wc", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJuYmYiOjB9.AqIAHlKSdpcDp7mD6sWsfKCQxI2hyJYsI7Y4Dh6N6pI", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJuYmYiOjE3MjIxNjc0Nzd9.2almhOGrigs8lXH9DLKBkkUCS6r5j7zpJXYsXJN39d4", + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJuYmYiOjE3MjIxNjc0NzcuMTIzNDU2fQ.Jhjav96AdbK5WDM3yyd48nPZgXs5bU1LNypxc0EiY8g", + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJuYmYiOiIxNzIyMTY3NDc3LjEyMzQ1NiJ9.AjRS20zLSXrDUW9YuD7xly_iRkRJb9UyKwd72MpNKf8", "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwibmJmIjoiMjAyNC0wNS0yMFQyMjowMzo0MiswMTowMCJ9." ); } @@ -142,7 +159,8 @@ private static Stream jwsWithInvalidNbfValues() { return Stream.of( "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwibmJmIjoiemVvbGl0ZSJ9.kmx4FteQrcATfc2Zx47WypsqrIC-e1IM4opeLJxLyrI", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwibmJmIjpudWxsfQ.Nyai4iIAzjR5OcUwaE8xQYbArYuBIzbKxaJDnvu1J_I", - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwibmJmIjotMTIzNDV9.rlnrK7unNEaaghPFhNQnDp1GRbCU0rGORO2yDf5YIZk" + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwibmJmIjotMTIzNDV9.rlnrK7unNEaaghPFhNQnDp1GRbCU0rGORO2yDf5YIZk", + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwibmJmIjotMTIzNDUuNjc4OX0.bXoLasbuZk8NZNyYj3HCNIIvXGUb96ffvfveXwHIX4g" ); } @@ -159,7 +177,8 @@ void givenJWSWithNbfTimeClaims_whenNotBeforeDateInvalid_thenTimeClaimNotValid(St private static Stream jwsWithFutureNbfValues() { return Stream.of( "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJuYmYiOjIzMjIxNjc0Nzd9.cMocfo6ghvuYBqDwZ9GBdfbCnMLsUcZe2GZRuaah0-c", - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJuYmYiOjE4MjIxNjc0Nzd9._m-Rhio9DLcVRfWo7S-KIvlTk29QuDgal-tqreN4y4E" + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJuYmYiOjE4MjIxNjc0Nzd9._m-Rhio9DLcVRfWo7S-KIvlTk29QuDgal-tqreN4y4E", + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJuYmYiOjIzMjIxNjc0NzcuMTIzNDU2fQ.VC7D0b27ETWQMuxbWsLdmHfPvIbwadH5qQHCoP7o_g0" ); } @@ -189,6 +208,21 @@ void givenJWSWithIatTimeClaims_thenTimeClaimsCorrect(String data) throws ParseEx assertThat(jws.claims().timeClaims()).containsExactly(TimeClaimFactory.fromEpochSeconds(ISSUED_AT_TIME, "1716239022")); } + private static Stream jwsWithValidIatValuesMicroseconds() { + return Stream.of( + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE3MTYyMzkwMjIuMTIzNDU2fQ.UinUyun2D9gvOzWrY4cUfSwxB7RhisK3et9x-9PyDC4", + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOiIxNzE2MjM5MDIyLjEyMzQ1NiJ9.qNgTvt_NlseJDTQ06s3JIAh2NPSwzrUQ8VM3-iJ94Eg" + ); + } + + @ParameterizedTest + @MethodSource("jwsWithValidIatValuesMicroseconds") + void givenJWSWithIatTimeClaimsMicroseconds_thenTimeClaimsCorrect(String data) throws ParseException { + JWS jws = JWSFactory.parse(data); + + assertThat(jws.claims().timeClaims()).containsExactly(TimeClaimFactory.fromEpochSeconds(ISSUED_AT_TIME, "1716239022.123456")); + } + private static Stream jwsWithValidIatDatesInThePast() { return Stream.of( "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE3MTYyMzkwMjJ9.y06rqsXv0DMutukwDaUJU0Sf-Ye3qrDkyFpOaj1J08A", @@ -196,6 +230,8 @@ private static Stream jwsWithValidIatDatesInThePast() { "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE1MTYyMzkwMjJ9.hqWGSaFpvbrXkOWc6lrnffhNWR19W_S1YKFBx2arWBk", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjB9.9TaucSjKgR3_gXUlzTGperv3PK9IAXO0ZVbgP9Wx4IY", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE3MjIxNjc0Nzd9.SWmvLUBWE5ddBWvSEWnrHM8W3rfzyYmEQVk6-ywFFgQ", + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE3MjIxNjc0NzcuMTIzNDU2fQ.iamH_Bs-iyDuqJXD4Rs49oDupZTT7JWqgMQRqJqFoMQ", + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOiIxNzIyMTY3NDc3LjEyMzQ1NiJ9.C4Z6HY5SPvHyxgUFHA3eBzOPaXoLUzY6uRReHhnSz1I", "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoiMjAyNC0wNS0yMFQyMjowMzo0MiswMTowMCJ9." ); } @@ -215,7 +251,9 @@ private static Stream jwsWithInvalidIatValues() { return Stream.of( "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOiJ6ZW9saXRlIn0.-IpgDK_M_jQJQ6FDa-wd25xGFJ2bHNthdYn1JlQNxjg", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOm51bGx9.uplfi-hImBCsHTs__K-0LU612y3EW92J1TbINlDGc-k", - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOi0xMjM0NX0.gVkVHyEasqw2NZ63un0T9Yd6bW8bmY9uMYndlptiQzQ"); + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOi0xMjM0NX0.gVkVHyEasqw2NZ63un0T9Yd6bW8bmY9uMYndlptiQzQ", + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOi0xMjM0NS42Nzg5fQ.kWOjxuK9euJp7rcgG4WcaXuEwjk2goaVrIEdS_lmwnU" + ); } @ParameterizedTest @@ -232,7 +270,8 @@ void givenJWSWithIatTimeClaims_whenIssuedAtDateInvalid_thenTimeClaimNotValid(Str private static Stream jwsWithFutureIatDates() { return Stream.of( "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjIzMjIxNjc0Nzd9.3UYGoLpdXRhnlai81VFV_iWmW90xnCimcYverY1-Zk4", - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE4MjIxNjc0Nzd9._H-G7WjMbg8IVADVbz1Lsd7I_bGQZBYKPf8S2Q9vxf4" + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE4MjIxNjc0Nzd9._H-G7WjMbg8IVADVbz1Lsd7I_bGQZBYKPf8S2Q9vxf4", + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjIzMjIxNjc0NzcuMTIzNDU2fQ.7ue0C6Wo06Hb_HoVHnQ7eMUPkQFN_Q6YyyNzqVQ8Qq8" ); } @ParameterizedTest From 9e7366bd92d0fa753924ffd7f74c0d90ea262294 Mon Sep 17 00:00:00 2001 From: Nirusu Date: Sat, 11 Jan 2025 01:47:37 +0100 Subject: [PATCH 3/3] Add exception handling for BigDecimal parsing --- .../model/jose/TimeClaimFactory.java | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/blackberry/jwteditor/model/jose/TimeClaimFactory.java b/src/main/java/com/blackberry/jwteditor/model/jose/TimeClaimFactory.java index b2110f9..f9211b2 100644 --- a/src/main/java/com/blackberry/jwteditor/model/jose/TimeClaimFactory.java +++ b/src/main/java/com/blackberry/jwteditor/model/jose/TimeClaimFactory.java @@ -80,22 +80,26 @@ private static ZonedDateTime dateTimeValue(String numberValue) { return null; } - // Try to parse string time value as BigDecimal to keep the floating point precision - BigDecimal epochSecondsDecimal = new BigDecimal(numberValue); - - // Truncate decimal part of the value to get the seconds - long seconds = epochSecondsDecimal.longValue(); - if (seconds < 0) { + try { + // Try to parse string time value as BigDecimal to keep the floating point precision + BigDecimal epochSecondsDecimal = new BigDecimal(numberValue); + + // Truncate decimal part of the value to get the seconds + long seconds = epochSecondsDecimal.longValue(); + if (seconds < 0) { + return null; + } + + // Get decimal part as nanoseconds + long nanoseconds = epochSecondsDecimal.subtract(new BigDecimal(seconds)) + .movePointRight(9) + .longValue(); + + Instant instant = Instant.ofEpochSecond(seconds, nanoseconds); + return ZonedDateTime.ofInstant(instant, UTC); + } catch (NumberFormatException | ArithmeticException e) { return null; } - - // Get decimal part as nanoseconds - long nanoseconds = epochSecondsDecimal.subtract(new BigDecimal(seconds)) - .movePointRight(9) - .longValue(); - - Instant instant = Instant.ofEpochSecond(seconds, nanoseconds); - return ZonedDateTime.ofInstant(instant, UTC); } private static ZonedDateTime parseDateTime(String value) {