1
0
Files
vault_totp/common/funcs.go
2025-12-23 20:58:56 -05:00

146 lines
3.8 KiB
Go

package common
import (
`math`
`time`
)
/*
CurrentTimeStep returns the current time step using the given
[time.Duration] `period`.
If `period` is equal to time.Duration(0), it will be set to [DefaultTimePeriod].
*/
func CurrentTimeStep(period time.Duration) (ts int64) {
return TimeStepFromTime(time.Now(), period)
}
/*
OffsetTimeStep is like [CurrentTimeStep] but returns the time step next/previous
to the current specified by `offset` (`offset` may be negative or positive).
*/
func OffsetTimeStep(period time.Duration, offset int64) (ts int64) {
var offsetDuration time.Duration
period = normalizePeriod(period)
offsetDuration = period * time.Duration(offset)
ts = TimeStepFromTime(time.Now().Add(offsetDuration), period)
return
}
/*
OffsetTimeStepToTime is like [TimeStepToTime] but returns the next/previous
time range in relation to time step `ts` specified by `offset` (`offset` may be negative or positive).
*/
func OffsetTimeStepToTime(ts int64, period time.Duration, offset int64) (start, end time.Time) {
return TimeStepToTime(ts+offset, period)
}
/*
TimeStepFromTime returns a time step (see [RFC 6238 § 1.2], [RFC 6238 § 5.2])
from a provided generation time `t` as a [time.Time] and a life length
`period` as a [time.Duration].
If `period` is equal to time.Duration(0), it will be set to [DefaultTimePeriod].
[RFC 6238 § 1.2]: https://datatracker.ietf.org/doc/html/rfc6238#section-1.2
[RFC 6238 § 5.2]: https://datatracker.ietf.org/doc/html/rfc6238#section-5.2
*/
func TimeStepFromTime(t time.Time, period time.Duration) (ts int64) {
period = normalizePeriod(period)
ts = int64(math.Floor(float64(t.Unix()) / period.Seconds()))
return
}
/*
TimeStepToTime returns a [time.Time] `start` and `end` from a time step `ts`
and a lifetime period `period` (as a [time.Duration]).
If `period` is equal to time.Duration(0), it will be set to [DefaultTimePeriod].
*/
func TimeStepToTime(ts int64, period time.Duration) (start, end time.Time) {
var fromEpoch time.Duration
period = normalizePeriod(period)
fromEpoch = (time.Second * time.Duration(ts*int64(period.Seconds()))).Truncate(time.Second)
start = time.Unix(0, 0).Add(fromEpoch).Truncate(time.Second)
end = start.Add(period).Truncate(time.Second)
return
}
// VaultEscape is used to normalize a string to a Vault-safe name (for e.g. secrets).
func VaultEscape(s *string) {
var c rune
var idx int
var last rune
var norm []rune
var reduced []rune = make([]rune, 0)
if s == nil {
return
}
norm = make([]rune, 0, len(*s))
// https://github.com/hashicorp/vault/issues/5645#issuecomment-434404750
// It seems they relaxed this a bit since then, as hyphens and periods are also allowed.
// https://github.com/hashicorp/vault/blob/main/sdk/framework/path.go#L26
// At time of writing, the regex is: (?P<name>\\w(([\\w-.@]+)?\\w)?)
// Per comments: "alphanumeric characters along with -, . and @."
for _, c = range *s {
// If it's "safe" chars, it's fine
if (c == '-' || c == '.') || // 0x2d, 0x2e
(c >= '0' && c <= '9') || // 0x30 to 0x39
(c == '@') || // 0x40
(c >= 'A' && c <= 'Z') || // 0x41 to 0x5a
(c == '_') || // 0x5f
(c >= 'a' && c <= 'z') { // 0x61 to 0x7a
norm = append(norm, c)
continue
}
// Otherwise normalize it to a safe char
norm = append(norm, VaultRepl)
}
// And remove repeating sequential replacers.
for idx, c = range norm[:] {
if idx == 0 {
last = c
reduced = append(reduced, c)
continue
}
if c == last && last == VaultRepl {
continue
}
reduced = append(reduced, c)
last = c
}
*s = string(reduced)
return
}
// normalizedPeriod returns `period` if non-zero, otherwise [DefaultTimePeriod].
func normalizePeriod(period time.Duration) (normalized time.Duration) {
if period != time.Duration(0) {
normalized = period
return
}
normalized = DefaultTimePeriod
return
}