This is an automated email from the ASF dual-hosted git repository.
tison pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/opendal-reqsign.git
The following commit(s) were added to refs/heads/main by this push:
new a925881 refactor: chrono to jiff (#634)
a925881 is described below
commit a925881ab574d65082a40a3bbe84fa0dc2850e1b
Author: tison <[email protected]>
AuthorDate: Mon Sep 29 15:00:01 2025 +0800
refactor: chrono to jiff (#634)
This closes #631
Signed-off-by: tison <[email protected]>
---
Cargo.toml | 3 +-
core/Cargo.toml | 2 +-
core/src/time.rs | 96 +++++++---------------
licenserc.toml | 2 +-
services/aliyun-oss/Cargo.toml | 2 +-
services/aliyun-oss/src/credential.rs | 6 +-
services/aliyun-oss/src/sign_request.rs | 22 ++---
services/aws-v4/Cargo.toml | 2 +-
services/aws-v4/src/credential.rs | 6 +-
services/aws-v4/src/provide_credential/cognito.rs | 6 +-
services/aws-v4/src/provide_credential/imds.rs | 10 +--
services/aws-v4/src/provide_credential/process.rs | 4 +-
.../src/provide_credential/s3_express_session.rs | 16 ++--
services/aws-v4/src/provide_credential/sso.rs | 10 +--
services/aws-v4/src/sign_request.rs | 12 +--
services/azure-storage/Cargo.toml | 2 +-
services/azure-storage/src/account_sas.rs | 14 ++--
services/azure-storage/src/credential.rs | 8 +-
.../src/provide_credential/azure_cli.rs | 11 +--
.../src/provide_credential/azure_pipelines.rs | 15 ++--
.../src/provide_credential/client_certificate.rs | 17 ++--
.../src/provide_credential/client_secret.rs | 3 +-
.../azure-storage/src/provide_credential/imds.rs | 8 +-
.../src/provide_credential/workload_identity.rs | 5 +-
services/azure-storage/src/sign_request.rs | 14 ++--
services/google/Cargo.toml | 2 +-
services/google/src/credential.rs | 18 ++--
.../src/provide_credential/authorized_user.rs | 6 +-
.../src/provide_credential/external_account.rs | 14 ++--
.../impersonated_service_account.rs | 14 ++--
.../google/src/provide_credential/vm_metadata.rs | 3 +-
services/google/src/sign_request.rs | 10 +--
services/huaweicloud-obs/Cargo.toml | 2 +-
services/huaweicloud-obs/src/sign_request.rs | 37 ++++-----
services/oracle/Cargo.toml | 4 +-
services/oracle/src/credential.rs | 6 +-
services/oracle/src/provide_credential/config.rs | 3 +-
.../oracle/src/provide_credential/config_file.rs | 3 +-
services/oracle/src/provide_credential/env.rs | 3 +-
services/tencent-cos/Cargo.toml | 2 +-
services/tencent-cos/src/credential.rs | 6 +-
.../assume_role_with_web_identity.rs | 2 +-
services/tencent-cos/src/sign_request.rs | 12 +--
services/tencent-cos/tests/credential_chain.rs | 4 +-
44 files changed, 190 insertions(+), 257 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 0feb155..90c7dbd 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -32,15 +32,14 @@ anyhow = "1"
async-trait = "0.1"
base64 = "0.22"
bytes = "1"
-chrono = "0.4.35"
criterion = { version = "0.7.0", features = ["async_tokio", "html_reports"] }
dotenv = "0.15"
env_logger = "0.11"
form_urlencoded = "1"
hex = "0.4"
hmac = "0.12"
-home = "0.5"
http = "1"
+jiff = "0.2"
log = "0.4"
macro_rules_attribute = "0.2.0"
once_cell = "1"
diff --git a/core/Cargo.toml b/core/Cargo.toml
index 124566c..e0f5037 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -32,11 +32,11 @@ anyhow.workspace = true
async-trait.workspace = true
base64.workspace = true
bytes.workspace = true
-chrono.workspace = true
form_urlencoded.workspace = true
hex.workspace = true
hmac.workspace = true
http.workspace = true
+jiff.workspace = true
log.workspace = true
percent-encoding.workspace = true
sha1.workspace = true
diff --git a/core/src/time.rs b/core/src/time.rs
index b8cb500..d2fd5f6 100644
--- a/core/src/time.rs
+++ b/core/src/time.rs
@@ -18,68 +18,25 @@
//! Time related utils.
use crate::Error;
-use chrono::format::Fixed;
-use chrono::format::Item;
-use chrono::format::Numeric;
-use chrono::format::Pad;
-use chrono::SecondsFormat;
-use chrono::Utc;
-/// DateTime is the alias for chrono::DateTime<Utc>.
-pub type DateTime = chrono::DateTime<Utc>;
+/// DateTime is the alias for `jiff::Timestamp`.
+pub type Timestamp = jiff::Timestamp;
/// Create datetime of now.
-pub fn now() -> DateTime {
- Utc::now()
+pub fn now() -> Timestamp {
+ Timestamp::now()
}
-/// DATE is a time format like `20220301`
-const DATE: &[Item<'static>] = &[
- Item::Numeric(Numeric::Year, Pad::Zero),
- Item::Numeric(Numeric::Month, Pad::Zero),
- Item::Numeric(Numeric::Day, Pad::Zero),
-];
-
/// Format time into date: `20220301`
-pub fn format_date(t: DateTime) -> String {
- t.format_with_items(DATE.iter()).to_string()
+pub fn format_date(t: Timestamp) -> String {
+ t.strftime("%Y%m%d").to_string()
}
-/// ISO8601 is a time format like `20220313T072004Z`.
-const ISO8601: &[Item<'static>] = &[
- Item::Numeric(Numeric::Year, Pad::Zero),
- Item::Numeric(Numeric::Month, Pad::Zero),
- Item::Numeric(Numeric::Day, Pad::Zero),
- Item::Literal("T"),
- Item::Numeric(Numeric::Hour, Pad::Zero),
- Item::Numeric(Numeric::Minute, Pad::Zero),
- Item::Numeric(Numeric::Second, Pad::Zero),
- Item::Literal("Z"),
-];
-
/// Format time into ISO8601: `20220313T072004Z`
-pub fn format_iso8601(t: DateTime) -> String {
- t.format_with_items(ISO8601.iter()).to_string()
+pub fn format_iso8601(t: Timestamp) -> String {
+ t.strftime("%Y%m%dT%H%M%SZ").to_string()
}
-/// HTTP_DATE is a time format like `Sun, 06 Nov 1994 08:49:37 GMT`.
-const HTTP_DATE: &[Item<'static>] = &[
- Item::Fixed(Fixed::ShortWeekdayName),
- Item::Literal(", "),
- Item::Numeric(Numeric::Day, Pad::Zero),
- Item::Literal(" "),
- Item::Fixed(Fixed::ShortMonthName),
- Item::Literal(" "),
- Item::Numeric(Numeric::Year, Pad::Zero),
- Item::Literal(" "),
- Item::Numeric(Numeric::Hour, Pad::Zero),
- Item::Literal(":"),
- Item::Numeric(Numeric::Minute, Pad::Zero),
- Item::Literal(":"),
- Item::Numeric(Numeric::Second, Pad::Zero),
- Item::Literal(" GMT"),
-];
-
/// Format time into http date: `Sun, 06 Nov 1994 08:49:37 GMT`
///
/// ## Note
@@ -88,13 +45,13 @@ const HTTP_DATE: &[Item<'static>] = &[
///
/// - Timezone is fixed to GMT.
/// - Day must be 2 digit.
-pub fn format_http_date(t: DateTime) -> String {
- t.format_with_items(HTTP_DATE.iter()).to_string()
+pub fn format_http_date(t: Timestamp) -> String {
+ t.strftime("%a, %d %b %Y %T GMT").to_string()
}
/// Format time into RFC3339: `2022-03-13T07:20:04Z`
-pub fn format_rfc3339(t: DateTime) -> String {
- t.to_rfc3339_opts(SecondsFormat::Secs, true)
+pub fn format_rfc3339(t: Timestamp) -> String {
+ t.strftime("%FT%TZ").to_string()
}
/// Parse time from RFC3339.
@@ -104,22 +61,31 @@ pub fn format_rfc3339(t: DateTime) -> String {
/// - `2022-03-13T07:20:04Z`
/// - `2022-03-01T08:12:34+00:00`
/// - `2022-03-01T08:12:34.00+00:00`
-pub fn parse_rfc3339(s: &str) -> crate::Result<DateTime> {
- Ok(chrono::DateTime::parse_from_rfc3339(s)
- .map_err(|err| {
- Error::unexpected(format!("parse '{s}' into rfc3339
failed")).with_source(err)
- })?
- .with_timezone(&Utc))
+pub fn parse_rfc3339(s: &str) -> crate::Result<Timestamp> {
+ s.parse().map_err(|err| {
+ Error::unexpected(format!("parse '{s}' into rfc3339
failed")).with_source(err)
+ })
+}
+
+/// Parse time from RFC2822.
+///
+/// All of them are valid time:
+///
+/// - `Sat, 13 Jul 2024 15:09:59 -0400`
+/// - `Mon, 15 Aug 2022 16:50:12 GMT`
+pub fn parse_rfc2822(s: &str) -> crate::Result<Timestamp> {
+ let zoned = jiff::fmt::rfc2822::parse(s).map_err(|err| {
+ Error::unexpected(format!("parse '{s}' into rfc2822
failed")).with_source(err)
+ })?;
+ Ok(zoned.timestamp())
}
#[cfg(test)]
mod tests {
- use chrono::TimeZone;
-
use super::*;
- fn test_time() -> DateTime {
- Utc.with_ymd_and_hms(2022, 3, 1, 8, 12, 34).unwrap()
+ fn test_time() -> Timestamp {
+ "2022-03-01T08:12:34Z".parse().unwrap()
}
#[test]
diff --git a/licenserc.toml b/licenserc.toml
index 47a56e5..1937a22 100644
--- a/licenserc.toml
+++ b/licenserc.toml
@@ -17,4 +17,4 @@
headerPath = "Apache-2.0-ASF.txt"
-includes = ['**/*.rs', '**/*.yml', '**/*.yaml', '**/*.toml']
\ No newline at end of file
+includes = ['**/*.rs', '**/*.yml', '**/*.yaml', '**/*.toml']
diff --git a/services/aliyun-oss/Cargo.toml b/services/aliyun-oss/Cargo.toml
index 5521684..ea71e8f 100644
--- a/services/aliyun-oss/Cargo.toml
+++ b/services/aliyun-oss/Cargo.toml
@@ -29,8 +29,8 @@ repository.workspace = true
[dependencies]
anyhow.workspace = true
async-trait.workspace = true
-chrono.workspace = true
http.workspace = true
+jiff.workspace = true
log.workspace = true
once_cell.workspace = true
percent-encoding.workspace = true
diff --git a/services/aliyun-oss/src/credential.rs
b/services/aliyun-oss/src/credential.rs
index d7c0229..9ec61ed 100644
--- a/services/aliyun-oss/src/credential.rs
+++ b/services/aliyun-oss/src/credential.rs
@@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
-use reqsign_core::time::{now, DateTime};
+use reqsign_core::time::{now, Timestamp};
use reqsign_core::utils::Redact;
use reqsign_core::SigningCredential;
use std::fmt::{Debug, Formatter};
@@ -30,7 +30,7 @@ pub struct Credential {
/// Security token for aliyun services.
pub security_token: Option<String>,
/// Expiration time for this credential.
- pub expires_in: Option<DateTime>,
+ pub expires_in: Option<Timestamp>,
}
impl Debug for Credential {
@@ -54,7 +54,7 @@ impl SigningCredential for Credential {
// Take 120s as buffer to avoid edge cases.
if let Some(valid) = self
.expires_in
- .map(|v| v > now() + chrono::TimeDelta::try_minutes(2).expect("in
bounds"))
+ .map(|v| v > now() + jiff::SignedDuration::from_mins(2))
{
return valid;
}
diff --git a/services/aliyun-oss/src/sign_request.rs
b/services/aliyun-oss/src/sign_request.rs
index 37b4adb..08df07b 100644
--- a/services/aliyun-oss/src/sign_request.rs
+++ b/services/aliyun-oss/src/sign_request.rs
@@ -22,7 +22,7 @@ use http::HeaderValue;
use once_cell::sync::Lazy;
use percent_encoding::utf8_percent_encode;
use reqsign_core::hash::base64_hmac_sha1;
-use reqsign_core::time::{format_http_date, now, DateTime};
+use reqsign_core::time::{format_http_date, now, Timestamp};
use reqsign_core::Result;
use reqsign_core::{Context, SignRequest};
use std::collections::HashSet;
@@ -35,7 +35,7 @@ const CONTENT_MD5: &str = "content-md5";
#[derive(Debug)]
pub struct RequestSigner {
bucket: String,
- time: Option<DateTime>,
+ time: Option<Timestamp>,
}
impl RequestSigner {
@@ -54,12 +54,12 @@ impl RequestSigner {
/// We should always take current time to sign requests.
/// Only use this function for testing.
#[cfg(test)]
- pub fn with_time(mut self, time: DateTime) -> Self {
+ pub fn with_time(mut self, time: Timestamp) -> Self {
self.time = Some(time);
self
}
- fn get_time(&self) -> DateTime {
+ fn get_time(&self) -> Timestamp {
self.time.unwrap_or_else(now)
}
}
@@ -97,7 +97,7 @@ impl RequestSigner {
&self,
req: &mut http::request::Parts,
cred: &Credential,
- signing_time: DateTime,
+ signing_time: Timestamp,
) -> Result<()> {
let string_to_sign = self.build_string_to_sign(req, cred,
signing_time, None)?;
let signature =
@@ -125,11 +125,11 @@ impl RequestSigner {
&self,
req: &mut http::request::Parts,
cred: &Credential,
- signing_time: DateTime,
+ signing_time: Timestamp,
expires: Duration,
) -> Result<()> {
let expiration_time = signing_time
- + chrono::TimeDelta::from_std(expires).map_err(|e| {
+ + jiff::SignedDuration::try_from(expires).map_err(|e| {
reqsign_core::Error::request_invalid(format!("Invalid
expiration duration: {e}"))
})?;
let string_to_sign = self.build_string_to_sign(req, cred,
signing_time, Some(expires))?;
@@ -154,7 +154,7 @@ impl RequestSigner {
query_pairs.push(("OSSAccessKeyId".to_string(),
cred.access_key_id.clone()));
query_pairs.push((
"Expires".to_string(),
- expiration_time.timestamp().to_string(),
+ expiration_time.as_second().to_string(),
));
query_pairs.push((
"Signature".to_string(),
@@ -200,7 +200,7 @@ impl RequestSigner {
&self,
req: &http::request::Parts,
cred: &Credential,
- signing_time: DateTime,
+ signing_time: Timestamp,
expires: Option<Duration>,
) -> Result<String> {
let mut s = String::new();
@@ -229,12 +229,12 @@ impl RequestSigner {
match expires {
Some(expires_duration) => {
let expiration_time = signing_time
- +
chrono::TimeDelta::from_std(expires_duration).map_err(|e| {
+ +
jiff::SignedDuration::try_from(expires_duration).map_err(|e| {
reqsign_core::Error::request_invalid(format!(
"Invalid expiration duration: {e}"
))
})?;
- writeln!(&mut s, "{}", expiration_time.timestamp())?;
+ writeln!(&mut s, "{}", expiration_time.as_second())?;
}
None => {
writeln!(&mut s, "{}", format_http_date(signing_time))?;
diff --git a/services/aws-v4/Cargo.toml b/services/aws-v4/Cargo.toml
index 80df230..47acb30 100644
--- a/services/aws-v4/Cargo.toml
+++ b/services/aws-v4/Cargo.toml
@@ -34,9 +34,9 @@ name = "aws"
anyhow.workspace = true
async-trait.workspace = true
bytes = "1.7.2"
-chrono.workspace = true
form_urlencoded.workspace = true
http.workspace = true
+jiff.workspace = true
log.workspace = true
percent-encoding.workspace = true
quick-xml.workspace = true
diff --git a/services/aws-v4/src/credential.rs
b/services/aws-v4/src/credential.rs
index 6e952e8..a0a1dbb 100644
--- a/services/aws-v4/src/credential.rs
+++ b/services/aws-v4/src/credential.rs
@@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
-use reqsign_core::time::{now, DateTime};
+use reqsign_core::time::{now, Timestamp};
use reqsign_core::utils::Redact;
use reqsign_core::SigningCredential;
use std::fmt::{Debug, Formatter};
@@ -30,7 +30,7 @@ pub struct Credential {
/// Session token for aws services.
pub session_token: Option<String>,
/// Expiration time for this credential.
- pub expires_in: Option<DateTime>,
+ pub expires_in: Option<Timestamp>,
}
impl Debug for Credential {
@@ -54,7 +54,7 @@ impl SigningCredential for Credential {
// Take 120s as buffer to avoid edge cases.
if let Some(valid) = self
.expires_in
- .map(|v| v > now() + chrono::TimeDelta::try_minutes(2).expect("in
bounds"))
+ .map(|v| v > now() + jiff::SignedDuration::from_mins(2))
{
return valid;
}
diff --git a/services/aws-v4/src/provide_credential/cognito.rs
b/services/aws-v4/src/provide_credential/cognito.rs
index 52ce42b..1de3730 100644
--- a/services/aws-v4/src/provide_credential/cognito.rs
+++ b/services/aws-v4/src/provide_credential/cognito.rs
@@ -17,9 +17,9 @@
use crate::Credential;
use async_trait::async_trait;
-use chrono::DateTime;
use http::{Method, Request, StatusCode};
use log::debug;
+use reqsign_core::time::Timestamp;
use reqsign_core::{Context, Error, ProvideCredential, Result};
use serde::Deserialize;
use serde_json::json;
@@ -224,8 +224,8 @@ impl CognitoIdentityCredentialProvider {
.map_err(|e| Error::unexpected(format!("failed to parse
credentials response: {e}")))?;
let creds = result.credentials;
- let expires_in = DateTime::from_timestamp(creds.expiration, 0)
- .ok_or_else(|| Error::unexpected("invalid expiration
timestamp".to_string()))?;
+ let expires_in = Timestamp::from_second(creds.expiration)
+ .map_err(|e| Error::unexpected(format!("invalid expiration date:
{e}")))?;
Ok(Credential {
access_key_id: creds.access_key_id,
diff --git a/services/aws-v4/src/provide_credential/imds.rs
b/services/aws-v4/src/provide_credential/imds.rs
index 61db8b5..a25af58 100644
--- a/services/aws-v4/src/provide_credential/imds.rs
+++ b/services/aws-v4/src/provide_credential/imds.rs
@@ -21,7 +21,7 @@ use async_trait::async_trait;
use bytes::Bytes;
use http::header::CONTENT_LENGTH;
use http::Method;
-use reqsign_core::time::{now, parse_rfc3339, DateTime};
+use reqsign_core::time::{now, parse_rfc3339, Timestamp};
use reqsign_core::{Context, Error, ProvideCredential, Result};
use serde::Deserialize;
use std::sync::{Arc, Mutex};
@@ -29,14 +29,14 @@ use std::sync::{Arc, Mutex};
#[derive(Debug, Clone)]
pub struct IMDSv2CredentialProvider {
endpoint: Option<String>,
- token: Arc<Mutex<(String, DateTime)>>,
+ token: Arc<Mutex<(String, Timestamp)>>,
}
impl Default for IMDSv2CredentialProvider {
fn default() -> Self {
Self {
endpoint: None,
- token: Arc::new(Mutex::new((String::new(), DateTime::default()))),
+ token: Arc::new(Mutex::new((String::new(), Timestamp::default()))),
}
}
}
@@ -105,8 +105,8 @@ impl IMDSv2CredentialProvider {
}
let ec2_token = resp.into_body();
// Set expires_in to 10 minutes to enforce re-read.
- let expires_in = now() +
chrono::TimeDelta::try_seconds(21600).expect("in bounds")
- - chrono::TimeDelta::try_seconds(600).expect("in bounds");
+ let expires_in =
+ now() + jiff::SignedDuration::from_secs(21600) -
jiff::SignedDuration::from_secs(600);
{
*self.token.lock().expect("lock poisoned") = (ec2_token.clone(),
expires_in);
diff --git a/services/aws-v4/src/provide_credential/process.rs
b/services/aws-v4/src/provide_credential/process.rs
index e672048..8782199 100644
--- a/services/aws-v4/src/provide_credential/process.rs
+++ b/services/aws-v4/src/provide_credential/process.rs
@@ -17,9 +17,9 @@
use crate::Credential;
use async_trait::async_trait;
-use chrono::{DateTime, Utc};
use ini::Ini;
use log::debug;
+use reqsign_core::time::Timestamp;
use reqsign_core::{Context, Error, ProvideCredential, Result};
use serde::Deserialize;
@@ -213,7 +213,7 @@ impl ProvideCredential for ProcessCredentialProvider {
let expires_in =
if let Some(exp_str) = &output.expiration {
- Some(exp_str.parse::<DateTime<Utc>>().map_err(|e| {
+ Some(exp_str.parse::<Timestamp>().map_err(|e| {
Error::unexpected(format!("failed to parse expiration
time: {e}"))
})?)
} else {
diff --git a/services/aws-v4/src/provide_credential/s3_express_session.rs
b/services/aws-v4/src/provide_credential/s3_express_session.rs
index fc79a3b..723b2bb 100644
--- a/services/aws-v4/src/provide_credential/s3_express_session.rs
+++ b/services/aws-v4/src/provide_credential/s3_express_session.rs
@@ -20,6 +20,7 @@ use async_trait::async_trait;
use bytes::Bytes;
use http::{header, Method, Request};
use log::debug;
+use reqsign_core::time::parse_rfc3339;
use reqsign_core::{Context, Error, ProvideCredential, Result, SignRequest};
use serde::Deserialize;
@@ -160,13 +161,12 @@ impl S3ExpressSessionProvider {
// Parse expiration time from ISO8601 format
let expiration =
-
chrono::DateTime::parse_from_rfc3339(&create_session_resp.credentials.expiration)
- .map_err(|e| {
- Error::unexpected(format!(
- "Failed to parse expiration time '{}': {e}",
- create_session_resp.credentials.expiration
- ))
- })?;
+
parse_rfc3339(&create_session_resp.credentials.expiration).map_err(|e| {
+ Error::unexpected(format!(
+ "Failed to parse expiration time '{}': {e}",
+ create_session_resp.credentials.expiration
+ ))
+ })?;
// Convert to Credential with expiration time
let creds = create_session_resp.credentials;
@@ -174,7 +174,7 @@ impl S3ExpressSessionProvider {
access_key_id: creds.access_key_id,
secret_access_key: creds.secret_access_key,
session_token: Some(creds.session_token),
- expires_in: Some(expiration.into()),
+ expires_in: Some(expiration),
})
}
diff --git a/services/aws-v4/src/provide_credential/sso.rs
b/services/aws-v4/src/provide_credential/sso.rs
index b000422..a0adaec 100644
--- a/services/aws-v4/src/provide_credential/sso.rs
+++ b/services/aws-v4/src/provide_credential/sso.rs
@@ -17,10 +17,10 @@
use crate::Credential;
use async_trait::async_trait;
-use chrono::{DateTime, Utc};
use http::{Method, Request, StatusCode};
use ini::Ini;
use log::{debug, warn};
+use reqsign_core::time::{now, Timestamp};
use reqsign_core::{Context, Error, ProvideCredential, Result};
use serde::Deserialize;
@@ -218,11 +218,11 @@ impl SSOCredentialProvider {
})?;
// Check if token is expired
- let expires_at: DateTime<Utc> =
token.expires_at.parse().map_err(|e| {
+ let expires_at =
token.expires_at.parse::<Timestamp>().map_err(|e| {
Error::unexpected(format!("failed to parse expiration
time: {e}"))
})?;
- if expires_at <= Utc::now() {
+ if expires_at <= now() {
warn!("SSO token is expired");
return Ok(None);
}
@@ -286,8 +286,8 @@ impl SSOCredentialProvider {
.map_err(|e| Error::unexpected(format!("failed to parse SSO
credentials: {e}")))?;
let role_creds = creds.role_credentials;
- let expires_in = DateTime::from_timestamp_millis(role_creds.expiration)
- .ok_or_else(|| Error::unexpected("invalid expiration
timestamp".to_string()))?;
+ let expires_in = Timestamp::from_millisecond(role_creds.expiration)
+ .map_err(|e| Error::unexpected(format!("invalid expiration
timestamp: {e}")))?;
Ok(Credential {
access_key_id: role_creds.access_key_id,
diff --git a/services/aws-v4/src/sign_request.rs
b/services/aws-v4/src/sign_request.rs
index 2aa7e52..6b27ddf 100644
--- a/services/aws-v4/src/sign_request.rs
+++ b/services/aws-v4/src/sign_request.rs
@@ -26,7 +26,7 @@ use http::{header, HeaderValue};
use log::debug;
use percent_encoding::{percent_decode_str, utf8_percent_encode};
use reqsign_core::hash::{hex_hmac_sha256, hex_sha256, hmac_sha256};
-use reqsign_core::time::{format_date, format_iso8601, now, DateTime};
+use reqsign_core::time::{format_date, format_iso8601, now, Timestamp};
use reqsign_core::{Context, Result, SignRequest, SigningRequest};
use std::fmt::Write;
use std::time::Duration;
@@ -39,7 +39,7 @@ pub struct RequestSigner {
service: String,
region: String,
- time: Option<DateTime>,
+ time: Option<Timestamp>,
}
impl RequestSigner {
@@ -60,7 +60,7 @@ impl RequestSigner {
/// We should always take current time to sign requests.
/// Only use this function for testing.
#[cfg(test)]
- pub fn with_time(mut self, time: DateTime) -> Self {
+ pub fn with_time(mut self, time: Timestamp) -> Self {
self.time = Some(time);
self
}
@@ -233,7 +233,7 @@ fn canonicalize_header(
ctx: &mut SigningRequest,
cred: &Credential,
expires_in: Option<Duration>,
- now: DateTime,
+ now: Timestamp,
) -> Result<()> {
// Header names and values need to be normalized according to Step 4 of
https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
for (_, value) in ctx.headers.iter_mut() {
@@ -298,7 +298,7 @@ fn canonicalize_query(
ctx: &mut SigningRequest,
cred: &Credential,
expires_in: Option<Duration>,
- now: DateTime,
+ now: Timestamp,
service: &str,
region: &str,
) -> Result<()> {
@@ -351,7 +351,7 @@ fn canonicalize_query(
Ok(())
}
-fn generate_signing_key(secret: &str, time: DateTime, region: &str, service:
&str) -> Vec<u8> {
+fn generate_signing_key(secret: &str, time: Timestamp, region: &str, service:
&str) -> Vec<u8> {
// Sign secret
let secret = format!("AWS4{secret}");
// Sign date
diff --git a/services/azure-storage/Cargo.toml
b/services/azure-storage/Cargo.toml
index 788cb83..ca3cfe7 100644
--- a/services/azure-storage/Cargo.toml
+++ b/services/azure-storage/Cargo.toml
@@ -31,9 +31,9 @@ anyhow.workspace = true
async-trait.workspace = true
base64.workspace = true
bytes.workspace = true
-chrono.workspace = true
form_urlencoded.workspace = true
http.workspace = true
+jiff.workspace = true
log.workspace = true
percent-encoding.workspace = true
reqsign-core.workspace = true
diff --git a/services/azure-storage/src/account_sas.rs
b/services/azure-storage/src/account_sas.rs
index bd04ee8..7eff18a 100644
--- a/services/azure-storage/src/account_sas.rs
+++ b/services/azure-storage/src/account_sas.rs
@@ -19,7 +19,7 @@ use reqsign_core::Result;
use reqsign_core::hash;
use reqsign_core::time;
-use reqsign_core::time::DateTime;
+use reqsign_core::time::Timestamp;
/// The default parameters that make up a SAS token
///
https://learn.microsoft.com/en-us/rest/api/storageservices/create-account-sas#specify-the-account-sas-parameters
@@ -35,15 +35,15 @@ pub struct AccountSharedAccessSignature {
resource: String,
resource_type: String,
permissions: String,
- expiry: DateTime,
- start: Option<DateTime>,
+ expiry: Timestamp,
+ start: Option<Timestamp>,
ip: Option<String>,
protocol: Option<String>,
}
impl AccountSharedAccessSignature {
/// Create a SAS token signer with default parameters
- pub fn new(account: String, key: String, expiry: DateTime) -> Self {
+ pub fn new(account: String, key: String, expiry: Timestamp) -> Self {
Self {
account,
key,
@@ -125,14 +125,14 @@ mod tests {
use super::*;
- fn test_time() -> DateTime {
- DateTime::from_str("2022-03-01T08:12:34Z").unwrap()
+ fn test_time() -> Timestamp {
+ Timestamp::from_str("2022-03-01T08:12:34Z").unwrap()
}
#[test]
fn test_can_generate_sas_token() {
let key = hash::base64_encode("key".as_bytes());
- let expiry = test_time() +
chrono::TimeDelta::try_minutes(5).expect("in bounds");
+ let expiry = test_time() + jiff::SignedDuration::from_mins(5);
let sign = AccountSharedAccessSignature::new("account".to_string(),
key, expiry);
let token_content = sign.token().expect("token decode failed");
let token = token_content
diff --git a/services/azure-storage/src/credential.rs
b/services/azure-storage/src/credential.rs
index abd1ec4..e824bc9 100644
--- a/services/azure-storage/src/credential.rs
+++ b/services/azure-storage/src/credential.rs
@@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
-use reqsign_core::time::{now, DateTime};
+use reqsign_core::time::{now, Timestamp};
use reqsign_core::utils::Redact;
use reqsign_core::SigningCredential;
use std::fmt::{Debug, Formatter};
@@ -40,7 +40,7 @@ pub enum Credential {
/// Bearer token.
token: String,
/// Expiration time for this credential.
- expires_in: Option<DateTime>,
+ expires_in: Option<Timestamp>,
},
}
@@ -82,7 +82,7 @@ impl SigningCredential for Credential {
}
// Check expiration for bearer tokens (take 20s as buffer to
avoid edge cases)
if let Some(expires) = expires_in {
- *expires > now() +
chrono::TimeDelta::try_seconds(20).expect("in bounds")
+ *expires > now() + jiff::SignedDuration::from_secs(20)
} else {
true
}
@@ -108,7 +108,7 @@ impl Credential {
}
/// Create a new credential with bearer token authentication.
- pub fn with_bearer_token(bearer_token: &str, expires_in: Option<DateTime>)
-> Self {
+ pub fn with_bearer_token(bearer_token: &str, expires_in:
Option<Timestamp>) -> Self {
Self::BearerToken {
token: bearer_token.to_string(),
expires_in,
diff --git a/services/azure-storage/src/provide_credential/azure_cli.rs
b/services/azure-storage/src/provide_credential/azure_cli.rs
index ce6dc82..69f112a 100644
--- a/services/azure-storage/src/provide_credential/azure_cli.rs
+++ b/services/azure-storage/src/provide_credential/azure_cli.rs
@@ -15,12 +15,12 @@
// specific language governing permissions and limitations
// under the License.
+use crate::credential::Credential;
use async_trait::async_trait;
+use reqsign_core::time::Timestamp;
use reqsign_core::{Context, ProvideCredential};
use serde::Deserialize;
-use crate::credential::Credential;
-
/// AzureCliCredentialProvider provides credentials from Azure CLI
///
/// This provider reads tokens from Azure CLI's local storage or invokes
@@ -96,12 +96,13 @@ impl ProvideCredential for AzureCliCredentialProvider {
// Calculate expiration time
let expires_on = if let Some(timestamp) = token.expires_on_timestamp {
- Some(chrono::DateTime::from_timestamp(timestamp, 0).unwrap())
+ Some(Timestamp::from_second(timestamp).unwrap())
} else if let Some(expires_str) = token.expires_on {
// Parse the string format "2023-10-31 21:59:10.000000"
- chrono::NaiveDateTime::parse_from_str(&expires_str, "%Y-%m-%d
%H:%M:%S%.f")
+ expires_str
+ .parse::<jiff::civil::DateTime>()
+ .and_then(|dt| jiff::tz::TimeZone::UTC.to_timestamp(dt))
.ok()
- .map(|dt| chrono::DateTime::from_naive_utc_and_offset(dt,
chrono::Utc))
} else {
None
};
diff --git a/services/azure-storage/src/provide_credential/azure_pipelines.rs
b/services/azure-storage/src/provide_credential/azure_pipelines.rs
index 6544528..a1849d0 100644
--- a/services/azure-storage/src/provide_credential/azure_pipelines.rs
+++ b/services/azure-storage/src/provide_credential/azure_pipelines.rs
@@ -17,12 +17,12 @@
use std::collections::HashMap;
+use crate::credential::Credential;
use async_trait::async_trait;
+use reqsign_core::time::now;
use reqsign_core::{Context, ProvideCredential};
use serde::Deserialize;
-use crate::credential::Credential;
-
/// AzurePipelinesCredentialProvider provides credentials using Azure
Pipelines workload identity
///
/// This provider uses OIDC tokens from Azure Pipelines to authenticate with
Azure AD
@@ -233,13 +233,8 @@ impl ProvideCredential for
AzurePipelinesCredentialProvider {
.await?;
// Calculate expiration time
- let expires_on = std::time::SystemTime::now()
-
.checked_add(std::time::Duration::from_secs(token_response.expires_in))
- .and_then(|t| {
- t.duration_since(std::time::UNIX_EPOCH)
- .ok()
- .map(|d| chrono::DateTime::from_timestamp(d.as_secs() as
i64, 0).unwrap())
- });
+ let expires_in =
std::time::Duration::from_secs(token_response.expires_in);
+ let expires_on = now().checked_add(expires_in).ok();
Ok(Some(Credential::with_bearer_token(
&token_response.access_token,
@@ -271,7 +266,7 @@ mod tests {
async fn test_provide_credential_no_environment() {
// When not in Azure Pipelines environment, should return None
let provider = AzurePipelinesCredentialProvider::new();
- let ctx = reqsign_core::Context::new()
+ let ctx = Context::new()
.with_file_read(reqsign_file_read_tokio::TokioFileRead)
.with_http_send(reqsign_http_send_reqwest::ReqwestHttpSend::default());
diff --git
a/services/azure-storage/src/provide_credential/client_certificate.rs
b/services/azure-storage/src/provide_credential/client_certificate.rs
index 9e1d6a6..7268568 100644
--- a/services/azure-storage/src/provide_credential/client_certificate.rs
+++ b/services/azure-storage/src/provide_credential/client_certificate.rs
@@ -18,23 +18,23 @@
use std::collections::HashMap;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
+use crate::credential::Credential;
use async_trait::async_trait;
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use base64::Engine;
use jsonwebtoken::{Algorithm, EncodingKey, Header};
+use reqsign_core::time::now;
use reqsign_core::{Context, ProvideCredential};
use rsa::pkcs8::DecodePrivateKey;
use rsa::RsaPrivateKey;
use serde::{Deserialize, Serialize};
use sha1::{Digest, Sha1};
-use crate::credential::Credential;
-
/// Generate a unique JWT ID using timestamp and a pseudo-random component
fn generate_jti(now: u64) -> String {
// Use timestamp in nanoseconds + a hash of the timestamp for uniqueness
- let nano_time = std::time::SystemTime::now()
- .duration_since(std::time::UNIX_EPOCH)
+ let nano_time = SystemTime::now()
+ .duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
@@ -315,13 +315,8 @@ impl ProvideCredential for
ClientCertificateCredentialProvider {
.await?;
// Calculate expiration time
- let expires_on = SystemTime::now()
- .checked_add(Duration::from_secs(token_response.expires_in))
- .and_then(|t| {
- t.duration_since(UNIX_EPOCH)
- .ok()
- .map(|d| chrono::DateTime::from_timestamp(d.as_secs() as
i64, 0).unwrap())
- });
+ let expires_in = Duration::from_secs(token_response.expires_in);
+ let expires_on = now().checked_add(expires_in).ok();
Ok(Some(Credential::with_bearer_token(
&token_response.access_token,
diff --git a/services/azure-storage/src/provide_credential/client_secret.rs
b/services/azure-storage/src/provide_credential/client_secret.rs
index 7294f7c..bde45cd 100644
--- a/services/azure-storage/src/provide_credential/client_secret.rs
+++ b/services/azure-storage/src/provide_credential/client_secret.rs
@@ -91,8 +91,7 @@ impl ProvideCredential for ClientSecretCredentialProvider {
match token {
Some(token_response) => {
let expires_on = reqsign_core::time::now()
- + chrono::TimeDelta::try_seconds(token_response.expires_in
as i64)
- .unwrap_or_else(||
chrono::TimeDelta::try_minutes(10).expect("in bounds"));
+ +
jiff::SignedDuration::from_secs(token_response.expires_in as i64);
Ok(Some(Credential::with_bearer_token(
&token_response.access_token,
diff --git a/services/azure-storage/src/provide_credential/imds.rs
b/services/azure-storage/src/provide_credential/imds.rs
index a09a41f..963b34f 100644
--- a/services/azure-storage/src/provide_credential/imds.rs
+++ b/services/azure-storage/src/provide_credential/imds.rs
@@ -17,6 +17,7 @@
use crate::Credential;
use async_trait::async_trait;
+use reqsign_core::time::Timestamp;
use reqsign_core::{Context, ProvideCredential, Result};
/// Load credential from Azure Instance Metadata Service (IMDS).
@@ -51,15 +52,16 @@ impl ProvideCredential for ImdsCredentialProvider {
let token = get_access_token("https://storage.azure.com/", ctx).await?;
let expires_on = if token.expires_on.is_empty() {
- reqsign_core::time::now() +
chrono::TimeDelta::try_minutes(10).expect("in bounds")
+ reqsign_core::time::now() + jiff::SignedDuration::from_mins(10)
} else {
// Azure IMDS returns expires_on as Unix timestamp (seconds since
epoch)
let timestamp = token.expires_on.parse::<i64>().map_err(|e| {
reqsign_core::Error::unexpected("failed to parse expires_on
timestamp")
.with_source(e)
})?;
- chrono::DateTime::from_timestamp(timestamp, 0)
- .ok_or_else(|| reqsign_core::Error::unexpected("invalid
expires_on timestamp"))?
+ Timestamp::from_second(timestamp).map_err(|e| {
+ reqsign_core::Error::unexpected(format!("invalid expires_on
timestamp: {e}"))
+ })?
};
Ok(Some(Credential::with_bearer_token(
diff --git a/services/azure-storage/src/provide_credential/workload_identity.rs
b/services/azure-storage/src/provide_credential/workload_identity.rs
index 17d0328..500186a 100644
--- a/services/azure-storage/src/provide_credential/workload_identity.rs
+++ b/services/azure-storage/src/provide_credential/workload_identity.rs
@@ -91,10 +91,7 @@ impl ProvideCredential for
WorkloadIdentityCredentialProvider {
.with_source(e)
})?
}
- None => {
- reqsign_core::time::now()
- + chrono::TimeDelta::try_minutes(10).expect("in
bounds")
- }
+ None => reqsign_core::time::now() +
jiff::SignedDuration::from_mins(10),
};
Ok(Some(Credential::with_bearer_token(
diff --git a/services/azure-storage/src/sign_request.rs
b/services/azure-storage/src/sign_request.rs
index 43eb071..c528a07 100644
--- a/services/azure-storage/src/sign_request.rs
+++ b/services/azure-storage/src/sign_request.rs
@@ -23,7 +23,7 @@ use http::{header, HeaderValue};
use log::debug;
use percent_encoding::percent_encode;
use reqsign_core::hash::{base64_decode, base64_hmac_sha256};
-use reqsign_core::time::{format_http_date, now, DateTime};
+use reqsign_core::time::{format_http_date, now, Timestamp};
use reqsign_core::{Context, Result, SignRequest, SigningMethod,
SigningRequest};
use std::fmt::Write;
use std::time::Duration;
@@ -33,7 +33,7 @@ use std::time::Duration;
/// - [Authorize with Shared
Key](https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key)
#[derive(Debug)]
pub struct RequestSigner {
- time: Option<DateTime>,
+ time: Option<Timestamp>,
}
impl RequestSigner {
@@ -49,7 +49,7 @@ impl RequestSigner {
/// We should always take current time to sign requests.
/// Only use this function for testing.
#[cfg(test)]
- pub fn with_time(mut self, time: DateTime) -> Self {
+ pub fn with_time(mut self, time: Timestamp) -> Self {
self.time = Some(time);
self
}
@@ -134,7 +134,7 @@ impl SignRequest for RequestSigner {
account_name.clone(),
account_key.clone(),
now()
- + chrono::TimeDelta::from_std(d).map_err(|e| {
+ +
jiff::SignedDuration::try_from(d).map_err(|e| {
reqsign_core::Error::unexpected("failed to
convert duration")
.with_source(e)
})?,
@@ -214,7 +214,7 @@ impl SignRequest for RequestSigner {
fn string_to_sign(
ctx: &mut SigningRequest,
account_name: &str,
- now_time: DateTime,
+ now_time: Timestamp,
) -> Result<String> {
let mut s = String::with_capacity(128);
@@ -374,7 +374,7 @@ fn string_to_sign(
/// ## Reference
///
/// - [Constructing the canonicalized headers
string](https://docs.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key#constructing-the-canonicalized-headers-string)
-fn canonicalize_header(ctx: &mut SigningRequest, now_time: DateTime) ->
Result<String> {
+fn canonicalize_header(ctx: &mut SigningRequest, now_time: Timestamp) ->
Result<String> {
ctx.headers.insert(
X_MS_DATE,
format_http_date(now_time).parse().map_err(|e| {
@@ -455,7 +455,7 @@ mod tests {
.with_env(OsEnv);
let cred = Credential::with_bearer_token(
"token",
- Some(now() + chrono::TimeDelta::try_hours(1).unwrap()),
+ Some(now() + jiff::SignedDuration::from_hours(1)),
);
let builder = RequestSigner::new();
diff --git a/services/google/Cargo.toml b/services/google/Cargo.toml
index cfb86d2..58d3864 100644
--- a/services/google/Cargo.toml
+++ b/services/google/Cargo.toml
@@ -28,8 +28,8 @@ repository.workspace = true
[dependencies]
async-trait.workspace = true
-chrono.workspace = true
http.workspace = true
+jiff.workspace = true
jsonwebtoken = "9.2"
log.workspace = true
percent-encoding.workspace = true
diff --git a/services/google/src/credential.rs
b/services/google/src/credential.rs
index 7167c13..ace9f94 100644
--- a/services/google/src/credential.rs
+++ b/services/google/src/credential.rs
@@ -16,7 +16,7 @@
// under the License.
use reqsign_core::{
- time::now, time::DateTime, utils::Redact, Result, SigningCredential as
KeyTrait,
+ time::now, time::Timestamp, utils::Redact, Result, SigningCredential as
KeyTrait,
};
use std::fmt::{self, Debug};
@@ -183,7 +183,7 @@ pub struct Token {
/// The access token.
pub access_token: String,
/// The expiration time of the token.
- pub expires_at: Option<DateTime>,
+ pub expires_at: Option<Timestamp>,
}
impl Debug for Token {
@@ -204,7 +204,7 @@ impl KeyTrait for Token {
match self.expires_at {
Some(expires_at) => {
// Consider token invalid if it expires within 2 minutes
- let buffer = chrono::TimeDelta::try_seconds(2 * 60).expect("in
bounds");
+ let buffer = jiff::SignedDuration::from_mins(2);
now() < expires_at - buffer
}
None => true, // No expiration means always valid
@@ -337,15 +337,15 @@ mod tests {
assert!(token.is_valid());
// Token with future expiration
- token.expires_at = Some(now() +
chrono::TimeDelta::try_hours(1).unwrap());
+ token.expires_at = Some(now() + jiff::SignedDuration::from_hours(1));
assert!(token.is_valid());
// Token that expires within 2 minutes
- token.expires_at = Some(now() +
chrono::TimeDelta::try_seconds(30).unwrap());
+ token.expires_at = Some(now() + jiff::SignedDuration::from_secs(30));
assert!(!token.is_valid());
// Expired token
- token.expires_at = Some(now() -
chrono::TimeDelta::try_hours(1).unwrap());
+ token.expires_at = Some(now() - jiff::SignedDuration::from_hours(1));
assert!(!token.is_valid());
// Empty access token
@@ -417,7 +417,7 @@ mod tests {
// Valid token only
let cred = Credential::with_token(Token {
access_token: "test".to_string(),
- expires_at: Some(now() + chrono::TimeDelta::try_hours(1).unwrap()),
+ expires_at: Some(now() + jiff::SignedDuration::from_hours(1)),
});
assert!(cred.is_valid());
assert!(!cred.has_service_account());
@@ -439,7 +439,7 @@ mod tests {
});
cred.token = Some(Token {
access_token: "test".to_string(),
- expires_at: Some(now() + chrono::TimeDelta::try_hours(1).unwrap()),
+ expires_at: Some(now() + jiff::SignedDuration::from_hours(1)),
});
assert!(cred.is_valid());
assert!(cred.has_service_account());
@@ -452,7 +452,7 @@ mod tests {
});
cred.token = Some(Token {
access_token: "test".to_string(),
- expires_at: Some(now() - chrono::TimeDelta::try_hours(1).unwrap()),
+ expires_at: Some(now() - jiff::SignedDuration::from_hours(1)),
});
assert!(cred.is_valid()); // Still valid because of service account
assert!(!cred.has_valid_token());
diff --git a/services/google/src/provide_credential/authorized_user.rs
b/services/google/src/provide_credential/authorized_user.rs
index 88b130a..e510e5e 100644
--- a/services/google/src/provide_credential/authorized_user.rs
+++ b/services/google/src/provide_credential/authorized_user.rs
@@ -94,9 +94,9 @@ impl ProvideCredential for AuthorizedUserCredentialProvider {
reqsign_core::Error::unexpected("failed to parse token
response").with_source(e)
})?;
- let expires_at = token_resp.expires_in.map(|expires_in| {
- now() + chrono::TimeDelta::try_seconds(expires_in as
i64).expect("in bounds")
- });
+ let expires_at = token_resp
+ .expires_in
+ .map(|expires_in| now() +
jiff::SignedDuration::from_secs(expires_in as i64));
let token = Token {
access_token: token_resp.access_token,
diff --git a/services/google/src/provide_credential/external_account.rs
b/services/google/src/provide_credential/external_account.rs
index 150a920..4cd7c2a 100644
--- a/services/google/src/provide_credential/external_account.rs
+++ b/services/google/src/provide_credential/external_account.rs
@@ -21,9 +21,9 @@ use http::header::{ACCEPT, CONTENT_TYPE};
use log::{debug, error};
use serde::{Deserialize, Serialize};
-use reqsign_core::{time::now, Context, ProvideCredential, Result};
-
use crate::credential::{external_account, Credential, ExternalAccount, Token};
+use reqsign_core::time::parse_rfc3339;
+use reqsign_core::{time::now, Context, ProvideCredential, Result};
/// The maximum impersonated token lifetime allowed, 1 hour.
const MAX_LIFETIME: Duration = Duration::from_secs(3600);
@@ -176,9 +176,9 @@ impl ExternalAccountCredentialProvider {
reqsign_core::Error::unexpected("failed to parse STS
response").with_source(e)
})?;
- let expires_at = token_resp.expires_in.map(|expires_in| {
- now() + chrono::TimeDelta::try_seconds(expires_in as
i64).expect("in bounds")
- });
+ let expires_at = token_resp
+ .expires_in
+ .map(|expires_in| now() +
jiff::SignedDuration::from_secs(expires_in as i64));
Ok(Token {
access_token: token_resp.access_token,
@@ -247,9 +247,7 @@ impl ExternalAccountCredentialProvider {
})?;
// Parse expire time from RFC3339 format
- let expires_at =
chrono::DateTime::parse_from_rfc3339(&token_resp.expire_time)
- .ok()
- .map(|dt| dt.with_timezone(&chrono::Utc));
+ let expires_at = parse_rfc3339(&token_resp.expire_time).ok();
Ok(Some(Token {
access_token: token_resp.access_token,
diff --git
a/services/google/src/provide_credential/impersonated_service_account.rs
b/services/google/src/provide_credential/impersonated_service_account.rs
index a1e53b6..5760488 100644
--- a/services/google/src/provide_credential/impersonated_service_account.rs
+++ b/services/google/src/provide_credential/impersonated_service_account.rs
@@ -21,9 +21,9 @@ use http::header::CONTENT_TYPE;
use log::{debug, error};
use serde::{Deserialize, Serialize};
-use reqsign_core::{time::now, Context, ProvideCredential, Result};
-
use crate::credential::{Credential, ImpersonatedServiceAccount, Token};
+use reqsign_core::time::parse_rfc3339;
+use reqsign_core::{time::now, Context, ProvideCredential, Result};
/// The maximum impersonated token lifetime allowed, 1 hour.
const MAX_LIFETIME: Duration = Duration::from_secs(3600);
@@ -135,9 +135,9 @@ impl ImpersonatedServiceAccountCredentialProvider {
reqsign_core::Error::unexpected("failed to parse token
response").with_source(e)
})?;
- let expires_at = token_resp.expires_in.map(|expires_in| {
- now() + chrono::TimeDelta::try_seconds(expires_in as
i64).expect("in bounds")
- });
+ let expires_at = token_resp
+ .expires_in
+ .map(|expires_in| now() +
jiff::SignedDuration::from_secs(expires_in as i64));
Ok(Token {
access_token: token_resp.access_token,
@@ -200,9 +200,7 @@ impl ImpersonatedServiceAccountCredentialProvider {
})?;
// Parse expire time from RFC3339 format
- let expires_at =
chrono::DateTime::parse_from_rfc3339(&token_resp.expire_time)
- .ok()
- .map(|dt| dt.with_timezone(&chrono::Utc));
+ let expires_at = parse_rfc3339(&token_resp.expire_time).ok();
Ok(Token {
access_token: token_resp.access_token,
diff --git a/services/google/src/provide_credential/vm_metadata.rs
b/services/google/src/provide_credential/vm_metadata.rs
index bd09ccd..48b2421 100644
--- a/services/google/src/provide_credential/vm_metadata.rs
+++ b/services/google/src/provide_credential/vm_metadata.rs
@@ -106,8 +106,7 @@ impl ProvideCredential for VmMetadataCredentialProvider {
.with_source(e)
})?;
- let expires_at = now()
- + chrono::TimeDelta::try_seconds(token_resp.expires_in as
i64).expect("in bounds");
+ let expires_at = now() +
jiff::SignedDuration::from_secs(token_resp.expires_in as i64);
let token = Token {
access_token: token_resp.access_token,
diff --git a/services/google/src/sign_request.rs
b/services/google/src/sign_request.rs
index fa4e2f9..b2d58a4 100644
--- a/services/google/src/sign_request.rs
+++ b/services/google/src/sign_request.rs
@@ -47,7 +47,7 @@ struct Claims {
impl Claims {
fn new(client_email: &str, scope: &str) -> Self {
- let current = now().timestamp() as u64;
+ let current = now().as_second() as u64;
Claims {
iss: client_email.to_string(),
@@ -155,9 +155,9 @@ impl RequestSigner {
reqsign_core::Error::unexpected("failed to parse token
response").with_source(e)
})?;
- let expires_at = token_resp.expires_in.map(|expires_in| {
- now() + chrono::TimeDelta::try_seconds(expires_in as
i64).expect("in bounds")
- });
+ let expires_at = token_resp
+ .expires_in
+ .map(|expires_in| now() +
jiff::SignedDuration::from_secs(expires_in as i64));
Ok(Token {
access_token: token_resp.access_token,
@@ -373,7 +373,7 @@ fn canonicalize_query(
req: &mut SigningRequest,
method: SigningMethod,
cred: &ServiceAccount,
- now: DateTime,
+ now: Timestamp,
service: &str,
region: &str,
) -> Result<()> {
diff --git a/services/huaweicloud-obs/Cargo.toml
b/services/huaweicloud-obs/Cargo.toml
index 6842eb2..3f0c464 100644
--- a/services/huaweicloud-obs/Cargo.toml
+++ b/services/huaweicloud-obs/Cargo.toml
@@ -29,8 +29,8 @@ repository.workspace = true
[dependencies]
anyhow.workspace = true
async-trait.workspace = true
-chrono.workspace = true
http.workspace = true
+jiff.workspace = true
log.workspace = true
once_cell.workspace = true
percent-encoding.workspace = true
diff --git a/services/huaweicloud-obs/src/sign_request.rs
b/services/huaweicloud-obs/src/sign_request.rs
index 6176956..893c0a1 100644
--- a/services/huaweicloud-obs/src/sign_request.rs
+++ b/services/huaweicloud-obs/src/sign_request.rs
@@ -34,7 +34,7 @@ use super::credential::Credential;
use reqsign_core::hash::base64_hmac_sha1;
use reqsign_core::time::format_http_date;
use reqsign_core::time::now;
-use reqsign_core::time::DateTime;
+use reqsign_core::time::Timestamp;
use reqsign_core::{SignRequest, SigningMethod, SigningRequest};
/// RequestSigner that implement Huawei Cloud Object Storage Service
Authorization.
@@ -43,7 +43,7 @@ use reqsign_core::{SignRequest, SigningMethod,
SigningRequest};
#[derive(Debug)]
pub struct RequestSigner {
bucket: String,
- time: Option<DateTime>,
+ time: Option<Timestamp>,
}
impl RequestSigner {
@@ -62,7 +62,7 @@ impl RequestSigner {
/// We should always take current time to sign requests.
/// Only use this function for testing.
#[cfg(test)]
- pub fn with_time(mut self, time: DateTime) -> Self {
+ pub fn with_time(mut self, time: Timestamp) -> Self {
self.time = Some(time);
self
}
@@ -110,8 +110,8 @@ impl SignRequest for RequestSigner {
ctx.query_push("AccessKeyId", &k.access_key_id);
ctx.query_push(
"Expires",
- (now + chrono::TimeDelta::from_std(expire).unwrap())
- .timestamp()
+ (now + jiff::SignedDuration::try_from(expire).unwrap())
+ .as_second()
.to_string(),
);
ctx.query_push(
@@ -144,7 +144,7 @@ impl SignRequest for RequestSigner {
fn string_to_sign(
ctx: &mut SigningRequest,
cred: &Credential,
- now: DateTime,
+ now: Timestamp,
method: SigningMethod,
bucket: &str,
) -> Result<String> {
@@ -169,7 +169,7 @@ fn string_to_sign(
writeln!(
&mut s,
"{}",
- (now +
chrono::TimeDelta::from_std(expires).unwrap()).timestamp()
+ (now +
jiff::SignedDuration::try_from(expires).unwrap()).as_second()
)?;
}
}
@@ -303,9 +303,9 @@ static SUBRESOURCES: Lazy<HashSet<&'static str>> =
Lazy::new(|| {
mod tests {
use std::str::FromStr;
- use chrono::Utc;
use http::header::HeaderName;
use http::Uri;
+ use reqsign_core::time::parse_rfc2822;
use reqsign_core::Result;
use reqsign_core::{Context, OsEnv, Signer};
use reqsign_file_read_tokio::TokioFileRead;
@@ -317,11 +317,8 @@ mod tests {
#[tokio::test]
async fn test_sign() -> Result<()> {
let loader = StaticCredentialProvider::new("access_key", "123456");
- let builder = RequestSigner::new("bucket").with_time(
- chrono::DateTime::parse_from_rfc2822("Mon, 15 Aug 2022 16:50:12
GMT")
- .unwrap()
- .with_timezone(&Utc),
- );
+ let builder =
+ RequestSigner::new("bucket").with_time(parse_rfc2822("Mon, 15 Aug
2022 16:50:12 GMT")?);
let ctx = Context::new()
.with_file_read(TokioFileRead)
@@ -359,11 +356,8 @@ mod tests {
#[tokio::test]
async fn test_sign_with_subresource() -> Result<()> {
let loader = StaticCredentialProvider::new("access_key", "123456");
- let builder = RequestSigner::new("bucket").with_time(
- chrono::DateTime::parse_from_rfc2822("Mon, 15 Aug 2022 16:50:12
GMT")
- .unwrap()
- .with_timezone(&Utc),
- );
+ let builder =
+ RequestSigner::new("bucket").with_time(parse_rfc2822("Mon, 15 Aug
2022 16:50:12 GMT")?);
let ctx = Context::new()
.with_file_read(TokioFileRead)
@@ -403,11 +397,8 @@ mod tests {
#[tokio::test]
async fn test_sign_list_objects() -> Result<()> {
let loader = StaticCredentialProvider::new("access_key", "123456");
- let builder = RequestSigner::new("bucket").with_time(
- chrono::DateTime::parse_from_rfc2822("Mon, 15 Aug 2022 16:50:12
GMT")
- .unwrap()
- .with_timezone(&Utc),
- );
+ let builder =
+ RequestSigner::new("bucket").with_time(parse_rfc2822("Mon, 15 Aug
2022 16:50:12 GMT")?);
let ctx = Context::new()
.with_file_read(TokioFileRead)
diff --git a/services/oracle/Cargo.toml b/services/oracle/Cargo.toml
index 651bb28..8531cea 100644
--- a/services/oracle/Cargo.toml
+++ b/services/oracle/Cargo.toml
@@ -30,14 +30,12 @@ repository.workspace = true
anyhow.workspace = true
async-trait.workspace = true
base64.workspace = true
-chrono.workspace = true
http.workspace = true
+jiff.workspace = true
log.workspace = true
reqsign-core.workspace = true
rsa.workspace = true
rust-ini.workspace = true
-serde.workspace = true
-
[dev-dependencies]
env_logger = "0.11"
diff --git a/services/oracle/src/credential.rs
b/services/oracle/src/credential.rs
index 64d95c6..eb9731c 100644
--- a/services/oracle/src/credential.rs
+++ b/services/oracle/src/credential.rs
@@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
-use reqsign_core::time::{now, DateTime};
+use reqsign_core::time::{now, Timestamp};
use reqsign_core::utils::Redact;
use reqsign_core::SigningCredential;
use std::fmt::{Debug, Formatter};
@@ -32,7 +32,7 @@ pub struct Credential {
/// Fingerprint of the API Key.
pub fingerprint: String,
/// Expiration time for this credential.
- pub expires_in: Option<DateTime>,
+ pub expires_in: Option<Timestamp>,
}
impl Debug for Credential {
@@ -59,7 +59,7 @@ impl SigningCredential for Credential {
// Take 120s as buffer to avoid edge cases.
if let Some(valid) = self
.expires_in
- .map(|v| v > now() + chrono::TimeDelta::try_minutes(2).expect("in
bounds"))
+ .map(|v| v > now() + jiff::SignedDuration::from_mins(2))
{
return valid;
}
diff --git a/services/oracle/src/provide_credential/config.rs
b/services/oracle/src/provide_credential/config.rs
index cec03da..71d8c5f 100644
--- a/services/oracle/src/provide_credential/config.rs
+++ b/services/oracle/src/provide_credential/config.rs
@@ -63,8 +63,7 @@ impl ProvideCredential for ConfigCredentialProvider {
fingerprint: fingerprint.clone(),
// Set expires_in to 10 minutes to enforce re-read
expires_in: Some(
- reqsign_core::time::now()
- + chrono::TimeDelta::try_minutes(10).expect("in
bounds"),
+ reqsign_core::time::now() +
jiff::SignedDuration::from_mins(10),
),
}))
}
diff --git a/services/oracle/src/provide_credential/config_file.rs
b/services/oracle/src/provide_credential/config_file.rs
index 2ea4f2f..8b7ebea 100644
--- a/services/oracle/src/provide_credential/config_file.rs
+++ b/services/oracle/src/provide_credential/config_file.rs
@@ -109,8 +109,7 @@ impl ProvideCredential for ConfigFileCredentialProvider {
key_file: expanded_key_file,
fingerprint: fingerprint.to_string(),
expires_in: Some(
- reqsign_core::time::now()
- + chrono::TimeDelta::try_minutes(10).expect("in
bounds"),
+ reqsign_core::time::now() +
jiff::SignedDuration::from_mins(10),
),
}))
}
diff --git a/services/oracle/src/provide_credential/env.rs
b/services/oracle/src/provide_credential/env.rs
index f92dbd8..582828d 100644
--- a/services/oracle/src/provide_credential/env.rs
+++ b/services/oracle/src/provide_credential/env.rs
@@ -65,8 +65,7 @@ impl ProvideCredential for EnvCredentialProvider {
key_file: expanded_key_file,
fingerprint: fingerprint.clone(),
expires_in: Some(
- reqsign_core::time::now()
- + chrono::TimeDelta::try_minutes(10).expect("in
bounds"),
+ reqsign_core::time::now() +
jiff::SignedDuration::from_mins(10),
),
}))
}
diff --git a/services/tencent-cos/Cargo.toml b/services/tencent-cos/Cargo.toml
index 298fd38..f513dcc 100644
--- a/services/tencent-cos/Cargo.toml
+++ b/services/tencent-cos/Cargo.toml
@@ -30,8 +30,8 @@ repository.workspace = true
[dependencies]
anyhow.workspace = true
async-trait.workspace = true
-chrono.workspace = true
http.workspace = true
+jiff.workspace = true
log.workspace = true
percent-encoding.workspace = true
reqsign-core.workspace = true
diff --git a/services/tencent-cos/src/credential.rs
b/services/tencent-cos/src/credential.rs
index cb5a63a..cb317b0 100644
--- a/services/tencent-cos/src/credential.rs
+++ b/services/tencent-cos/src/credential.rs
@@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
-use reqsign_core::time::{now, DateTime};
+use reqsign_core::time::{now, Timestamp};
use reqsign_core::utils::Redact;
use reqsign_core::SigningCredential;
use std::fmt::{Debug, Formatter};
@@ -30,7 +30,7 @@ pub struct Credential {
/// Security token for temporary credentials
pub security_token: Option<String>,
/// Expiration time for this credential
- pub expires_in: Option<DateTime>,
+ pub expires_in: Option<Timestamp>,
}
impl Debug for Credential {
@@ -52,7 +52,7 @@ impl SigningCredential for Credential {
// Take 120s as buffer to avoid edge cases.
if let Some(valid) = self
.expires_in
- .map(|v| v > now() + chrono::TimeDelta::try_minutes(2).expect("in
bounds"))
+ .map(|v| v > now() + jiff::SignedDuration::from_mins(2))
{
return valid;
}
diff --git
a/services/tencent-cos/src/provide_credential/assume_role_with_web_identity.rs
b/services/tencent-cos/src/provide_credential/assume_role_with_web_identity.rs
index c0ee0dc..fb00487 100644
---
a/services/tencent-cos/src/provide_credential/assume_role_with_web_identity.rs
+++
b/services/tencent-cos/src/provide_credential/assume_role_with_web_identity.rs
@@ -106,7 +106,7 @@ impl ProvideCredential for
AssumeRoleWithWebIdentityCredentialProvider {
.header(CONTENT_LENGTH, bs.len())
.header("X-TC-Action", "AssumeRoleWithWebIdentity")
.header("X-TC-Region", ®ion)
- .header("X-TC-Timestamp", now().timestamp())
+ .header("X-TC-Timestamp", now().as_second())
.header("X-TC-Version", "2018-08-13")
.body(bs.into())?;
diff --git a/services/tencent-cos/src/sign_request.rs
b/services/tencent-cos/src/sign_request.rs
index 89c9034..779a1c7 100644
--- a/services/tencent-cos/src/sign_request.rs
+++ b/services/tencent-cos/src/sign_request.rs
@@ -23,7 +23,7 @@ use http::request::Parts;
use log::debug;
use percent_encoding::{percent_decode_str, utf8_percent_encode};
use reqsign_core::hash::{hex_hmac_sha1, hex_sha1};
-use reqsign_core::time::{format_http_date, now, DateTime};
+use reqsign_core::time::{format_http_date, now, Timestamp};
use reqsign_core::{Context, Result, SignRequest, SigningRequest};
use std::time::Duration;
@@ -32,7 +32,7 @@ use std::time::Duration;
/// - [Tencent COS
Signature](https://cloud.tencent.com/document/product/436/7778)
#[derive(Debug, Default)]
pub struct RequestSigner {
- time: Option<DateTime>,
+ time: Option<Timestamp>,
}
impl RequestSigner {
@@ -48,7 +48,7 @@ impl RequestSigner {
/// We should always take current time to sign requests.
/// Only use this function for testing.
#[cfg(test)]
- pub fn with_time(mut self, time: DateTime) -> Self {
+ pub fn with_time(mut self, time: Timestamp) -> Self {
self.time = Some(time);
self
}
@@ -116,13 +116,13 @@ impl SignRequest for RequestSigner {
fn build_signature(
ctx: &mut SigningRequest,
cred: &Credential,
- now: DateTime,
+ now: Timestamp,
expires: Duration,
) -> String {
let key_time = format!(
"{};{}",
- now.timestamp(),
- (now + chrono::TimeDelta::from_std(expires).unwrap()).timestamp()
+ now.as_second(),
+ (now + jiff::SignedDuration::try_from(expires).unwrap()).as_second()
);
let sign_key = hex_hmac_sha1(cred.secret_key.as_bytes(),
key_time.as_bytes());
diff --git a/services/tencent-cos/tests/credential_chain.rs
b/services/tencent-cos/tests/credential_chain.rs
index e381ae2..6645714 100644
--- a/services/tencent-cos/tests/credential_chain.rs
+++ b/services/tencent-cos/tests/credential_chain.rs
@@ -199,9 +199,7 @@ impl ProvideCredential for SecurityTokenProvider {
secret_id: "temp_id".to_string(),
secret_key: "temp_key".to_string(),
security_token: Some("security_token".to_string()),
- expires_in: Some(
- reqsign_core::time::now() +
chrono::TimeDelta::try_hours(1).expect("in bounds"),
- ),
+ expires_in: Some(reqsign_core::time::now() +
jiff::SignedDuration::from_hours(1)),
}))
}
}