Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 20 additions & 12 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ permissions:
contents: read
jobs:
default-features:
name: Default Features (batteries_included + v4)
name: Default Features (batteries_included + v4 + (${{ matrix.backend }}))
runs-on: ubuntu-latest
strategy:
matrix:
backend: [time, chrono]
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
Expand All @@ -28,13 +31,10 @@ jobs:
components: clippy
- name: Install nextest
uses: taiki-e/install-action@nextest
- name: Clippy (default features)
uses: actions-rs/cargo@v1
with:
command: clippy
args: -- -D warnings
- name: Test (default features)
run: cargo nextest run
- name: Clippy (default features + ${{ matrix.backend }})
run: cargo clippy --features ${{ matrix.backend }} -- -D warnings
- name: Test (default features + ${{ matrix.backend }})
run: cargo nextest run --features ${{ matrix.backend }}
test:
name: Test Suite
runs-on: ubuntu-latest
Expand All @@ -58,8 +58,10 @@ jobs:
override: true
- name: Install nextest
uses: taiki-e/install-action@nextest
- name: Run tests
run: cargo nextest run --no-default-features --features ${{ matrix.feature }}
- name: Run tests (time)
run: cargo nextest run --no-default-features --features ${{ matrix.feature }},time
- name: Run tests (chrono)
run: cargo nextest run --no-default-features --features ${{ matrix.feature }},chrono
clippy:
name: Clippy
runs-on: ubuntu-latest
Expand All @@ -82,10 +84,16 @@ jobs:
toolchain: stable
override: true
components: clippy
- uses: actions-rs/cargo@v1
- name: Clippy (time)
uses: actions-rs/cargo@v1
with:
command: clippy
args: --no-default-features --features ${{ matrix.feature }},time -- -D warnings
- name: Clippy (chrono)
uses: actions-rs/cargo@v1
with:
command: clippy
args: --no-default-features --features ${{ matrix.feature }} -- -D warnings
args: --no-default-features --features ${{ matrix.feature }},chrono -- -D warnings
audit:
name: Security Audit
runs-on: ubuntu-latest
Expand Down
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ v4_public = ["v4", "public", "core", "ed25519-dalek", "ring/std"]
core = []
generic = ["core", "serde", "erased-serde", "serde_json"]
batteries_included = ["generic"]
# Time backend features (mutually exclusive)
time = ["dep:time"]
chrono = ["dep:chrono"]
default = ["batteries_included", "v4_local", "v4_public"]
paserk = ["dep:paserk"]

