Skip to content

Commit 3a29e1c

Browse files
author
Simon Coffey
committed
Support decimal values in cacheDuration parsing
Since the introduction of cacheDuration parsing in 25cbddd we've been seeing parsing failures for one of our IdPs, whose cacheDuration value is set to cacheDuration="PT6H0M0.000S" This is a valid ISO8601 duration, however the regexp being used for parsing doesn't provide for the possibility of milliseconds. The ISO8601 spec[1] supports decimal values for at least some of a duration's components (S4.4.3.2): > If necessary for a particular application, the lowest order > components may have a decimal fraction. It's not entirely plain which components this might refer to; it could potentially be any of them (i.e. whichever component happens to be the smallest in a given duration, which would mean P0.5Y would be valid). However, applying decimal logic to the nominal components (Y/M/D) isn't possible in our current implementation, as the DateTime#next_year, next_month and next_day methods that we're using to construct the new timestamp only respect integer values. If we parsed decimals but then ignored them, someone setting a duration of P0.5Y would silently receive a duration of zero. Even if we were to try and adjust our implementation to support fractions in the nominal components, given that all nominal values can vary in length (months have different lengths, years can be leap years, days can have leap seconds), the question of what a fractional nominal component actually means is ill-defined. This commit therefore adds fractional support to just the H/M/S components of the duration parsing. To ensure that we actually respect these values, I've updated our parsed value coercion to always produce floats. The spec permits arbitrary decimal precision, and supports both commas and dots as the decimal separator, which is reflected in the implementation. [1] https://www.loc.gov/standards/datetime/iso-tc154-wg5_n0038_iso_wd_8601-1_2016-02-16.pdf
1 parent cb9b848 commit 3a29e1c

2 files changed

Lines changed: 18 additions & 15 deletions

File tree

lib/onelogin/ruby-saml/utils.rb

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,18 @@ class Utils
1616
DSIG = "http://www.w3.org/2000/09/xmldsig#"
1717
XENC = "http://www.w3.org/2001/04/xmlenc#"
1818
DURATION_FORMAT = %r(^
19-
(-?)P # 1: Duration sign
19+
(-?)P # 1: Duration sign
2020
(?:
21-
(?:(\d+)Y)? # 2: Years
22-
(?:(\d+)M)? # 3: Months
23-
(?:(\d+)D)? # 4: Days
21+
(?:(\d+)Y)? # 2: Years
22+
(?:(\d+)M)? # 3: Months
23+
(?:(\d+)D)? # 4: Days
2424
(?:T
25-
(?:(\d+)H)? # 5: Hours
26-
(?:(\d+)M)? # 6: Minutes
27-
(?:(\d+)S)? # 7: Seconds and optional milliseconds
25+
(?:(\d+(?:[.,]\d+)?)H)? # 5: Hours
26+
(?:(\d+(?:[.,]\d+)?)M)? # 6: Minutes
27+
(?:(\d+(?:[.,]\d+)?)S)? # 7: Seconds
2828
)?
2929
|
30-
(\d+)W # 8: Weeks
30+
(\d+)W # 8: Weeks
3131
)
3232
$)x
3333

@@ -59,13 +59,8 @@ def self.parse_duration(duration, timestamp=Time.now.utc)
5959
raise Exception.new("Invalid ISO 8601 duration")
6060
end
6161

62-
durYears = matches[2].to_i
63-
durMonths = matches[3].to_i
64-
durDays = matches[4].to_i
65-
durHours = matches[5].to_i
66-
durMinutes = matches[6].to_i
67-
durSeconds = matches[7].to_f
68-
durWeeks = matches[8].to_i
62+
durYears, durMonths, durDays, durHours, durMinutes, durSeconds, durWeeks =
63+
matches[2..8].map { |match| match ? match.tr(',', '.').to_f : 0.0 }
6964

7065
if matches[1] == "-"
7166
durYears = -durYears

test/utils_test.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ class UtilsTest < Minitest::Test
1212
# Nominal wraparounds
1313
"P13M" => "1971-02-01T00:00:00.000Z",
1414
"P31D" => "1970-02-01T00:00:00.000Z",
15+
16+
# Decimal values
17+
"PT0.5H" => "1970-01-01T00:30:00.000Z",
18+
"PT0.5M" => "1970-01-01T00:00:30.000Z",
19+
"PT0.5S" => "1970-01-01T00:00:00.500Z",
20+
21+
# Comma decimal separator
22+
"PT0,5S" => "1970-01-01T00:00:00.500Z"
1523
}
1624

1725
def result(duration, reference = 0)

0 commit comments

Comments
 (0)