Expand Down Expand Up @@ -78,7 +81,8 @@ hmac = { version = "0.12", optional = true }
sha2 = { version = "0.10", optional = true }
subtle = "2.6"
zeroize = { version = "1.8", features = ["zeroize_derive"] }
time = { version = "0.3.47", features = ["parsing", "formatting"] }
time = { version = "0.3.47", features = ["parsing", "formatting"], optional = true }
chrono = { version = "0.4", features = ["std"], optional = true }
rand_core = "0.9"
digest = "0.10"
paserk = { version = "0.4.0", optional = true }
Expand Down
6 changes: 6 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,14 @@ pub enum Error {

// ==================== Time Format Errors ====================
/// Error formatting time values
#[cfg(feature = "time")]
#[error("time format error: {0}")]
TimeFormat(#[from] time::error::Format),

/// Error parsing time values
#[cfg(feature = "chrono")]
#[error("chrono parse error: {0}")]
ChronoParse(#[from] chrono::ParseError),
}

/// A specialized Result type for rusty_paseto operations.
Expand Down
19 changes: 15 additions & 4 deletions src/generic/claims/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,26 @@ mod unit_tests {
//TODO: need more comprehensive tests than these to flesh out the additionl error types
use super::*;
use anyhow::Result;
//use chrono::prelude::*;
use std::convert::TryFrom;

#[cfg(feature = "time")]
use time::format_description::well_known::Rfc3339;

#[cfg(feature = "time")]
fn now_rfc3339() -> String {
time::OffsetDateTime::now_utc().format(&Rfc3339).expect("format failed")
}

#[cfg(feature = "chrono")]
fn now_rfc3339() -> String {
chrono::Utc::now().to_rfc3339()
}

#[test]
fn test_expiration_claim() -> Result<()> {
// setup
// a good time format
let now = time::OffsetDateTime::now_utc().format(&Rfc3339)?;
let now = now_rfc3339();

assert!(ExpirationClaim::try_from("hello").is_err());
let claim = ExpirationClaim::try_from(now);
Expand All @@ -56,7 +67,7 @@ mod unit_tests {
fn test_not_before_claim() -> Result<()> {
// setup
// a good time format
let now = time::OffsetDateTime::now_utc().format(&Rfc3339)?;
let now = now_rfc3339();

assert!(NotBeforeClaim::try_from("hello").is_err());
let claim = NotBeforeClaim::try_from(now);
Expand All @@ -72,7 +83,7 @@ mod unit_tests {
fn test_issued_at_claim() -> Result<()> {
// setup
// a good time format
let now = time::OffsetDateTime::now_utc().format(&Rfc3339)?;
let now = now_rfc3339();

assert!(IssuedAtClaim::try_from("hello").is_err());
let claim = IssuedAtClaim::try_from(now);
Expand Down
40 changes: 30 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
//!
//!
//! # Usage
//! `rusty_paseto` is meant to be flexible and configurable for your specific use case. Whether you want to get started quickly with sensible defaults, create your own version of `rusty_paseto` in order to customize your own defaults and functionality or just want to use the core PASETO crypto features, the crate is heavily feature gated to allow for your needs.
//! `rusty_paseto` is meant to be flexible and configurable for your specific use case. Whether you want to get started quickly with sensible defaults, create your own version of `rusty_paseto` in order to customize your own defaults and functionality or just want to use the core PASETO crypto features, the crate is heavily feature gated to allow for your needs.

//! ## Architecture

Expand Down Expand Up @@ -164,7 +164,7 @@
//! ```
//! ### core
//!
//! The core architectural layer is the most basic PASETO implementation as it accepts a Payload, optional Footer and (if v3 or v4) an optional Implicit Assertion along with the appropriate key to encrypt/sign and decrypt/verify basic strings.
//! The core architectural layer is the most basic PASETO implementation as it accepts a Payload, optional Footer and (if v3 or v4) an optional Implicit Assertion along with the appropriate key to encrypt/sign and decrypt/verify basic strings.
//!
//! <img src="https://github.com/rrrodzilla/rusty_paseto/raw/main/assets/RustyPasetoCoreArchitecture.png" width="150" />
//!
Expand Down Expand Up @@ -386,9 +386,13 @@
//! // must include
//! use std::convert::TryFrom;
//! let key = PasetoSymmetricKey::<V4, Local>::from(Key::from(b"wubbalubbadubdubwubbalubbadubdub"));
//! // real-world example using the time crate to expire 5 minutes from now
//! // real-world example: expire 5 minutes from now
//! # #[cfg(feature = "time")]
//! # use time::format_description::well_known::Rfc3339;
//! # #[cfg(feature = "time")]
//! # let in_5_minutes = (time::OffsetDateTime::now_utc() + time::Duration::minutes(5)).format(&Rfc3339)?;
//! # #[cfg(feature = "chrono")]
//! # let in_5_minutes = (chrono::Utc::now() + chrono::Duration::minutes(5)).to_rfc3339();
//!
//! let token = PasetoBuilder::<V4, Local>::default()
//! // note the TryFrom implmentation for ExpirationClaim
Expand All @@ -406,18 +410,22 @@
//!
//! A **1 hour** `ExpirationClaim` is set by default because the use case for non-expiring tokens in the world of security tokens is fairly limited.
//! Omitting an expiration claim or forgetting to require one when processing them
//! is almost certainly an oversight rather than a deliberate choice.
//! is almost certainly an oversight rather than a deliberate choice.
//!
//! When it is a deliberate choice, you have the opportunity to deliberately remove this claim from the Builder.
//! The method call required to do so ensures readers of the code understand the implicit risk.
//! ```rust
//! # #[cfg(feature = "default")]
//! # {
//! # use rusty_paseto::prelude::*;
//! # use time::format_description::well_known::Rfc3339;
//! # use std::convert::TryFrom;
//! # let in_5_minutes = (time::OffsetDateTime::now_utc() + time::Duration::minutes(5)).format(&Rfc3339)?;
//! # let key = PasetoSymmetricKey::<V4, Local>::from(Key::from(b"wubbalubbadubdubwubbalubbadubdub"));
//! # #[cfg(feature = "time")]
//! # use time::format_description::well_known::Rfc3339;
//! # #[cfg(feature = "time")]
//! # let in_5_minutes = (time::OffsetDateTime::now_utc() + time::Duration::minutes(5)).format(&Rfc3339)?;
//! # #[cfg(feature = "chrono")]
//! # let in_5_minutes = (chrono::Utc::now() + chrono::Duration::minutes(5)).to_rfc3339();
//! let token = PasetoBuilder::<V4, Local>::default()
//! .set_claim(ExpirationClaim::try_from(in_5_minutes)?)
//! // even if you set an expiration claim (as above) it will be ignored
Expand All @@ -435,15 +443,20 @@
//! # #[cfg(all(test,feature = "v4_local"))]
//! # {
//! # use rusty_paseto::prelude::*;
//! # use time::format_description::well_known::Rfc3339;
//! # // must include
//! # use std::convert::TryFrom;
//! # let key = PasetoSymmetricKey::<V4, Local>::from(Key::from(b"wubbalubbadubdubwubbalubbadubdub"));
//! # // real-world example using the time crate to expire 5 minutes from now
//! # #[cfg(feature = "time")]
//! # use time::format_description::well_known::Rfc3339;
//! # #[cfg(feature = "time")]
//! # let in_5_minutes = (time::OffsetDateTime::now_utc() + time::Duration::minutes(5)).format(&Rfc3339)?;
//! // real-world example using the time crate to prevent the token from being used before 2
//! // minutes from now
//! # #[cfg(feature = "chrono")]
//! # let in_5_minutes = (chrono::Utc::now() + chrono::Duration::minutes(5)).to_rfc3339();
//! // set not-before to 2 minutes in the future
//! # #[cfg(feature = "time")]
//! let in_2_minutes = (time::OffsetDateTime::now_utc() + time::Duration::minutes(2)).format(&Rfc3339)?;
//! # #[cfg(feature = "chrono")]
//! # let in_2_minutes = (chrono::Utc::now() + chrono::Duration::minutes(2)).to_rfc3339();
//!
//! let token = PasetoBuilder::<V4, Local>::default()
//! //json payload key: "exp"
Expand Down Expand Up @@ -671,6 +684,13 @@ compile_error!(
See: https://github.com/rrrodzilla/rusty_paseto/issues/48"
);

// Ensure exactly one time backend is selected when the prelude is used.
#[cfg(all(feature = "time", feature = "chrono"))]
compile_error!("features `time` and `chrono` are mutually exclusive. Enable exactly one time backend");

#[cfg(all(not(feature = "time"), not(feature = "chrono")))]
compile_error!("a time backend is required. Enable either the `time` feature or `chrono`");

//public interface
#[cfg(feature = "core")]
pub mod core;
Expand Down
5 changes: 5 additions & 0 deletions src/prelude/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,10 @@ pub enum GeneralPasetoError {
},
/// An error with the data format
#[error(transparent)]
#[cfg(feature = "time")]
RFC3339Date(#[from] time::error::Format),
/// An error parsing a date/time value with chrono
#[error(transparent)]
#[cfg(feature = "chrono")]
ChronoParse(#[from] chrono::ParseError),
}
Loading